http_router 0.3.17 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -8,5 +8,6 @@ group :development do
8
8
  gem 'rake'
9
9
  gem 'sinatra'
10
10
  gem 'rbench'
11
+ gem 'code_stats'
11
12
  gem 'tumbler', ">= 0.0.11"
12
13
  end
@@ -53,14 +53,10 @@ class HttpRouter
53
53
  new_node
54
54
  end
55
55
  @linear.sort!{|a, b|
56
- if a.first.respond_to?(:priority) and b.first.respond_to?(:priority)
57
- b.first.priority <=> a.first.priority
58
- elsif a.first.respond_to?(:priority)
59
- -1
60
- elsif b.first.respond_to?(:priority)
61
- 1
62
- else
63
- 0
56
+ if a.first.respond_to?(:priority) and b.first.respond_to?(:priority) ; b.first.priority <=> a.first.priority
57
+ elsif a.first.respond_to?(:priority) ; -1
58
+ elsif b.first.respond_to?(:priority) ; 1
59
+ else ; 0
64
60
  end
65
61
  }
66
62
  n
@@ -151,103 +147,108 @@ class HttpRouter
151
147
  val
152
148
  end
153
149
 
154
- def find_on_parts(request, parts, params)
150
+ def find_on_parts(request, parts, params = [], routes = [])
155
151
  if parts and !parts.empty?
156
- if potential = potential_match(request, parts, params)
157
- return potential
158
- end
152
+ if parts.size == 1 and parts.first == ''
153
+ potential, match_parts, match_params = catch(:match) { find_on_parts(request, nil, params) }
154
+ process_match(potential, nil, match_params, routes) if potential and potential.value and (router.ignore_trailing_slash? or potential.value.route.trailing_slash_ignore?)
155
+ end
159
156
  if @linear && !@linear.empty?
160
- response = nil
161
- dupped_parts = nil
162
- dupped_params = nil
157
+ response, dupped_parts, dupped_params = nil, nil, nil
163
158
  next_node = @linear.find do |(tester, node)|
164
159
  if tester.respond_to?(:matches?) and match = tester.matches?(parts)
165
- dupped_parts = parts.dup
166
- dupped_params = params.dup
160
+ dupped_parts, dupped_params = parts.dup, params.dup
167
161
  dupped_params << escape_val(tester.consume(match, dupped_parts))
168
- parts.replace(dupped_parts) if response = node.find_on_parts(request, dupped_parts, dupped_params)
162
+ node.find_on_parts(request, dupped_parts, dupped_params, routes)
169
163
  elsif tester.respond_to?(:match) and match = tester.match(parts.whole_path) and match.begin(0) == 0
170
- dupped_parts = router.split(parts.whole_path[match[0].size, parts.whole_path.size])
171
- dupped_params = params.dup
172
- parts.replace(dupped_parts) if response = node.find_on_parts(request, dupped_parts, dupped_params)
164
+ dupped_parts, dupped_params = router.split(parts.whole_path[match[0].size, parts.whole_path.size]), params.dup
165
+ node.find_on_parts(request, dupped_parts, dupped_params, routes)
173
166
  else
174
167
  nil
175
168
  end
176
169
  end
177
- if response
178
- params.replace(dupped_params)
179
- return response
180
- end
181
170
  end
182
171
  if match = @lookup && @lookup[parts.first]
183
- part = parts.shift
184
- if match = match.find_on_parts(request, parts, params)
185
- return match
186
- else
187
- parts.unshift(part)
188
- end
172
+ match.find_on_parts(request, parts[1, parts.size - 1], params, routes)
189
173
  end
190
174
  if @catchall
191
- params << escape_val(@catchall.variable.consume(nil, parts))
192
- return @catchall.find_on_parts(request, parts, params)
175
+ dupped_parts, dupped_params = parts.dup, params.dup
176
+ dupped_params << escape_val(@catchall.variable.consume(nil, dupped_parts))
177
+ @catchall.find_on_parts(request, dupped_parts, dupped_params, routes)
193
178
  end
194
179
  end
