howl-router 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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