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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7762704358bdeef51e547cbcf8fd5c82ed290850
4
- data.tar.gz: 6be61141decbb09555cb5419c5b68e1db83369a9
3
+ metadata.gz: dfff431b9195d8c14a112b7b379afd00c0bf4c04
4
+ data.tar.gz: 50df61f61b201f3a22d14e0e62a1ddf6d125bc6d
5
5
  SHA512:
6
- metadata.gz: d2456c6071cad44e55290fcf59db6460a163da793073d0acb76ff36eb14a1c49562efccbb54203d34b7343d9f414dd006590ffffc714b32b186e5bb9e736f535
7
- data.tar.gz: 9bcbe7162ef0a76bfe25597233df1b25c753ce2c68435e7e8f324ea7a020be6cb4b755e723ba6a3480ac0ce123e5b7f6ae254eae77196caa0d95e4553869f3b8
6
+ metadata.gz: 5926b02c4d7cdc8a1d126f6229c9af946b2a1655115ae34545e3acee887eea749c9232375a8a2fb11749b2526197ef7834c5b1a5642c1ded6c724b43c389bacf
7
+ data.tar.gz: d4bb4df2257a0ac345677c052a4859de0826b0f47f41f51a97f116b9dd6f73d9360a3f6b63a4d15f042c26d21535c94ba6da01fd0567f171658a893f8f37f244
@@ -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
- verb = verb.downcase.to_sym
34
- (router.routes_with_verbs[verb] ||= []) << (route = Route.new(path, &block))
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.path_for_generation
154
+ return matcher.mustermann? ? matcher.expand(params_for_expand) : route.path
153
155
  end
154
156
  raise InvalidRouteException
155
157
  end
@@ -49,7 +49,7 @@ class Howl
49
49
 
50
50
  # @return [Boolean] This matcher's handler is mustermann ?
51
51
  def mustermann?
52
- handler.class == Mustermann::Rails
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, :type => :rails, :capture => @capture)
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
- verb = verb.downcase.to_sym
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 = { :htm => :html } unless defined?(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
- :parent, :use_layout, :controller, :user_agent
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 ||= []
@@ -1,7 +1,7 @@
1
1
  class Howl
2
2
  class Route
3
- attr_accessor :block, :capture, :router, :params, :name,
4
- :order, :default_values, :path_for_generation, :verb
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?
@@ -1,47 +1,33 @@
1
1
  class Howl
2
2
  class Router
3
- attr_reader :current_order, :routes, :routes_with_verbs
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.is_a?(Hash) ? [request['PATH_INFO'], request['REQUEST_METHOD'], {}] :
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
- # Convert hash key into symbol.
17
- request_params = request_params.inject({}) do |result, entry|
18
- result[entry[0].to_sym] = entry[1]
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!(route.params).merge!(match_data.names.inject({}){|result, name|
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
- }.compact
43
- raise_method_not_allowed(request, all_matched_routes) if result.empty?
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
@@ -1,3 +1,3 @@
1
1
  class Howl
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -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 of regexp" do
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
@@ -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)\"", body
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 html', body
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 html', body
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 => "/(:foo(/:bar))") { "#{params[:foo]}-#{params[:bar]}" }
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.2.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-09-18 00:00:00.000000000 Z
11
+ date: 2013-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack