pakyow-core 0.8rc1 → 0.8.rc4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/pakyow-core/lib/core/app.rb +448 -0
- data/pakyow-core/lib/core/base.rb +35 -12
- data/pakyow-core/lib/core/{configuration → config}/app.rb +38 -35
- data/pakyow-core/lib/core/config/base.rb +30 -0
- data/pakyow-core/lib/core/config/cookies.rb +21 -0
- data/pakyow-core/lib/core/config/logger.rb +37 -0
- data/pakyow-core/lib/core/{configuration → config}/server.rb +3 -1
- data/pakyow-core/lib/core/exceptions.rb +3 -0
- data/pakyow-core/lib/core/helpers.rb +20 -16
- data/pakyow-core/lib/core/loader.rb +1 -1
- data/pakyow-core/lib/core/middleware/logger.rb +170 -16
- data/pakyow-core/lib/core/middleware/static.rb +20 -8
- data/pakyow-core/lib/core/multilog.rb +19 -0
- data/pakyow-core/lib/core/request.rb +52 -30
- data/pakyow-core/lib/core/route_eval.rb +390 -0
- data/pakyow-core/lib/core/route_lookup.rb +17 -5
- data/pakyow-core/lib/core/route_set.rb +17 -210
- data/pakyow-core/lib/core/route_template_defaults.rb +18 -12
- data/pakyow-core/lib/core/router.rb +41 -31
- data/pakyow-core/lib/utils/dir.rb +19 -0
- data/pakyow-core/lib/utils/hash.rb +14 -4
- data/pakyow-core/lib/views/errors/404.html +77 -0
- data/pakyow-core/lib/views/errors/500.html +56 -0
- metadata +30 -53
- data/pakyow-core/bin/pakyow +0 -18
- data/pakyow-core/lib/commands/USAGE +0 -9
- data/pakyow-core/lib/commands/USAGE-CONSOLE +0 -12
- data/pakyow-core/lib/commands/USAGE-NEW +0 -11
- data/pakyow-core/lib/commands/USAGE-SERVER +0 -12
- data/pakyow-core/lib/commands/console.rb +0 -18
- data/pakyow-core/lib/commands/server.rb +0 -8
- data/pakyow-core/lib/core/application.rb +0 -330
- data/pakyow-core/lib/core/cache.rb +0 -25
- data/pakyow-core/lib/core/configuration/base.rb +0 -31
- data/pakyow-core/lib/core/fn_context.rb +0 -5
- data/pakyow-core/lib/core/log.rb +0 -39
- data/pakyow-core/lib/core/middleware/not_found.rb +0 -40
- data/pakyow-core/lib/core/middleware/presenter.rb +0 -25
- data/pakyow-core/lib/core/middleware/router.rb +0 -33
- data/pakyow-core/lib/core/middleware/setup.rb +0 -15
- data/pakyow-core/lib/core/presenter_base.rb +0 -11
- data/pakyow-core/lib/core/route_template.rb +0 -77
- data/pakyow-core/lib/generators/pakyow/app/app_generator.rb +0 -36
- data/pakyow-core/lib/generators/pakyow/app/templates/README +0 -54
- data/pakyow-core/lib/generators/pakyow/app/templates/app.rb +0 -12
- data/pakyow-core/lib/generators/pakyow/app/templates/config.ru +0 -3
- data/pakyow-core/lib/generators/pakyow/app/templates/public/favicon.ico +0 -0
- data/pakyow-core/lib/generators/pakyow/app/templates/rakefile +0 -2
- data/pakyow-core/lib/generators/pakyow/app/templates/views/main.html +0 -1
- data/pakyow-core/lib/generators/pakyow/app/templates/views/pakyow.html +0 -12
@@ -0,0 +1,390 @@
|
|
1
|
+
module Pakyow
|
2
|
+
class RouteEval
|
3
|
+
attr_reader :path
|
4
|
+
|
5
|
+
def initialize(path = '/', hooks = { :before => [], :after => []}, fns = {}, group_name = nil, namespace = false)
|
6
|
+
@path = path
|
7
|
+
@scope = {:path => path, :hooks => hooks, :group_name => group_name, :namespace => namespace}
|
8
|
+
@routes = {:get => [], :post => [], :put => [], :delete => []}
|
9
|
+
@lookup = {:routes => {}, :grouped => {}}
|
10
|
+
@fns = fns
|
11
|
+
@groups = {}
|
12
|
+
@templates = {}
|
13
|
+
@handlers = []
|
14
|
+
|
15
|
+
eval(&RouteTemplateDefaults.defaults)
|
16
|
+
end
|
17
|
+
|
18
|
+
def eval(template = false, &block)
|
19
|
+
# if we're evaling a template, need to push
|
20
|
+
# member routes to the end (they're always
|
21
|
+
# created first, but need to be the last out)
|
22
|
+
if template
|
23
|
+
@member_routes = @routes
|
24
|
+
@routes = {:get => [], :post => [], :put => [], :delete => []}
|
25
|
+
end
|
26
|
+
|
27
|
+
instance_exec(&block)
|
28
|
+
|
29
|
+
if template
|
30
|
+
merge_routes(@routes, @member_routes)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def merge(fns, routes, handlers, lookup)
|
35
|
+
# routes.merge!(@routes)
|
36
|
+
merge_routes(routes, @routes)
|
37
|
+
handlers.concat(@handlers)
|
38
|
+
# lookup.merge!(@lookup)
|
39
|
+
fns.merge!(@fns)
|
40
|
+
|
41
|
+
merge_lookup(lookup, @lookup)
|
42
|
+
|
43
|
+
return fns, routes, handlers, lookup
|
44
|
+
end
|
45
|
+
|
46
|
+
# Creates or retreives a named route function. When retrieving,
|
47
|
+
#
|
48
|
+
def fn(name, &block)
|
49
|
+
@fns[name] = block and return if block
|
50
|
+
@fns[name]
|
51
|
+
end
|
52
|
+
|
53
|
+
# def action(name, &block)
|
54
|
+
# @fns[name] = block and return if block
|
55
|
+
# @fns[name]
|
56
|
+
# end
|
57
|
+
|
58
|
+
def action(method, *args, &block)
|
59
|
+
fn, hooks = self.class.parse_action_args(args)
|
60
|
+
fns = block_given? ? [block] : [fn]
|
61
|
+
@fns[method] = build_fns(fns, hooks)
|
62
|
+
end
|
63
|
+
|
64
|
+
def default(*args, &block)
|
65
|
+
register_route(:get, '/', :default, *args, &block)
|
66
|
+
end
|
67
|
+
|
68
|
+
def get(*args, &block)
|
69
|
+
register_route(:get, *args, &block)
|
70
|
+
end
|
71
|
+
|
72
|
+
def put(*args, &block)
|
73
|
+
register_route(:put, *args, &block)
|
74
|
+
end
|
75
|
+
|
76
|
+
def post(*args, &block)
|
77
|
+
register_route(:post, *args, &block)
|
78
|
+
end
|
79
|
+
|
80
|
+
def delete(*args, &block)
|
81
|
+
register_route(:delete, *args, &block)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Creates a handler.
|
85
|
+
#
|
86
|
+
def handler(*args, &block)
|
87
|
+
name, code, fns, hooks = self.class.parse_handler_args(args)
|
88
|
+
fns ||= []
|
89
|
+
# add passed block to fns
|
90
|
+
fns << block if block_given?
|
91
|
+
|
92
|
+
# build the final list of fns
|
93
|
+
fns = build_fns(fns, hooks)
|
94
|
+
|
95
|
+
@handlers << [name, code, fns]
|
96
|
+
end
|
97
|
+
|
98
|
+
def group(*args, &block)
|
99
|
+
name, hooks = self.class.parse_group_args(args)
|
100
|
+
|
101
|
+
evaluator = RouteEval.new(@scope[:path], merge_hooks(hooks, @scope[:hooks]), @fns, name)
|
102
|
+
evaluator.eval(&block)
|
103
|
+
|
104
|
+
@fns, @routes, @handlers, @lookup = evaluator.merge(@fns, @routes, @handlers, @lookup)
|
105
|
+
end
|
106
|
+
|
107
|
+
def namespace(*args, &block)
|
108
|
+
path, name, hooks = self.class.parse_namespace_args(args)
|
109
|
+
|
110
|
+
#TODO shouldn't this be in parse_namespace_args?
|
111
|
+
hooks = name if name.is_a?(Hash)
|
112
|
+
|
113
|
+
evaluator = RouteEval.new(File.join(@scope[:path], path), merge_hooks(hooks, @scope[:hooks]), @fns, name, true)
|
114
|
+
evaluator.eval(&block)
|
115
|
+
|
116
|
+
@fns, @routes, @handlers, @lookup = evaluator.merge(@fns, @routes, @handlers, @lookup)
|
117
|
+
end
|
118
|
+
|
119
|
+
def template(*args, &block)
|
120
|
+
name, hooks = self.class.parse_template_args(args)
|
121
|
+
@templates[name] = [hooks, block]
|
122
|
+
end
|
123
|
+
|
124
|
+
def expand(t_name, g_name, *args, &block)
|
125
|
+
path, hooks = self.class.parse_expansion_args(args)
|
126
|
+
|
127
|
+
template = @templates[t_name]
|
128
|
+
|
129
|
+
evaluator = RouteEval.new(File.join(@scope[:path], path), merge_hooks(merge_hooks(hooks, @scope[:hooks]), template[0]), @fns, g_name, true)
|
130
|
+
evaluator.eval(&block)
|
131
|
+
evaluator.eval(true, &template[1])
|
132
|
+
|
133
|
+
@fns, @routes, @handlers, @lookup = evaluator.merge(@fns, @routes, @handlers, @lookup)
|
134
|
+
end
|
135
|
+
|
136
|
+
protected
|
137
|
+
|
138
|
+
def merge_hooks(h1, h2)
|
139
|
+
# normalize
|
140
|
+
h1 = normalize_hooks(h1)
|
141
|
+
h2 = normalize_hooks(h2)
|
142
|
+
|
143
|
+
# merge
|
144
|
+
h1[:before].concat(h2[:before])
|
145
|
+
h1[:after].concat(h2[:after])
|
146
|
+
h1[:around].concat(h2[:around])
|
147
|
+
|
148
|
+
return h1
|
149
|
+
end
|
150
|
+
|
151
|
+
def merge_routes(r1, r2)
|
152
|
+
r1[:get].concat(r2[:get])
|
153
|
+
r1[:put].concat(r2[:put])
|
154
|
+
r1[:post].concat(r2[:post])
|
155
|
+
r1[:delete].concat(r2[:delete])
|
156
|
+
|
157
|
+
return r1
|
158
|
+
end
|
159
|
+
|
160
|
+
def merge_lookup(l1, l2)
|
161
|
+
l1[:routes].merge!(l2[:routes])
|
162
|
+
l1[:grouped].merge!(l2[:grouped])
|
163
|
+
|
164
|
+
return l1
|
165
|
+
end
|
166
|
+
|
167
|
+
def copy_hooks(hooks)
|
168
|
+
{
|
169
|
+
:before => (hooks[:before] || []).dup,
|
170
|
+
:after => (hooks[:after] || []).dup,
|
171
|
+
:around => (hooks[:around] || []).dup,
|
172
|
+
}
|
173
|
+
end
|
174
|
+
|
175
|
+
def register_route(method, *args, &block)
|
176
|
+
path, name, fns, hooks = self.class.parse_route_args(args)
|
177
|
+
|
178
|
+
fns ||= []
|
179
|
+
# add passed block to fns
|
180
|
+
fns << block if block_given?
|
181
|
+
|
182
|
+
# merge route hooks with scoped hooks
|
183
|
+
hooks = merge_hooks(hooks || {}, @scope[:hooks])
|
184
|
+
|
185
|
+
# build the final list of fns
|
186
|
+
fns = build_fns(fns, hooks)
|
187
|
+
|
188
|
+
if path.is_a?(Regexp)
|
189
|
+
regex = path
|
190
|
+
vars = []
|
191
|
+
else
|
192
|
+
# prepend scope path if we're in a scope
|
193
|
+
path = File.join(@scope[:path], path)
|
194
|
+
path = StringUtils.normalize_path(path)
|
195
|
+
|
196
|
+
# get regex and vars for path
|
197
|
+
regex, vars = build_route_matcher(path)
|
198
|
+
end
|
199
|
+
|
200
|
+
# create the route tuple
|
201
|
+
route = [regex, vars, name, fns, path]
|
202
|
+
|
203
|
+
@routes[method] << route
|
204
|
+
|
205
|
+
# add route to lookup, unless it's namespaced (because
|
206
|
+
# then it can only be accessed through the grouping)
|
207
|
+
unless namespace?
|
208
|
+
@lookup[:routes][name] = route
|
209
|
+
end
|
210
|
+
|
211
|
+
# add to grouped lookup, if we're in a group
|
212
|
+
if group?
|
213
|
+
(@lookup[:grouped][@scope[:group_name]] ||= {})[name] = route
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def build_route_matcher(path)
|
218
|
+
return path, [] if path.is_a?(Regexp)
|
219
|
+
|
220
|
+
# check for vars
|
221
|
+
return path, [] unless path[0,1] == ':' || path.index('/:')
|
222
|
+
|
223
|
+
# we have vars
|
224
|
+
vars = []
|
225
|
+
regex_route = path
|
226
|
+
route_segments = path.split('/')
|
227
|
+
route_segments.each_with_index { |segment, i|
|
228
|
+
if segment.include?(':')
|
229
|
+
var = segment.gsub(':', '')
|
230
|
+
vars << { :var => var.to_sym, :url_position => i }
|
231
|
+
regex_route = regex_route.sub(segment, '(?<' + var + '>(\w|[-.~:@!$\'\(\)\*\+,;])*)')
|
232
|
+
end
|
233
|
+
}
|
234
|
+
reg = Regexp.new("^#{regex_route}$")
|
235
|
+
return reg, vars
|
236
|
+
end
|
237
|
+
|
238
|
+
def group?
|
239
|
+
!@scope[:group_name].nil?
|
240
|
+
end
|
241
|
+
|
242
|
+
def namespace?
|
243
|
+
@scope[:namespace]
|
244
|
+
end
|
245
|
+
|
246
|
+
# yields current path to the block for modification,
|
247
|
+
# then updates paths for member routes
|
248
|
+
def nested_path
|
249
|
+
new_path = yield(@scope[:group_name], @path)
|
250
|
+
|
251
|
+
# update paths of member routes
|
252
|
+
@member_routes.each {|type,routes|
|
253
|
+
routes.each { |route|
|
254
|
+
path = StringUtils.normalize_path(File.join(new_path, route[4].gsub(/^#{StringUtils.normalize_path(@path)}/, '')))
|
255
|
+
regex, vars = build_route_matcher(path)
|
256
|
+
route[0] = regex
|
257
|
+
route[1] = vars
|
258
|
+
route[4] = path
|
259
|
+
}
|
260
|
+
}
|
261
|
+
|
262
|
+
@path = new_path
|
263
|
+
end
|
264
|
+
|
265
|
+
def build_fns(main_fns, hooks)
|
266
|
+
hooks = normalize_hooks(hooks)
|
267
|
+
fns = []
|
268
|
+
fns.concat(hooks[:around]) if hooks && hooks[:around]
|
269
|
+
fns.concat(hooks[:before]) if hooks && hooks[:before]
|
270
|
+
fns.concat(main_fns) if main_fns
|
271
|
+
fns.concat(hooks[:after]) if hooks && hooks[:after]
|
272
|
+
fns.concat(hooks[:around]) if hooks && hooks[:around]
|
273
|
+
fns
|
274
|
+
end
|
275
|
+
|
276
|
+
def normalize_hooks(hooks)
|
277
|
+
hooks ||= {}
|
278
|
+
|
279
|
+
[:before, :after, :around].each do |type|
|
280
|
+
# force array
|
281
|
+
hooks[type] = Array(hooks[type])
|
282
|
+
|
283
|
+
# lookup hook fns if not already a Proc
|
284
|
+
hooks[type] = hooks[type].map do |hook|
|
285
|
+
hook.is_a?(Symbol) ? fn(hook) : hook
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
return hooks
|
290
|
+
end
|
291
|
+
|
292
|
+
class << self
|
293
|
+
def parse_route_args(args)
|
294
|
+
ret = []
|
295
|
+
args.each { |arg|
|
296
|
+
if arg.is_a?(Hash) # we have hooks
|
297
|
+
ret[3] = arg
|
298
|
+
elsif arg.is_a?(Array) # we have fns
|
299
|
+
ret[2] = arg
|
300
|
+
elsif arg.is_a?(Proc) # we have a fn
|
301
|
+
ret[2] = [arg]
|
302
|
+
elsif arg.is_a?(Symbol) # we have a name
|
303
|
+
ret[1] = arg
|
304
|
+
elsif !arg.nil? # we have a path
|
305
|
+
ret[0] = arg
|
306
|
+
end
|
307
|
+
}
|
308
|
+
ret
|
309
|
+
end
|
310
|
+
|
311
|
+
def parse_namespace_args(args)
|
312
|
+
ret = []
|
313
|
+
args.each { |arg|
|
314
|
+
if arg.is_a?(Hash) # we have hooks
|
315
|
+
ret[2] = arg
|
316
|
+
elsif arg.is_a?(Symbol) # we have a name
|
317
|
+
ret[1] = arg
|
318
|
+
elsif !arg.nil? # we have a path
|
319
|
+
ret[0] = arg
|
320
|
+
end
|
321
|
+
}
|
322
|
+
ret
|
323
|
+
end
|
324
|
+
|
325
|
+
def parse_group_args(args)
|
326
|
+
ret = []
|
327
|
+
args.each { |arg|
|
328
|
+
if arg.is_a?(Hash) # we have hooks
|
329
|
+
ret[1] = arg
|
330
|
+
elsif !arg.nil? # we have a name
|
331
|
+
ret[0] = arg
|
332
|
+
end
|
333
|
+
}
|
334
|
+
ret
|
335
|
+
end
|
336
|
+
|
337
|
+
def parse_handler_args(args)
|
338
|
+
ret = []
|
339
|
+
args.each { |arg|
|
340
|
+
if arg.is_a?(Hash) # we have hooks
|
341
|
+
ret[3] = arg
|
342
|
+
elsif arg.is_a?(Proc) # we have a fn
|
343
|
+
ret[2] = [arg]
|
344
|
+
elsif arg.is_a?(Integer) # we have a code
|
345
|
+
ret[1] = arg
|
346
|
+
elsif !arg.nil? # we have a name
|
347
|
+
ret[0] = arg
|
348
|
+
end
|
349
|
+
}
|
350
|
+
ret
|
351
|
+
end
|
352
|
+
|
353
|
+
def parse_template_args(args)
|
354
|
+
ret = []
|
355
|
+
args.each { |arg|
|
356
|
+
if arg.is_a?(Hash) # we have hooks
|
357
|
+
ret[1] = arg
|
358
|
+
elsif !arg.nil? # we have a name
|
359
|
+
ret[0] = arg
|
360
|
+
end
|
361
|
+
}
|
362
|
+
ret
|
363
|
+
end
|
364
|
+
|
365
|
+
def parse_expansion_args(args)
|
366
|
+
ret = []
|
367
|
+
args.each { |arg|
|
368
|
+
if arg.is_a?(Hash) # we have hooks
|
369
|
+
ret[1] = arg
|
370
|
+
elsif !arg.nil? # we have a path
|
371
|
+
ret[0] = arg
|
372
|
+
end
|
373
|
+
}
|
374
|
+
ret
|
375
|
+
end
|
376
|
+
|
377
|
+
def parse_action_args(args)
|
378
|
+
ret = []
|
379
|
+
args.each { |arg|
|
380
|
+
if arg.is_a?(Hash) # we have hooks
|
381
|
+
ret[1] = arg
|
382
|
+
elsif arg.is_a?(Proc) # we have a fn
|
383
|
+
ret[0] = arg
|
384
|
+
end
|
385
|
+
}
|
386
|
+
ret
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
@@ -7,8 +7,9 @@ module Pakyow
|
|
7
7
|
include Helpers
|
8
8
|
|
9
9
|
def path(name, data = nil)
|
10
|
-
route =
|
11
|
-
|
10
|
+
if route = get_named_route(name)
|
11
|
+
data ? populate(route, data) : File.join('/', route[4])
|
12
|
+
end
|
12
13
|
end
|
13
14
|
|
14
15
|
def group(name)
|
@@ -19,7 +20,11 @@ module Pakyow
|
|
19
20
|
protected
|
20
21
|
|
21
22
|
def get_named_route(name)
|
22
|
-
|
23
|
+
if defined? @group
|
24
|
+
Router.instance.route(name, @group)
|
25
|
+
else
|
26
|
+
Router.instance.route(name)
|
27
|
+
end
|
23
28
|
end
|
24
29
|
|
25
30
|
def populate(route, data = {})
|
@@ -28,10 +33,17 @@ module Pakyow
|
|
28
33
|
split_path = Request.split_url(route[4])
|
29
34
|
|
30
35
|
vars.each {|v|
|
31
|
-
split_path[v[:
|
36
|
+
split_path[v[:url_position]] = data.delete(v[:var])
|
32
37
|
}
|
33
38
|
|
34
|
-
File.join('/', split_path.join('/'))
|
39
|
+
populated = File.join('/', split_path.join('/'))
|
40
|
+
|
41
|
+
# add remaining data to query string
|
42
|
+
unless data.empty?
|
43
|
+
populated << '/?' + data.map { |k,v| "#{k}=#{v}" }.join('&')
|
44
|
+
end
|
45
|
+
|
46
|
+
return populated
|
35
47
|
end
|
36
48
|
end
|
37
49
|
end
|
@@ -1,118 +1,19 @@
|
|
1
1
|
module Pakyow
|
2
2
|
class RouteSet
|
3
|
+
attr_reader :routes, :lookup
|
4
|
+
|
3
5
|
def initialize
|
4
6
|
@routes = {:get => [], :post => [], :put => [], :delete => []}
|
5
|
-
|
6
|
-
@routes_by_name = {}
|
7
|
-
@grouped_routes_by_name = {}
|
8
|
-
|
9
|
-
@fns = {}
|
10
|
-
@groups = {}
|
11
|
-
|
12
|
-
@templates = {}
|
13
|
-
|
7
|
+
@lookup = { :routes => {}, :grouped => {}}
|
14
8
|
@handlers = []
|
15
|
-
|
16
|
-
@scope = {:name => nil, :path => '/', :hooks => {:before => [], :after => []}}
|
17
|
-
end
|
18
|
-
|
19
|
-
# Creates or retreives a named route function. When retrieving,
|
20
|
-
#
|
21
|
-
def fn(name, &block)
|
22
|
-
@fns[name] = block and return if block
|
23
|
-
|
24
|
-
#TODO rewrite to not return array
|
25
|
-
[@fns[name]]
|
26
|
-
end
|
27
|
-
|
28
|
-
def default(*args, &block)
|
29
|
-
self.register_route(:get, '/', *args, &block)
|
30
|
-
end
|
31
|
-
|
32
|
-
def get(*args, &block)
|
33
|
-
self.register_route(:get, *args, &block)
|
34
|
-
end
|
35
|
-
|
36
|
-
def put(*args, &block)
|
37
|
-
self.register_route(:put, *args, &block)
|
38
|
-
end
|
39
|
-
|
40
|
-
def post(*args, &block)
|
41
|
-
self.register_route(:post, *args, &block)
|
42
|
-
end
|
43
|
-
|
44
|
-
def delete(*args, &block)
|
45
|
-
self.register_route(:delete, *args, &block)
|
46
|
-
end
|
47
|
-
|
48
|
-
# Returns a lambda that routes request to a controller/action.
|
49
|
-
#TODO move to RouteHelpers
|
50
|
-
def call(controller, action)
|
51
|
-
lambda {
|
52
|
-
controller = Object.const_get(controller)
|
53
|
-
action ||= Configuration::Base.app.default_action
|
54
|
-
|
55
|
-
instance = controller.new
|
56
|
-
request.controller = instance
|
57
|
-
request.action = action
|
58
|
-
|
59
|
-
instance.send(action)
|
60
|
-
}
|
61
|
-
end
|
62
|
-
|
63
|
-
# Creates a handler.
|
64
|
-
#
|
65
|
-
def handler(name, *args, &block)
|
66
|
-
code, fn = args
|
67
|
-
|
68
|
-
fn = code and code = nil if code.is_a?(Proc)
|
69
|
-
fn = block if block_given?
|
70
|
-
|
71
|
-
@handlers << [name, code, [fn]]
|
72
|
-
end
|
73
|
-
|
74
|
-
def group(name, *args, &block)
|
75
|
-
# deep clone existing hooks to reset at the close of this group
|
76
|
-
original_hooks = Marshal.load(Marshal.dump(@scope[:hooks]))
|
77
|
-
|
78
|
-
@scope[:hooks] = self.merge_hooks(@scope[:hooks], args[0]) if @scope[:hooks] && args[0]
|
79
|
-
|
80
|
-
@scope[:name] = name
|
81
|
-
@groups[name] = []
|
82
|
-
@grouped_routes_by_name[name] = {}
|
83
|
-
|
84
|
-
self.instance_exec(&block)
|
85
|
-
@scope[:name] = nil
|
86
|
-
|
87
|
-
@scope[:hooks] = original_hooks
|
9
|
+
@fns = {}
|
88
10
|
end
|
89
11
|
|
90
|
-
def
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
original_path = @scope[:path]
|
95
|
-
@scope[:path] = File.join(@scope[:path], path)
|
96
|
-
|
97
|
-
self.group(name, hooks || {}, &block)
|
98
|
-
@scope[:path] = original_path
|
99
|
-
end
|
12
|
+
def eval(&block)
|
13
|
+
evaluator = RouteEval.new
|
14
|
+
evaluator.eval(&block)
|
100
15
|
|
101
|
-
|
102
|
-
@templates[name] = block
|
103
|
-
end
|
104
|
-
|
105
|
-
def expand(t_name, g_name, path = nil, data = nil, &block)
|
106
|
-
data = path and path = nil if path.is_a?(Hash)
|
107
|
-
|
108
|
-
# evaluate block in context of some class that implements
|
109
|
-
# method_missing to store map of functions
|
110
|
-
# (e.g. index, show)
|
111
|
-
t = RouteTemplate.new(block, g_name, path, self)
|
112
|
-
|
113
|
-
# evaluate template in same context, where func looks up funcs
|
114
|
-
# from map and extends get (and others) to add proper names
|
115
|
-
t.expand(@templates[t_name], data)
|
16
|
+
@fns, @routes, @handlers, @lookup = evaluator.merge(@fns, @routes, @handlers, @lookup)
|
116
17
|
end
|
117
18
|
|
118
19
|
# Returns a route tuple:
|
@@ -121,7 +22,7 @@ module Pakyow
|
|
121
22
|
def match(path, method)
|
122
23
|
path = StringUtils.normalize_path(path)
|
123
24
|
|
124
|
-
@routes[method.to_sym].each{|r|
|
25
|
+
@routes[method.to_sym].each{|r|
|
125
26
|
case r[0]
|
126
27
|
when Regexp
|
127
28
|
if data = r[0].match(path)
|
@@ -147,113 +48,19 @@ module Pakyow
|
|
147
48
|
|
148
49
|
# Name based route lookup
|
149
50
|
def route(name, group = nil)
|
150
|
-
return @
|
51
|
+
return @lookup[:routes][name] if group.nil?
|
151
52
|
|
152
|
-
if grouped_routes = @
|
153
|
-
grouped_routes[name]
|
53
|
+
if grouped_routes = @lookup[:grouped][group]
|
54
|
+
return grouped_routes[name]
|
55
|
+
else
|
56
|
+
#TODO error
|
154
57
|
end
|
155
58
|
end
|
156
59
|
|
157
|
-
protected
|
158
|
-
|
159
|
-
def merge_hooks(h1, h2)
|
160
|
-
# normalize
|
161
|
-
h1[:before] ||= []
|
162
|
-
h1[:after] ||= []
|
163
|
-
h1[:around] ||= []
|
164
|
-
h2[:before] ||= []
|
165
|
-
h2[:after] ||= []
|
166
|
-
h2[:around] ||= []
|
167
|
-
|
168
|
-
# merge
|
169
|
-
h1[:before].concat(h2[:before])
|
170
|
-
h1[:after].concat(h2[:after])
|
171
|
-
h1[:around].concat(h2[:around])
|
172
|
-
h1
|
173
|
-
end
|
174
|
-
|
175
|
-
def register_route(method, path, *args, &block)
|
176
|
-
name, fns, hooks = self.parse_route_args(args)
|
177
|
-
|
178
|
-
fns ||= []
|
179
|
-
# add passed block to fns
|
180
|
-
fns << block if block_given?
|
181
|
-
|
182
|
-
hooks = self.merge_hooks(hooks || {}, @scope[:hooks])
|
183
|
-
|
184
|
-
# build the final list of fns
|
185
|
-
fns = self.build_fns(fns, hooks)
|
186
|
-
|
187
|
-
# prepend scope path if we're in a scope
|
188
|
-
path = File.join(@scope[:path], path)
|
189
|
-
path = StringUtils.normalize_path(path)
|
190
|
-
|
191
|
-
# get regex and vars for path
|
192
|
-
regex, vars = self.build_route_matcher(path)
|
193
|
-
|
194
|
-
# create the route tuple
|
195
|
-
route = [regex, vars, name, fns, path]
|
196
|
-
|
197
|
-
@routes[method] << route
|
198
|
-
@routes_by_name[name] = route
|
199
|
-
|
200
|
-
# store group references if we're in a scope
|
201
|
-
return unless group = @scope[:name]
|
202
|
-
@groups[group] << route
|
203
|
-
@grouped_routes_by_name[group][name] = route
|
204
|
-
end
|
205
|
-
|
206
|
-
def parse_route_args(args)
|
207
|
-
ret = []
|
208
|
-
args.each { |arg|
|
209
|
-
if arg.is_a?(Hash) # we have hooks
|
210
|
-
ret[2] = arg
|
211
|
-
elsif arg.is_a?(Array) # we have fns
|
212
|
-
ret[1] = arg
|
213
|
-
elsif arg.is_a?(Proc) # we have a fn
|
214
|
-
ret[1] = [arg]
|
215
|
-
elsif !arg.nil? # we have a name
|
216
|
-
ret[0] = arg
|
217
|
-
end
|
218
|
-
}
|
219
|
-
ret
|
220
|
-
end
|
221
|
-
|
222
|
-
def build_fns(main_fns, hooks)
|
223
|
-
fns = []
|
224
|
-
fns.concat(hooks[:around]) if hooks && hooks[:around]
|
225
|
-
fns.concat(hooks[:before]) if hooks && hooks[:before]
|
226
|
-
fns.concat(main_fns) if main_fns
|
227
|
-
fns.concat(hooks[:after]) if hooks && hooks[:after]
|
228
|
-
fns.concat(hooks[:around]) if hooks && hooks[:around]
|
229
|
-
fns
|
230
|
-
end
|
231
|
-
|
232
|
-
def build_route_matcher(path)
|
233
|
-
return path, [] if path.is_a?(Regexp)
|
234
60
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
# we have vars
|
239
|
-
vars = []
|
240
|
-
position_counter = 1
|
241
|
-
regex_route = path
|
242
|
-
route_segments = path.split('/')
|
243
|
-
route_segments.each_with_index { |segment, i|
|
244
|
-
if segment.include?(':')
|
245
|
-
vars << { :position => position_counter, :var => segment.gsub(':', '').to_sym }
|
246
|
-
if i == route_segments.length-1 then
|
247
|
-
regex_route = regex_route.sub(segment, '((\w|[-.~:@!$\'\(\)\*\+,;])*)')
|
248
|
-
position_counter += 2
|
249
|
-
else
|
250
|
-
regex_route = regex_route.sub(segment, '((\w|[-.~:@!$\'\(\)\*\+,;])*)')
|
251
|
-
position_counter += 2
|
252
|
-
end
|
253
|
-
end
|
254
|
-
}
|
255
|
-
reg = Regexp.new("^#{regex_route}$")
|
256
|
-
return reg, vars
|
61
|
+
# Name based fn lookup
|
62
|
+
def fn(name)
|
63
|
+
return @fns[name]
|
257
64
|
end
|
258
65
|
end
|
259
66
|
end
|