diff --git a/lib/app.js b/lib/app.js index 54459ec6..ff7c3c76 100644 --- a/lib/app.js +++ b/lib/app.js @@ -35,12 +35,11 @@ var domain = require('domain') , StaticFileController = require('./controller/static_file_controller').StaticFileController , sessions = require('./sessions') - , CookieCollection = require('./cookies').CookieCollection , Request = require('./request').Request , Response = response.Response , InFlight = require('./in_flight').InFlight - , i18n = utils.i18n - , usingCoffee; // Global variable for CoffeeScript + , usingCoffee // Global variable for CoffeeScript + , controllerInit = require('./controller/init') // Set up a bunch of aliases geddy.inFlight = new InFlight(); @@ -339,72 +338,13 @@ var App = function () { }); } - , initBodyParse = function (params, reqObj, cb) { - var body = '' - , bodyParams - , contentType = reqObj.headers['content-type']; - - // If it's a plain form-post, save the request-body, and parse it into - // params as well - if ((reqObj.method == 'POST' || reqObj.method == 'PUT') && - (contentType && - (contentType.indexOf('form-urlencoded') > -1 || - contentType.indexOf('application/json') > -1))) { - - bodyParams = {}; - // Node 0.10, new streams - // FIXME: Assumes the entire request body is in the buffer, - // probably not right - if (typeof reqObj.read == 'function') { - reqObj.addListener('readable', function (data) { - body += reqObj.read(); - }); - } - // Node 0.8, old streams - else { - reqObj.addListener('data', function (data) { - body += data.toString(); - }); - } - // Parse the body into params once it's finished - reqObj.addListener('end', function () { - if (contentType.indexOf('form-urlencoded') > -1) { - bodyParams = querystring.parse(body); - } - else if (contentType.indexOf('application/json') > -1) { - try { - bodyParams = JSON.parse(body); - } - catch (e) {} - } - - geddy.mixin(params, bodyParams); - - reqObj.body = body; - - cb('parseBody'); - }); - } - else { - cb('parseBody'); - } - } - , getParams = function (router, reqUrl, method) { var params = router.first(reqUrl, method); params.controller = utils.string.camelize(params.controller, {initialCap: true}); return params; - } - - , enhanceParams = function (params, urlParams, reqObj, finish) { - // Merge params from the URL and the query-string to produce a - // Grand Unified Params object - geddy.mixin(params, urlParams); - initBodyParse(params, reqObj, finish); }; - this.router = null; this.modelRegistry = {}; this.templateRegistry = {}; @@ -548,25 +488,9 @@ var App = function () { , controllerName , actionName , err - , steps = { - parseBody: false - , sessions: false - } - , finish; - - finish = function (step) { - steps[step] = true; - for (var p in steps) { - if (!steps[p]) { - return false; - } - } - controllerInst._handleAction.call(controllerInst, params.action); - // TODO Replace this with readable-stream module for 0.8 support - if (typeof req.read != 'function') { - reqObj.sync(); // Flush buffered events and begin emitting - } - }; + , initKeys + , initializer + , cb; reqUrl = getUrl(req); urlParams = getUrlParams(reqUrl); @@ -580,51 +504,56 @@ var App = function () { params = getParams(self.router, reqUrl, method); if (params) { - enhanceParams(params, urlParams, reqObj, finish); - // Instantiate the controller controllerInst = controller.create(params.controller); if (controllerInst) { + // Enhance the parsed params with URL params + geddy.mixin(params, urlParams); + + // FIXME: Backward-compat shim for old action-name 'destroy' if (params.action == 'destroy' && - typeof controllerInst[params.action] != 'function') { + typeof controllerInst.destroy != 'function') { params.action = 'remove'; } if (typeof controllerInst[params.action] == 'function') { - controllerInst.accessTime = accessTime; - - // We have a controller instance; register it with the - // in-flight data - geddy.inFlight.updateEntry(reqObj._geddyId, { - controller: controllerInst + utils.mixin(controllerInst, { + app: self + , accessTime: accessTime + , request: reqObj + , response: respObj + , method: method + , params: params + , name: params.controller }); - controllerInst.cookies = new CookieCollection(req); - - if (geddy.config.sessions) { - controllerInst.session = - new sessions.Session(controllerInst, function () { - controllerInst.flash = controllerInst.session.flash; - finish('sessions'); - }); - } - else { - finish('sessions'); - } - - controllerInst.app = self; - - controllerInst.request = reqObj; reqObj.controller = controllerInst; - controllerInst.response = respObj; respObj.controller = controllerInst; - controllerInst.method = method; - controllerInst.params = params; - controllerInst.name = params.controller; - controllerInst.i18n = new i18n.I18n(controllerInst); + initKeys = [ + 'cookies' + , 'i18n' + , 'inFlight' + , 'parseBody' + , 'session' + ]; + + cb = function () { + controllerInst._handleAction.call(controllerInst, params.action); + // TODO Replace this with readable-stream module for 0.8 support + if (typeof req.read != 'function') { + reqObj.sync(); // Flush buffered events and begin emitting + } + }; + initializer = new utils.async.Initializer(initKeys, cb); + + initKeys.forEach(function (key) { + controllerInit[key].call(controllerInst, function () { + initializer.complete(key); + }); + }); } // No action, 500 error @@ -644,7 +573,7 @@ var App = function () { } } - // Either static, 404, or 405 + // Either 405, static, or 404 else { self.handleNoMatchedRoute(method, reqUrl, params, reqObj, respObj); } diff --git a/lib/controller/init.js b/lib/controller/init.js new file mode 100644 index 00000000..1bce74ea --- /dev/null +++ b/lib/controller/init.js @@ -0,0 +1,96 @@ +var utils = require('utilities') + , sessions = require('../sessions') + , CookieCollection = require('../cookies').CookieCollection + , i18n = utils.i18n + , init; + +init = { + + cookies: function (cb) { + this.cookies = new CookieCollection(this.request); + cb(); + } + +, i18n: function (cb) { + this.i18n = new i18n.I18n(this); + cb(); + } + +, inFlight: function (cb) { + // Register controller with the in-flight data + // Shouldn't be necessary domains-based error-handling + geddy.inFlight.updateEntry(this.request._geddyId, { + controller: this + }); + cb(); + } + +, parseBody: function (cb) { + var body = '' + , bodyParams + , reqObj = this.request + , contentType = reqObj.headers['content-type']; + + // If it's a plain form-post, save the request-body, and parse it into + // params as well + if ((reqObj.method == 'POST' || reqObj.method == 'PUT') && + (contentType && + (contentType.indexOf('form-urlencoded') > -1 || + contentType.indexOf('application/json') > -1))) { + + bodyParams = {}; + // Node 0.10, new streams + // FIXME: Assumes the entire request body is in the buffer, + // probably not right + if (typeof reqObj.read == 'function') { + reqObj.addListener('readable', function (data) { + body += reqObj.read(); + }); + } + // Node 0.8, old streams + else { + reqObj.addListener('data', function (data) { + body += data.toString(); + }); + } + // Parse the body into params once it's finished + reqObj.addListener('end', function () { + if (contentType.indexOf('form-urlencoded') > -1) { + bodyParams = querystring.parse(body); + } + else if (contentType.indexOf('application/json') > -1) { + try { + bodyParams = JSON.parse(body); + } + catch (e) {} + } + + geddy.mixin(this.params, bodyParams); + + reqObj.body = body; + + cb(); + }); + } + else { + cb(); + } + } + +, session: function (cb) { + var self = this; + if (geddy.config.sessions) { + this.session = + new sessions.Session(this, function () { + self.flash = self.session.flash; + cb(); + }); + } + else { + cb(); + } + } + +}; + +module.exports = init;