pakyow-core 0.8.rc4 → 0.8.0
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 +4 -4
- data/pakyow-core/CHANGES +4 -0
- data/pakyow-core/lib/core/app.rb +51 -50
- data/pakyow-core/lib/core/app_context.rb +10 -0
- data/pakyow-core/lib/core/base.rb +4 -1
- data/pakyow-core/lib/core/errors.rb +13 -0
- data/pakyow-core/lib/core/helpers.rb +6 -3
- data/pakyow-core/lib/core/loader.rb +5 -5
- data/pakyow-core/lib/core/middleware/static.rb +3 -2
- data/pakyow-core/lib/core/request.rb +8 -9
- data/pakyow-core/lib/core/response.rb +12 -0
- data/pakyow-core/lib/core/route_eval.rb +252 -159
- data/pakyow-core/lib/core/route_lookup.rb +4 -5
- data/pakyow-core/lib/core/route_merger.rb +76 -0
- data/pakyow-core/lib/core/route_module.rb +21 -0
- data/pakyow-core/lib/core/route_set.rb +15 -8
- data/pakyow-core/lib/core/route_template_defaults.rb +29 -31
- data/pakyow-core/lib/core/router.rb +15 -10
- data/pakyow-core/lib/utils/dir.rb +6 -3
- data/pakyow-core/lib/utils/hash.rb +32 -29
- data/pakyow-core/lib/utils/string.rb +30 -27
- metadata +15 -12
- data/pakyow-core/lib/core/exceptions.rb +0 -3
@@ -2,5 +2,17 @@ module Pakyow
|
|
2
2
|
|
3
3
|
# The Response object.
|
4
4
|
class Response < Rack::Response
|
5
|
+
attr_reader :format
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
super
|
9
|
+
|
10
|
+
self["Content-Type"] ||= 'text/html'
|
11
|
+
end
|
12
|
+
|
13
|
+
def format=(format)
|
14
|
+
@format = format
|
15
|
+
self["Content-Type"] = Rack::Mime.mime_type(".#{format}")
|
16
|
+
end
|
5
17
|
end
|
6
18
|
end
|
@@ -1,84 +1,65 @@
|
|
1
1
|
module Pakyow
|
2
2
|
class RouteEval
|
3
|
-
|
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
|
3
|
+
include RouteMerger
|
17
4
|
|
18
|
-
|
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
|
5
|
+
attr_reader :path, :fns, :hooks, :group, :routes, :handlers, :lookup, :templates
|
26
6
|
|
27
|
-
|
7
|
+
HTTP_METHODS = [:get, :post, :put, :patch, :delete]
|
8
|
+
DEFAULT_MIXINS = ['Restful']
|
28
9
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
10
|
+
class << self
|
11
|
+
def with_defaults(*args)
|
12
|
+
instance = self.new(*args)
|
33
13
|
|
34
|
-
|
35
|
-
|
36
|
-
merge_routes(routes, @routes)
|
37
|
-
handlers.concat(@handlers)
|
38
|
-
# lookup.merge!(@lookup)
|
39
|
-
fns.merge!(@fns)
|
14
|
+
# Mixin defaults
|
15
|
+
DEFAULT_MIXINS.each { |mixin| instance.include(Pakyow::Routes.const_get(mixin)) }
|
40
16
|
|
41
|
-
|
17
|
+
return instance
|
18
|
+
end
|
42
19
|
|
43
|
-
|
44
|
-
|
20
|
+
def from_scope(route_eval, overrides = {})
|
21
|
+
args = [:path, :fns, :hooks, :templates, :group].inject([]) do |acc, arg|
|
22
|
+
acc << (overrides.fetch(arg) { route_eval.send(arg) })
|
23
|
+
end
|
45
24
|
|
46
|
-
|
47
|
-
|
48
|
-
def fn(name, &block)
|
49
|
-
@fns[name] = block and return if block
|
50
|
-
@fns[name]
|
25
|
+
instance = self.new(*args)
|
26
|
+
end
|
51
27
|
end
|
52
28
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
fns = block_given? ? [block] : [fn]
|
61
|
-
@fns[method] = build_fns(fns, hooks)
|
62
|
-
end
|
29
|
+
def initialize(path = '/', fns = nil, hooks = nil, templates = nil, group = nil)
|
30
|
+
@path = path
|
31
|
+
@fns = fns || {}
|
32
|
+
@routes = HTTP_METHODS.inject({}) { |acc, m| acc[m] = []; acc }
|
33
|
+
@hooks = hooks || { before: [], after: [] }
|
34
|
+
@templates = templates || {}
|
35
|
+
@group = group
|
63
36
|
|
64
|
-
|
65
|
-
|
37
|
+
@lookup = { routes: {}, grouped: {} }
|
38
|
+
@handlers = []
|
66
39
|
end
|
67
40
|
|
68
|
-
|
69
|
-
|
41
|
+
# Path for evals within this eval
|
42
|
+
#
|
43
|
+
def descendent_path
|
44
|
+
@descendent_path || @path
|
70
45
|
end
|
71
46
|
|
72
|
-
def
|
73
|
-
|
47
|
+
def include(route_module)
|
48
|
+
merge(route_module.route_eval)
|
74
49
|
end
|
75
50
|
|
76
|
-
def
|
77
|
-
|
51
|
+
def eval(&block)
|
52
|
+
instance_exec(&block)
|
78
53
|
end
|
79
54
|
|
80
|
-
|
81
|
-
|
55
|
+
# Creates or retreives a named route function. When retrieving,
|
56
|
+
#
|
57
|
+
def fn(name, &block)
|
58
|
+
if block_given?
|
59
|
+
@fns[name] = block
|
60
|
+
else
|
61
|
+
@fns[name]
|
62
|
+
end
|
82
63
|
end
|
83
64
|
|
84
65
|
# Creates a handler.
|
@@ -98,81 +79,68 @@ module Pakyow
|
|
98
79
|
def group(*args, &block)
|
99
80
|
name, hooks = self.class.parse_group_args(args)
|
100
81
|
|
101
|
-
evaluator = RouteEval.
|
82
|
+
evaluator = RouteEval.from_scope(self, path: descendent_path, group: name, hooks: hooks)
|
102
83
|
evaluator.eval(&block)
|
103
84
|
|
104
|
-
|
85
|
+
merge(evaluator)
|
105
86
|
end
|
106
87
|
|
107
88
|
def namespace(*args, &block)
|
108
89
|
path, name, hooks = self.class.parse_namespace_args(args)
|
109
90
|
|
110
|
-
|
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)
|
91
|
+
evaluator = RouteEval.from_scope(self, path: File.join(descendent_path, path), group: name, hooks: hooks)
|
114
92
|
evaluator.eval(&block)
|
115
93
|
|
116
|
-
|
94
|
+
merge(evaluator)
|
117
95
|
end
|
118
96
|
|
119
97
|
def template(*args, &block)
|
120
98
|
name, hooks = self.class.parse_template_args(args)
|
99
|
+
|
121
100
|
@templates[name] = [hooks, block]
|
122
101
|
end
|
123
102
|
|
124
|
-
def expand(t_name, g_name, *args, &block)
|
103
|
+
def expand(t_name, g_name = nil, *args, &block)
|
125
104
|
path, hooks = self.class.parse_expansion_args(args)
|
105
|
+
path ||= ''
|
126
106
|
|
127
107
|
template = @templates[t_name]
|
128
108
|
|
129
|
-
evaluator =
|
109
|
+
evaluator = RouteExpansionEval.from_scope(self, path: File.join(descendent_path, path), group: g_name, hooks: hooks)
|
110
|
+
evaluator.direct_path = path
|
111
|
+
evaluator.template = template
|
130
112
|
evaluator.eval(&block)
|
131
|
-
evaluator.eval(true, &template[1])
|
132
113
|
|
133
|
-
|
114
|
+
merge(evaluator)
|
134
115
|
end
|
135
116
|
|
136
|
-
|
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
|
117
|
+
def default(*args, &block)
|
118
|
+
build_route(:get, '/', :default, *args, &block)
|
149
119
|
end
|
150
120
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
r1[:delete].concat(r2[:delete])
|
156
|
-
|
157
|
-
return r1
|
121
|
+
HTTP_METHODS.each do |method|
|
122
|
+
define_method method do |*args, &block|
|
123
|
+
build_route(method, *args, &block)
|
124
|
+
end
|
158
125
|
end
|
159
126
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
127
|
+
# For the expansion of templates
|
128
|
+
def method_missing(method, *args, &block)
|
129
|
+
if template_defined?(method)
|
130
|
+
expand(method, *args, &block)
|
131
|
+
else
|
132
|
+
super
|
133
|
+
# action(method, *args, &block)
|
134
|
+
end
|
165
135
|
end
|
166
136
|
|
167
|
-
def
|
168
|
-
|
169
|
-
:before => (hooks[:before] || []).dup,
|
170
|
-
:after => (hooks[:after] || []).dup,
|
171
|
-
:around => (hooks[:around] || []).dup,
|
172
|
-
}
|
137
|
+
def template_defined?(template)
|
138
|
+
!@templates[template].nil?
|
173
139
|
end
|
174
140
|
|
175
|
-
|
141
|
+
protected
|
142
|
+
|
143
|
+
def build_route(method, *args, &block)
|
176
144
|
path, name, fns, hooks = self.class.parse_route_args(args)
|
177
145
|
|
178
146
|
fns ||= []
|
@@ -180,7 +148,7 @@ module Pakyow
|
|
180
148
|
fns << block if block_given?
|
181
149
|
|
182
150
|
# merge route hooks with scoped hooks
|
183
|
-
hooks = merge_hooks(hooks || {}, @
|
151
|
+
hooks = merge_hooks(hooks || {}, @hooks)
|
184
152
|
|
185
153
|
# build the final list of fns
|
186
154
|
fns = build_fns(fns, hooks)
|
@@ -190,28 +158,26 @@ module Pakyow
|
|
190
158
|
vars = []
|
191
159
|
else
|
192
160
|
# prepend scope path if we're in a scope
|
193
|
-
path = File.join(@
|
194
|
-
path =
|
161
|
+
path = File.join(@path, path)
|
162
|
+
path = Utils::String.normalize_path(path)
|
195
163
|
|
196
164
|
# get regex and vars for path
|
197
165
|
regex, vars = build_route_matcher(path)
|
198
166
|
end
|
199
167
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
@routes[method] << route
|
168
|
+
register_route([regex, vars, name, fns, path, method])
|
169
|
+
end
|
204
170
|
|
205
|
-
|
206
|
-
|
207
|
-
unless namespace?
|
208
|
-
@lookup[:routes][name] = route
|
209
|
-
end
|
171
|
+
def register_route(route)
|
172
|
+
@routes[route[5]] << route
|
210
173
|
|
211
|
-
# add to grouped lookup, if we're in a group
|
212
174
|
if group?
|
213
|
-
(@lookup[:grouped][@
|
175
|
+
bucket = (@lookup[:grouped][@group] ||= {})
|
176
|
+
else
|
177
|
+
bucket = @lookup[:routes]
|
214
178
|
end
|
179
|
+
|
180
|
+
bucket[route[2]] = route
|
215
181
|
end
|
216
182
|
|
217
183
|
def build_route_matcher(path)
|
@@ -236,30 +202,7 @@ module Pakyow
|
|
236
202
|
end
|
237
203
|
|
238
204
|
def group?
|
239
|
-
!@
|
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
|
205
|
+
!@group.nil?
|
263
206
|
end
|
264
207
|
|
265
208
|
def build_fns(main_fns, hooks)
|
@@ -273,22 +216,6 @@ module Pakyow
|
|
273
216
|
fns
|
274
217
|
end
|
275
218
|
|
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
219
|
class << self
|
293
220
|
def parse_route_args(args)
|
294
221
|
ret = []
|
@@ -373,7 +300,82 @@ module Pakyow
|
|
373
300
|
}
|
374
301
|
ret
|
375
302
|
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
class RouteExpansionEval < RouteEval
|
307
|
+
attr_writer :direct_path
|
376
308
|
|
309
|
+
def eval(&block)
|
310
|
+
@template_eval = RouteTemplateEval.from_scope(self, path: path, group: @group, hooks: @hooks)
|
311
|
+
@template_eval.direct_path = @direct_path
|
312
|
+
@template_eval.eval(&@template_block)
|
313
|
+
|
314
|
+
@path = @template_eval.routes_path
|
315
|
+
|
316
|
+
super
|
317
|
+
end
|
318
|
+
|
319
|
+
def template=(template)
|
320
|
+
@template_block = template[1]
|
321
|
+
|
322
|
+
@hooks = merge_hooks(@hooks, template[0])
|
323
|
+
end
|
324
|
+
|
325
|
+
def action(method, *args, &block)
|
326
|
+
fn, hooks = self.class.parse_action_args(args)
|
327
|
+
fn = block if block_given?
|
328
|
+
|
329
|
+
# get route info from template
|
330
|
+
route = @template_eval.route_for_action(method)
|
331
|
+
|
332
|
+
all_fns = route[3]
|
333
|
+
all_fns[:fns].unshift(fn) if fn
|
334
|
+
|
335
|
+
hooks = merge_hooks(hooks, all_fns[:hooks])
|
336
|
+
route[3] = build_fns(all_fns[:fns], hooks)
|
337
|
+
|
338
|
+
register_route(route)
|
339
|
+
end
|
340
|
+
|
341
|
+
def action_group(*args, &block)
|
342
|
+
name, hooks = self.class.parse_action_group_args(args)
|
343
|
+
group = @template_eval.group_named(name)
|
344
|
+
|
345
|
+
hooks = merge_hooks(hooks, group[0])
|
346
|
+
group(name, hooks, &block)
|
347
|
+
end
|
348
|
+
|
349
|
+
def action_namespace(*args, &block)
|
350
|
+
name, hooks = self.class.parse_action_namespace_args(args)
|
351
|
+
namespace = @template_eval.namespace_named(name)
|
352
|
+
|
353
|
+
hooks = merge_hooks(hooks, namespace[1])
|
354
|
+
namespace(name, namespace[0], hooks, &block)
|
355
|
+
end
|
356
|
+
|
357
|
+
def method_missing(method, *args, &block)
|
358
|
+
if @template_eval.has_action?(method)
|
359
|
+
action(method, *args, &block)
|
360
|
+
elsif @template_eval.has_namespace?(method)
|
361
|
+
action_namespace(method, *args, &block)
|
362
|
+
elsif @template_eval.has_group?(method)
|
363
|
+
action_group(method, *args, &block)
|
364
|
+
else
|
365
|
+
super
|
366
|
+
end
|
367
|
+
rescue NoMethodError
|
368
|
+
raise UnknownTemplatePart, "No action, namespace, or group named '#{method}'"
|
369
|
+
end
|
370
|
+
|
371
|
+
def expand(*args, &block)
|
372
|
+
args[2] = File.join(@template_eval.nested_path.gsub(@path, ''), args[2])
|
373
|
+
super(*args, &block)
|
374
|
+
end
|
375
|
+
|
376
|
+
private
|
377
|
+
|
378
|
+
class << self
|
377
379
|
def parse_action_args(args)
|
378
380
|
ret = []
|
379
381
|
args.each { |arg|
|
@@ -385,6 +387,97 @@ module Pakyow
|
|
385
387
|
}
|
386
388
|
ret
|
387
389
|
end
|
388
|
-
|
390
|
+
|
391
|
+
def parse_action_namespace_args(args)
|
392
|
+
ret = []
|
393
|
+
args.each { |arg|
|
394
|
+
if arg.is_a?(Hash) # we have hooks
|
395
|
+
ret[1] = arg
|
396
|
+
elsif arg.is_a?(Symbol) # we have a name
|
397
|
+
ret[0] = arg
|
398
|
+
end
|
399
|
+
}
|
400
|
+
ret
|
401
|
+
end
|
402
|
+
|
403
|
+
def parse_action_group_args(args)
|
404
|
+
ret = []
|
405
|
+
args.each { |arg|
|
406
|
+
if arg.is_a?(Hash) # we have hooks
|
407
|
+
ret[1] = arg
|
408
|
+
elsif !arg.nil? # we have a name
|
409
|
+
ret[0] = arg
|
410
|
+
end
|
411
|
+
}
|
412
|
+
ret
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
class RouteTemplateEval < RouteEval
|
418
|
+
attr_accessor :direct_path
|
419
|
+
|
420
|
+
def initialize(*args)
|
421
|
+
super
|
422
|
+
|
423
|
+
@groups = {}
|
424
|
+
@namespaces = {}
|
425
|
+
|
426
|
+
@routes_path = path
|
427
|
+
@nested_path = path
|
428
|
+
end
|
429
|
+
|
430
|
+
def has_action?(name)
|
431
|
+
!route_for_action(name).nil?
|
432
|
+
end
|
433
|
+
|
434
|
+
def has_group?(name)
|
435
|
+
!group_named(name).nil?
|
436
|
+
end
|
437
|
+
|
438
|
+
def has_namespace?(name)
|
439
|
+
!namespace_named(name).nil?
|
440
|
+
end
|
441
|
+
|
442
|
+
def route_for_action(name)
|
443
|
+
lookup.fetch(:grouped, {}).fetch(@group, {})[name]
|
444
|
+
end
|
445
|
+
|
446
|
+
def namespace_named(name)
|
447
|
+
@namespaces[name]
|
448
|
+
end
|
449
|
+
|
450
|
+
def group_named(name)
|
451
|
+
@groups[name]
|
452
|
+
end
|
453
|
+
|
454
|
+
def build_fns(fns, hooks)
|
455
|
+
{
|
456
|
+
fns: fns,
|
457
|
+
hooks: hooks,
|
458
|
+
}
|
459
|
+
end
|
460
|
+
|
461
|
+
def namespace(*args)
|
462
|
+
path, name, hooks = self.class.parse_namespace_args(args)
|
463
|
+
@namespaces[name] = [path, hooks]
|
464
|
+
end
|
465
|
+
|
466
|
+
def group(*args)
|
467
|
+
name, hooks = self.class.parse_group_args(args)
|
468
|
+
@groups[name] = [hooks]
|
469
|
+
end
|
470
|
+
|
471
|
+
def routes_path(&block)
|
472
|
+
return @routes_path unless block_given?
|
473
|
+
@routes_path = yield(@routes_path)
|
474
|
+
@path = @routes_path
|
475
|
+
end
|
476
|
+
|
477
|
+
def nested_path(&block)
|
478
|
+
return @nested_path unless block_given?
|
479
|
+
@nested_path = yield(@nested_path)
|
480
|
+
end
|
389
481
|
end
|
390
482
|
end
|
483
|
+
|