195
- request_node and request_node.find_on_request_methods(request) or resolve_node(request)
180
+ if request_node
181
+ request_node.find_on_request_methods(request, parts, params, routes)
182
+ elsif arbitrary_node
183
+ arbitrary_node.find_on_arbitrary(request, parts, params, routes)
184
+ elsif @value
185
+ process_match(self, parts, params, routes)
186
+ else
187
+ nil
188
+ end
196
189
  end
197
190
 
198
- def create_linear
199
- @linear ||= []
191
+ def process_match(node, parts, params, routes)
192
+ if node.value.route.partially_match?
193
+ routes.push << [node, parts, params]
194
+ node
195
+ else
196
+ throw :match, [node, parts, params]
197
+ end
200
198
  end
201
199
 
202
- def create_lookup
203
- @lookup ||= {}
204
- end
205
-
206
200
  protected
207
- def resolve_node(request)
208
- if arbitrary_node
209
- arbitrary_node.find_on_arbitrary(request)
210
- elsif @value
211
- self
212
- else
213
- nil
214
- end
201
+ def create_linear
202
+ @linear ||= []
215
203
  end
216
-
217
- def potential_match(request, parts, params)
218
- parts.size == 1 and parts.first == '' and potential = find_on_parts(request, nil, params) and (router.ignore_trailing_slash? or (potential.value and potential.value.route.trailing_slash_ignore?)) and parts.shift ? potential : nil
204
+
205
+ def create_lookup
206
+ @lookup ||= {}
219
207
  end
220
208
  end
221
209
 
222
210
  class ArbitraryNode < Node
223
- def find_on_arbitrary(request)
224
- next_node = @linear && !@linear.empty? && @linear.find { |(procs, node)| procs.all?{|p| p.call(request)} }
225
- next_node && next_node.last || @catchall
211
+ def find_on_arbitrary(request, parts, params, routes)
212
+ next_node = @linear && !@linear.empty? && @linear.find { |(procs, node)|
213
+ params_hash = node.value.hashify_params(params)
214
+ procs.all?{|p| p.call(request, params_hash, node.value.route.dest)}
215
+ }
216
+ if next_node
217
+ process_match(next_node.last, parts, params, routes)
218
+ elsif @catchall
219
+ process_match(@catchall, parts, params, routes)
220
+ end
226
221
  end
227
222
  end
228
223
 
229
224
  class RequestNode < Node
230
225
  RequestMethods = [:request_method, :host, :port, :scheme, :user_agent, :ip, :fullpath, :query_string].freeze
231
226
  attr_accessor :request_method
232
- def find_on_request_methods(request)
227
+ def find_on_request_methods(request, parts, params, routes)
233
228
  next_node = if @request_method
234
229
  request_value = request.send(request_method)
235
- linear_node(request, request_value) or lookup_node(request, request_value) or catchall_node(request)
230
+ linear_node(request, parts, params, request_value, routes) or
231
+ lookup_node(request, parts, params, request_value, routes) or
232
+ catchall_node(request, parts, params, request_value, routes)
233
+ end
234
+ if next_node
235
+ process_match(next_node, parts, params, routes)
236
+ else
237
+ find_on_parts(request, parts, params, routes)
236
238
  end
237
- next_node or resolve_node(request)
238
239
  end
239
240
  private
240
- def linear_node(request, request_value)
241
+ def linear_node(request, parts, params, request_value, routes)
241
242
  if @linear && !@linear.empty?
242
243
  node = @linear.find { |(regexp, node)| regexp === request_value }
243
- node.last.find_on_request_methods(request) if node
244
+ node.last.find_on_request_methods(request, parts, params, routes) if node
244
245
  end
245
246
  end
246
- def lookup_node(request, request_value)
247
- @lookup[request_value].find_on_request_methods(request) if @lookup and @lookup[request_value]
247
+ def lookup_node(request, parts, params, request_value, routes)
248
+ @lookup[request_value].find_on_request_methods(request, parts, params, routes) if @lookup and @lookup[request_value]
248
249
  end
