http_router 0.10.2 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/.gitignore +2 -1
  2. data/Rakefile +7 -5
  3. data/benchmarks/gen2.rb +1 -1
  4. data/benchmarks/rack_mount.rb +8 -14
  5. data/examples/rack_mapper.ru +12 -13
  6. data/examples/variable_with_regex.ru +1 -1
  7. data/http_router.gemspec +1 -1
  8. data/lib/http_router.rb +159 -62
  9. data/lib/http_router/generation_helper.rb +29 -0
  10. data/lib/http_router/generator.rb +150 -0
  11. data/lib/http_router/node.rb +27 -17
  12. data/lib/http_router/node/abstract_request_node.rb +31 -0
  13. data/lib/http_router/node/host.rb +9 -0
  14. data/lib/http_router/node/lookup.rb +8 -10
  15. data/lib/http_router/node/path.rb +23 -38
  16. data/lib/http_router/node/request_method.rb +16 -0
  17. data/lib/http_router/node/root.rb +104 -10
  18. data/lib/http_router/node/scheme.rb +9 -0
  19. data/lib/http_router/node/user_agent.rb +9 -0
  20. data/lib/http_router/regex_route_generation.rb +10 -0
  21. data/lib/http_router/request.rb +7 -17
  22. data/lib/http_router/response.rb +4 -0
  23. data/lib/http_router/route.rb +16 -277
  24. data/lib/http_router/route_helper.rb +126 -0
  25. data/lib/http_router/util.rb +1 -37
  26. data/lib/http_router/version.rb +1 -1
  27. data/test/common/generate.txt +1 -1
  28. data/test/generation.rb +15 -11
  29. data/test/generic.rb +2 -3
  30. data/test/helper.rb +15 -10
  31. data/test/rack/test_route.rb +0 -5
  32. data/test/test_misc.rb +50 -40
  33. data/test/test_mounting.rb +27 -26
  34. data/test/test_recognition.rb +1 -76
  35. metadata +104 -161
  36. data/.rspec +0 -1
  37. data/lib/http_router/node/arbitrary.rb +0 -30
  38. data/lib/http_router/node/request.rb +0 -52
  39. data/lib/http_router/rack.rb +0 -19
  40. data/lib/http_router/rack/builder.rb +0 -61
  41. data/lib/http_router/rack/url_map.rb +0 -16
  42. data/lib/http_router/regex_route.rb +0 -39
data/.gitignore CHANGED
@@ -4,4 +4,5 @@ pkg
4
4
  rdoc
5
5
  Gemfile.lock
6
6
  .rvmrc
7
- *.rbc
7
+ *.rbc
8
+ js/npm-debug.log
data/Rakefile CHANGED
@@ -96,10 +96,12 @@ namespace :test do
96
96
  c = $1
97
97
  raise "out was nil" if out.nil?
98
98
  test = out.shift
99
- c.gsub!(/Last-Modified:.*?[\r\n]*/)
100
- test.gsub!(/Last-Modified:.*?[\r\n]*/)
101
- raise "expected #{c.inspect}, received #{test.inspect}" unless c.strip == test.strip
102
- assertion_count += 1
99
+ if c['Last-Modified'] == test['Last-Modified']
100
+ assertion_count += 1
101
+ else
102
+ raise "expected #{c.inspect}, received #{test.inspect}" unless c.strip == test.strip
103
+ assertion_count += 1
104
+ end
103
105
  end
104
106
  end
105
107
  raise "no assertions were raised in #{example}" if assertion_count.zero?
@@ -131,7 +133,7 @@ namespace :test do
131
133
  msg = expected.dup
132
134
  msg << " was expected to be "
133
135
  msg << "\#{__example_runner.inspect}"
134
- current_example << "raise \"#{msg.gsub('"', '\\"')}\" unless __example_runner.strip == #{expected}\n" if in_example
136
+ current_example << "raise \"#{msg.gsub('"', '\\"')}\" unless (__example_runner.respond_to?(:strip) ? __example_runner.strip : __example_runner) == #{expected}\n" if in_example
135
137
  when ''
136
138
  unless current_example.empty?
137
139
  examples << current_example
data/benchmarks/gen2.rb CHANGED
@@ -10,7 +10,7 @@ u.add('/simple')
10
10
  u.add('/simple/:variable') .name(:one_variable).to{}
11
11
  u.add('/simple/:var1/:var2/:var3') .name(:three_variables).to{}
