http_router 0.10.2 → 0.11.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.
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