249
- def catchall_node(request)
250
- @catchall.find_on_request_methods(request) if @catchall
250
+ def catchall_node(request, parts, params, request_value, routes)
251
+ @catchall.find_on_request_methods(request, parts, params, routes) if @catchall
251
252
  end
252
253
  end
253
254
 
@@ -1,11 +1,14 @@
1
1
  class HttpRouter
2
2
  class Parts < Array
3
+ SLASH = '/'.freeze
4
+ SLASH_RX = Regexp.new(SLASH)
5
+
3
6
  def initialize(path)
4
- super((path[0] == ?/ ? path[1, path.size] : path).split('/'))
7
+ super((path[0] == ?/ ? path[1, path.size] : path).split(SLASH_RX))
5
8
  end
6
9
 
7
10
  def whole_path
8
- @whole_path ||= join('/')
11
+ @whole_path ||= join(SLASH)
9
12
  end
10
13
 
11
14
  def shift
@@ -1,6 +1,6 @@
1
1
  class HttpRouter
2
2
  class Path
3
- attr_reader :parts, :route, :splitting_indexes
3
+ attr_reader :parts, :route, :splitting_indexes, :path
4
4
  def initialize(route, path, parts, splitting_indexes)
5
5
  @route, @path, @parts, @splitting_indexes = route, path, parts, splitting_indexes
6
6
 
@@ -25,6 +25,10 @@ class HttpRouter
25
25
  "
26
26
  end
27
27
 
28
+ def hashify_params(params)
29
+ variable_names.zip(params).inject({}) { |h, (k,v)| h[k] = v; h }
30
+ end
31
+
28
32
  def ===(other_path)
29
33
  return false if @parts.size != other_path.parts.size
