3
3
4
4
var NoActionException = require ( './exceptions/NoAction' )
5
5
, Class = require ( 'uberclass' )
6
- , path = require ( 'path' ) ;
6
+ , path = require ( 'path' )
7
+ , util = require ( 'util' )
8
+ , i = require ( 'i' ) ( )
9
+ , debug = require ( 'debug' ) ( 'clever-controller' )
10
+ , routedControllers = [ ] ;
7
11
8
12
module . exports = Class . extend (
9
13
/* @Static */
@@ -22,57 +26,92 @@ module.exports = Class.extend(
22
26
23
27
setup : function ( ) {
24
28
var self = this ;
25
- if ( typeof injector !== 'undefined' && this . autoRouting !== false && this . route !== false ) {
26
- injector . inject ( function ( app ) {
27
- self . autoRoute ( app ) ;
28
- } ) ;
29
+ if ( this . autoRouting !== false && this . route !== false && routedControllers . indexOf ( this . route ) === - 1 ) {
30
+ // Do not route multiple times
31
+ routedControllers . push ( this . route ) ;
32
+
33
+ if ( typeof this . app !== 'undefined' ) {
34
+
35
+ // app has been provided for us in the Static of this Controller so we can attach our routes to it
36
+ this . autoRoute ( this . app ) ;
37
+
38
+ } else if ( typeof injector !== 'undefined' ) {
39
+
40
+ // Use the clever-injector to get the express app so we can attach our routes to it
41
+ injector . inject ( function ( app ) {
42
+ self . autoRoute ( app ) ;
43
+ } ) ;
44
+
45
+ }
29
46
}
30
47
} ,
31
48
32
49
autoRoute : function ( app ) {
33
- var middleware = [ ] ;
50
+ var middleware = [ ]
51
+ , routes = this . route . split ( '|' )
52
+
53
+ debug ( 'Autorouting for route ' + routes . join ( ', ' ) ) ;
34
54
55
+ // Check for middleware that we need to put before the actual attach()
35
56
if ( this . autoRouting instanceof Array ) {
57
+ debug ( 'Found middleware for ' + routes . join ( ', ' ) + ' - ' + util . inspect ( this . autoRouting ) . replace ( / \n / ig, ' ' ) ) ;
58
+
36
59
this . autoRouting . forEach ( function ( mw ) {
37
60
middleware . push ( typeof mw === 'string' ? this . callback ( mw ) : mw ) ;
38
61
} . bind ( this ) ) ;
39
62
}
40
63
64
+ // Add our attach() function
41
65
middleware . push ( this . attach ( ) ) ;
42
66
43
- app . all . apply ( app , [ [ this . route , ':action' , ':id?' ] . join ( '/' ) ] . concat ( middleware ) ) ; // /example/:action/:id?
44
- app . all . apply ( app , [ [ this . route , ':action?' ] . join ( '/' ) ] . concat ( middleware ) ) ; // /example/?:action?
67
+ // Bind the actual routes
68
+ routes . forEach ( function ( route ) {
69
+ var actionIdRoute = [ route , ':action' , ':id?' ] . join ( '/' )
70
+ , actionRoute = [ route , ':action?' ] . join ( '/' ) ;
71
+
72
+ debug ( 'Attaching route ' + actionIdRoute ) ;
73
+ app . all . apply ( app , [ actionIdRoute ] . concat ( middleware ) ) ; // /example/:action/:id?
74
+
75
+ debug ( 'Attaching route ' + actionRoute ) ;
76
+ app . all . apply ( app , [ actionRoute ] . concat ( middleware ) ) ; // /example/?:action?
77
+ } ) ;
45
78
} ,
46
79
47
80
extend : function ( ) {
48
81
var extendingArgs = [ ] . slice . call ( arguments )
49
82
, autoRouting = ( extendingArgs . length === 2 )
50
83
? extendingArgs [ 0 ] . autoRouting !== false
51
- : false
84
+ : this . autoRouting
52
85
, definedRoute = ( extendingArgs . length === 2 )
53
86
? extendingArgs [ 0 ] . route !== undefined
54
- : false ;
87
+ : this . route ;
55
88
89
+ // Figure out if we are autoRouting and do not have a defined route already
56
90
if ( autoRouting && ! definedRoute ) {
57
91
var stack = new Error ( ) . stack . split ( '\n' )
58
- , stack = stack . splice ( 1 , stack . length - 1 )
92
+ , stack = stack . splice ( 1 , stack . length - 1 )
59
93
, extendingFilePath = false
60
94
, extendingFileName = false
61
95
, route = null ;
62
96
97
+ // Walk backwards over the stack to find the filename where this is defined
63
98
while ( stack . length > 0 && extendingFilePath === false ) {
64
99
var file = stack . shift ( ) ;
65
100
if ( ! / c l e v e r - c o n t r o l l e r / ig. test ( file ) && ! / u b e r c l a s s / ig. test ( file ) ) {
66
101
if ( / \( ( [ ^ \[ \: ] + ) .* \) / ig. test ( file ) ) {
67
102
extendingFilePath = RegExp . $1 ;
68
- extendingFileName = path . basename ( extendingFilePath ) . replace ( / ( c o n t r o l l e r ) ? . j s / ig , '' ) . toLowerCase ( ) ;
103
+ extendingFileName = path . basename ( extendingFilePath ) ;
69
104
}
70
105
}
71
106
}
72
107
73
- if ( [ '' , 'controller' ] . indexOf ( extendingFileName ) === - 1 && this . route === false ) {
74
- route = [ '/' , extendingFileName ] . join ( '' ) ;
75
- // debug( 'Binding automatic route name??' )
108
+ // Determine the route names if we have found a file
109
+ if ( [ '' , 'controller.js' ] . indexOf ( extendingFileName . toLowerCase ( ) ) === - 1 ) {
110
+ var singular = i . singularize ( extendingFileName . replace ( / ( c o n t r o l l e r ) ? .j s / ig, '' ) . toLowerCase ( ) )
111
+ , plural = i . pluralize ( singular ) ;
112
+
113
+ route = [ '/' , singular , '|' , '/' , plural ] . join ( '' ) ;
114
+
76
115
if ( extendingArgs . length === 2 ) {
77
116
extendingArgs [ 0 ] . route = route ;
78
117
} else {
@@ -81,7 +120,7 @@ module.exports = Class.extend(
81
120
}
82
121
}
83
122
84
-
123
+ // Call extend on the parent
85
124
return this . _super . apply ( this , extendingArgs ) ;
86
125
}
87
126
} ,
@@ -95,115 +134,128 @@ module.exports = Class.extend(
95
134
96
135
setup : function ( req , res , next ) {
97
136
try {
98
- return this . performanceSafeSetup ( req , res , next ) ;
99
- } catch ( e ) {
100
- return [ e ] ;
137
+ return this . performanceSafeSetup ( req , res , next ) ;
138
+ } catch ( e ) {
139
+ return [ e ] ;
101
140
}
102
141
} ,
103
142
104
- performanceSafeSetup : function ( req , res , next ) {
105
- var method = null ,
106
- funcName = null ;
143
+ performanceSafeSetup : function ( req , res , next ) {
144
+ var method = null
145
+ , funcName = null
146
+ , parts = null ;
107
147
108
148
this . next = next ;
109
149
this . req = req ;
110
150
this . res = res ;
111
151
112
152
// Override routes where you attach specifically to a single route
113
- if ( this . Class . actionRouting && / \/ / . test ( this . req . url ) ) {
114
- var parts = this . req . url . split ( '/' ) ;
115
- funcName = parts [ parts . length - 1 ] ;
153
+ if ( this . Class . actionRouting && / \/ / . test ( this . req . url ) ) {
154
+ parts = this . req . url . split ( '/' ) ;
155
+ funcName = parts [ parts . length - 1 ] ;
116
156
117
157
if ( / \# | \? / . test ( funcName ) ) {
118
- funcName = funcName . split ( / \# | \? / ) [ 0 ] ;
158
+ funcName = funcName . split ( / \# | \? / ) [ 0 ] ;
119
159
}
120
160
121
- if ( isNaN ( funcName ) ) {
161
+ if ( isNaN ( funcName ) ) {
122
162
funcName = funcName + 'Action' ;
123
- if ( typeof this [ funcName ] == 'function' ) {
124
- return [ null , funcName , next ] ;
163
+ if ( typeof this [ funcName ] == 'function' ) {
164
+ debug ( 'actionRouting mapped to ' + funcName ) ;
165
+
166
+ return [ null , funcName , next ] ;
125
167
}
126
168
}
127
169
}
128
170
129
171
// Route based on an action first if we can
130
- if ( this . Class . actionRouting && typeof this . req . params !== 'undefined' && typeof this . req . params . action !== 'undefined' ) {
172
+ if ( this . Class . actionRouting && typeof this . req . params !== 'undefined' && typeof this . req . params . action !== 'undefined' ) {
131
173
// Action Defined Routing
132
- if ( isNaN ( this . req . params . action ) ) {
174
+ if ( isNaN ( this . req . params . action ) ) {
133
175
funcName = this . req . params . action + 'Action' ;
134
176
135
- if ( typeof this [ funcName ] == 'function' ) {
136
- return [ null , funcName , next ] ;
177
+ if ( typeof this [ funcName ] == 'function' ) {
178
+ debug ( 'actionRouting mapped to ' + funcName ) ;
179
+ return [ null , funcName , next ] ;
137
180
} else {
138
181
throw new NoActionException ( ) ;
139
182
}
140
183
} else {
141
184
// HTTP Method Based Routing
142
185
method = this . req . method . toLowerCase ( ) + 'Action' ;
143
- if ( typeof this [ method ] == 'function' ) {
186
+ if ( typeof this [ method ] == 'function' ) {
187
+ debug ( 'http method route mapped to ' + method ) ;
144
188
145
189
this . req . params . id = this . req . params . action ;
146
190
delete this . req . params . action ;
147
191
148
- return [ null , method , next ] ;
192
+ return [ null , method , next ] ;
149
193
} else {
150
194
throw new NoActionException ( ) ;
151
195
}
152
196
}
153
197
}
154
198
155
199
// Route based on the HTTP Method, otherwise throw an exception
156
- if ( this . Class . restfulRouting ) {
157
- if ( this . isGet ( ) && ( this . req . params === undefined || this . req . params . id === undefined ) && typeof this . listAction === 'function' ) {
200
+ if ( this . Class . restfulRouting ) {
201
+ if ( this . isGet ( ) && ( this . req . params === undefined || this . req . params . id === undefined ) && typeof this . listAction === 'function' ) {
158
202
method = 'listAction' ;
203
+
204
+ debug ( 'restfulRouting mapped to ' + method ) ;
159
205
} else {
160
206
method = this . req . method . toLowerCase ( ) + 'Action' ;
161
- if ( typeof this [ method ] != 'function' ) {
207
+ if ( typeof this [ method ] != 'function' ) {
162
208
throw new NoActionException ( ) ;
163
209
}
210
+
211
+ debug ( 'restfulRouting mapped to ' + method ) ;
164
212
}
165
213
}
166
214
167
215
// If we got this far without an action but with a method, then route based on that
168
- return [ null , method , next ] ;
216
+ return [ null , method , next ] ;
169
217
} ,
170
218
171
- init : function ( error , method , next ) {
172
- if ( error && error instanceof NoActionException ) {
219
+ init : function ( error , method , next ) {
220
+ if ( error && error instanceof NoActionException ) {
221
+ debug ( 'No route mapping found, calling next()' ) ;
222
+
173
223
this . next ( ) ;
174
224
} else {
175
225
try {
176
- if ( error )
226
+ if ( error )
177
227
throw error ;
178
228
179
- if ( method !== null ) {
229
+ if ( method !== null ) {
180
230
this . action = method ;
181
- this [ method ] ( this . req , this . res ) ;
231
+
232
+ debug ( 'calling ' + this . action ) ;
233
+ this [ method ] ( this . req , this . res ) ;
182
234
} else {
183
235
this . next ( ) ;
184
236
}
185
237
186
- } catch ( e ) {
187
- this . handleException ( e ) ;
238
+ } catch ( e ) {
239
+ this . handleException ( e ) ;
188
240
}
189
241
}
190
242
} ,
191
243
192
- send : function ( content , code , type ) {
244
+ send : function ( content , code , type ) {
193
245
var toCall = type || this . resFunc ;
194
- if ( code ) {
195
- this . res [ toCall ] ( code , content ) ;
246
+ if ( code ) {
247
+ this . res [ toCall ] ( code , content ) ;
196
248
} else {
197
- this . res [ toCall ] ( content ) ;
249
+ this . res [ toCall ] ( content ) ;
198
250
}
199
251
} ,
200
252
201
- render : function ( template , data ) {
202
- this . res . render ( template , data ) ;
253
+ render : function ( template , data ) {
254
+ this . res . render ( template , data ) ;
203
255
} ,
204
256
205
- handleException : function ( exception ) {
206
- this . send ( { error : 'Unhandled exception: ' + exception , stack : exception . stack } , 500 ) ;
257
+ handleException : function ( exception ) {
258
+ this . send ( { error : 'Unhandled exception: ' + exception , stack : exception . stack } , 500 ) ;
207
259
} ,
208
260
209
261
isGet : function ( ) {
0 commit comments