12
12
  u.add('/simple/:v1/:v2/:v3/:v4/:v5/:v6/:v7/:v8') .name(:eight_variables).to{}
13
- u.add('/with_condition/:cond1/:cond2').matching(:cond1 => /^\d+$/, :cond2 => /^[a-z]+$/) .name(:two_conditions).to{}
13
+ u.add('/with_condition/:cond1/:cond2').matches_with(:cond1 => /^\d+$/, :cond2 => /^[a-z]+$/) .name(:two_conditions).to{}
14
14
 
15
15
  TIMES = 50_000
16
16
 
@@ -3,12 +3,13 @@ require 'rbench'
3
3
  require 'rack'
4
4
  require 'rack/mount'
5
5
  #require '../usher/lib/usher'
6
- require 'lib/http_router'
6
+ $: << 'lib'
7
+ require 'http_router'
7
8
 
8
9
  set = Rack::Mount::RouteSet.new do |set|
9
10
  set.add_route(proc{|env| [200, {'Content-type'=>'text/html'}, []]}, {:path => '/simple'}, {}, :simple)
10
11
  set.add_route(proc{|env| [200, {'Content-type'=>'text/html'}, []]}, {:path => '/simple/again'}, {}, :again)
11
- set.add_route(proc{|env| [200, {'Content-type'=>'text/html'}, []]}, {:path => '/dynamic/:variable'}, {}, :variable)
12
+ set.add_route(proc{|env| [200, {'Content-type'=>'text/html'}, []]}, {:path => %r{/simple/(.*?)}}, {}, :more)
12
13
  end
13
14
 
14
15
  #u = Usher::Interface.for(:rack)
@@ -16,32 +17,25 @@ end
16
17
  #u.add('/simple/again').to(proc{|env| [200, {'Content-type'=>'text/html'}, []]})
17
18
  #u.add('/dynamic/anything').to(proc{|env| [200, {'Content-type'=>'text/html'}, []]})
18
19
 
19
- hr = HttpRouter.new
20
- hr.add('/simple').to(proc{|env| [200, {'Content-type'=>'text/html'}, []]})
21
- hr.add('/simple/again').to(proc{|env| [200, {'Content-type'=>'text/html'}, []]})
22
- hr.add('/dynamic/anything').to(proc{|env| [200, {'Content-type'=>'text/html'}, []]})
23
-
24
20
  TIMES = 50_000
25
21
 
26
22
  simple_env = Rack::MockRequest.env_for('/simple')
27
23
  simple2_env = Rack::MockRequest.env_for('/simple/again')
28
- simple_and_dynamic_env = Rack::MockRequest.env_for('/dynamic/anything')
24
+ dynamic_env = Rack::MockRequest.env_for('/simple/something')
29
25
 
30
- 3.times do
31
26
 
32
27
  RBench.run(TIMES) do
33
28
 
34
29
  report "2 levels, static" do
35
- set.url(simple_env, :simple)
30
+ set.call(simple_env).first == 200 or raise
36
31
  end
37
32
 
38
33
  report "4 levels, static" do
39
- set.url(simple_env, :again)
34
+ set.call(simple2_env).first == 200 or raise
40
35
  end
41
36
 
42
- report "4 levels, 1 dynamic" do
43
- set.url(simple_env, :variable, {:variable => 'onemore'})
37
+ report "4 levels, static" do
38
+ set.call(dynamic_env).first == 200 or raise
44
39
  end
45
40
 
46
41
  end
47
- end
@@ -1,19 +1,18 @@
1
1
  require 'http_router'
2
- HttpRouter::Rack.override_rack_builder!
2
+ run HttpRouter.new do
3
+ add('/get/:id', :match_with => {:id => /\d+/}) { |env|
4
+ [200, {'Content-type' => 'text/plain'}, ["My id is #{env['router.params'][:id]}, which is a number\n"]]
5
+ }
3
6
 
4
- map('/get/:id', :matching => {:id => /\d+/}) { |env|
5
- [200, {'Content-type' => 'text/plain'}, ["My id is #{env['router.params'][:id]}, which is a number\n"]]
6
- }
7
-
8
- # you have post, get, head, put and delete.
9
- post('/get/:id') { |env|
10
- [200, {'Content-type' => 'text/plain'}, ["My id is #{env['router.params'][:id]} and you posted!\n"]]
11
- }
12
-
13
- map('/get/:id') { |env|
14
- [200, {'Content-type' => 'text/plain'}, ["My id is #{env['router.params'][:id]}\n"]]
15
- }
7
+ # you have post, get, head, put and delete.
8
+ post('/get/:id') { |env|
9
+ [200, {'Content-type' => 'text/plain'}, ["My id is #{env['router.params'][:id]} and you posted!\n"]]
10
+ }
16
11
 
