Geddy is built on the same MVC principles that many popular frameworks are based on. Every Geddy app has its models, controllers, and views as well as config files and routes.
├── app
│ ├── controllers
│ │ ├── application.js
│ │ └── main.js
│ ├── helpers
│ ├── models
│ └── views
│ ├── layouts
│ │ └── application.html.ejs
│ └── main
│ └── index.html.ejs
├── config
├── development.js
├── environment.js
├── init.js
├── production.js
└── router.js
├── lib
├── log
├── node_modules
└── public
geddy.config
Geddy has built in configuration management. Global config options should go in your 'config/environments.js` file. Likewise, your production and development config options should go in their respective files
If you want to start up your app in a specific environment, use the -e option:
$ geddy -e production
geddy.log[level]
Geddy automatically logs requests to an access log, and you can log anything you'd like to stdout or a file. It supports 9 different log levels from debug to emergency.
access: outputs to the access log and stdoutdebug: debug level logginginfo: info level loggingnotice: notice level loggingwarning: warning level loggingerror: error level logging, prints to stdout and stderrcritical: critical level loggingalert: alert level loggingemergency: emergency level logginggeddy.log.debug('someting to debug')
// prints `something to debug` to the console
geddy.log.error('something went wrong')
// prints 'something went wrong' to stderr and the console
Geddy uses Barista as its router. Its API is very similar to rails routes. Both general-purpose resource-based routes and individually defined routes are possible.
router.match(path [, method])
defines the url to match to a controller action.
path [string]: the url to match to an actionmethod [string]: the http method to matchrouter.match('/').to('Main.index');
// will route any request to '/' to the Main controller's index action
router.match('/products/:id', 'GET').to('products.show')
// will route '/products/5' to Products.show()
// and set the id paramer to be 5
router.match('/profiles/:username', 'GET').to('users.show')
// will route '/products/dan' to Users.show()
// and set the username paramer to be dan
router.match('/products/:id(.:format)', 'GET').to('products.show')
// things enclosed in parentheses are optional
router.match(path).to(action)
defines the action to map the path to.
action [string]: a controller name plus an action name as a stringaction [object]: an object that defines a controller and action propertyrouter.match('/').to('Main.index');
// will route any request to '/' to the Main controller's index action
router.match('/').to({controller: 'Main', action: 'index'});
// will route any request to '/' to the Main controller's index action
router.get(path)
Equivalent to router.match(path, 'GET')
router.post(path)
Equivalent to router.match(path, 'POST')
router.put(path)
Equivalent to router.match(path, 'PUT')
router.del(path)
Equivalent to router.match(path, 'DELETE')
router.resource(controller)
generates standard resource routes for a controller name
controller [string]: the camelCased controller name that needs resourceful routesrouter.resource('products')
// is equivalent to:
router.get('/products(.:format)').to('products.index')
router.get('/products/add(.:format)').to('products.add')
router.get('/products/:id(.:format)').to('products.show')
router.get('/products/:id/edit(.:format)').to('products.edit')
router.post('/products(.:format)').to('products.create')
router.put('/products/:id(.:format)').to('products.update')
router.del('/products/:id(.:format)').to('products.destroy')
Controllers define the different actions that your users can interact with.
this.request
The raw http.ServerRequest object for this request/response cycle.
this.response
The raw http.ServerResponse object for this request/response cycle.
this.params
The parsed params for the request. params is also passed as an argument to the action, it was added as an instance field for convenience.
this.cookies
Cookies collection from the request
this.name
The name of the controller constructor function, in CamelCase with uppercase initial letter.
this.respondsWith
Content-type the controller can respond with.
this.respondsWith = ['txt','json','html'];
before(filter, [options])
Adds an action to be performed before a response is rendered.
filter [function] Action to add to the beforeFilter list. If the action is asynchronous, takes a single callback parameter to call when the action is finished.except [array] List of actions where the before-filter should not be performed.only [array] List of actions where the before-filter should only be performed.async [boolean] When set to true, the before-filter is asynchronous, and requires a callbackthis.before(someFunction);
// runs someFunction before the response is rendered
this.before(someFunction, {except: ['index', 'home']});
// won't run someFunction if this is the index or home action
this.before(someFunction, {only: ['add', 'update', 'remove']}
// will only run someFunction if this is the add, update, or remove action
after(filter, [options])
Adds an action to be performed after a response is rendered.
filter [function] Action to add to the afterFilter list. If the action is asynchronous, takes a single callback parameter to call when the action is finished.except [array] List of actions where the after-filter should not be performed.only [array] List of actions where the after-filter should only be performed.async [boolean] When set to true, the after-filter is asynchronous, and requires a callbackthis.after(someFunction);
// runs someFunction after the response is rendered
this.after(someFunction, {except: ['index', 'home']});
// won't run someFunction if this is the index or home action
this.after(someFunction, {only: ['add', 'update', 'remove']}
// will only run someFunction if this is the add, update, or remove action
protectFromForgery()
Prevents cross-site requests by requiring a same-origin token for destructive HTTP methods (PUT, POST, DELETE)
redirect(to)
to is a string, it will redirect to the url in that stringcontroller [string]: a controller nameaction [string]: an action nameformat [string]: the file extensionSends a 302 redirect to the client, based on either a simple string-URL, or a controller/action/format combination.
this.redirect('/users/1');
// will redirect the browser to /users/1
this.redirect({controller: 'users', action: 'show', id: 1});
// will redirect the browser to /users/1
error(err)
Respond to a request with an appropriate HTTP error-code. If a status-code is set on the error object, uses that as the error's status-code. Otherwise, responds with a 500 for the status-code.
statusCode [number] optional HTTP status code to send to the client, defaults to 500message [string] the error message text to send to the clientthis.error();
// sends a 500
var err = new Error('Whoopsy daisy');
this.error(err);
// sends a 500 with a specific message
var err = new Error();
err.statusCode = 420;
this.error(err);
// sends a 420
transfer(action)
Transfer a request from its original action to a new one. The entire request cycle is repeated, including before-filters.
action [string]: name of the new action designated to handle the request.action [object]: The new action designated to handle the request.respond(data, options)
Performs content-negotiation, and renders a response.
data [object]: an object with properties to send to the viewlayout [string]: the path to the layout file to uselayout [false]: a flag to not use a layout fileformat [string]: the format to rendertemplate [string]: The path (without file extensions) to the template to use to render this responsestatusCode [number]: The HTTP status-code to use with this responsethis.respond(params);
// send the params object to the view, then send the response
this.respond({posts: posts});
// send the passed in object to the view, then send the response
this.respond(params, {template: 'path/to/template'});
// send params to path/to/template, render it, then send the response
this.respond(null, {statusCode: 201});
// send the params object as the response in json format
this.respond(params, {format: 'json'});
// send a 201/created with no body
The flash is a special part of the session which is cleared with each request. This means that values stored there will only be available in the next request. This is useful for storing error messages, etc. It is accessed in much the same way as the session, like a hash.
It also includes a few convenience methods for getting/setting commonly used types of flash-messages.
flash.alert([message])
Gets or sets the alert flash-messages for a session. If the 'message' (value) parameter is included it sets the value. If the 'message' paramter is not included, it retrieves the value and returns it.
message [string|object]: The contents of the flash-messagethis.flash.alert('Check it out!');
// Sets the 'alert' flash-message to 'Check it out!'
this.flash.alert();
// Returns 'This is fantastic!'
flash.error([message])
Gets or sets the error flash-messages for a session. If the 'message' (value) parameter is included it sets the value. If the 'message' paramter is not included, it retrieves the value and returns it.
message [string|object]: The contents of the flash-messagethis.flash.error('Yikes! Something wrong wrong.');
// Sets the 'error' flash-message to 'Yikes! Something wrong wrong.'
this.flash.error();
// Returns 'This is fantastic!'
flash.success([message])
Gets or sets the success flash-messages for a session. If the 'message' (value) parameter is included it sets the value. If the 'message' paramter is not included, it retrieves the value and returns it.
message [string|object]: The contents of the flash-messagethis.flash.success('Whoa! It worked.');
// Sets the 'success' flash-message to 'Whoa! It worked.'
this.flash.success();
// Returns 'This is fantastic!'
flash.info([message])
Gets or sets the info flash-messages for a session. If the 'message' (value) parameter is included it sets the value. If the 'message' paramter is not included, it retrieves the value and returns it.
message [string|object]: The contents of the flash-messagethis.flash.info('FYI. Just sayin.');
// Sets the 'info' flash-message to 'FYI. Just sayin.'
this.flash.info();
// Returns 'This is fantastic!'
flash.set([type], message)
Sets the flash-messages for a session, for a custom type, or the entire flash-message object
type [string]: The flash-message type. If not included, this call sets
the entire flash-message objectmessage [string|object]: The contents of the flash-messagethis.flash.set('foo', 'Foo bar baz');
// Sets the 'foo' flash-message to 'Foo bar baz'
this.flash.set({bar: 'Baz bar qux});
// Sets the entire flash-message object
flash.get([type])
Retrieves the flash-messages for a session, for a custom type, or the entire flash-message object
type [string]: The flash-message type. If not included, this call
retrieves the entire flash-message objectthis.flash.set('foo', 'Foo bar baz');
this.flash.get('foo');
// Returns 'Foo bar baz'
this.flash.get();
// Returns an object: {foo: 'Foo bar baz'}
flash.keep([type])
Normally flash-message are wiped out when they are used in the current request.
keep makes them persist and be available to the next request.
type [string]: The type of message to preserve until the next request.
If the type param is not included, preserves the entire flash-message objectthis.flash.keep('error');
// Keep the error flash around after a redirect
flash.discard([type])
Mark a particular flash-message entry (or the entire object) to be discarded at the end of the current request.
type [string]: The type of message to discard at the end of the current request.
If the type param is not included, discards the entire flash-message objectthis.flash.discard('error');
// Discard the current error flash-message
Geddy uses the Model module for its model layer. Model is an abstract ORM that is compatible with many different types of databases, including Postgres, in-memory, MongoDB and Riak.
Model uses a pretty simple syntax for defining a model. (It should look familiar to anyone who has used an ORM like ActiveRecord, DataMapper, Django's models, or SQLAlchemy.)
defineProperties(properties)
defines the properties for your model.
properties [object]: an object keyed by name of properties to definevar User = function () {
this.defineProperties({
login: {type: 'string', required: true}
, password: {type: 'string', required: true}
, lastName: {type: 'string'}
, firstName: {type: 'string'}
});
}
property(name, type, options)
defines a single property
name [string]: the name of the propertytype [string]: the type of the property'string''text''number''int''boolean''object''datetime''date''time'required [boolean]: sets the property to be requiredthis.property('login', 'string', {required: true});
this.property('password', 'string', {required: true});
this.property('joined', 'datetime');
this.property('premium', 'boolean');
validatesPresent(property, options)
Sets up a validation to make sure that the property is present.
property [string]: the name of the property to validatemessage [string]: a message to give the user if the validation failsthis.validatesPresent('login');
// makes sure that the login property is present
validatesFormat(property, regex, options)
Sets up a validation to make sure that the property is formatted correctly.
property [string]: the name of the property to validateregex [regex]: a regular expression that the property value must passmessage [string]: a message to give the user if the validation failsthis.validatesFormat('login', /[a-z]+/, {message: 'cannot contain numbers'});
// makes sure that the login property does not contain numbers
validatesLength(property, qualifier, options)
Sets up a validation to make sure that the property meets certain length requirements.
property [string]: the name of the property to validatemin [number]: the minimum length of the propertymax [number]: the maximum length of the propertymessage [string]: a message to give the user if the validation failsthis.validatesLength('login', {min: 3});
// makes sure that the login property is at least 3 characters long
this.validatesLength('login', {max: 20});
// makes sure that the login property is not longer than 20 characters
this.validatesLength('login', 3)
// makes sure that the login property is exactly 3 characters long
validatesConfirmed(property, param, options)
Sets up a validation to make sure that the property has been confirmed.
property [string]: the name of the property to validateparam [string]: the param required to matchmessage [string]: a message to give the user if the validation failsthis.validatesConfirmed('password', 'confirmPassword');
// confirms that password and confirmPassword are equal
validatesWithFunction(property, fn, options)
Sets up a validation to make sure that the property has been confirmed.
property [string]: the name of the property to validatefn [function]: a function which, when passed the value of the property, will return true or falsemessage [string]: a message to give the user if the validation failsthis.validatesWithFunction('password', function (val) {
// Something that returns true or false
return val.length > 0;
});
// uses the function to see if th length of password is greater than 0
hasOne(model)
Sets up a has-one relationship between this model and another.
model [string]: the name of the model that this model has one of.this.hasOne('Profile');
// sets up a has one relationship
// (something) -> has one -> profile
hasMany(model)
Sets up a has-many relationship between this model and another.
model [string]: the pluralized name of the model that this model has many of.this.hasMany('Friends');
// sets up a has many relationship
// (something) -> has many -> friends
belongsTo(model)
Sets up a belongs-to relationship between this model and another. A belongs-to is often used as the inverse of a has-many or has-one. Note however that this is not required -- associations are unidirectional.
model [string]: the singular name of the model that this model belongs to.this.belongsTo('User');
// sets up a belongs-to relationship
// (something) -> belongs to -> a user
this.adapter
Defines the database adapter for this model
this.adapter = 'mongo';
// makes this model use mongo for it's database
this.adapter = 'riak'
this.adapter = 'postgres'
this.adapter = 'memory'
Instance methods can be defined in the model definition as well.
var User = function () {
...
this.someMethod = function () {
// Do some stuff
};
// sets up a someMethod method on each instance of this model
...
};
isValid()
Returns true if the model instance passes all validations, otherwise it returns false.
user.isValid()
save(fn)
Saves the instance to the database.
fn [function]: the function to be called when saving is completeuser.save(function (err, data) {
// do things
});
// saves the user then calls the callback function
updateProperties(properties)
Updates the properties of a model and asserts that they are valid; This method will not call save on the instance.
properties [object]: an object who's keys are property names and its values are the values to change the property to.user.updateProperties({
login: 'alerxst'
});
// updates the login property and validates it
.add{target_model_name}( instance )
If a model has a hasMany relationship established with another model, you can use this method to add instaces of one model to it’s “parent” model.
instace [modelInstance]: The instance to addvar user = geddy.model.User.create(userParams);
var post = geddy.model.Post.create(postParams);
user.addPost(post);
.set{target_model_name}( instance )
If a model has a hasOne relationship established with another model, you can use this method to add an instace of one model to it’s “parent” model.
instace [modelInstance]: The instance to setvar user = geddy.model.User.create(userParams);
var account = geddy.model.Account.create(accountParams);
user.setAccount(account);
.get{target_model_name}( fn )
If a model has a hasOne relationship established with another model, you can use this method to add an instace of one model to it’s “parent” model.
hasMany: the plural name of the model you’d like to get a collection ofhasOne: the singular name of the model you like to get an instance offn [function]: The function to call once the models are retrieved.var user = geddy.model.User.create(params);
// hasOne
user.getAccount(function (err, account) {
// do stuff with the user’s account
});
// hasMany
user.getPosts(function (err, posts) {
// do stuff with the user’s posts
});
Static methods can be added by creating a method on the model definition object.
var User = function () {
this.property('login', 'string', {required: true});
this.property('password', 'string', {required: true});
};
User.findByLogin = function (login, callback) {
User.all({login: login}, callback);
}
create(params)
Creates a new model instance and returns it.
params [object]: an object whos keys are model propertiesvar params = {
login: 'alex'
, password: 'lerxst'
, lastName: 'Lifeson'
, firstName: 'Alex'
};
var user = User.create(params);
first(query, options, fn)
Use the first method to find a single item. You can pass it an id, or a set of query parameters in the form of an object-literal. In the case of a query, it will return the first item that matches, according to whatever sort you've specified.
query [string]: if the query is a string, it will be assumed that it's an idquery [object]: if the query is an object, it will be interpreted as a Query objectUser.first('sdfs-asd-1', function (err, user) {
// do stuff with user
});
User.first({login: 'alerxst'}, function (err, user) {
// do stuff with user
});
all(query, options, fn)
Use the all method to find lots of items. Pass it a set of query parameters in the form of an object-literal, where each key is a field to compare, and the value is either a simple value for comparison (equal to), or another object-literal where the key is the comparison-operator, and the value is the value to use for the comparison.
query [object]: if the query is an object, it will be interpreted as a Query objectsort [object]: each key is a property name, each value can either be asc or descUser.all({location: 'san francisco'}, function (err, users) {
// do stuff with users
});
User.all({location: 'san francisco'}, {sort: {createdAt: 'desc'}}, function (err, users) {
// do stuff with users
});
remove(id, fn)
Remove an instance from the database by id.
id [string]: the id of the instance to be removedUser.remove('abc-123', function (err, data) {
// do something now that it's removed.
});
Model uses a simple API for finding and sorting items. Again, it should look familiar to anyone who has used a similar ORM for looking up records. The only wrinkle with Model is that the API is (as you might expect for a NodeJS library) asynchronous.
eql: equal tone: not equal togt: greater thanlt: less thangte: greater than or equallte: less than or equallike: likeA simple string-value for a query parameter is the same as 'eql'. {foo: 'bar'}
is the same as {foo: {eql: 'bar'}}.
Model supports combining queries with OR and negating queries with NOT.
To perform an 'or' query, use an object-literal with a key of 'or', and an array of query-objects to represent each set of alternative conditions.
To negate a query with 'not', simply use a query-object where 'not' is the key, and the value is the set of conditions to negate.
{foo: 'BAR', bar: {ne: null}}
// Where "foo" is 'BAR' and "bar" is not null
{foo: {'like': 'B'}}
// Where "foo" begins with 'B'
{foo: {lt: 2112}, bar: 'BAZ'}
// Where foo is less than 2112, and bar is 'BAZ'
{or: [{foo: 'BAR'}, {bar: 'BAZ'}]}
// Where "foo" is 'BAR' OR "bar" is 'BAZ'
{or: [{foo {ne: 'BAR'}}, {bar: null}, {baz: {lt: 2112}}]}
// Where "foo" is not 'BAR' OR "bar" is null OR "baz" is less than 2112
{not: {foo: 'BAR', bar: 'BAZ'}}
// Where NOT ("foo" is 'BAR' and "bar" is 'BAZ')
{not: {foo: 'BAZ', bar: {lt: 1001}}}
// Where NOT ("foo" is 'BAZ' and "bar" is less than 1001)
{or: [{foo: {'like': 'b'}}, {foo: 'foo'}], not: {foo: 'baz'}}
// Where ("foo" is like 'b' OR "foo" is 'foo') and NOT "foo" is 'baz'
Both the base model 'constructors,' and model instances are EventEmitters. The emit events during the create/update/remove lifecycle of model instances. In all cases, the plain-named event is fired after the event in question, and the 'before'-prefixed event, of course happens before.
The 'constructor' for a model emits the following events:
Model-item instances emit these events:
Geddy's view layer provides a versatile set of templating languages and helpers to get you started quickly.
The view layer supports these four templating engines:
To use a certain template engine just give the view a corresponding extension listed above.
When using the Geddy CLI to generate parts of your application you can use different template languages by giving an argument to the command, here are some examples:
$ geddy app --mustache my_app
$ geddy scaffold -m user
$ geddy app --jade my_app
$ geddy scaffold -j user
$ geddy app --handle my_app
$ geddy scaffold -H user
$ geddy app --swig my_app
$ geddy scaffold --swig user
urlFor(options<String/Object>)
Returns a URL based on the options provided
'back' [String] The 'back' string will return a URL that points to the last URL in historyrelPath [Boolean] If true, the relative URL is returned(Default: false)protocol [String] The protocol to use(Default: What your Geddy instance is using('http' default))username [String] Includes a username in the path. Requires password or it'll be ignoredpassword [String] Includes a username in the path. Requires password or it'll be ignoredsubdomain [String] Specifies the subdomain to prepend to domaindomain [String] Specifies the domain to point to. Required if relPath is falsehost [String] Alias for hostport [Integer] Specify the port to connect tocontroller [String] Specifies the controller to use for the pathaction [String] Specifies the action to use for the pathid [String] Specifies an ID to use for displaying specific itemstrailingSlash [Boolean] If true, adds a trailing slash to the end of the path/domainfragment [String] Appends a fragment to the end of the path/domainanchor [String] Alias for fragmentoptions is a String it will just be returned, unless the String is equal to 'back'options added will be considered as a query to be appended to the URLurlFor('http://google.com')
// => 'http://google.com'
urlFor({controller: 'tasks', action: 'new', host: 'somehost.com'})
// => 'http://somehost.com/tasks/new'
urlFor({controller: 'tasks', action: 'new', relPath: true})
// => '/tasks/new'
urlFor({controller: 'tasks', action: 'new', relPath: true, trailingSlash: true})
// => '/tasks/new/'
urlFor({host: 'somehost.com', protocol: 'https', username: 'username', password: 'password'})
// => 'https://username:password@somehost.com'
urlFor({controller: 'tasks', action: 'new', host: 'somehost.com', protocol: 'https'})
// => 'https://somehost.com/tasks/new'
urlFor({controller: 'tasks', action: 'edit', id: 'IwTEf55ivH', host: 'somehost.com'})
// => 'http://somehost.com/tasks/IwTEf55ivH/edit'
urlFor({controller: 'tasks', action: 'new', host: 'somehost.com', anchor: 'submit'})
// => 'http://somehost.com/tasks/new#submit'
urlFor({controller: 'tasks', action: 'new', host: 'somehost.com', authToken: 'some_token'})
// => 'http://somehost.com/tasks/new?authToken=some_token'
contentTag(tag<String>, content<String>, htmlOptions<Object>)
Returns an HTML element from a given tag and includes the content and all htmlOptions
data[Array] The data attribute takes an Array containing data attributes you want, when parsed they each get parsed as a full data attribute(e,g: data: {goTo: 'google.com'} will be data-go-to="google.com").contentTag('p', 'this is some content')
// => '<p>this is some content</p>'
contentTag('input', 'sample value')
// => '<input value="sample value" />'
contentTag('input', 'sample value', {value: 'override sample value'})
// => '<input autofocus="autofocus" type="text" value="sample value" />'
contentTag('input', 'sample value', {type: 'text', autofocus: true})
// => '<input autofocus="autofocus" type="text" value="sample value" />'
contentTag('a', 'http://google.com')
// => '<a href="http://google.com">http://google.com</a>'
contentTag('a', 'hey there', {href: 'http://google.com'})
// => '<a href="http://google.com">hey there</a>'
contentTag('a', 'hey there', {href: 'http://google.com', data: { goTo: 'http://google.com'} })
// => '<a data-go-to="http://google.com" href="http://google.com">hey there</a>'
contentTag('a', 'hey there', {href: 'http://google.com', data_go_to: 'http://google.com'})
// => '<a data-go-to="http://google.com" href="http://google.com">hey there</a>'
`selectTagString(optionsArray
Creates a HTML select tag using the given optionsArray to create HTML option elements.
optionsArray could be an array of strings, numbers or an object with value and text properties to be used for the value attribute and option element content respectively,
along with an attr object which will include any other html options. (you can even pass selected:true and 'value:VALUE' with the attr object as well, but the outer ones, if there is any, will take precedence)
selectTag(['geddy', 'alex', 'neil'])
// => '<select><option value="geddy">geddy</option><option value="alex">alex</option><option value="neil">neil</option></select>'
selectTag(['open', 'close'], todo.status, { class:'span6', name:'status' })
// => '<select class="span6" name="status"><option selected="selected" value="open">open</option><option value="close">close</option></select>'
selectTag([{value: 1, text: "Text 1"}, {value: 2, text: "Text 2"}], 2)
// => <select><option value="1">Text 1</option><option selected="selected" value="2">Text 2</option></select>
selectTag([{text: "Text 1", attrs: {value: 1, class: 'anoption', data: {thing: 'vip', rate: '0.99'}}}, {value: 2, text: "Text 2", attrs: {value: 0, data: {thing: 'basic'}}}], 2)
// => <select><option data-thing="vip" data-rate="0.99" class="anoption" value="1">Text 1</option><option data-thing="basic" selected="selected" value="2">Text 2</option></select>
v0.7
Along with flexible template system in Geddy you have access to various built in helpers and custom helpers.
When you create your application a helpers directory will be added to the app directory, all the files in this directory will be required when you start the server up.
The exported helpers, are available in your views and throughout your application.
For example, we have this script in a file in our app/helpers directory:
module.exports = function upperCase(str) {
return str.toUpperCase();
};
Now when we start the server up we can access it in our views by doing:
<%= upperCase("some string") %>
// => "SOME STRING"
All the exported helpers are global to the templates, so any of them can be accessed from any template.
You can also access them from controllers/models and any other part of your application by using geddy.viewHelpers here's an example using our upperCase helper:
console.log(geddy.viewHelper.upperCase("some string"));
// => "SOME STRING"
Yield is a function that's only available on layout templates. It yields the template content, which is inserted in the place where the yield function is called.
partial(partialURL<String>, data<Object>)
Partial takes a partialURL which is the location to a partial template and a data object which is the data to render the partial with(params, etc), then it renders the partial and puts the contents in place where the partial function was called.
truncate(string<String>, options<Integer/Object>)
Truncates a given string after a specified length if string is longer than length. The last character will be replaced with an omission for a total length not exceeding length.
options is an integer it will be assumed that is the desired lengthlength [Integer] Length the output string will be(Default: 30)len [Integer] Alias for lengthomission [String] Replace the last letters with an omission(Default: '...')ellipsis [String] Alias for omissionseperator [String/RegExp] Break the truncated text at the nearest seperatortruncateHTMLruncate('Once upon a time in a world', {length: 10})
// => 'Once up...'
truncate('Once upon a time in a world', {length: 20, omission: '...(continued)'})
// => 'Once u...(continued)'
truncate('Once upon a time in a world', {length: 15, seperator: /\s/})
// => 'Once upon a...'
// Normal Output: => 'Once upon a ...'
truncate('Once upon a time in a world', {length: 15, seperator: ' '})
// => 'Once upon a...'
// Normal Output: => 'Once upon a ...'
truncate('<p>Once upon a time</p>', {length: 20})
// => '<p>Once upon a ti...'
truncateHTML(string<String>, options<Integer/Object>)
Truncates a given string after a specified length if string is longer than length. The lat character will be replace with an omission for a total length not exceeding length. If once is true, only the first string in the first HTML element will be truncated leaving others as they were.
once[Boolean] If true only the first string in the first HTML element will be truncated(Default: false)truncate are available for truncateHTMLtruncateHTML('<p>Once upon a time in a world</p>', {length: 10})
// => '<p>Once up...</p>'
truncateHTML('<p>Once upon a time <small>in a world</small></p>', {length: 10})
// => '<p>Once up...<small>in a wo...</small></p>'
truncateHTML('<p>Once upon a time <small>in a world</small></p>', {length: 10, once: true})
// => '<p>Once up...<small>in a world</small></p>'
imageLink(source<String>, link<String/Object>, imageOptions<Object>, linkOptions<Object>)
Returns an anchor element to a given link with the given linkOptions, with the content being a image element to the given source and includes its imageOptions
linkto is used on the backend so any linkOption will be used for linkToimageTag is used on the backend as well so any imageOptions will be used for imageTagimageLink('images/google.png', 'http://google.com')
// => '<a href="http://google.com"><img alt="images/google.png" src="images/google.png" /></a>'
imageLink('images/google.png', 'http://google.com', {alt: ''}
// => '<a href="http://google.com"><img alt="" src="images/google.png" /></a>'
imageLink('images/google.png', 'http://google.com', {alt: '', size: '40x50'})
// => '<a href="http://google.com"><img alt="" height="50" src="images/google.png" width="40" /></a>'
imageTag(source<String>, htmlOptions<Object>)
Returns an image tag with the src to a source and includes all the given htmlOptions
size[String] Takes a string including the width and height "{width}x{height}"(e,g: '40x50') or it can take a single string included an integer "{size}"(e,g: '40') The first being results in "height='50' width='40'" the second results in the height and width being the same value. Note: If the format doesn't comply, it will be ignoredimageTag('images/google.png')
// => '<img alt="images/google.png" src="images/google.png" />'
imageTag('images/google.png', {alt: ''})
// => '<img alt="" src="images/google.png" />'
imageTag('images/google.png', {alt: '', size: '40x50'})
// => '<img alt="" height="50" src="images/google.png" width="40" />'
imageTag('images/google.png', {alt: '', size: 'a string'})
// => '<img alt="" src="images/google.png" />'
styleLink(source<String>, htmlOptions<Object>)
Generates a style element pointing to source and includes all the given htmlOptions
styleLink('/css/styles.css')
// => '<link href="/css/style.css" />'
styleLink('/css/styles.css', {type: 'text/javascript'})
// => '<link href="/css/style.css" rel="stylesheet" />'
scriptLink(source<String>, htmlOptions<Object>)
Generates a script element pointing to source and includes all the given htmlOptions
scriptLink('/js/script.js')
// => '<script src="/js/script.js"></script>'
scriptLink('/js/script.js', {type: 'text/javascript'})
// => '<script src="/js/script.js" type="text/javascript"></script>'
linkTo(content<String>, options<String/Object>, htmlOptions<Object>)
Generates a link from the given options, then returns a anchor tag with the content and the htmlOptions provided
linkTo('some content', 'http://google.com')
// => '<a href="http://google.com">some content</a>'
linkTo('some content', 'http://google.com', {data: {goTo: 'http://google.com'}})
// => '<a data-go-to="http://google.com" href="http://google.com">some content</a>'
urlFor(options<String/Object>)
Returns a URL based on the options provided
'back' [String] The 'back' string will return a URL that points to the last URL in historyrelPath [Boolean] If true, the relative URL is returned(Default: false)protocol [String] The protocol to use(Default: What your Geddy instance is using('http' default))username [String] Includes a username in the path. Requires password or it'll be ignoredpassword [String] Includes a username in the path. Requires password or it'll be ignoredsubdomain [String] Specifies the subdomain to prepend to domaindomain [String] Specifies the domain to point to. Required if relPath is falsehost [String] Alias for hostport [Integer] Specify the port to connect tocontroller [String] Specifies the controller to use for the pathaction [String] Specifies the action to use for the pathid [String] Specifies an ID to use for displaying specific itemstrailingSlash [Boolean] If true, adds a trailing slash to the end of the path/domainfragment [String] Appends a fragment to the end of the path/domainanchor [String] Alias for fragmentoptions is a String it will just be returned, unless the String is equal to 'back'options added will be considered as a query to be appended to the URLurlFor('http://google.com')
// => 'http://google.com'
urlFor({controller: 'tasks', action: 'new', host: 'somehost.com'})
// => 'http://somehost.com/tasks/new'
urlFor({controller: 'tasks', action: 'new', relPath: true})
// => '/tasks/new'
urlFor({controller: 'tasks', action: 'new', relPath: true, trailingSlash: true})
// => '/tasks/new/'
urlFor({host: 'somehost.com', protocol: 'https', username: 'username', password: 'password'})
// => 'https://username:password@somehost.com'
urlFor({controller: 'tasks', action: 'new', host: 'somehost.com', protocol: 'https'})
// => 'https://somehost.com/tasks/new'
urlFor({controller: 'tasks', action: 'edit', id: 'IwTEf55ivH', host: 'somehost.com'})
// => 'http://somehost.com/tasks/IwTEf55ivH/edit'
urlFor({controller: 'tasks', action: 'new', host: 'somehost.com', anchor: 'submit'})
// => 'http://somehost.com/tasks/new#submit'
urlFor({controller: 'tasks', action: 'new', host: 'somehost.com', authToken: 'some_token'})
// => 'http://somehost.com/tasks/new?authToken=some_token'
contentTag(tag<String>, content<String>, htmlOptions<Object>)
Returns an HTML element from a given tag and includes the content and all htmlOptions
data[Array] The data attribute takes an Array containing data attributes you want, when parsed they each get parsed as a full data attribute(e,g: data: {goTo: 'google.com'} will be data-go-to="google.com").contentTag('p', 'this is some content')
// => '<p>this is some content</p>'
contentTag('input', 'sample value')
// => '<input value="sample value" />'
contentTag('input', 'sample value', {value: 'override sample value'})
// => '<input autofocus="autofocus" type="text" value="sample value" />'
contentTag('input', 'sample value', {type: 'text', autofocus: true})
// => '<input autofocus="autofocus" type="text" value="sample value" />'
contentTag('a', 'http://google.com')
// => '<a href="http://google.com">http://google.com</a>'
contentTag('a', 'hey there', {href: 'http://google.com'})
// => '<a href="http://google.com">hey there</a>'
contentTag('a', 'hey there', {href: 'http://google.com', data: { goTo: 'http://google.com'} })
// => '<a data-go-to="http://google.com" href="http://google.com">hey there</a>'
contentTag('a', 'hey there', {href: 'http://google.com', data_go_to: 'http://google.com'})
// => '<a data-go-to="http://google.com" href="http://google.com">hey there</a>'
`selectTagString(optionsArray
Creates a HTML select tag using the given optionsArray to create HTML option elements.
optionsArray could be an array of strings, numbers or an object with value and text properties to be used for the value attribute and option element content respectively.
selectTag(['geddy', 'alex', 'neil'])
// => '<select><option value="geddy">geddy</option><option value="alex">alex</option><option value="neil">neil</option></select>'
selectTag(['open', 'close'], todo.status, { class:'span6', name:'status' })
// => '<select class="span6" name="status"><option selected="selected" value="open">open</option><option value="close">close</option></select>'
selectTag([{value: 1, text: "Text 1"}, {value: 2, text: "Text 2"}], 2)
// => <select><option value="1">Text 1</option><option selected="selected" value="2">Text 2</option></select>
Geddy has a robust CLI tool to help you generate apps, run your tests, or play with your app in a console.
—environment, -e: Environment to use—port, -p: Port to connect server to—workers, -w: Number of workers to use (default: 1)—debug, -d: Sets the log level to output debug messages to console—jade, -j: When generating views, use Jade templates(Default: EJS)—handle, -H: When generating views, use Handlebars templates(Default: EJS)—mustache, -m: When generating views, use Mustache templates(Default: EJS)—swig, -s: When generating views, use Swig templates(Default: EJS)—version, -v: Output the version of Geddy installed—help, -h: Output the list of commands and optionsgeddy takes no arguments, it will run the geddy app in the current directory.
$ cd path/to/app
$ geddy
// will run the app in path/to/app
app takes a single argument being the name you'd like, then it will generate a base application. If no name is given the command will fail. If you include the —jade, —handle, —mustache, or '-swig' option you can substitute the templating language to your liking
$ geddy app app_name
// creates a geddy app using EJS
resource takes one or more arguments, the first being a name and the others being a set of model properties. This will create a controller, a model including the given model properties and a resource route.
$ geddy resource user name description password
// This will create a user model, users controller, and user routes
scaffold takes one or more arguments, the first being a name and the others being a set of model properties. Scaffolding includes a controller, a model including the given model properties as well as a default model adapter a resource route and will create all views. If you also include the options —jade, —handle, —mustache, or '-swig' you can substitute the template language to your liking.
$geddy scaffold user name description password
// This will create a user model, users controller, user views, and user routes
controller takes a single argument being a name. It will create a new controller, a route and an index view. If you also include the options —jade, —handle, —mustache, or '-swig' you can substitute the template language to your liking.
$ geddy controller users
model takes one or more arguments, the first being a name and the others being a set of model properties. This will create a new model including the model properties given.
$ geddy model user name description password
// creates a user model with name, description and password properties
secret doesn't take any arguments, it will find your config/environment file and create a new secret in it deleting any other secret.
$ geddy secret
console doesn't take any arguments, it will start a geddy console.
$ geddy console
jake takes a task name, it will run a jake command in your apps context from your app's Jakefile
$ geddy jake test
// will run the test task in your app's Jakefile after loading up your app environment
Sessions are used to keep track of current connections to the server.
get( key )
Gets a key from the current session.
key [string]: the key to return the value ofvar user = this.session.get(‘user’);
set( key, value )
Saves a value to the current session as a key
key [string]: the key to set on the sessionvalue [object]: the value to save to the sessionthis.session.set(‘user’, user);
unset( key )
Removes a key and value from the current session
key [string]: the key to removethis.session.unset(‘user’);
isExpired()
Returns true if the current session has expired
this.session.isExpired
We use the following example as a reference. There will be some differences with other environments.
geddy secret if you need the secrets for sessions, etc. To circumvent this, create a .npmignore file and include all the contents from the .gitignore except the config/secrets.json line. Nodejitsu ignores the .gitignore file only if a .npmignore file is included as well.If you haven't already you'll need to sign up and log in to Nodejitsu which you can do from the jitsu executable.
jitsu signup
jitsu login
Now once you've created an account on Nodejitsu we need to prepare the application you have for deployment. First we'll edit(or create) a package.json file in the app's root directory
{
"name": "node-example",
"version": "0.0.1",
"dependencies": {
"geddy": "0.6.x"
},
"subdomain": "geddy-example",
"scripts": {
"start": "app.js"
},
"engines": {
"node": "0.8.x"
}
}
Here we have a subdomain key/value this tells Nodejitsu what subdomain to host the application on(e,g,. geddy-example.jit.su). We also have a start script pointing to app.js in the root directory, we'll go over what to put here in a second. Of course you should edit this to include anything else you want, like other dependences or an author.
Now we need to create a app.js file so that Nodejitsu can use it to boot the Geddy server, here's what it should look like
var geddy = require('geddy');
geddy.start({
environment: 'production'
});
In the object we're giving to geddy.start you can use any other arguments you'd for the configuration files, these will override the ones loaded for the environment. For more information about this file you can go here
Now that our application is set up for deployment, we need to deploy it which is just a single command
jitsu deploy
Now you can go to http://geddy-example.jit.su and see your application!
geddy secret when you deploy. Currently there's no way to circumvent this other than removing it from your .gitignore file. More info here: https://github.com/mde/geddy/issues/309Add a package.json file to your app's root directory
{
"name": "node-example",
"version": "0.0.1",
"dependencies": {
"geddy": "0.6.x"
},
"engines": {
"node": "0.8.x",
"npm": "1.1.x"
}
}
Now we need to create a app.js file so that the Procfile can use it to boot the Geddy server, here's what it should look like
var geddy = require('geddy');
geddy.startCluster({
hostname: '0.0.0.0',
port: process.env.PORT || '3000',
// you can manually set this to production, or set an environment variable via heroku..
environment: 'production'
// just uncomment the below line, and delete the above line.
// you will need to set an environment variable in heroku by running
// heroku config:set NODE_ENV=production
//environment: process.env.NODE_ENV || 'development'
});
In the object we're giving to geddy.startCluster you can use any other arguments you'd for the configuration files, these will override the ones loaded for the environment. For more information about this file you can go here
Add a Procfile text file to your app's root directory, this is read by Heroku when booting the app
web: node app.js
Add a Procfile text file to your app's root directory
web: node app.js
remove the line for config\secrets.json in your .gitignore file - note: This is insecure, on public repo's as it exposes your cookie's secret hash.
Now it's time to create a heroku app.
$ heroku create --stack cedar
Add everything to git and push to heroku
$ git push heroku master
For more information about deploying and supporting Node Apps on Heroku see the Getting Started with Node.js on Heroku article.
Geddy provides internationalization support out of the box, with localized error messages in the following languages:
You can set a specific locale at the request level, or a default one for your entire app. The default locale if you don't set one is American English ('en-us').
Set a locale in your environment.js by setting the defaultLocale property on the i18n config option:
...
, i18n: {
defaultLocale: 'ja-jp'
}
...
Geddy will load i18n data according to the loadPaths property of your app's i18n config -- an array of paths to look in for JSON files containing i18n data. By default, it contains an entry for 'config/locales' in your app.
Geddy's i18n code loads the data into data structures based on the filename. (So the data in /foo/bar/en-uk.json gets loaded into the en-uk locale, etc.)
Internationalization data is loaded from JSON files in the following format:
{
"model.validatesPresent": "「{name}」の入力が必要です。"
, "model.validatesAbsent": "「{name}」の入力は不要です。"
, "model.validatesConfirmed": "「{name}」と「{qual}」が一致しません。"
, "model.validatesFormat": "「{name}」のフォーマットが正しくありません。"
, "model.validatesExactLength": "「{name}」は{qual}文字でなければいけません。"
, "model.validatesMinLength": "「{name}」は{min}文字以上でなければいけません。"
, "model.validatesMaxLength": "「{name}」は{max}文字以内でなければいけません。"
, "model.validatesWithFunction": "「{name}」は有効ではありません。"
}
Geddy controllers have an i18n property which has a getText method (shortcut alias t). Pass this method the desired key, and an optional data-object for parametric replacement, and an optional locale. If you don't pass a locale, it will fall back to the app's defaultLocale.
This i18n object is also available as a local variable in your templates.
Use it like this in a controller (assuming a defaultLocale of 'ja-jp'):
// Returns 「HOWDY」の入力が必要です。
this.i18n.t('model.validatesPresent', {name: 'HOWDY'});
// Returns "HOWDY" ist ein Pflichtfeld.
this.i18n.t('model.validatesPresent', {name: 'HOWDY'}, 'de-de');
Geddy provides tons of useful utilities to make tasks easier, they are provided by the utilities module in NPM. All the utilities are availble through the geddy global object(e.g., geddy.array.humanize()).
setIndentLevel(level<Number>)
SetIndentLevel changes the indent level for XML.stringify and returns it
setIndentLevel(6)
// => 6
stringify(obj<Object>, opts<Object>)
whitespace [Boolean] Don't insert indents and newlines after xml entities(Default: true)name [String] Use custom name as global namespace(Default: typeof obj)fragment [Boolean] If true no header fragment is added to the top(Default: false)level [Number] Remove this many levels from the output(Default: 0)arrayRoot [Boolean] (Default: true)Stringify returns an XML representation of the given obj
stringify({user: 'name'})
// => '<?xml version="1.0" encoding="UTF-8"?>\n<object>\n <user>name</user>\n</object>\n'
stringify(['user'])
// => '<?xml version="1.0" encoding="UTF-8"?>\n<strings type="array">\n <string>user</string>\n</strings>'
stringify({user: 'name'}, {fragment: true})
// => '<object>\n<user>name</user>\n</object>\n'
humanize(array<Array>)
Creates a string containing the array elements in a readable format
humanize(["array", "array", "array"])
// => "array, array and array"
humanize(["array", "array"])
// => "array and array"
include(array<Array>, item<Any>)
Checks if an item is included in an array
include(["array"], "array")
// => true
include(["array"], 'nope')
// => false
include(["array", false], false)
// => true
getFileExtension(path<String>)
Gets the file extension for a path and returns it
getFileExtension('users.json')
// => 'json'
paramify(obj<Object>, o<Object>)
consolidate [Boolean] take values from elements that can return(Default: false)includeEmpty [Boolean] include keys in the string for all elements, even(Default: false)snakeize [Boolean] change param names from camelCase to snake_case.(Default: false)escapeVals [Boolean] escape the values for XML entities.(Default: false)Convert a JS Object to a querystring (key=val&key=val). Values in arrays
paramify({username: 'user', token: 'token', secret: 'secret'})
// => 'username=user&token=token&secret=secret'
paramify({username: 'user', auth: ['token', 'secret']}, {conslidate: true})
// => 'username=user&auth=token&auth=secret'
paramify({username: 'user', token: ''}, {includeEmpty: true})
// => 'username=user&token='
paramify({username: 'user', token: '<token'}, {escapeVals: true})
// => 'username=user&token=%26lt%3Btoken'
objectify(str<String>, o<Object>)
consolidate [Boolean] Convert multiple instances of the same(Default: true)Convert the values in a query string (key=val&key=val) to an Object
objectify('name=user')
// => {name: 'user'}
objectify('name=user&name=user2')
// => {name: ['user', 'user2']}
objectify('name=user&name=user2', {consolidate: false})
// => {name: 'user2'}
proxyEmit(name<String>, args<Array>)
Emit an event by name and arguments or add it to the buffer if no outlet is set
proxyEmit("close")
// => undefined
proxyEmit("data", "some content here")
// => undefined
emit(name<String>, args<Array>)
Emit an event by name and arguments
emit("close")
// => undefined
emit("data", "some content here")
// => undefined
sync(outlet<Object>)
Flush the buffer and continue piping new events to the outlet
sync(new EventEmitter())
// => undefined
addItem(key<String>, val<Any>)
Adds a new key/value to the collection
addItem('testA', 'AAAA');
// => 'AAAA'
addItem('testV', 'VVVV');
// => 'VVVV'
getItem(p<String/Number>)
Retrieves the value for the given identifier that being a key or index
getItem('testA');
// => 'AAAA'
getItem(1);
// => 'VVVV'
setItem(p<String/Number>, val<Any>)
Sets the item in the collection with the given val, overwriting the existsing item
setItem('testA', 'aaaa');
// => 'aaaa'
setItem(1, 'vvvv');
// => 'vvvv'
removeItem(p<String/Number>)
Removes the item for the given identifier
removeItem('testA')
// => true
removeItem(3)
// => false
getByKey(key<String>)
Retrieves the value for the given key
getByKey('testA');
// => 'AAAA'
getByKey('testV');
// => 'VVVV'
setByKey(key<String>, val<Any>)
Sets a item by key assigning the given val
setByKey('testA', 'aaaa');
// => 'aaaa'
setByKey('testV', 'vvvv');
// => 'vvvv'
removeByKey(key<String>)
Removes a collection item by key
removeByKey('testA')
// => true
removeByKey('testC')
// => false
getByIndex(ind<Number>)
Retrieves the value for the given index
getByIndex(0);
// => 'AAAA'
getByIndex(1);
// => 'VVVV'
setByIndex(ind<Number>, val<Any>)
Sets a item by index assigning the given val
setByIndex(0, 'aaaa');
// => 'aaaa'
setByIndex(1, 'vvvv');
// => 'vvvv'
removeByIndex(ind<Number>)
Removes a collection item by index
removeByIndex(0)
// => true
removeByIndex(3)
// => false
hasKey(key<String>)
Checks if a key item exists in the collection
hasKey('testA')
// => true
hasKey('testC')
// => false
hasValue(val<Any>)
Checks if a key item in the collection has a given val
hasValue('aaaa')
// => true
hasValue('cccc')
// => false
allKeys(str<String>)
Joins all the keys into a string
allKeys(", ")
// => "testA, testV"
replaceKey(oldKey<String>, newKey<String>)
Joins all the keys into a string
replaceKey("testV", "testC")
// => undefined
insertAtIndex(ind<Number>, key<String>, val<Any>)
Inserts a key/value at a specific index in the collection
insertAtIndex(1, "testB", "bbbb")
// => true
insertAfterKey(refKey<String>, key<String>, val<Any>)
Inserts a key/value item after the given reference key in the collection
insertAfterKey("testB", "testB1", "b1b1b1b1b1b1")
// => true
getPosition(key<String>)
Retrieves the index of the key item
getPosition("testA")
// => 0
getPosition("testB1")
// => 2
each(func<Function>, opts<Object>)
keyOnly [Boolean] Only give the function the keyvalueOnly [Boolean] Only give the function the valueLoops through the collection and calls the given function
each(function (val, key) {
console.log("Key: " + key + " Value: " + val);
})
each(function (key) {
console.log("Key: " + key);
}, {keyOnly: true})
each(function (val) {
console.log("Val: " + val);
}, {valueOnly: true})
eachKey(func<Function>)
Loops through the collection and calls the given function
each(function (key) {
console.log("Key: " + key);
}, {keyOnly: true})
eachValue(func<Function>)
Loops through the collection and calls the given function
each(function (val) {
console.log("Val: " + val);
}, {valueOnly: true})
clone()
Creates a cloned version of the current collection and returns it
clone()
// => SortedCollection
concat(hNew<Object>)
Join a given collection with the current one
concat(new SortedCollection())
// => undefined
push(key<String>, val<Any>)
Appends a new item to the collection
push("testZ", "zzzz")
// => 5
pop()
Pops off the last item in the collection and returns it's value
pop()
// => "zzzz"
unshift(key<String>, val<Any>)
Prepends a new item to the beginning of the collection
unshift("testA0", "a0a0a0a0")
// => 6
shift()
Removes the first item in the list and returns it's value
shift()
// => "a0a0a0a0"
splice(index<Number>, numToRemove<Number>, hash<Object>)
Removes items from index to the given max and then adds the given collections items
splice(2, 1, new SortedCollection())
// => undefined
reverse()
Reverse the collection item list
reverse()
// => undefined
supportedFormats
Object of supported strftime formats
getSupportedFormats()
return the list of formats in a string
getSupportedFormats()
// => "aAbhBcCdDefFgGHI..."
strftime(dt<Date>, format<String>)
Formats the given date with the strftime formated
strftime(new Date(), "%w")
// => 3
calcCentury(year<Number>)
Find the century for the given year
calcCentury()
// => "21"
calcCentury(2000)
// => "20"
calcDays(dt<Date>)
Calculate the day number in the year a particular date is on
calcDays(new Date())
// => 150
getMeridiem(h<Number>)
Return 'AM' or 'PM' based on hour in 24-hour format
getMeridiem(14)
// => "PM"
getMeridiem(7)
// => "AM"
hrMil2Std(hour<String>)
Convert a 24-hour formatted hour to 12-hour format
hrMil2Std("14")
// => 2
hrMil2Std("7")
// => 7
hrStd2Mil(hour<String>, pm<Boolean>)
Convert a 12-hour formatted hour with meridian flag to 24-hour format
hrStd2Mil("7", true)
// => 14
hrStd2Mil("7")
// => 7
add(dt<Date>, interv<String>, incr<Number>)
Add to a Date in intervals of different size, from milliseconds to years
add(new Date(), "hour", 1)
// => Date
add(new Date(), "minute", 10)
// => Date
diff(date1<Date>, date2<Date>, interv<String>)
Get the difference in a specific unit of time (e.g., number of months, weeks, days, etc.) between two dates.
diff(new Date(), new Date(), "hour")
// => 0
diff(new Date(), new Date(), "minute")
// => 0
parse(val<String>)
Convert various sorts of strings to JavaScript Date objects
parse("12:00 March 5 1950")
// => Sun Mar 05 1950 12:00:00 GMT-0500 (EST)
relativeTime(dt<Date>, opts<Object>)
abbreviated [Boolean] Use short strings(Default: false)Convert a Date to an English sentence representing
relativeTime(new Date())
// => 'less than a minute ago'
toISO8601(dt<Date>)
Convert a Date to an ISO8601-formatted string
toISO8601(new Date())
// => '2012-10-17T17:57:03.892-04'
merge(object<Object>, otherObject<Object>)
Merge merges otherObject into object and takes care of deep merging of objects
merge({user: 'geddy'}, {key: 'key'})
// => {user: 'geddy', key: 'key'}
merge({user: 'geddy', key: 'geddyKey'}, {key: 'key'})
// => {user: 'geddy', key: 'key'}
reverseMerge(object<Object>, defaultObject<Object>)
ReverseMerge merges object into defaultObject
reverseMerge({user: 'geddy'}, {key: 'key'})
// => {user: 'geddy', key: 'key'}
reverseMerge({user: 'geddy', key: 'geddyKey'}, {key: 'key'})
// => {user: 'geddy', key: 'geddyKey'}
isEmpty(object<Object>)
isEmpty checks if an Object is empty
isEmpty({user: 'geddy'})
// => false
isEmpty({})
// => true
toArray(object<Object>)
Converts an object to an array of objects each including the original key/value
toArray({user: 'geddy'})
// => [{key: 'user', value: 'geddy'}]
isPortOpen(port<Number>, host<String>, callback<Function>)
Checks if the given port in the given host is open
isPortOpen(3000, 'localhost', function (err, isOpen) {
if (err) { throw err; }
console.log(isOpen)
})
request(opts<Object>, callback<Function>)
Sends requests to the given url sending any data if the method is POST or PUT
url [String] The URL to send the request tomethod [String] The method to use for the request(Default: 'GET')headers [Object] Headers to send on requestsdata [String] Data to send on POST and PUT requestsdataType [String] The type of data to sendrequest({url: 'google.com', method: 'GET'}, function (err, data) {
if (err) { throw err; }
console.log(data)
})
inflections
A list of rules and replacements for different inflection types
parse(type<String>, word<String>)
Parse a word from the given inflection type
parse('plurals', 'carrier')
// => 'carriers'
parse('singulars', 'pluralities')
// => 'plurality'
pluralize(word<String>)
Create a plural inflection for a word
pluralize('carrier')
// => 'carriers'
singularize(word<String>)
Create a singular inflection for a word
singularize('pluralities')
// => 'plurality'
cpR(fromPath<String>, toPath<String>, opts<Object>)
silent [Boolean] If false then will log the commandCopies a directory/file to a destination
cpR('path/to/directory', 'destination/path')
// => undefined
mkdirP(dir<String>, mode<Number>)
Create the given directory(ies) using the given mode permissions
mkdirP('dir', 0755)
// => undefined
mkdirP('recursive/dirs')
// => undefined
readdirR(dir<String>, opts<Object>)
format [String] Set the format to return(Default: Array)Reads the given directory returning it's contents
readdirR('dir')
// => ['dir', 'item.txt']
readdirR('path/to/dir')
// => ['path/to/dir', 'path/to/dir/item.txt']
rmRf(p<String>, opts<Object>)
silent [String] If false then logs the commandDeletes the given directory/file
rmRf('file.txt')
// => undefined
isAbsolute(p<String>)
Checks if a given path is absolute or relative
isAbsolute('/path/to/file.txt')
// => true
isAbsolute('C:\\path\\to\\file.txt')
// => true
absolutize(p<String>)
Returns the absolute path for the given path
absolutize(path/to/dir)
// => /home/user/path/to/dir
searchParentPath(p<String>, callback<Function>)
Search for a directory/file in the current directory and parent directories
searchParentPath('path/to/file.txt', function (err, path) {
if (err) { throw err; }
console.log(path)
})
watch(path<String>, callback<Function>)
Watch a given path then calls the callback once a change occurs
watch('path/to/dir', function (currStat, oldStat) {
console.log('the current mtime is: ' + currStat.mtime);
console.log('the previous mtime was: ' + oldStat.mtime);
})
requireLocal(module<String>, message<String>)
Require a local module from the node_modules in the current directory
requireLocal('utilities', 'optional error message')
// => { ... }
escapeRegExpChars(string<String>)
Escapes regex control-characters in strings used to build regexes dynamically
escapeRegExpChars('/\s.*/')
// => '\\\\/s\\\\.\\\\*\\\\/'
toArray(string<String>)
Converts a string to an array
toArray('geddy')
// => ['g', 'e', 'd', 'd', 'y']
reverse(string<String>)
Reverses a string
reverse('geddy')
// => 'yddeg'
ltrim(string<String>, char<String>)
Ltrim trims char from the left of a string and returns it if no char is given it will trim spaces
ltrim('&geddy', '&')
// => 'geddy'
ltrim(' geddy')
// => 'geddy'
rtrim(string<String>, char<String>)
Rtrim trims char from the right of a string and returns it if no char is given it will trim spaces
rtrim('geddy&', '&')
// => 'geddy'
rtrim('geddy ')
// => 'geddy'
trim(string<String>, char<String>)
Trim trims char from the left and right of a string and returns it if no char is given it will trim spaces
trim('&&&&geddy&', '&')
// => 'geddy'
trim(' geddy ')
// => 'geddy'
chop(string<String>)
Returns a new String with the last character removed. If the string ends with \r\n, both characters are removed. Applying chop to an empty string returns an empty string.
chop('geddy&')
// => 'geddy'
lpad(string<String>, char<String>, width<Number>)
Lpad adds char to the left of string until the length of string is more than width
lpad('geddy', '&', 6)
// => '&geddy'
rpad(string<String>, char<String>, width<Number>)
Rpad adds char to the right of string until the length of string is more than width
rpad('geddy', '&', 7)
// => 'geddy&'
pad(string<String>, char<String>, width<Number>)
Pad adds char to the left and right of string until the length of string is more than width
rpad('geddy', '&', 6)
// => '&geddy&'
truncate(string<String>, options<Integer/Object>, callback<Function>)
length [Integer] Length the output string will be(Default: string.length)len [Integer] Alias for lengthomission [String] Replace last characters with an omission(Default: '...')ellipsis [String] Alias for omission(Default: '...')seperator [String/RegExp] Break the truncated test at the nearest seperatorTruncates a given string after a specified length if string is longer than length. The last characters will be replaced with an omission for a total length not exceeding length. If callback is given it will fire if string is truncated.
truncate('Once upon a time in a world', { length: 10 })
// => 'Once up...'
truncate('Once upon a time in a world', { length: 10, omission: '///' })
// => 'Once up///'
truncate('Once upon a time in a world', { length: 15, seperator: /\s/ })
// => 'Once upon a...'
truncate('Once upon a time in a world', { length: 15, seperator: ' ' })
// => 'Once upon a...'
truncate('<p>Once upon a time in a world</p>', { length: 20 })
// => '<p>Once upon a ti...'
truncateHTML(string<String>, options<Integer/Object>, callback<Function>)
once [Boolean] If true, it will only be truncated once, otherwise the(Default: false)Truncates a given string inside HTML tags after a specified length if string is longer than length. The last characters will be replaced with an omission for a total length not exceeding length. If callback is given it will fire if string is truncated. If once` is true only the first string in the first HTML tags will be truncated leaving the others as they were
truncateHTML('<p>Once upon a time in a world</p>', { length: 10 })
// => '<p>Once up...</p>'
truncateHTML('<p>Once upon a time <small>in a world</small></p>', { length: 10 })
// => '<p>Once up...<small>in a wo...</small></p>'
truncateHTML('<p>Once upon a time <small>in a world</small></p>', { length: 10, once: true })
// => '<p>Once up...<small>in a world</small></p>'
nl2br(string<String>)
Nl2br returns a string where all newline chars are turned into line break HTML tags
nl2br("geddy\n")
// => 'geddy<br />'
snakeize(string<String>, separ='_'<String>)
Snakeize converts camelCase and CamelCase strings to snake_case strings
snakeize("geddyJs")
// => 'geddy_js'
snakeize("GeddyJs")
// => 'geddy_js'
camelize(string<String>, options<Object>)
initialCap [Boolean] If initialCap is true the returnedleadingUnderscore [Boolean] If leadingUnderscore os true then ifCamelize takes a string and optional options and returns a camelCase version of the given string
camelize("geddy_js")
// => 'geddyJs'
camelize("geddy_js", {initialCap: true})
// => 'GeddyJs'
camelize("geddy_js", {leadingUnderscore: true})
// => 'geddyJs'
camelize("_geddy_js", {leadingUnderscore: true})
// => '_geddyJs'
decapitalize(string<String>)
Decapitalize returns the given string with the first letter uncapitalized.
decapitalize("Geddy")
// => 'geddy'
capitalize(string<String>)
capitalize returns the given string with the first letter capitalized.
decapitalize("geddy")
// => 'Geddy'
dasherize(string<String>, replace='-'<String>)
Dasherize returns the given string converting camelCase and snakeCase to dashes or replace them with the replace character.
dasherize("geddyJs")
// => 'geddy-js'
dasherize("geddyJs", "_")
// => 'geddy_js'
include(searchIn<String>, searchFor<String>)
Searches for a particular string in another string
include('geddy', 'js')
// => false
include('geddyjs', 'js')
// => true
getInflections(string<String>, options<Object>)
initialCap [Boolean]Inflection returns an object that contains different inflections created from the given name
getInflections('user')
// => {filename: { ... }, constructor: { ... }, property: { ... }}
Geddy provides built-in authentication which integrates with Passport to allow auth against either local accounts or third-party social services like Facebook and Twitter.
To set up a new Geddy app with built-in authentication, create your application
like normal, then run the geddy auth command inside, like so:
$ geddy app by_tor
$ cd by_tor
$ geddy auth
This will pull down Geddy-Passport using NPM, and install all the needed code into your app. This includes the needed Passport libraries, and the Geddy models and controllers for the local User accounts and the login process.
The geddy auth generator should only be used in a new Geddy app. If you
run it inside an existing app, it may overwrite existing files that you wanted
to keep.
If you need to add auth to an existing app, you can take a look at the Geddy-Passport project, which is itself a Geddy app scaffold, and use the pieces you need.
You'll need to add the settings for Passport in your config/secrets.json file. That includes the redirect locations for after an auth failure/success, and the OAuth keys for your app. The setting will look similar to this:
{
"passport": {
"successRedirect": "/",
"failureRedirect": "/login",
"twitter": {
"consumerKey": "XXXXXX",
"consumerSecret": "XXXXXX"
},
"facebook": {
"clientID": "XXXXXX",
"clientSecret": "XXXXXX"
},
"yammer": {
"clientID": "XXXXXX",
"clientSecret": "XXXXXX"
}
}
}
Local User accounts just go through the usual RESTful actions you'd get in a normal Geddy resource. Start at "/users/add" to create a new User. You can modify "/app/models/user.js" to add any other properties you want.
A successful login with a third-party service like Facebook or Twitter will create a linked local User account if one does not exist.
After a user successfully authenticates, she will end up redirected to the
successRedirect you've specified, and there will be two new items in the
user's session:
Use a before-filter, and redirect to the login page if there is no userId in
the user's session. If there is a userId, that means the user is
authenticated. There is a built-in reequireAuth function in the Passport
helper-library, which does just this.
The User controller for local accounts is protected like this:
var passport = require('../helpers/passport')
, cryptPass = passport.cryptPass
, requireAuth = passport.requireAuth;
var Users = function () {
this.before(requireAuth, {
except: ['add', 'create']
});
// Rest of controller omitted
This allows new accounts to be created, because the 'add' and 'create' actions are exempted, but only authenticated users can view or update existing users.