Skip to content

Commit 69bddff

Browse files
committed
Controller hook (while bloated) works again with dynamic routing. Implemented sails.after() for handling until()-type eventing.
1 parent f73918e commit 69bddff

File tree

24 files changed

+505
-464
lines changed

24 files changed

+505
-464
lines changed

lib/configuration/defaults.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ module.exports = function (sails) {
4545

4646
// Default hooks
4747
hooks: {
48-
views: require('../hooks/views')(),
49-
notFound: require('../hooks/notFound')(),
50-
dynamicRoutes: require('../hooks/dynamicRoutes')(),
51-
error: require('../hooks/error')(),
52-
csrf: require('../hooks/csrf')()
48+
// views: require('../hooks/views')(),
49+
// notFound: require('../hooks/notFound')(),
50+
// dynamicRoutes: require('../hooks/dynamicRoutes')(),
51+
// error: require('../hooks/error')(),
52+
// csrf: require('../hooks/csrf')()
5353
},
5454

5555
// Port to run this app on

lib/configuration/load.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module.exports = function (sails) {
66

77
var _ = require( 'lodash' ),
88
async = require('async'),
9-
CaptainsLog = require('../util/logger')(sails),
9+
CaptainsLog = require('../logger')(sails),
1010
ModuleLoader = require('../moduleloader');
1111

1212

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

lib/hooks/controllers/index.js

+363
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
module.exports = function (sails) {
2+
3+
4+
/**
5+
* Module dependencies.
6+
*/
7+
8+
var _ = require( 'lodash' ),
9+
async = require( 'async' ),
10+
util = require( '../../util' ),
11+
Modules = require( '../../moduleloader' );
12+
13+
14+
15+
/**
16+
* Expose `Controller` hook definition
17+
*/
18+
19+
return {
20+
21+
22+
/**
23+
* Middleware that available as part of the public API
24+
*/
25+
26+
middleware: {},
27+
28+
29+
30+
/**
31+
* Routes to bind before or after routing
32+
*/
33+
34+
routes: {
35+
36+
before: {},
37+
38+
after: {}
39+
},
40+
41+
42+
/**
43+
* Wipe everything and (re)load middleware from controllers, policies, config, and views.
44+
*
45+
* @api private
46+
*/
47+
48+
loadMiddleware: function (cb) {
49+
var self = this;
50+
51+
sails.log.verbose('Building middleware registry...');
52+
53+
async.auto({
54+
55+
policies: [function (cb) {
56+
sails.log.verbose('Loading app policies...');
57+
58+
// Load policy modules
59+
sails.policies = Modules.optional({
60+
dirname : sails.config.paths.policies,
61+
filter : /(.+)\.(js|coffee)$/,
62+
replaceExpr : null
63+
});
64+
65+
// Register policies
66+
_.extend(self.middleware, sails.policies);
67+
68+
cb();
69+
}],
70+
71+
views: ['policies', function (cb) {
72+
73+
// Load views, just so we know whether they exist or not
74+
sails.views = Modules.optional({
75+
dirname : sails.config.paths.views,
76+
filter : /(.+)\..+$/,
77+
replaceExpr : null,
78+
dontLoad : true
79+
});
80+
81+
// If there are any matching views which don't have an action
82+
// create middleware to serve them
83+
_.each(sails.views, function (view, controllerId) {
84+
85+
// Create middleware for a top-level view
86+
if (view === true) {
87+
self.middleware[controllerId] = ViewMiddleware;
88+
}
89+
90+
// Create middleware for each subview
91+
else {
92+
self.middleware[controllerId] = {};
93+
for (var actionId in sails.views[controllerId]) {
94+
self.middleware[controllerId][actionId] = ViewMiddleware;
95+
}
96+
}
97+
98+
});
99+
100+
cb();
101+
}],
102+
103+
controllers: ['policies', 'views', function (cb) {
104+
105+
sails.log.verbose('Loading app controllers...');
106+
107+
// Load app controllers
108+
sails.controllers = Modules.optional({
109+
dirname : sails.config.paths.controllers,
110+
filter : /(.+)Controller\.(js|coffee)$/,
111+
replaceExpr : /Controller/
112+
});
113+
114+
// Get federated controllers where actions are specified each in their own file
115+
var federatedControllers = Modules.optional({
116+
dirname : sails.config.paths.controllers,
117+
pathFilter : /(.+)\/(.+)\.(js|coffee)$/
118+
});
119+
sails.controllers = _.extend(sails.controllers,federatedControllers);
120+
121+
122+
// Register controllers
123+
_.each(sails.controllers, function (controller, controllerId) {
124+
125+
// Override whatever was here before
126+
if (!_.isObject(self.middleware[controllerId])) {
127+
self.middleware[controllerId] = {};
128+
}
129+
130+
// Mix in middleware from controllers
131+
_.each(controller, function (action, actionId) {
132+
133+
// If the action is set to `false`, explicitly disable it
134+
if (action === false) {
135+
delete self.middleware[controllerId][actionId];
136+
}
137+
138+
// Otherwise mix it in
139+
else if (_.isFunction(action)) {
140+
self.middleware[controllerId][actionId] = action;
141+
}
142+
143+
});
144+
145+
146+
147+
////////////////////////////////////////////////////////
148+
// (LEGACY SUPPORT)
149+
// Prepend policies to chain, as per policy configuration
150+
151+
var controllerPolicy = sails.config.policies[controllerId];
152+
153+
// If a policy doesn't exist for this controller, use '*'
154+
if ( _.isUndefined(controllerPolicy) ) {
155+
controllerPolicy = sails.config.policies['*'];
156+
}
157+
158+
// Normalize policy to an array
159+
controllerPolicy = normalizePolicy( controllerPolicy );
160+
161+
// If this is a top-level policy, apply it immediately
162+
if ( _.isArray(controllerPolicy) ) {
163+
164+
// If this controller is a container object, apply the policy to all the actions
165+
if ( _.isObject(self.middleware[controllerId]) ) {
166+
_.each(self.middleware[controllerId], function (action, actionId) {
167+
self.middleware[controllerId][actionId] = controllerPolicy.concat([self.middleware[controllerId][actionId]]);
168+
});
169+
}
170+
171+
// Otherwise apply the policy directly to the controller
172+
else if ( _.isFunction(self.middleware[controllerId]) ) {
173+
self.middleware[controllerId] = controllerPolicy.concat([self.middleware[controllerId]]);
174+
}
175+
}
176+
177+
// If this is NOT a top-level policy, and merely a container of other policies,
178+
// iterate through each of this controller's actions and apply policies in a way that makes sense
179+
else {
180+
_.each(self.middleware[controllerId], function (action, actionId) {
181+
182+
var actionPolicy = sails.config.policies[controllerId][actionId];
183+
184+
// If a policy doesn't exist for this controller, use the controller-local '*'
185+
if ( _.isUndefined(actionPolicy) ) {
186+
actionPolicy = sails.config.policies[controllerId]['*'];
187+
}
188+
189+
// if THAT doesn't exist, use the global '*' policy
190+
if ( _.isUndefined(actionPolicy) ) {
191+
actionPolicy = sails.config.policies['*'];
192+
}
193+
194+
// Normalize action policy to an array
195+
actionPolicy = normalizePolicy( actionPolicy );
196+
197+
self.middleware[controllerId][actionId] = actionPolicy.concat([self.middleware[controllerId][actionId]]);
198+
});
199+
}
200+
201+
////////////////////////////////////////////////////////
202+
203+
});
204+
205+
cb();
206+
}]
207+
208+
}, cb);
209+
},
210+
211+
212+
213+
214+
/**
215+
* Generate resourceful routes based on modules
216+
*
217+
* @api private
218+
*/
219+
220+
autoRoute: function (cb) {
221+
222+
var self;
223+
224+
// Start iterating through controllers
225+
_.each(sails.controllers, function (controller, controllerId) {
226+
227+
// Instead of using the actual controller definition,
228+
// look up the version in the middleware registry,
229+
// since it might have policies attached
230+
controller = this.middleware[controllerId];
231+
232+
// If a controller is the middleware itself,
233+
// create a route for it directly, then bail out
234+
if (_.isFunction(controller) || _.isArray(controller) ) {
235+
this.routes.after['/' + controllerId] = controller;
236+
return;
237+
}
238+
239+
// Build routes for each action
240+
_.each(controller, function (target, actionId) {
241+
242+
// If this isn't a valid target, bail out
243+
if (! (_.isFunction(target) || _.isArray(target)) ) {
244+
sails.log.warn('Action ('+actionId+') in "'+controllerId+'" could not be dynamically routed because it isn\'t an array or a function.');
245+
return;
246+
}
247+
248+
// Check for verb in actionId
249+
var detectedVerb = util.detectVerb(actionId);
250+
actionId = detectedVerb.original;
251+
var verb = detectedVerb.verb;
252+
253+
// If a verb is set, the prefix looks like `get /`
254+
// otherwise, it's just a trailing slash
255+
var prefix = verb ? verb + ' /' : '/';
256+
257+
// Bind dynamic routes
258+
if (actionId === 'index') {
259+
this.routes.after[prefix + controllerId] = target;
260+
}
261+
this.routes.after[prefix + controllerId + '/' + actionId] = target;
262+
263+
}, this);
264+
265+
}, this);
266+
267+
268+
// If the views hook is enabled, or when it is, also auto-bind views
269+
sails.after('hook:load:views', function () {
270+
271+
// If there are any matching views which don't have an action
272+
// create middleware to serve them
273+
_.each(sails.views, function (view, controllerId) {
274+
275+
// Create middleware for a top-level view
276+
if (view === true) {
277+
self.routes.after['/' + controllerId] = self.middleware[controllerId];
278+
return;
279+
}
280+
281+
// Create middleware for each subview
282+
else {
283+
// Build routes for each action
284+
for (var actionId in sails.views[controllerId]) {
285+
286+
if (actionId === 'index') {
287+
self.routes.after['get /' + controllerId] = self.middleware[controllerId][actionId];
288+
}
289+
self.routes.after['get /' + controllerId + '/' + actionId] = self.middleware[controllerId][actionId];
290+
}
291+
}
292+
293+
}, self);
294+
});
295+
296+
cb(); // Done.
297+
},
298+
299+
300+
/**
301+
* Initialize is fired first thing when the hook is loaded
302+
*
303+
* @api public
304+
*/
305+
306+
initialize: function (cb) {
307+
async.series([
308+
this.loadMiddleware,
309+
this.autoRoute
310+
], cb);
311+
}
312+
};
313+
314+
315+
316+
317+
/**
318+
* Convert policy into array notation
319+
*
320+
* @param {Object} options
321+
* @api private
322+
*/
323+
324+
function normalizePolicy ( policy ) {
325+
326+
// Recursively normalize lists of policies
327+
if (_.isArray(policy)) {
328+
for (var i in policy) {
329+
normalizePolicy(policy[i]);
330+
}
331+
return policy;
332+
}
333+
334+
else if (_.isString(policy) || _.isFunction(policy)) {
335+
return [ policy ];
336+
}
337+
338+
else if (!policy) {
339+
return [ function (req,res,next) { res.send(403); } ];
340+
}
341+
342+
else if (policy === true) {
343+
return [ function (req,res,next) { next(); } ];
344+
}
345+
346+
sails.log.error('Cannot map invalid policy: ', policy);
347+
return [function (req,res) {
348+
throw new Error('Invalid policy: ' + policy);
349+
}];
350+
}
351+
352+
353+
354+
/**
355+
* Simple view middleware used to serve views w/o controllers
356+
*/
357+
358+
function ViewMiddleware (req,res) {
359+
res.view();
360+
}
361+
362+
};
363+

0 commit comments

Comments
 (0)