howl-router 0.2.0 → 0.3.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/lib/howl-router.rb +7 -5
- data/lib/howl-router/matcher.rb +2 -2
- data/lib/howl-router/padrino/core.rb +23 -5
- data/lib/howl-router/padrino/ext/class_methods.rb +18 -2
- data/lib/howl-router/padrino/route.rb +2 -2
- data/lib/howl-router/route.rb +2 -3
- data/lib/howl-router/router.rb +33 -27
- data/lib/howl-router/version.rb +1 -1
- data/test/howl_test.rb +27 -2
- data/test/padrino_test.rb +15 -15
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dfff431b9195d8c14a112b7b379afd00c0bf4c04
|
4
|
+
data.tar.gz: 50df61f61b201f3a22d14e0e62a1ddf6d125bc6d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5926b02c4d7cdc8a1d126f6229c9af946b2a1655115ae34545e3acee887eea749c9232375a8a2fb11749b2526197ef7834c5b1a5642c1ded6c724b43c389bacf
|
7
|
+
data.tar.gz: d4bb4df2257a0ac345677c052a4859de0826b0f47f41f51a97f116b9dd6f73d9360a3f6b63a4d15f042c26d21535c94ba6da01fd0567f171658a893f8f37f244
|
data/lib/howl-router.rb
CHANGED
@@ -16,6 +16,10 @@ class Howl
|
|
16
16
|
:server_error => 500
|
17
17
|
}
|
18
18
|
|
19
|
+
def initialize(&block)
|
20
|
+
instance_eval(&block) if block_given?
|
21
|
+
end
|
22
|
+
|
19
23
|
# Generate a route, and add to routes.
|
20
24
|
#
|
21
25
|
# @param [String, Symbol] verb The verb decide a acceptable request method.
|
@@ -30,10 +34,8 @@ class Howl
|
|
30
34
|
# @return [Howl::Route] Return a generated Howl::Route instance.
|
31
35
|
#
|
32
36
|
def add(verb, path, options = {}, &block)
|
33
|
-
|
34
|
-
|
35
|
-
route.path_for_generation = options[:path_for_generation] if options[:path_for_generation]
|
36
|
-
route.verb = verb
|
37
|
+
route = Route.new(path, &block)
|
38
|
+
route.verb = verb.downcase.to_sym
|
37
39
|
route.router = router
|
38
40
|
router.routes << route
|
39
41
|
route
|
@@ -149,7 +151,7 @@ class Howl
|
|
149
151
|
else
|
150
152
|
params_for_expand = params.dup
|
151
153
|
end
|
152
|
-
return matcher.mustermann? ? matcher.expand(params_for_expand) : route.
|
154
|
+
return matcher.mustermann? ? matcher.expand(params_for_expand) : route.path
|
153
155
|
end
|
154
156
|
raise InvalidRouteException
|
155
157
|
end
|
data/lib/howl-router/matcher.rb
CHANGED
@@ -49,7 +49,7 @@ class Howl
|
|
49
49
|
|
50
50
|
# @return [Boolean] This matcher's handler is mustermann ?
|
51
51
|
def mustermann?
|
52
|
-
handler.
|
52
|
+
handler.instance_of?(Mustermann::Sinatra)
|
53
53
|
end
|
54
54
|
|
55
55
|
# @return [Mustermann::Rails] Return a Mustermann::Rails when @path is string.
|
@@ -57,7 +57,7 @@ class Howl
|
|
57
57
|
def handler
|
58
58
|
@handler ||= case @path
|
59
59
|
when String
|
60
|
-
Mustermann.new(@path, :
|
60
|
+
Mustermann.new(@path, :capture => @capture)
|
61
61
|
when Regexp
|
62
62
|
/^(?:#{@path})$/
|
63
63
|
end
|
@@ -4,10 +4,9 @@ class Howl
|
|
4
4
|
module Padrino
|
5
5
|
class Core < ::Howl
|
6
6
|
def add(verb, path, options = {}, &block)
|
7
|
-
|
8
|
-
(router.routes_with_verbs[verb] ||= []) << (route = Route.new(path, &block))
|
7
|
+
route = Route.new(path, &block)
|
9
8
|
route.path_for_generation = options[:path_for_generation] if options[:path_for_generation]
|
10
|
-
route.verb = verb
|
9
|
+
route.verb = verb.downcase.to_sym
|
11
10
|
route.router = router
|
12
11
|
router.routes << route
|
13
12
|
route
|
@@ -16,9 +15,7 @@ class Howl
|
|
16
15
|
def call(env)
|
17
16
|
request = Request.new(env)
|
18
17
|
return bad_request unless HTTP_VERBS.include?(request.request_method.downcase.to_sym)
|
19
|
-
|
20
18
|
compile unless compiled?
|
21
|
-
|
22
19
|
begin
|
23
20
|
matched_routes = recognize(request)
|
24
21
|
[200, {}, matched_routes]
|
@@ -31,6 +28,27 @@ class Howl
|
|
31
28
|
end
|
32
29
|
end
|
33
30
|
|
31
|
+
def path(name, *args)
|
32
|
+
params = args.delete_at(args.last.is_a?(Hash) ? -1 : 0) || {}
|
33
|
+
saved_args = args.dup
|
34
|
+
router.routes.each do |route|
|
35
|
+
next unless route.name == name
|
36
|
+
matcher = route.matcher
|
37
|
+
if !args.empty? and matcher.mustermann?
|
38
|
+
matcher_names = matcher.names
|
39
|
+
params_for_expand = Hash[matcher_names.map{|matcher_name|
|
40
|
+
[matcher_name.to_sym, (params[matcher_name.to_sym] || args.shift)]
|
41
|
+
}]
|
42
|
+
params_for_expand.merge!(Hash[params.select{|k, v| !matcher_names.include?(name.to_sym) }])
|
43
|
+
args = saved_args.dup
|
44
|
+
else
|
45
|
+
params_for_expand = params.dup
|
46
|
+
end
|
47
|
+
return matcher.mustermann? ? matcher.expand(params_for_expand) : route.path_for_generation
|
48
|
+
end
|
49
|
+
raise InvalidRouteException
|
50
|
+
end
|
51
|
+
|
34
52
|
private
|
35
53
|
|
36
54
|
def generate_response(key, headers = {})
|
@@ -2,7 +2,7 @@
|
|
2
2
|
class Howl
|
3
3
|
module Padrino
|
4
4
|
module ClassMethods
|
5
|
-
CONTENT_TYPE_ALIASES = {
|
5
|
+
CONTENT_TYPE_ALIASES = {:htm => :html} unless defined?(CONTENT_TYPE_ALIASES)
|
6
6
|
ROUTE_PRIORITY = {:high => 0, :normal => 1, :low => 2} unless defined?(ROUTE_PRIORITY)
|
7
7
|
|
8
8
|
def router
|
@@ -92,7 +92,7 @@ class Howl
|
|
92
92
|
invoke_hook(:route_added, verb, path, block)
|
93
93
|
|
94
94
|
# Howl route construction
|
95
|
-
path[0, 0] = "/" if path == "(.:format)"
|
95
|
+
path[0, 0] = "/" if path == "(.:format)?"
|
96
96
|
route = router.add(verb.downcase.to_sym, path, route_options)
|
97
97
|
route.name = name if name
|
98
98
|
route.action = action
|
@@ -139,6 +139,7 @@ class Howl
|
|
139
139
|
mime_types = types.map {|t| mime_type(t) }.compact
|
140
140
|
url_format = params[:format].to_sym if params[:format]
|
141
141
|
accepts = request.accept.map {|a| a.to_str }
|
142
|
+
accepts = [] if accepts == ["*/*"]
|
142
143
|
|
143
144
|
# per rfc2616-sec14:
|
144
145
|
# Assume */* if no ACCEPT header is given.
|
@@ -175,6 +176,21 @@ class Howl
|
|
175
176
|
matched_format
|
176
177
|
end
|
177
178
|
end
|
179
|
+
|
180
|
+
def process_path_for_parent_params(path, parent_params)
|
181
|
+
parent_prefix = parent_params.flatten.compact.uniq.map do |param|
|
182
|
+
map = (param.respond_to?(:map) && param.map ? param.map : param.to_s)
|
183
|
+
part = "#{map}/:#{param.to_s.singularize}_id/"
|
184
|
+
part = "(#{part})?" if param.respond_to?(:optional) && param.optional?
|
185
|
+
part
|
186
|
+
end
|
187
|
+
|
188
|
+
[parent_prefix, path].flatten.join("")
|
189
|
+
end
|
190
|
+
|
191
|
+
def process_path_for_provides(path, format_params)
|
192
|
+
path << "(.:format)?" unless path[-11, 11] == '(.:format)?'
|
193
|
+
end
|
178
194
|
end
|
179
195
|
end
|
180
196
|
end
|
@@ -3,8 +3,8 @@ require 'howl-router/route'
|
|
3
3
|
class Howl
|
4
4
|
module Padrino
|
5
5
|
class Route < ::Howl::Route
|
6
|
-
attr_accessor :action, :cache, :cache_key, :cache_expires_in,
|
7
|
-
:
|
6
|
+
attr_accessor :action, :cache, :cache_key, :cache_expires_in, :parent,
|
7
|
+
:use_layout, :controller, :user_agent, :path_for_generation
|
8
8
|
|
9
9
|
def before_filters(&block)
|
10
10
|
@_before_filters ||= []
|
data/lib/howl-router/route.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Howl
|
2
2
|
class Route
|
3
|
-
attr_accessor :block, :capture, :router, :
|
4
|
-
:order, :default_values, :
|
3
|
+
attr_accessor :block, :capture, :router, :name,
|
4
|
+
:order, :default_values, :verb
|
5
5
|
|
6
6
|
# @param [String, Regexp] path The path associate to this route.
|
7
7
|
# @yield The block associate to this route.
|
@@ -15,7 +15,6 @@ class Howl
|
|
15
15
|
#
|
16
16
|
def initialize(path, &block)
|
17
17
|
@path = path
|
18
|
-
@params = {}
|
19
18
|
@capture = {}
|
20
19
|
@order = 0
|
21
20
|
@block = block if block_given?
|
data/lib/howl-router/router.rb
CHANGED
@@ -1,47 +1,33 @@
|
|
1
1
|
class Howl
|
2
2
|
class Router
|
3
|
-
attr_reader :current_order, :routes
|
3
|
+
attr_reader :current_order, :routes
|
4
4
|
|
5
5
|
def initialize
|
6
6
|
reset!
|
7
7
|
end
|
8
8
|
|
9
9
|
def recognize(request)
|
10
|
-
path_info, verb, request_params = request
|
11
|
-
[request.path_info, request.request_method, request.params]
|
12
|
-
verb = verb.downcase.to_sym
|
10
|
+
path_info, verb, request_params = parse_request(request)
|
13
11
|
ignore_slash_path_info = path_info
|
14
12
|
ignore_slash_path_info = path_info[0..-2] if path_info != "/" and path_info[-1] == "/"
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
result
|
20
|
-
end
|
21
|
-
|
22
|
-
all_matched_routes = @routes.select do |route|
|
23
|
-
matcher = route.matcher
|
24
|
-
matcher.match(matcher.mustermann? ? ignore_slash_path_info : path_info)
|
25
|
-
end
|
26
|
-
raise NotFound if all_matched_routes.empty?
|
27
|
-
|
28
|
-
raise_method_not_allowed(request, all_matched_routes) unless routes_with_verbs.has_key?(verb)
|
29
|
-
result = all_matched_routes.map{|route|
|
13
|
+
matched_routes = scan_routes(path_info, ignore_slash_path_info)
|
14
|
+
raise NotFound if matched_routes.empty?
|
15
|
+
raise_method_not_allowed(request, matched_routes) unless matched_routes.find{|r|r.verb == verb}
|
16
|
+
result = matched_routes.map do |route|
|
30
17
|
next unless verb == route.verb
|
31
18
|
params, matcher = {}, route.matcher
|
32
19
|
match_data = matcher.match(matcher.mustermann? ? ignore_slash_path_info : path_info)
|
33
20
|
if match_data.names.empty?
|
34
21
|
params[:captures] = match_data.captures
|
35
22
|
else
|
36
|
-
params.merge!(
|
23
|
+
params.merge!(match_data.names.inject({}){|result, name|
|
37
24
|
result[name.to_sym] = match_data[name] ? Rack::Utils.unescape(match_data[name]) : nil
|
38
25
|
result
|
39
26
|
}).merge!(request_params){|key, self_value, new_value| self_value || new_value }
|
40
27
|
end
|
41
28
|
[route, params]
|
42
|
-
|
43
|
-
raise_method_not_allowed(request,
|
44
|
-
result
|
29
|
+
end.compact
|
30
|
+
result.empty? ? raise_method_not_allowed(request, matched_routes) : result
|
45
31
|
end
|
46
32
|
|
47
33
|
def increment_order
|
@@ -50,18 +36,38 @@ class Howl
|
|
50
36
|
|
51
37
|
def compile
|
52
38
|
return if @current_order.zero?
|
53
|
-
@routes_with_verbs.each_value{|routes_with_verb|
|
54
|
-
routes_with_verb.sort!{|a, b| a.order <=> b.order }
|
55
|
-
}
|
56
39
|
@routes.sort!{|a, b| a.order <=> b.order }
|
57
40
|
end
|
58
41
|
|
59
42
|
def reset!
|
60
43
|
@routes = []
|
61
|
-
@routes_with_verbs = {}
|
62
44
|
@current_order = 0
|
63
45
|
end
|
64
46
|
|
47
|
+
private
|
48
|
+
|
49
|
+
def scan_routes(path_info, ignore_slash_path_info)
|
50
|
+
@routes.select do |route|
|
51
|
+
matcher = route.matcher
|
52
|
+
matcher.match(matcher.mustermann? ? ignore_slash_path_info : path_info)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse_request(request)
|
57
|
+
if request.is_a?(Hash)
|
58
|
+
[request['PATH_INFO'], request['REQUEST_METHOD'].downcase.to_sym, {}]
|
59
|
+
else
|
60
|
+
[request.path_info, request.request_method.downcase.to_sym, parse_request_params(request.params)]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse_request_params(params)
|
65
|
+
params.inject({}) do |result, entry|
|
66
|
+
result[entry[0].to_sym] = entry[1]
|
67
|
+
result
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
65
71
|
def raise_method_not_allowed(request, matched_routes)
|
66
72
|
request.acceptable_methods = matched_routes.map(&:verb)
|
67
73
|
raise MethodNotAllowed
|
data/lib/howl-router/version.rb
CHANGED
data/test/howl_test.rb
CHANGED
@@ -84,7 +84,7 @@ describe Howl do
|
|
84
84
|
describe "generate path" do
|
85
85
|
before(:each){ @howl.reset! }
|
86
86
|
|
87
|
-
should "basic route
|
87
|
+
should "basic route" do
|
88
88
|
index = @howl.add(:get, "/"){}
|
89
89
|
index.name = :index
|
90
90
|
foo_bar = @howl.add(:post, "/foo/bar"){}
|
@@ -97,7 +97,32 @@ describe Howl do
|
|
97
97
|
assert_equal @howl.path(:users, :user_id => 1), "/users/1"
|
98
98
|
assert_equal @howl.path(:users, :user_id => 1, :query => "string"), "/users/1?query=string"
|
99
99
|
end
|
100
|
-
end
|
101
100
|
|
101
|
+
should "regexp" do
|
102
|
+
index = @howl.add(:get, /.+?/){}
|
103
|
+
index.name = :index
|
104
|
+
foo_bar = @howl.add(:post, /\d+/){}
|
105
|
+
foo_bar.name = :foo_bar
|
106
|
+
|
107
|
+
assert_equal @howl.path(:index), /.+?/
|
108
|
+
assert_equal @howl.path(:foo_bar), /\d+/
|
109
|
+
end
|
110
|
+
end
|
102
111
|
|
112
|
+
describe "#new allows block" do
|
113
|
+
should "#new support for block." do
|
114
|
+
@app = Howl.new do
|
115
|
+
foo = add(:get, "/"){"foo"}
|
116
|
+
bar = add(:post, "/"){"bar"}
|
117
|
+
foo.name = :foo
|
118
|
+
bar.name = :bar
|
119
|
+
end
|
120
|
+
get("/")
|
121
|
+
assert_equal "foo", body
|
122
|
+
post("/")
|
123
|
+
assert_equal "bar", body
|
124
|
+
assert_equal @app.path(:foo), "/"
|
125
|
+
assert_equal @app.path(:bar), "/"
|
126
|
+
end
|
127
|
+
end
|
103
128
|
end
|
data/test/padrino_test.rb
CHANGED
@@ -448,14 +448,14 @@ describe "Howl::Padrino" do
|
|
448
448
|
should "should inject the action name into the request" do
|
449
449
|
mock_app do
|
450
450
|
controller :posts do
|
451
|
-
get('/omnomnom(/:id)') { request.action.inspect }
|
451
|
+
get('/omnomnom(/:id)?') { request.action.inspect }
|
452
452
|
controller :mini do
|
453
453
|
get([:a, :b, :c]) { request.action.inspect }
|
454
454
|
end
|
455
455
|
end
|
456
456
|
end
|
457
457
|
get "/posts/omnomnom"
|
458
|
-
assert_equal "\"/omnomnom(/:id)
|
458
|
+
assert_equal "\"/omnomnom(/:id)?\"", body
|
459
459
|
get "/mini/a/b/c"
|
460
460
|
assert_equal ":a", body
|
461
461
|
end
|
@@ -873,7 +873,7 @@ describe "Howl::Padrino" do
|
|
873
873
|
|
874
874
|
should 'allow optionals' do
|
875
875
|
mock_app do
|
876
|
-
get(:show, :map => "/stories/:type(/:category)") do
|
876
|
+
get(:show, :map => "/stories/:type(/:category)?") do
|
877
877
|
"#{params[:type]}/#{params[:category]}"
|
878
878
|
end
|
879
879
|
end
|
@@ -1129,17 +1129,17 @@ describe "Howl::Padrino" do
|
|
1129
1129
|
mock_app do
|
1130
1130
|
provides :xml
|
1131
1131
|
|
1132
|
-
get("/foo"){ "Foo in #{content_type}" }
|
1133
|
-
get("/bar"){ "Bar in #{content_type}" }
|
1132
|
+
get("/foo"){ "Foo in #{content_type.inspect}" }
|
1133
|
+
get("/bar"){ "Bar in #{content_type.inspect}" }
|
1134
1134
|
end
|
1135
1135
|
|
1136
1136
|
get '/foo', {}, { 'HTTP_ACCEPT' => 'application/xml' }
|
1137
|
-
assert_equal 'Foo in xml', body
|
1137
|
+
assert_equal 'Foo in :xml', body
|
1138
1138
|
get '/foo'
|
1139
|
-
assert_equal 'Foo in xml', body
|
1139
|
+
assert_equal 'Foo in :xml', body
|
1140
1140
|
|
1141
1141
|
get '/bar', {}, { 'HTTP_ACCEPT' => 'application/xml' }
|
1142
|
-
assert_equal 'Bar in
|
1142
|
+
assert_equal 'Bar in nil', body
|
1143
1143
|
end
|
1144
1144
|
|
1145
1145
|
should "does not allow global provides in controller" do
|
@@ -1147,18 +1147,18 @@ describe "Howl::Padrino" do
|
|
1147
1147
|
controller :base do
|
1148
1148
|
provides :xml
|
1149
1149
|
|
1150
|
-
get(:foo, "/foo"){ "Foo in #{content_type}" }
|
1151
|
-
get(:bar, "/bar"){ "Bar in #{content_type}" }
|
1150
|
+
get(:foo, "/foo"){ "Foo in #{content_type.inspect}" }
|
1151
|
+
get(:bar, "/bar"){ "Bar in #{content_type.inspect}" }
|
1152
1152
|
end
|
1153
1153
|
end
|
1154
1154
|
|
1155
1155
|
get '/foo', {}, { 'HTTP_ACCEPT' => 'application/xml' }
|
1156
|
-
assert_equal 'Foo in xml', body
|
1156
|
+
assert_equal 'Foo in :xml', body
|
1157
1157
|
get '/foo'
|
1158
|
-
assert_equal 'Foo in xml', body
|
1158
|
+
assert_equal 'Foo in :xml', body
|
1159
1159
|
|
1160
1160
|
get '/bar', {}, { 'HTTP_ACCEPT' => 'application/xml' }
|
1161
|
-
assert_equal 'Bar in
|
1161
|
+
assert_equal 'Bar in nil', body
|
1162
1162
|
end
|
1163
1163
|
|
1164
1164
|
should "map non named routes in controllers" do
|
@@ -1385,7 +1385,7 @@ describe "Howl::Padrino" do
|
|
1385
1385
|
|
1386
1386
|
should 'works with optionals params' do
|
1387
1387
|
mock_app do
|
1388
|
-
get("/foo(/:bar)") { params[:bar] }
|
1388
|
+
get("/foo(/:bar)?") { params[:bar] }
|
1389
1389
|
end
|
1390
1390
|
|
1391
1391
|
get "/foo/bar"
|
@@ -1488,7 +1488,7 @@ describe "Howl::Padrino" do
|
|
1488
1488
|
|
1489
1489
|
should "use optionals params" do
|
1490
1490
|
mock_app do
|
1491
|
-
get(:index, :map => "
|
1491
|
+
get(:index, :map => "/:foo(/:bar)?") { "#{params[:foo]}-#{params[:bar]}" }
|
1492
1492
|
end
|
1493
1493
|
get "/foo"
|
1494
1494
|
assert_equal "foo-", body
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: howl-router
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- namusyaka
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|