http_router 0.8.6 → 0.8.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -25,7 +25,6 @@ Takes the following options:
25
25
 
26
26
  * `:default_app` - The default #call made on non-matches. Defaults to a 404 generator.
27
27
  * `:ignore_trailing_slash` - Ignores the trailing slash when matching. Defaults to true.
28
- * `:redirect_trailing_slash` - Redirect on trailing slash matches to non-trailing slash paths. Defaults to false.
29
28
  * `:middleware` - Perform matching without deferring to matched route. Defaults to false.
30
29
 
31
30
  ### `#add(name, options)`
@@ -7,6 +7,18 @@ require 'http_router'
7
7
  #require 'http_router'
8
8
 
9
9
  u = HttpRouter.new
10
+
11
+ #puts Benchmark.measure {
12
+ # ('aa'..'nn').each do |first|
13
+ # ('a'..'n').each do |second|
14
+ # u.add("/#{first}/#{second}").to {|env| [200, {'Content-type'=>'text/html'}, []]}
15
+ # end
16
+ # end
17
+ # puts "u.routes.size: #{u.routes.size}"
18
+ #}
19
+
20
+ u.add('/').to {|env| [200, {'Content-type'=>'text/html'}, []]}
21
+
10
22
  u.add('/simple').to {|env| [200, {'Content-type'=>'text/html'}, []]}
11
23
  u.add('/simple/again').to {|env| [200, {'Content-type'=>'text/html'}, []]}
12
24
  #u.add('/simple/again/and/again').compile.to {|env| [200, {'Content-type'=>'text/html'}, []]}
@@ -15,17 +27,6 @@ u.add('/dynamic/:variable').to {|env| [200, {'Content-type'=>'text/html'}, []]}
15
27
  #u.add('/greedy/:greed').matching(:greed => /.*/).compile.to {|env| [200, {'Content-type'=>'text/html'}, []]}
16
28
  #u.add('/greedy/hey.:greed.html').to {|env| [200, {'Content-type'=>'text/html'}, []]}
17
29
 
18
- u.compile rescue nil
19
-
20
- puts Benchmark.measure {
21
- ('aa'..'nn').each do |first|
22
- ('a'..'n').each do |second|
23
- u.add("/#{first}/#{second}").to {|env| [200, {'Content-type'=>'text/html'}, []]}
24
- end
25
- end
26
- #
27
- puts "u.routes.size: #{u.routes.size}"
28
- }
29
30
  #
30
31
  TIMES = 50_000
31
32
 