12
+ map('/get/:id') { |env|
13
+ [200, {'Content-type' => 'text/plain'}, ["My id is #{env['router.params'][:id]}\n"]]
14
+ }
15
+ end
17
16
 
18
17
  # $ curl http://127.0.0.1:3000/get/foo
19
18
  # => My id is foo
@@ -1,7 +1,7 @@
1
1
  require 'http_router'
2
2
 
3
3
  run HttpRouter.new {
4
- get('/get/:id').matching(:id => /\d+/).to { |env| [200, {'Content-type' => 'text/plain'}, ["id is #{Integer(env['router.params'][:id]) * 2} * 2\n"]]}
4
+ get('/get/:id', :id => /\d+/).to { |env| [200, {'Content-type' => 'text/plain'}, ["id is #{Integer(env['router.params'][:id]) * 2} * 2\n"]]}
5
5
  }
6
6
 
7
7
  # $ curl http://127.0.0.1:3000/get/123
data/http_router.gemspec CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
28
28
  s.add_development_dependency 'rbench'
29
29
  s.add_development_dependency 'json'
30
30
  s.add_development_dependency 'phocus'
31
- s.add_development_dependency 'bundler', '~> 1.0.0'
31
+ s.add_development_dependency 'bundler'
32
32
  s.add_development_dependency 'thin', '= 1.2.8'
33
33
 
34
34
  if s.respond_to? :specification_version then
data/lib/http_router.rb CHANGED
@@ -7,25 +7,29 @@ require 'http_router/node'
7
7
  require 'http_router/request'
8
8
  require 'http_router/response'
9
9
  require 'http_router/route'
10
- require 'http_router/rack'
11
- require 'http_router/regex_route'
10
+ require 'http_router/generator'
11
+ require 'http_router/route_helper'
12
+ require 'http_router/generation_helper'
13
+ require 'http_router/regex_route_generation'
12
14
  require 'http_router/util'
13
15
 
14
16
  class HttpRouter
15
-
16
- attr_reader :root, :routes, :known_methods, :named_routes, :nodes
17
- attr_accessor :default_app, :url_mount
18
-
19
17
  # Raised when a url is not able to be generated for the given parameters
20
18
  InvalidRouteException = Class.new(RuntimeError)
21
19
  # Raised when a Route is not able to be generated due to a missing parameter.
22
20
  MissingParameterException = Class.new(RuntimeError)
23
- # Raised when a Route is compiled twice
24
- DoubleCompileError = Class.new(RuntimeError)
25
21
  # Raised an invalid request value is used
26
22
  InvalidRequestValueError = Class.new(RuntimeError)
27
23
  # Raised when there are extra parameters passed in to #url
28
24
  TooManyParametersException = Class.new(RuntimeError)
25
+ # Raised when there are left over options
26
+ LeftOverOptions = Class.new(RuntimeError)
27
+
28
+ RecognizeResponse = Struct.new(:matches, :acceptable_methods)
29
+
30
+ attr_reader :root, :routes, :named_routes, :nodes
31
+ attr_writer :route_class
32
+ attr_accessor :default_app, :url_mount, :default_host, :default_port, :default_scheme
29
33
 
30
34
  # Creates a new HttpRouter.
31
35
  # Can be called with either <tt>HttpRouter.new(proc{|env| ... }, { .. options .. })</tt> or with the first argument omitted.
@@ -34,14 +38,13 @@ class HttpRouter
34
38
  # * :default_app -- Default application used if there is a non-match on #call. Defaults to 404 generator.
35
39
  # * :ignore_trailing_slash -- Ignore a trailing / when attempting to match. Defaults to +true+.
36
40
  # * :redirect_trailing_slash -- On trailing /, redirect to the same path without the /. Defaults to +false+.
37
- # * :known_methods -- Array of http methods tested for 405s.
38
41
  def initialize(*args, &blk)
39
42
  default_app, options = args.first.is_a?(Hash) ? [nil, args.first] : [args.first, args[1]]
