UNPKG

@balderdash/sails-edge

Version:

API-driven framework for building realtime apps, using MVC conventions (based on Express and Socket.io)

247 lines (193 loc) 7.46 kB
/** * Module dependencies. */ var _ = require('lodash'), util = require('sails-util'); /** * Expose route parser. * @type {Function} */ module.exports = function (sails) { return interpretRouteSyntax; /** * interpretRouteSyntax * * "Teach" router to understand references to controllers. * * @param {[type]} route [description] * @return {[type]} [description] * @api private */ function interpretRouteSyntax (route) { var target = route.target, path = route.path, verb = route.verb, options = route.options; if (_.isObject(target) && !_.isFunction(target) && !_.isArray(target)) { // Merge target into `options` to get hold of relevant // route options: options = _.merge(options, target); // Support { controller: 'FooController' } notation if (!_.isUndefined(target.controller)) { return bindController(path, target, verb, options); } // Support resourceful sub-mappings for verbless routes // e.g. '/someRoute': { post: 'FooController.bar', get: '...', /* ... */ } // If verb was manually specified in route (e.g. `get /someRoute`), ignore the sub-mappings if ( !options.detectedVerb ) { if ( target.get ) { sails.router.bind (path, target['get'],'get', options); } if ( target.post ) { sails.router.bind (path, target['post'],'post', options); } if ( target.put ) { sails.router.bind (path, target['put'],'put', options); } if ( target['delete'] ) { sails.router.bind (path, target['delete'],'delete', options); } // TODO: if there is a legitimate use case for it, add other HTTP verbs here for completeness. } // Lone action syntax, e.g.: // '/someRoute': { action: 'find', model: 'foo' } // // (useful for explicitly routing a URL to a blueprint action) if ( !_.isUndefined(target.action) ) { // Merge target def. into route options: options.action = target.action; return bindBlueprintAction(path, target.action, verb, options); } } // Support string ('FooController.bar') notation if (_.isString(target)) { // Handle dot notation var parsedTarget = target.match(/^([^.]+)\.?([^.]*)?$/); // If target matches a controller (or, if views hook enabled, a view) // go ahead and assume that this is a dot notation route var controllerId = util.normalizeControllerId(parsedTarget[1]); var actionId = _.isString(parsedTarget[2]) ? parsedTarget[2].toLowerCase() : 'index'; // If this is a known controller, bind it if ( controllerId && ( sails.middleware.controllers[controllerId] || (sails.config.hooks.views.blueprints && sails.middleware.views[controllerId]) ) ) { return bindController (path, { controller: controllerId, action: actionId }, verb, options); } } // Ignore unknown route syntax // If it needs to be understood by another hook, the hook would have also received // the typeUnknown event, so we're done. return; } /** * Bind route to a controller/action. * * @param {[type]} path [description] * @param {[type]} target [description] * @param {[type]} verb [description] * @param {[type]} options[description] * @return {[type]} [description] * @api private */ function bindController ( path, target, verb, options ) { // Normalize controller and action ids var controllerId = util.normalizeControllerId(target.controller); var actionId = _.isString(target.action) ? target.action.toLowerCase() : null; // Look up appropriate controller/action and make sure it exists var controller = sails.middleware.controllers[controllerId]; // Fall back to matching view if (!controller) { controller = sails.middleware.views[controllerId]; } // If a controller and/or action was specified, // but it's not a match, warn the user if ( ! ( controller && _.isObject(controller) )) { sails.after('lifted', function () { sails.log.error( 'Ignored attempt to bind route (' + path + ') to unknown controller ::', controllerId+'.' ); }); return; } if ( actionId && !controller[actionId] ) { sails.after('lifted', function () { sails.log.error( 'Ignored attempt to bind route (' + path + ') to unknown controller.action ::', controllerId + '.' + (actionId || 'index') ); }); return; } // (if unspecified, default actionId to 'index' actionId = actionId || 'index'; // Merge the target controller/action into our route options: options.controller = controllerId; options.action = actionId; // Determine the model connected to this controller either by: // -> on the routes config // -> on the controller var modelId = options.model || controllerId; // If the orm hook is enabled, it has already been loaded by this time, // so just double-check to see if the attached model exists in `sails.models`. if (sails.hooks.orm && sails.models && sails.models[modelId]) { // If a model with matching identity exists, // extend route options with the id of the model. options.model = modelId; var Model = sails.models[modelId]; // Mix in the known associations for this model to the route options. options = _.merge({ associations: _.cloneDeep(Model.associations) }, options); // Mix in the relevant blueprint config options = _.defaults(options, { populate: sails.config.blueprints.populate, defaultLimit: sails.config.blueprints.defaultLimit }); } // 1. Bind the specified `action` var subTarget = controller[actionId]; if (_.isArray(subTarget)) { _.each(subTarget, function bindEachMiddlewareInSubTarget (fn) { sails.router.bind(path, controllerHandler(fn), verb, options); }); return; } // -- or -- // 2. Bind a controller which is actually a function to the destination route (rare) sails.router.bind(path, controllerHandler(subTarget), verb, options); // Wrap up the controller middleware to supply access to // the original target when requests comes in function controllerHandler (originalFn) { if ( !_.isFunction(originalFn) ) { sails.after('lifted', function () { sails.log.error( 'In '+controllerId + '.' + actionId+', ignored invalid attempt to bind route to a non-function controller:', originalFn, 'for path: ', path, verb ? ('and verb: ' + verb) : ''); }); return; } // Bind intercepted middleware function to route return originalFn; } return; } /** * Bind specified blueprint action to the specified route. * * @param {[type]} path [description] * @param {[type]} blueprintActionID [description] * @param {[type]} verb [description] * @param {[type]} options [description] * @return {[type]} [description] */ function bindBlueprintAction (path, blueprintActionID, verb, options){ // Look up appropriate blueprint action and make sure it exists var blueprint = sails.middleware.blueprints[blueprintActionID]; // If a 'blueprint' was specified, but it doesn't exist, warn the user and ignore it. if ( ! ( blueprint && _.isFunction(blueprint) )) { sails.after('lifted', function () { sails.log.error( 'Ignored attempt to bind route (' + path + ') to unknown blueprint action (`'+blueprintActionID+'`).' ); }); return; } sails.router.bind(path, blueprint, verb, options); } };