30
34
  @parts.each_with_index {|p,i|
@@ -56,15 +60,19 @@ class HttpRouter
56
60
  params.each do |k,v|
57
61
  case v
58
62
  when Array
59
- v.each { |v_part| uri << '&' << Rack::Utils.escape(k.to_s) << '%5B%5D=' << Rack::Utils.escape(v_part.to_s) }
63
+ v.each { |v_part| uri << '&' << ::Rack::Utils.escape(k.to_s) << '%5B%5D=' << ::Rack::Utils.escape(v_part.to_s) }
60
64
  else
61
- uri << '&' << Rack::Utils.escape(k.to_s) << '=' << Rack::Utils.escape(v.to_s)
65
+ uri << '&' << ::Rack::Utils.escape(k.to_s) << '=' << ::Rack::Utils.escape(v.to_s)
62
66
  end
63
67
  end
64
68
  uri[uri_size] = ??
65
69
  end
66
70
  end
67
71
 
72
+ def static?
73
+ variables.empty?
74
+ end
75
+
68
76
  def variables
69
77
  unless @variables
70
78
  @variables = @parts.select{|p| p.is_a?(Variable)}
@@ -1,6 +1,6 @@
1
1
  # Replacement for {Rack::Builder} which using HttpRouter to map requests instead of a simple Hash.
2
2
  # As well, add convenience methods for the request methods.
3
- class Rack::Builder
3
+ class HttpRouter::Rack::Builder < ::Rack::Builder
4
4
  def initialize(&block)
5
5
  super
6
6
  end
@@ -1,4 +1,4 @@
1
- class Rack::URLMap
1
+ class HttpRouter::Rack::URLMap < ::Rack::URLMap
2
2
  def initialize(map = {})
3
3
  @router = HttpRouter.new
4
4
  map.each { |path, app| (path =~ /^(https?):\/\/(.*?)(\/.*)/ ? @router.add($3).host($2).scheme($1) : @router.add(path)).partial.to(app) }
@@ -0,0 +1,18 @@
1
+ class HttpRouter
2
+ module Rack
3
+ autoload :URLMap, 'http_router/rack/url_map'
4
+ autoload :Builder, 'http_router/rack/buidler'
5
+
6
+ # Monkey-patches Rack::Builder to use HttpRouter.
7
+ # See examples/rack_mapper.rb
8
+ def self.override_rack_builder!
9
+ ::Rack.class_eval("OriginalBuilder = Builder; HttpRouterBuilder = HttpRouter::Rack::Builder; remove_const :Builder; Builder = HttpRouterBuilder")
10
+ end
11
+
12
+ # Monkey-patches Rack::URLMap to use HttpRouter.
13
+ # See examples/rack_mapper.rb
14
+ def self.override_rack_urlmap!
15
+ ::Rack.class_eval("OriginalURLMap = URLMap; HttpRouterURLMap = HttpRouter::Rack::URLMap; remove_const :URLMap; URLMap = HttpRouterURLMap")
16
+ end
17
+ end
18
+ end
@@ -21,8 +21,8 @@ class HttpRouter
21
21
  def initialize(path, params, matched_path, remaining_path = nil)
22
22
  raise if matched_path.nil?
23
23
  super
24
- path.splitting_indexes and path.splitting_indexes.each{|i| params[i] = params[i].split('/')}
25
- @params_as_hash = path.variable_names.zip(params).inject({}) {|h, (k,v)| h[k] = v; h }
24
+ path.splitting_indexes and path.splitting_indexes.each{|i| params[i] = params[i].split(HttpRouter::Parts::SLASH_RX)}
25
+ @params_as_hash = path.hashify_params(params)
26
26
  end
27
27
 
28
28
  def matched?
@@ -10,10 +10,23 @@ class HttpRouter
10
10
  end
11
11
 
12
12
  def find(request)
13
- params = []
14
- parts = get_parts(request)
15
- node = find_on_parts(request, parts, params)
16
- process_response(node, parts, params, request)
13
+ routes = []
14
+ node, parts, params = catch(:match) { find_on_parts(request, get_parts(request), [], routes) }
15
+ if !routes.empty?
16
+ routes.map { |node, parts, params| process_response(node, parts, params, request) }
17
+ elsif node
18
+ process_response(node, parts, params, request)
19
+ elsif !router.request_methods_specified.empty?
20
+ alternate_methods = (router.request_methods_specified - [request.request_method]).select do |alternate_method|
21
+ test_request = request.dup
22
+ test_request.env['REQUEST_METHOD'] = alternate_method
23
+ routes = []
24
+ catch(:match) { find_on_parts(test_request, get_parts(request), [], routes) } || !routes.empty?
25
+ end
26
+ alternate_methods.empty? ? nil : Response.unmatched(405, {"Allow" => alternate_methods.join(", ")})
27
+ else
28
+ nil
29
+ end
17
30
  end
18
31
 
19
32
  def get_parts(request)
@@ -24,25 +37,11 @@ class HttpRouter
24
37
 
25
38
  private
26
39
  def process_response(node, parts, params, request)
27
- if node && node.value
28
- if parts.empty?
29
- Response.matched(node.value, params, request.path_info)
30
- elsif node.value.route.partially_match?
31
- rest = '/' << parts.join('/')
32
- Response.matched(node.value, params, request.path_info[0, request.path_info.size - rest.size], rest)
33
- else
34
- nil
35
- end
36
- elsif !router.request_methods_specified.empty?
37
- alternate_methods = (router.request_methods_specified - [request.request_method]).select do |alternate_method|
38
- test_request = request.dup
39
- test_request.env['REQUEST_METHOD'] = alternate_method
40
- node = find_on_parts(test_request, get_parts(request), [])
41
- node && node.value
42
- end
43
- alternate_methods.empty? ? nil : Response.unmatched(405, {"Allow" => alternate_methods.join(", ")})
44
- else
45
- nil
40
+ if parts.nil? || parts.empty?
41
+ Response.matched(node.value, params, request.path_info)
42
+ elsif node.value.route.partially_match?
43
+ rest = '/' << parts.join('/')
44
+ Response.matched(node.value, params, request.path_info[0, request.path_info.size - rest.size], rest)
46
45
  end
47
46
  end
48
47
  end
@@ -167,10 +167,10 @@ class HttpRouter
167
167
 
168
168
  # Convenient regexp matching on an entire path. Returns +self+
169
169
  def match_path(matcher)
170
- arbitrary{|env| match = matcher.match(env.path_info); !match.nil? and match.begin(0) == 0 and match[0].size == env.path_info.size}
170
+ arbitrary{|env, params, dest| match = matcher.match(env.path_info); !match.nil? and match.begin(0) == 0 and match[0].size == env.path_info.size}
171
171
  end
172
172
 
173
- # Adds an arbitrary proc matcher to a Route. Receives either a block, or a proc. The proc will receive a Rack::Request object and must return true for the Route to be matched. Returns +self+.
173
+ # Adds an arbitrary proc matcher to a Route. Receives either a block, or a proc. The proc will receive a Rack::Request object, a Hash of the params, and the destination for this route. The proc must return true if the Route is matched. Returns +self+.
174
174
  def arbitrary(proc = nil, &block)
175
175
  guard_compiled
176
176
  @arbitrary << (proc || block)
@@ -240,22 +240,17 @@ class HttpRouter
240
240
  # Generates a URL for this route. See HttpRouter#url for how the arguments for this are structured.
241
241
  def url(*args)
242
242
  options = args.last.is_a?(Hash) ? args.pop : nil
243
- options ||= {} if default_values
244
- options = default_values.merge(options) if default_values && options
243
+ options = options.nil? ? default_values.dup : default_values.merge(options) if default_values
244
+ options.delete_if{ |k,v| v.nil? } if options
245
245
  path = if args.empty?
246
246
  matching_path(options)
247
247
  else
248
248
  matching_path(args, options)
249
249
  end
250
250
  raise UngeneratableRouteException unless path
251
-
252
- mount_point = nil
253
- if !router.url_mount.nil?
254
- mount_point = router.url_mount.url(options)
255
- end
256
-
251
+ mount_point = router.url_mount && router.url_mount.url(options)
257
252
  result = path.url(args, options)
258
- mount_point.nil? ? result : File.join(mount_point, result)
253
+ mount_point ? File.join(mount_point, result) : result
259
254
  end
260
255
 
261
256
  def significant_variable_names
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  class HttpRouter #:nodoc
3
- VERSION = '0.3.17'
3
+ VERSION = '0.4.0'
4
4
  end
data/lib/http_router.rb CHANGED
@@ -11,6 +11,7 @@ require 'http_router/path'
11
11
  require 'http_router/optional_compiler'
12
12
  require 'http_router/parts'
13
13
  require 'http_router/version'
14
+ require 'http_router/rack'
14
15
 
15
16
  class HttpRouter
16
17
  # Raised when a Route is not able to be generated.
@@ -33,18 +34,6 @@ class HttpRouter
33
34
  attr_reader :named_routes, :routes, :root, :request_methods_specified
34
35
  attr_accessor :url_mount
35
36
 
36
- # Monkey-patches Rack::Builder to use HttpRouter.
37
- # See examples/rack_mapper.rb
38
- def self.override_rack_mapper!
39
- require File.join('ext', 'rack', 'rack_mapper')
40
- end
41
-
42
- # Monkey-patches Rack::Builder to use HttpRouter.
43
- # See examples/rack_mapper.rb
44
- def self.override_rack_urlmap!
45
- require File.join('ext', 'rack', 'rack_urlmap')
46
- end
47
-
48
37
  # Creates a new HttpRouter.
49
38
  # Can be called with either <tt>HttpRouter.new(proc{|env| ... }, { .. options .. })</tt> or with the first argument omitted.
50
39
  # If there is a proc first, then it's used as the default app in the case of a non-match.
@@ -56,7 +45,7 @@ class HttpRouter
56
45
  def initialize(*args, &block)
57
46
  default_app, options = args.first.is_a?(Hash) ? [nil, args.first] : [args.first, args[1]]
58
47
  @options = options
59
- @default_app = default_app || options && options[:default_app] || proc{|env| Rack::Response.new("Not Found", 404).finish }
48
+ @default_app = default_app || options && options[:default_app] || proc{|env| ::Rack::Response.new("Not Found", 404).finish }
60
49
  @ignore_trailing_slash = options && options.key?(:ignore_trailing_slash) ? options[:ignore_trailing_slash] : true
61
50
  @redirect_trailing_slash = options && options.key?(:redirect_trailing_slash) ? options[:redirect_trailing_slash] : false
62
51
  @middleware = options && options.key?(:middleware) ? options[:middleware] : false
@@ -148,7 +137,14 @@ class HttpRouter
148
137
 
149
138
  # Returns the HttpRouter::Response object if the env is matched, otherwise, returns +nil+.
150
139
  def recognize(env)
151
- response = @root.find(env.is_a?(Hash) ? Rack::Request.new(env) : env)
140
+ response = recognize_full(env)
141
+ response.is_a?(Array) ? response.first : response
142
+ end
143
+
144
+ # Returns the HttpRouter::Response object if the env is matched, an array of HttpRouter::Response objects or otherwise, returns +nil+. If it
145
+ # returns an array, this represents a set of possible matches.
146
+ def recognize_full(env)
147
+ @root.find(env.is_a?(Hash) ? ::Rack::Request.new(env) : env)
152
148
  end
153
149
 
154
150
  # Generate a URL for a specified route. This will accept a list of variable values plus any other variable names named as a hash.
@@ -167,12 +163,9 @@ class HttpRouter
167
163
  # # ==> "/123.html?fun=inthesun"
168
164
  def url(route, *args)
169
165
  case route
170
- when Symbol
171
- url(@named_routes[route], *args)
172
- when nil
173
- raise UngeneratableRouteException
174
- else
175
- route.url(*args)
166
+ when Symbol then url(@named_routes[route], *args)
167
+ when nil then raise UngeneratableRouteException
168
+ else route.url(*args)
176
169
  end
177
170
  end
178
171
 
@@ -181,17 +174,28 @@ class HttpRouter
181
174
  # be available under the key <tt>router.params</tt>. The HttpRouter::Response object will be available under the key <tt>router.response</tt> if
182
175
  # a response is available.
183
176
  def call(env)
184
- request = Rack::Request.new(env)
177
+ request = ::Rack::Request.new(env)
185
178
  if redirect_trailing_slash? && (request.head? || request.get?) && request.path_info[-1] == ?/
186
- response = Rack::Response.new
179
+ response = ::Rack::Response.new
187
180
  response.redirect(request.path_info[0, request.path_info.size - 1], 302)
188
181
  response.finish
189
182
  else
190
183
  env['router'] = self
191
- if response = recognize(request) and !@middleware
192
- if response.matched? && response.route.dest
184
+ if response = recognize_full(request) and !@middleware
185
+ if response.is_a?(Array)
186
+ call_env = env.dup
187
+ response.each do |match|
188
+ if match.route.dest.respond_to?(:call)
189
+ process_params(call_env, match)
190
+ consume_path!(call_env, match)
191
+ app_response = match.route.dest.call(call_env)
192
+ return app_response unless app_response.first == 404 or app_response.first == 410
193
+ else
194
+ return response
195
+ end
196
+ end
197
+ elsif response.matched? && response.route.dest
193
198
  process_params(env, response)
194
- consume_path!(request, response) if response.partial_match?
195
199
  return response.route.dest.call(env) if response.route.dest.respond_to?(:call)
196
200
  elsif !response.matched?
197
201
  return [response.status, response.headers, []]
@@ -239,7 +243,6 @@ class HttpRouter
239
243
  new_route = route.clone(cloned_router)
240
244
  cloned_router.add_route(new_route).compile
241
245
  new_route.name(route.named) if route.named
242
-
243
246
  if route.dest
244
247
  begin
245
248
  new_route.to route.dest.clone
@@ -265,9 +268,9 @@ class HttpRouter
265
268
 
266
269
  private
267
270
 
268
- def consume_path!(request, response)
269
- request.env["SCRIPT_NAME"] = (request.env["SCRIPT_NAME"] + response.matched_path)
270
- request.env["PATH_INFO"] = response.remaining_path.nil? || response.remaining_path == '' ? '/' : response.remaining_path
271
+ def consume_path!(env, response)
272
+ env["SCRIPT_NAME"] = (env["SCRIPT_NAME"] + response.matched_path)
273
+ env["PATH_INFO"] = response.remaining_path.nil? || response.remaining_path == '' ? '/' : response.remaining_path
271
274
  end
272
275
 
273
276
  def process_params(env, response)
@@ -131,6 +131,13 @@ describe "HttpRouter#generate" do
131
131
  end
132
132
  end
133
133
 
134
+ context "with nil values" do
135
+ it "shouldn't use nil values" do
136
+ @router.add("/url(/:var)").name(:test).compile
137
+ @router.url(:test, :var => nil).should == "/url"
138
+ end
139
+ end
140
+
134
141
  context "with a matching" do
135
142
  it "should raise an exception when the route is invalid" do
136
143
  @router.add("/:var").matching(:var => /\d+/).name(:test).compile
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe "Rack::Urlmap replacement" do
4
4
  it "should map urls" do
5
- HttpRouter.override_rack_urlmap!
5
+ HttpRouter::Rack.override_rack_urlmap!
6
6
  map = Rack::URLMap.new(
7
7
  "http://www.example.org/test" => proc {|env| [200, {}, ['test']]},
8
8
  "http://www.example.org/:test" => proc {|env| [200, {}, ['variable']]}
@@ -58,38 +58,54 @@ describe "HttpRouter#recognize" do
58
58
 
59
59
  end
60
60
 
61
+ context("with multiple partial matching") do
62
+ it "should match partially" do
63
+ @router.add("/test").partial.to{|env| [200, {}, ['/test',env['PATH_INFO']]]}
64
+ @router.add("/").partial.to{|env| [200, {}, ['/',env['PATH_INFO']]]}
65
+ @router.call(Rack::MockRequest.env_for('/test/optional')).last.should == ['/test', '/optional']
66
+ @router.call(Rack::MockRequest.env_for('/testing/optional')).last.should == ['/', '/testing/optional']
67
+ end
68
+ end
69
+
70
+
61
71
  context("with proc acceptance") do
62
72
  it "should match" do
63
- @router.add("/test").arbitrary(Proc.new{|req| req.host == 'hellodooly' }).to(:test1)
64
- @router.add("/test").arbitrary(Proc.new{|req| req.host == 'lovelove' }).arbitrary{|req| req.port == 80}.to(:test2)
65
- @router.add("/test").arbitrary(Proc.new{|req| req.host == 'lovelove' }).arbitrary{|req| req.port == 8080}.to(:test3)
73
+ @router.add("/test").arbitrary(Proc.new{|req, params, dest| req.host == 'hellodooly' }).to(:test1)
74
+ @router.add("/test").arbitrary(Proc.new{|req, params, dest| req.host == 'lovelove' }).arbitrary{|req, params, dest| req.port == 80}.to(:test2)
75
+ @router.add("/test").arbitrary(Proc.new{|req, params, dest| req.host == 'lovelove' }).arbitrary{|req, params, dest| req.port == 8080}.to(:test3)
66
76
  response = @router.recognize(Rack::MockRequest.env_for('http://lovelove:8080/test'))
67
77
  response.dest.should == :test3
68
78
  end
69
79
 
70
80
  it "should still use an existing less specific node if possible" do
71
81
  @router.add("/test").to(:test4)
72
- @router.add("/test").arbitrary(Proc.new{|req| req.host == 'hellodooly' }).to(:test1)
73
- @router.add("/test").arbitrary(Proc.new{|req| req.host == 'lovelove' }).arbitrary{|req| req.port == 80}.to(:test2)
74
- @router.add("/test").arbitrary(Proc.new{|req| req.host == 'lovelove' }).arbitrary{|req| req.port == 8080}.to(:test3)
82
+ @router.add("/test").arbitrary(Proc.new{|req, params, dest| req.host == 'hellodooly' }).to(:test1)
83
+ @router.add("/test").arbitrary(Proc.new{|req, params, dest| req.host == 'lovelove' }).arbitrary{|req, params, dest| req.port == 80}.to(:test2)
84
+ @router.add("/test").arbitrary(Proc.new{|req, params, dest| req.host == 'lovelove' }).arbitrary{|req, params, dest| req.port == 8080}.to(:test3)
75
85
  response = @router.recognize(Rack::MockRequest.env_for('http://lovelove:8081/test'))
76
86
  response.dest.should == :test4
77
87
  end
78
88
 
79
89
  it "should match with request conditions" do
80
- @router.add("/test").get.arbitrary(Proc.new{|req| req.host == 'lovelove' }).arbitrary{|req| req.port == 80}.to(:test1)
81
- @router.add("/test").get.arbitrary(Proc.new{|req| req.host == 'lovelove' }).arbitrary{|req| req.port == 8080}.to(:test2)
90
+ @router.add("/test").get.arbitrary(Proc.new{|req, params, dest| req.host == 'lovelove' }).arbitrary{|req, params, dest| req.port == 80}.to(:test1)
91
+ @router.add("/test").get.arbitrary(Proc.new{|req, params, dest| req.host == 'lovelove' }).arbitrary{|req, params, dest| req.port == 8080}.to(:test2)
82
92
  response = @router.recognize(Rack::MockRequest.env_for('http://lovelove:8080/test'))
83
93
  response.dest.should == :test2
84
94
  end
85
95
 
86
96
  it "should still use an existing less specific node if possible with request conditions" do
87
- @router.add("/test").get.arbitrary(Proc.new{|req| req.host == 'lovelove' }).arbitrary{|req| req.port == 80}.to(:test1)
88
- @router.add("/test").get.arbitrary(Proc.new{|req| req.host == 'lovelove' }).arbitrary{|req| req.port == 8080}.to(:test2)
97
+ @router.add("/test").get.arbitrary(Proc.new{|req, params, dest| req.host == 'lovelove' }).arbitrary{|req, params, dest| req.port == 80}.to(:test1)
98
+ @router.add("/test").get.arbitrary(Proc.new{|req, params, dest| req.host == 'lovelove' }).arbitrary{|req, params, dest| req.port == 8080}.to(:test2)
89
99
  @router.add("/test").get.to(:test3)
90
100
  response = @router.recognize(Rack::MockRequest.env_for('http://lovelove:8081/test'))
91
101
  response.dest.should == :test3
92
102
  end
103
+
104
+ it "should pass params and dest" do
105
+ @router.add("/:test").get.arbitrary(Proc.new{|req, params, dest| params[:test] == 'test' and dest == :test1 }).to(:test1)
106
+ response = @router.recognize(Rack::MockRequest.env_for('/test'))
107
+ response.dest.should == :test1
108
+ end
93
109
  end
94
110
 
95
111
  context("with trailing slashes") do
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: http_router
3
3
  version: !ruby/object:Gem::Version
4
- hash: 49
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 3
9
- - 17
10
- version: 0.3.17
8
+ - 4
9
+ - 0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Joshua Hull
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-09-08 00:00:00 -07:00
18
+ date: 2010-10-04 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -169,8 +169,6 @@ files:
169
169
  - examples/variable.ru
170
170
  - examples/variable_with_regex.ru
171
171
  - http_router.gemspec
172
- - lib/ext/rack/rack_mapper.rb
173
- - lib/ext/rack/rack_urlmap.rb
174
172
  - lib/http_router.rb
175
173
  - lib/http_router/glob.rb
176
174
  - lib/http_router/interface/sinatra.rb
@@ -178,6 +176,9 @@ files:
178
176
  - lib/http_router/optional_compiler.rb
179
177
  - lib/http_router/parts.rb
180
178
  - lib/http_router/path.rb
179
+ - lib/http_router/rack.rb
180
+ - lib/http_router/rack/builder.rb
181
+ - lib/http_router/rack/url_map.rb
181
182
  - lib/http_router/response.rb
182
183
  - lib/http_router/root.rb
183
184
  - lib/http_router/route.rb