40
- @options = options
43
+ @options = options
41
44
  @default_app = default_app || options && options[:default_app] || proc{|env| ::Rack::Response.new("Not Found", 404, {'X-Cascade' => 'pass'}).finish }
42
45
  @ignore_trailing_slash = options && options.key?(:ignore_trailing_slash) ? options[:ignore_trailing_slash] : true
43
46
  @redirect_trailing_slash = options && options.key?(:redirect_trailing_slash) ? options[:redirect_trailing_slash] : false
44
- @known_methods = Set.new(options && options[:known_methods] || [])
47
+ @route_class = Route
45
48
  reset!
46
49
  instance_eval(&blk) if blk
47
50
  end
@@ -62,33 +65,55 @@ class HttpRouter
62
65
  #
63
66
  # Returns the route object.
64
67
  def add(*args, &app)
65
- opts = args.last.is_a?(Hash) ? args.pop : {}
68
+ uncompile
69
+ opts = args.last.is_a?(Hash) ? args.pop : nil
66
70
  path = args.first
67
- route = add_route((Regexp === path ? RegexRoute : Route).new(self, path, opts))
71
+ route = route_class.new
72
+ add_route route
73
+ route.path = path if path
74
+ route.process_opts(opts) if opts
68
75
  route.to(app) if app
69
76
  route
70
77
  end
71
78
 
72
79
  def add_route(route)
73
80
  @routes << route
74
- route
81
+ @named_routes[route.name] << route if route.name
82
+ route.router = self
83
+ end
84
+
85
+ # Extends the route class with custom features.
86
+ #
87
+ # Example:
88
+ # router = HttpRouter.new { extend_route { attr_accessor :controller } }
89
+ # router.add('/foo', :controller => :foo).to{|env| [200, {}, ['foo!']]}
90
+ # matches, other_methods = router.recognize(Rack::MockRequest.env_for('/foo'))
91
+ # matches.first.route.controller
92
+ # # ==> :foo
93
+ def extend_route(&blk)
94
+ @route_class = Class.new(Route) if @route_class == Route
95
+ @route_class.class_eval(&blk)
96
+ @extended_route_class = nil
97
+ end
98
+
99
+ def route_class
100
+ @extended_route_class ||= begin
101
+ @route_class.send(:include, RouteHelper)
102
+ @route_class.send(:include, GenerationHelper)
103
+ @route_class
104
+ end
75
105
  end
76
106
 
77
107
  # Adds a path that only responds to the request method +GET+.
78
108
  #
79
109
  # Returns the route object.
80
- def get(path, opts = {}, &app); add_with_request_method(path, :get, opts, &app); end
110
+ def get(path, opts = {}, &app); add_with_request_method(path, [:get, :head], opts, &app); end
81
111
 
82
112
  # Adds a path that only responds to the request method +POST+.
83
113
  #
84
114
  # Returns the route object.
85
115
  def post(path, opts = {}, &app); add_with_request_method(path, :post, opts, &app); end
86
116
 
87
- # Adds a path that only responds to the request method +HEAD+.
88
- #
89
- # Returns the route object.
90
- def head(path, opts = {}, &app); add_with_request_method(path, :head, opts, &app); end
91
-
92
117
  # Adds a path that only responds to the request method +DELETE+.
93
118
  #
94
119
  # Returns the route object.
@@ -99,35 +124,50 @@ class HttpRouter
99
124
  # Returns the route object.
100
125
  def put(path, opts = {}, &app); add_with_request_method(path, :put, opts, &app); end
101
126
 
127
+ # Adds a path that only responds to the request method +PATCH+.
128
+ #
129
+ # Returns the route object.
130
+ def patch(path, opts = {}, &app); add_with_request_method(path, :patch, opts, &app); end
131
+
132
+ # Adds a path that only responds to the request method +OPTIONS+.
133
+ #
134
+ # Returns the route object.
135
+ def trace(path, opts = {}, &app); add_with_request_method(path, :trace, opts, &app); end
136
+
102
137
  # Adds a path that only responds to the request method +OPTIONS+.
103
138
  #
104
139
  # Returns the route object.
105
- def options(path, opts = {}, &app); add_with_request_method(path, :options, opts, &app); end
140
+ def conenct(path, opts = {}, &app); add_with_request_method(path, :conenct, opts, &app); end
106
141
 
107
142
  # Performs recoginition without actually calling the application and returns an array of all
108
143
  # matching routes or nil if no match was found.
