App

An app is a class where you define and implement your core business logic. App it self is an event emitter. Properties can be overridden by subclassing existent app.

Define app

You can define an App like this:


  var App = require('genji').App;
  var BlogApp = App(/* instance properties */ properties);

Let's go through a simple example to see how we can define an app.


var App = require('genji').App;

var BlogApp = App({// instance properties object
    /**
     * Name of your app
     */
    name: 'Blog',

    /**
     * App constructor function
     */
    init: function(db) {
      this.db = db;
    },

    /**
     * Create a blog post
     */
    createPost: function(title, content, callback) {
      // Make the post object
      var post = {title: title, content: content, created: new Date()};
      // save to the db
      this.db.save(post).then(function(result){
        // error first callback style
        callback(null, result);
      });
    }
  });

The BlogApp you just defined can be used as a standalone application class. It means you don't need the http stack to run your app code. Once you get the BlogApp class, you initialize and use it just like any other classes. The init function will be called on initialization and it's optional.

Handle result

There are two different ways to handle result of the instance function. One is to add callback as the last argument and handle result inline. Another is to listen to the event emitted from object of app instance.

Inline callback style


var myBlog =  new BlogApp(db);

// create a blog post in db
myBlog.createPost('My awesome post', 'Some contents', function(err, result) {
  // handle error and result here
  // this == myBlog, so you can emit event manually
  this.emit('createPost', err, result);
});

Event style

Default callback

Sometime you don't care about if the post is saved or not. And you may wish to handle the result in other part of your code. So when you call instance function and the last argument is not a callback, genji will generate a default callback for you. You call the callback as usual and an event with the name of the instance method will be triggered.


// the event callback's argument is same as how you call the callback inside the "createPost" function
myBlog.on('createPost', function(err, result) {
  // handle the event
});

//...

// create and forget
myBlog.createPost('I do not care about the result', 'Yes, there is no callback after me.');

There is one exception. When your instance function is synchronized and returns non-undefined value on calling. The event/callback will not be emitted/invoked in async operation.


  getDB: function(cb) {
    cb(); // will call `theCallback`, if no callback gived event will be emitted
    process.nextTick(function(){
      cb(); // `theCallback` will not be called end emit event
    });
    return this.db;
  }

  function theCallback() {
    // this function will never be called in async operation
  }

  var db = myBlog.getDB(theCallback);

Delegation

You can delegate all the events to other event emitter object by setting the delegate property to that emitter.


var otherEmitter = new EventEmitter();

myBlog.delegate = otherEmitter;

// event name will be prefixed by app name on delegation by default
otherEmitter.on('Blog:createPost', function(err, result) {
  // handle the event
});

myBlog.on('createPost', function(err, result) {
  // this will never be called as you allready delegate events to `otherEmitter`
});

myBlog.createPost('title', 'content');

Extending

It's easy to extend an existent app class, use the app class you want to extend just like you use App:


var AwesomeBlogApp = BlogApp({

    name: 'AwesomeBlog',

    createPost: function(title, content, callback) {
      // ...
    },

    getPost: function(id, callback) {
      // ...
    }
  });

Conventions

App introduced a very thin mechanism to organize business logic code. To make app works simply and efficiently with other part of system, here are the conventions you may need to know and understand.

Session

Although we call defined app class, but actually we use individial instance functions in a functional programming style in conjuction with Site and Router. This allow genji to reuse a single app instance for multiple requests instead of constructing for each one of them. You may noticed that, there's no session info passed to createPost in the above example, this is not possible in a real world application. So Genji put the session object which may contains credential or other user specific info as the first argument of app's instance function when working with Site. So your instance function will have some sort of session first callback last style of function signature.


createPost: function(session, title, content, callback) {
  if (session.group === 'author' && session.user) {
    // Make the post object with user info
    var post = {
      user: session.user,
      title: title, content: content, created: new Date()
    };
    this.db.save(post).then(function(result){
      callback(null, result);
    });
  } else {
    // do something
  }
}

To make the app class more reusable, don't put any http stack object as the function argument (e.g. request/response object of node.js etc.). That will make you loose the flexiblity and coupled with http stack. Leave that job to genji.Site to make you life easier.

The this object

You can always use this refer to the instance of app inside of instance function, callback function and event listening function.

Naming and url mapping

Site use name property of app and it's functions' name for mapping url automatically. For example:

Error first callback style

We follow the native node.js api's callback style, which put the error object at the first argument of a callback function. It's true for the event listening function as well.

API

App is exposed by require('genji').App. Inherits from EventEmitter.

Properties

Methods