http_router 0.8.6 → 0.8.7

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.
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