109
- def recognize(env)
110
- call(env, false)
144
+ def recognize(env, &callback)
145
+ if callback
146
+ request = call(env, &callback)
147
+ [request.called?, request.acceptable_methods]
148
+ else
149
+ matches = []
150
+ callback ||= Proc.new {|match| matches << match}
151
+ request = call(env, &callback)
152
+ [matches.empty? ? nil : matches, request.acceptable_methods]
153
+ end
111
154
  end
112
155
 
113
156
  # Rack compatible #call. If matching route is found, and +dest+ value responds to #call, processing will pass to the matched route. Otherwise,
114
157
  # the default application will be called. The router will be available in the env under the key <tt>router</tt>. And parameters matched will
115
158
  # be available under the key <tt>router.params</tt>.
116
- def call(env, perform_call = true)
117
- rack_request = ::Rack::Request.new(env)
118
- request = Request.new(rack_request.path_info, rack_request, perform_call)
119
- response = catch(:success) { @root[request] }
120
- if perform_call
121
- response or no_response(env)
122
- else
123
- request.matches.empty? ? nil : request.matches
124
- end
159
+ def call(env, &callback)
160
+ compile
161
+ call(env, &callback)
125
162
  end
163
+ alias_method :compiling_call, :call
126
164
 
127
165
  # Resets the router to a clean state.
128
166
  def reset!
167
+ uncompile
129
168
  @routes, @named_routes, @root = [], Hash.new{|h,k| h[k] = []}, Node::Root.new(self)
130
169
  @default_app = Proc.new{ |env| ::Rack::Response.new("Your request couldn't be found", 404).finish }
170
+ @default_host, @default_port, @default_scheme = 'localhost', 80, 'http'
131
171
  end
132
172
 
133
173
  # Assigns the default application.
@@ -140,22 +180,32 @@ class HttpRouter
140
180
  #
141
181
  # Example:
142
182
  # router = HttpRouter.new
143
- # router.add('/:foo.:format').name(:test).to{|env| [200, {}, []]}
144
- # router.url(:test, 123, 'html')
183
+ # router.add('/:foo.:format', :name => :test).to{|env| [200, {}, []]}
184
+ # router.path(:test, 123, 'html')
145
185
  # # ==> "/123.html"
146
- # router.url(:test, 123, :format => 'html')
186
+ # router.path(:test, 123, :format => 'html')
147
187
  # # ==> "/123.html"
148
- # router.url(:test, :foo => 123, :format => 'html')
188
+ # router.path(:test, :foo => 123, :format => 'html')
149
189
  # # ==> "/123.html"
150
- # router.url(:test, :foo => 123, :format => 'html', :fun => 'inthesun')
190
+ # router.path(:test, :foo => 123, :format => 'html', :fun => 'inthesun')
151
191
  # # ==> "/123.html?fun=inthesun"
152
192
  def url(route, *args)
153
- case route
154
- when Symbol then @named_routes.key?(route) && @named_routes[route].each{|r| url = r.url(*args); return url if url}
155
- when Route then return route.url(*args)
156
- end
157
- raise(InvalidRouteException)
193
+ compile
194
+ url(route, *args)
158
195
  end
196
+ alias_method :compiling_url, :url
197
+
198
+ def url_ns(route, *args)
199
+ compile
200
+ url_ns(route, *args)
201
+ end
202
+ alias_method :compiling_url_ns, :url_ns
203
+
204
+ def path(route, *args)
205
+ compile
206
+ path(route, *args)
207
+ end
208
+ alias_method :compiling_path, :path
159
209
 
160
210
  # This method is invoked when a Path object gets called with an env. Override it to implement custom path processing.
161
211
  def process_destination_path(path, env)
@@ -182,14 +232,8 @@ class HttpRouter
182
232
  def clone(klass = self.class)
183
233
  cloned_router = klass.new(@options)
184
234
  @routes.each do |route|
185
- new_route = route.clone(cloned_router)
235
+ new_route = route.create_clone(cloned_router)
186
236
  cloned_router.add_route(new_route)
187
- new_route.name(route.named) if route.named
188
- begin
189
- new_route.to route.dest.clone
190
- rescue
191
- new_route.to route.dest
192
- end
193
237
  end
194
238
  cloned_router
195
239
  end
@@ -204,21 +248,14 @@ class HttpRouter
204
248
  env['PATH_INFO'] = ''
205
249
  end
206
250
 