@@ -40,11 +41,15 @@ u.call(Rack::MockRequest.env_for('/simple')).first == 200 or raise
40
41
  5.times {
41
42
  RBench.run(TIMES) do
42
43
 
44
+ report "1 levels, static" do
45
+ u.call(Rack::MockRequest.env_for('/')).first == 200 or raise
46
+ end
47
+
43
48
  report "2 levels, static" do
44
49
  u.call(Rack::MockRequest.env_for('/simple')).first == 200 or raise
45
50
  end
46
51
 
47
- report "4 levels, static" do
52
+ report "3 levels, static" do
48
53
  u.call(Rack::MockRequest.env_for('/simple/again')).first == 200 or raise
49
54
  end
50
55
 
@@ -52,7 +57,7 @@ u.call(Rack::MockRequest.env_for('/simple')).first == 200 or raise
52
57
  # u.call(simple3_env).first == 200 or raise
53
58
  #end
54
59
 
55
- report "4 levels, 1 dynamic" do
60
+ report "1 static, 1 dynamic" do
56
61
  u.call(Rack::MockRequest.env_for('/dynamic/anything')).first == 200 or raise
57
62
  end
58
63
 
@@ -10,16 +10,13 @@ require 'http_router/route'
10
10
  require 'http_router/path'
11
11
  require 'http_router/rack'
12
12
  require 'http_router/regex_route'
13
- require 'http_router/optional_compiler'
14
13
 
15
14
  class HttpRouter
16
15
 
17
16
  attr_reader :root, :routes, :known_methods, :named_routes, :nodes
18
17
  attr_accessor :default_app, :url_mount
19
18
 
20
- # Raised when a Route is not able to be generated.
21
- UngeneratableRouteException = Class.new(RuntimeError)
22
- # Raised when a Route is generated that isn't valid.
19
+ # Raised when a url is not able to be generated for the given parameters
23
20
  InvalidRouteException = Class.new(RuntimeError)
24
21
  # Raised when a Route is not able to be generated due to a missing parameter.
25
22
  MissingParameterException = Class.new(RuntimeError)
@@ -109,22 +106,16 @@ class HttpRouter
109
106
  # be available under the key <tt>router.params</tt>.
110
107
  def call(env, perform_call = true)
111
108
  rack_request = ::Rack::Request.new(env)
112
- if redirect_trailing_slash? && (rack_request.head? || rack_request.get?) && rack_request.path_info[-1] == ?/
113
- response = ::Rack::Response.new
114
- response.redirect(request.path_info[0, request.path_info.size - 1], 302)
115
- response.finish
109
+ request = Request.new(rack_request.path_info, rack_request, perform_call)
110
+ response = catch(:success) { @root[request] }
111
+ if response
112
+ response
113
+ elsif response.nil?
114
+ no_response(env, perform_call)
115
+ elsif perform_call
116
+ @default_app.call(env)
116
117
  else
117
- request = Request.new(rack_request.path_info, rack_request, perform_call)
118
- response = catch(:success) { @root[request] }
119
- if response
120
- response
121
- elsif response.nil?
122
- no_response(env, perform_call)
123
- elsif perform_call
124
- @default_app.call(env)
125
- else
126
- nil
127
- end
118
+ nil
128
119
  end
129
120
  end
130
121
 
@@ -155,9 +146,9 @@ class HttpRouter
155
146
  # # ==> "/123.html?fun=inthesun"
156
147
  def url(route, *args)
157
148
  case route
158
- when Symbol then @named_routes.key?(route) ? @named_routes[route].url(*args) : raise(UngeneratableRouteException)
149
+ when Symbol then @named_routes.key?(route) ? @named_routes[route].url(*args) : raise(InvalidRouteException)
159
150
  when Route then route.url(*args)
160
- else raise UngeneratableRouteException
151
+ else raise InvalidRouteException
161
152
  end
162
153
  end
163
154
 
@@ -18,6 +18,12 @@ class HttpRouter
18
18
  inject_root_ivar(path_ivar, blk)
19
19
  "#{"if request.path_finished?" unless @allow_partial}
20
20
  catch(:pass) do
21
+ #{"if request.path.size == 1 && request.path.first == '' && (request.rack_request.head? || request.rack_request.get?) && request.rack_request.path_info[-1] == ?/
22
+ response = ::Rack::Response.new
23
+ response.redirect(request.rack_request.path_info[0, request.rack_request.path_info.size - 1], 302)
24
+ throw :success, response.finish
25
+ end" if @router.redirect_trailing_slash?}
26
+
21
27
  #{"if request.path.empty?#{" or (request.path.size == 1 and request.path.first == '')" if @router.ignore_trailing_slash?}" unless @allow_partial}
22
28
  if request.perform_call
23
29
  env = request.rack_request.dup.env
@@ -17,15 +17,16 @@ class HttpRouter
17
17
  def to_code
18
18
  lookup_ivar = :"@lookup_#{router.next_counter}"
19
19
  inject_root_ivar(lookup_ivar, @map)
20
+ method_prefix = "lookup_#{router.next_counter} "
20
21
  inject_root_methods @map.keys.map {|k|
21
- method = :"lookup_#{object_id} #{k}"
22
+ method = :"#{method_prefix}#{k}"
22
23
  "define_method(#{method.inspect}) do |request|
23
24
  part = request.path.shift
24
25
  #{@map[k].map{|n| n.to_code} * "\n"}
25
26
  request.path.unshift part
26
27
  end"}.join("\n")
27
28
  code = "
28
- send(\"lookup_#{object_id} \#{request.path.first}\", request) if !request.path_finished? && #{lookup_ivar}.key?(request.path.first)
29
+ send(\"#{method_prefix}\#{request.path.first}\", request) if !request.path_finished? && #{lookup_ivar}.key?(request.path.first)
29
30
  "
30
31
  end
31
32
  end
@@ -27,11 +27,11 @@ class HttpRouter
27
27
  new_part
28
28
  }
29
29
  @path_validation_regex = Regexp.new("^#{@path_validation_regex}$")
30
- instance_eval "
30
+ instance_eval <<-EOT, __FILE__, __LINE__ + 1
31
31
  def raw_url(args,options)
32
32
  \"#{code}\"
33
33
  end
34
- ", __FILE__, __LINE__
34
+ EOT
35
35
  end
36
36
  end
37
37
 
@@ -52,7 +52,7 @@ class HttpRouter
52
52
 
53
53
  private
54
54
  def raw_url(args, options)
55
- raise UngeneratableRouteException
55
+ raise InvalidRouteException
56
56
  end
57
57
  end
58
58
  end
@@ -1,4 +1,3 @@
1
-
2
1
  class HttpRouter
3
2
  class Route
4
3
  attr_reader :default_values, :matches_with, :router, :path, :conditions
@@ -6,7 +5,6 @@ class HttpRouter
6
5
  def initialize(router, path, opts = {})
7
6
  @router = router
8
7
  @original_path = path
9
- @path = path
10
8
  @opts = opts
11
9
  @matches_with = {}
12
10
  @default_values = opts[:default_values] || {}
@@ -14,7 +12,6 @@ class HttpRouter
14
12
  @match_partially = true
15
13
  path.slice!(-1)
16
14
  end
17
- @paths = OptionalCompiler.new(path).paths
18
15
  process_opts
19
16
  end
20
17
 
@@ -149,14 +146,14 @@ class HttpRouter
149
146
  else
150
147
  matching_path(args, options)
151
148
  end
152
- raise UngeneratableRouteException unless path
149
+ raise InvalidRouteException unless path
153
150
  result, params = path.url(args, options)
154
151
  mount_point = router.url_mount && router.url_mount.url(options)
155
152
  mount_point ? [File.join(mount_point, result), params] : [result, params]
156
153
  end
157
154
 
158
155
  def significant_variable_names
159
- @significant_variable_names ||= @path.scan(/(^|[^\\])[:\*]([a-zA-Z0-9_]+)/).map{|p| p.last.to_sym}
156
+ @significant_variable_names ||= @original_path.scan(/(^|[^\\])[:\*]([a-zA-Z0-9_]+)/).map{|p| p.last.to_sym}
160
157
  end
161
158
 
162
159
  def matching_path(params, other_hash = nil)
@@ -192,7 +189,25 @@ class HttpRouter
192
189
 
193
190
  def compile
194
191
  return if @compiled
195
- @paths.map! do |path|
192
+ start_index, end_index = 0, 1
193
+ raw_paths, chars = [""], @original_path.split('')
194
+ until chars.empty?
195
+ case fc = chars.first[0]
196
+ when ?(
197
+ chars.shift
198
+ (start_index...end_index).each { |path_index| raw_paths << raw_paths[path_index].dup }
199
+ start_index = end_index
200
+ end_index = raw_paths.size
201
+ when ?)
202
+ chars.shift
203
+ start_index -= end_index - start_index
204
+ else
205
+ c = if chars[0][0] == ?\\ && (chars[1][0] == ?( || chars[1][0] == ?)); chars.shift; chars.shift; else; chars.shift; end
206
+ (start_index...end_index).each { |path_index| raw_paths[path_index] << c }
207
+ end
208
+ end
209
+ raw_paths.reverse!
210
+ @paths = raw_paths.map do |path|
196
211
  param_names = []
197
212
  node = @router.root
198
213
  path.split(/\//).each do |part|
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  class HttpRouter #:nodoc
3
- VERSION = '0.8.6'
3
+ VERSION = '0.8.7'
4
4
  end
@@ -59,7 +59,7 @@ class TestGenerate < MiniTest::Unit::TestCase
59
59
  assert_generate '/var/fooz', "/:var1(/:var2)", 'var', 'fooz'
60
60
  assert_generate '/var', "/:var1(/:var2)", :var1 => 'var'
61
61
  assert_generate '/var/fooz', "/:var1(/:var2)", :var1 => 'var', :var2 => 'fooz'
62
- assert_raises(HttpRouter::UngeneratableRouteException) { router.url(router.add("/:var1(/:var2)").to(:test), :var2 => 'fooz') }
62
+ assert_raises(HttpRouter::InvalidRouteException) { router.url(router.add("/:var1(/:var2)").to(:test), :var2 => 'fooz') }
63
63
  end
64
64
 
65
65
  def test_optionals_with_format
@@ -26,4 +26,11 @@ class TestMisc < MiniTest::Unit::TestCase
26
26
  r.reset!
27
27
  assert !r.recognize(Rack::MockRequest.env_for('/hi'))
28
28
  end
29
+
30
+ def test_redirect_trailing_slash
31
+ r = HttpRouter.new(:redirect_trailing_slash => true) { add('/hi').to(:test) }
32
+ response = r.recognize(Rack::MockRequest.env_for('/hi/'))
33
+ assert 304, response[0]
34
+ assert "/hi", response[1]["Location"]
35
+ end
29
36
  end
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: 51
4
+ hash: 49
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 8
9
- - 6
10
- version: 0.8.6
9
+ - 7
10
+ version: 0.8.7
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: 2011-06-07 00:00:00 -07:00
18
+ date: 2011-06-10 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -202,7 +202,6 @@ files:
202
202
  - lib/http_router/node/root.rb
203
203
  - lib/http_router/node/spanning_regex.rb
204
204
  - lib/http_router/node/variable.rb
205
- - lib/http_router/optional_compiler.rb
206
205
  - lib/http_router/path.rb
207
206
  - lib/http_router/rack.rb
208
207
  - lib/http_router/rack/builder.rb
@@ -213,9 +212,7 @@ files:
213
212
  - lib/http_router/route.rb
214
213
  - lib/http_router/version.rb
215
214
  - test/helper.rb
216
- - test/rack/test_dispatch.rb
217
215
  - test/rack/test_route.rb
218
- - test/rack/test_urlmap.rb
219
216
  - test/test_arbitrary.rb
220
217
  - test/test_generate.rb
221
218
  - test/test_greedy.rb
@@ -1,37 +0,0 @@
1
- class HttpRouter
2
- class OptionalCompiler
3
- attr_reader :paths
4
- def initialize(path)
5
- @start_index, @end_index = 0, 1
6
- @paths, @chars = [""], path.split('')
7
- until @chars.empty?
8
- case @chars.first[0]
9
- when ?( then @chars.shift and double_paths
10
- when ?) then @chars.shift and half_paths
11
- when ?\\
12
- @chars[1] == ?( || @chars[1] == ?) ? @chars.shift : add_to_current_set(@chars.shift)
13
- add_to_current_set(@chars.shift)
14
- else
15
- add_to_current_set(@chars.shift)
16
- end
17
- end
18
- @paths.reverse!
19
- end
20
-
21
- private
22
- def add_to_current_set(c)
23
- (@start_index...@end_index).each { |path_index| @paths[path_index] << c }
24
- end
25
-
26
- # over current working set, double @paths
27
- def double_paths
28
- (@start_index...@end_index).each { |path_index| @paths << @paths[path_index].dup }
29
- @start_index = @end_index
30
- @end_index = @paths.size
31
- end
32
-
33
- def half_paths
34
- @start_index -= @end_index - @start_index
35
- end
36
- end
37
- end
@@ -1,120 +0,0 @@
1
- #describe "HttpRouter route dispatching with redirect_on_trailing_delimiters" do
2
- # before(:each) do
3
- # @route_set = HttpRouter.new(:redirect_trailing_slash => true)
4
- # @route_set.extend(CallWithMockRequestMixin)
5
- # @app = MockApp.new("Hello World!")
6
- # @route_set.add('/sample').to(@app)
7
- # end
8
- #
9
- # it "should dispatch a request" do
10
- # response = @route_set.call_with_mock_request('/sample/')
11
- # response.headers["Location"].should == "/sample"
12
- # end
13
- #
14
- #end
15
- #
16
- #describe "HttpRouter route dispatching" do
17
- # before(:each) do
18
- # @route_set = HttpRouter.new(:redirect_trailing_slash => true)
19
- # @route_set.extend(CallWithMockRequestMixin)
20
- # @app = MockApp.new("Hello World!")
21
- # end
22
- #
23
- # describe "HTTP GET" do
24
- # before(:each) do
25
- # @route_set.reset!
26
- # @route_set.add('/sample').request_method('GET').to(@app)
27
- # end
28
- #
29
- # it "should dispatch a request" do
30
- # response = @route_set.call_with_mock_request
31
- # response.body.should eql("Hello World!")
32
- # end
33
- #
34
- # it "should write router.params" do
35
- # response = @route_set.call_with_mock_request
36
- # @app.env["router.params"].should == {}
37
- # end
38
- #
39
- # it "should write router.params for default values" do
40
- # @route_set.add("/foobar", :default_values => {:hi => :there}).compile
41
- # response = @route_set.call_with_mock_request("/foobar")
42
- # env = Rack::MockRequest.env_for("/foobar")
43
- # @route_set.call(env)
44
- # env['router.params'].should == {:hi => :there}
45
- # end
46
- # end
47
- #
48
- # describe "HTTP POST" do
49
- # before(:each) do
50
- # @route_set.reset!
51
- # @route_set.add('/sample').post.to(@app)
52
- # @route_set.add('/sample').to(MockApp.new("You shouldn't get here if you are using POST"))
53
- # end
54
- #
55
- # it "should dispatch a POST request" do
56
- # response = @route_set.call_with_mock_request('/sample', 'POST')
57
- # response.body.should eql("Hello World!")
58
- # end
59
- #
60
- # it "shouldn't dispatch a GET request" do
61
- # response = @route_set.call_with_mock_request('/sample', 'GET')
62
- # response.body.should eql("You shouldn't get here if you are using POST")
63
- # end
64
- #
65
- # it "should write router.params" do
66
- # response = @route_set.call_with_mock_request("/sample", 'POST')
67
- # @app.env["router.params"].should == {}
68
- # end
69
- # end
70
- #
71
- # it "should returns HTTP 405 if the method mis-matches" do
72
- # @route_set.reset!
73
- # @route_set.post('/sample').to(@app)
74
- # @route_set.put('/sample').to(@app)
75
- # response = @route_set.call_with_mock_request('/sample', 'GET')
76
- # response.status.should eql(405)
77
- # response['Allow'].should == 'POST, PUT'
78
- # end
79
- #
80
- # it "should returns HTTP 404 if route doesn't exist" do
81
- # response = @route_set.call_with_mock_request("/not-existing-url")
82
- # response.status.should eql(404)
83
- # end
84
- #
85
- # describe "shortcuts" do
86
- # describe "get" do
87
- # before(:each) do
88
- # @route_set.reset!
89
- # @route_set.get('/sample').head.to(@app)
90
- # end
91
- #
92
- # it "should dispatch a GET request" do
93
- # response = @route_set.call_with_mock_request("/sample", "GET")
94
- # response.body.should eql("Hello World!")
95
- # end
96
- #
97
- # it "should dispatch a HEAD request" do
98
- # response = @route_set.call_with_mock_request("/sample", "HEAD")
99
- # response.body.should eql("Hello World!")
100
- # end
101
- # end
102
- # end
103
- #
104
- # describe "non rack app destinations" do
105
- # it "should route to a default application when using a hash" do
106
- # $captures = []
107
- # @default_app = lambda do |e|
108
- # $captures << :default
109
- # Rack::Response.new("Default").finish
110
- # end
111
- # @router = HttpRouter.new
112
- # @router.default(@default_app)
113
- # @router.add("/default").to(:action => "default")
114
- # response = @router.call(Rack::MockRequest.env_for("/default"))
115
- # $captures.should == [:default]
116
- # end
117
- # end
118
- #
119
- #end
120
- #
@@ -1,12 +0,0 @@
1
- class TestRackUrlmap < MiniTest::Unit::TestCase
2
-
3
- #def test_map_urls
4
- # HttpRouter::Rack.override_rack_urlmap!
5
- # map = Rack::URLMap.new(
6
- # "http://www.example.org/test" => proc {|env| [200, {}, ['test']]},
7
- # "http://www.example.org/:test" => proc {|env| [200, {}, ['variable']]}
8
- # )
9
- # assert_equal 'test', map.call(Rack::MockRequest.env_for('http://www.example.org/test')).last.join
10
- # assert_equal 'variable', map.call(Rack::MockRequest.env_for('http://www.example.org/whhhaaa')).last.join
11
- #end
12
- end