pakyow-core 0.8rc1 → 0.8.rc4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|