207
- def no_response(env)
208
- supported_methods = @known_methods.select do |m|
209
- next if m == env['REQUEST_METHOD']
210
- test_env = ::Rack::Request.new(env.clone)
211
- test_env.env['REQUEST_METHOD'] = m
212
- test_env.env['_HTTP_ROUTER_405_TESTING_ACCEPTANCE'] = true
213
- test_request = Request.new(test_env.path_info, test_env, 405)
214
- @root[test_request]
215
- !test_request.matches.empty?
216
- end
217
- supported_methods.empty? ? @default_app.call(env) : [405, {'Allow' => supported_methods.sort.join(", ")}, []]
251
+ def no_response(request, env)
252
+ request.acceptable_methods.empty? ?
253
+ @default_app.call(env) : [405, {'Allow' => request.acceptable_methods.sort.join(", ")}, []]
218
254
  end
219
255
 
220
256
  def to_s
221
- "#<HttpRouter:0x#{object_id.to_s(16)} number of routes (#{routes.size}) ignore_trailing_slash? (#{ignore_trailing_slash?}) redirect_trailing_slash? (#{redirect_trailing_slash?}) known_methods (#{known_methods.to_a.join(', ')})>"
257
+ compile
258
+ "#<HttpRouter:0x#{object_id.to_s(16)} number of routes (#{routes.size}) ignore_trailing_slash? (#{ignore_trailing_slash?}) redirect_trailing_slash? (#{redirect_trailing_slash?})>"
222
259
  end
223
260
 
224
261
  def inspect
@@ -226,9 +263,69 @@ class HttpRouter
226
263
  "#{to_s}\n#{'=' * head.size}\n#{@root.inspect}"
227
264
  end
228
265
 
266
+ def uncompile
267
+ return unless @compiled
268
+ instance_eval "undef :path; alias :path :compiling_path
269
+ undef :url; alias :url :compiling_url
270
+ undef :url_ns; alias :url_ns :compiling_url_ns
271
+ undef :call; alias :call :compiling_call", __FILE__, __LINE__
272
+ @root.uncompile
273
+ @compiled = false
274
+ end
275
+
276
+ def raw_url(route, *args)
277
+ case route
278
+ when Symbol then @named_routes.key?(route) && @named_routes[route].each{|r| url = r.url(*args); return url if url}
279
+ when Route then return route.url(*args)
280
+ end
281
+ raise(InvalidRouteException.new "No route (url) could be generated for #{route.inspect}")
282
+ end
283
+
284
+ def raw_url_ns(route, *args)
285
+ case route
286
+ when Symbol then @named_routes.key?(route) && @named_routes[route].each{|r| url = r.url_ns(*args); return url if url}
287
+ when Route then return route.url_ns(*args)
288
+ end
289
+ raise(InvalidRouteException.new "No route (url_ns) could be generated for #{route.inspect}")
290
+ end
291
+
292
+ def raw_path(route, *args)
293
+ case route
294
+ when Symbol then @named_routes.key?(route) && @named_routes[route].each{|r| path = r.path(*args); return path if path}
295
+ when Route then return route.path(*args)
296
+ end
297
+ raise(InvalidRouteException.new "No route (path) could be generated for #{route.inspect}")
298
+ end
299
+
300
+ def raw_call(env, &blk)
301
+ rack_request = ::Rack::Request.new(env)
302
+ request = Request.new(rack_request.path_info, rack_request)
303
+ if blk
304
+ @root.call(request, &blk)
305
+ request
306
+ else
307
+ @root.call(request) or no_response(request, env)
308
+ end
309
+ end
310
+
229
311
  private
312
+ def compile
313
+ return if @compiled
314
+ @root.compile(@routes)
315
+ @named_routes.each do |_, routes|
316
+ routes.sort!{|r1, r2| r2.max_param_count <=> r1.max_param_count }
317
+ end
318
+
319
+ instance_eval "undef :path; alias :path :raw_path
320
+ undef :url; alias :url :raw_url
321
+ undef :url_ns; alias :url_ns :raw_url_ns
322
+ undef :call; alias :call :raw_call", __FILE__, __LINE__
323
+ @compiled = true
324
+ end
325
+
230
326
  def add_with_request_method(path, method, opts = {}, &app)
231
- route = add(path, opts).send(method.to_sym)
327
+ opts[:request_method] = method
328
+ route = add(path, opts)
232
329
  route.to(app) if app
233
330
  route
234
331
  end