http_router 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1 +1,4 @@
1
1
  pkg
2
+ *.gem
3
+ .bundle
4
+ rdoc
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source :rubygems
2
+
3
+ gem 'rack', '>=1.0'
4
+ gem 'url_mount', '>=0.2.1'
5
+
6
+ group :development do
7
+ gem 'rspec'
8
+ gem 'rake'
9
+ gem 'sinatra'
10
+ gem 'rbench'
11
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,43 @@
1
+ ---
2
+ dependencies:
3
+ rake:
4
+ group:
5
+ - :development
6
+ version: ">= 0"
7
+ url_mount:
8
+ group:
9
+ - :default
10
+ version: ">= 0.2.1"
11
+ rspec:
12
+ group:
13
+ - :development
14
+ version: ">= 0"
15
+ rack:
16
+ group:
17
+ - :default
18
+ version: ">= 1.0"
19
+ sinatra:
20
+ group:
21
+ - :development
22
+ version: ">= 0"
23
+ rbench:
24
+ group:
25
+ - :development
26
+ version: ">= 0"
27
+ specs:
28
+ - rake:
29
+ version: 0.8.7
30
+ - rack:
31
+ version: 1.1.0
32
+ - rbench:
33
+ version: 0.2.3
34
+ - rspec:
35
+ version: 1.3.0
36
+ - sinatra:
37
+ version: "1.0"
38
+ - url_mount:
39
+ version: 0.2.1
40
+ hash: 31358295b6fc19c69280f917c4fa3db0d1a7d48d
41
+ sources:
42
+ - Rubygems:
43
+ uri: http://gemcutter.org
data/Rakefile CHANGED
@@ -1,11 +1,8 @@
1
+ require 'rubygems'
1
2
  require 'spec'
2
3
  require 'spec/rake/spectask'
3
4
  Spec::Rake::SpecTask.new(:spec) do |t|
4
5
  t.spec_opts ||= []
5
- t.ruby_opts << "-rrubygems"
6
- t.ruby_opts << "-Ilib"
7
- t.ruby_opts << "-rhttp_router"
8
- t.ruby_opts << "-rspec/spec_helper"
9
6
  t.spec_opts << "--options" << "spec/spec.opts"
10
7
  t.spec_files = FileList['spec/**/*_spec.rb']
11
8
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.3
1
+ 0.2.4
data/http_router.gemspec CHANGED
@@ -21,7 +21,8 @@ Gem::Specification.new do |s|
21
21
  s.test_files = `git ls-files spec`.split("\n")
22
22
 
23
23
  # dependencies
24
- s.add_dependency "rack", ">= 1.0.0"
24
+ s.add_dependency "rack", ">= 1.0.0"
25
+ s.add_dependency "url_mount", ">=0.2"
25
26
 
26
27
  if s.respond_to? :specification_version then
27
28
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
@@ -1,13 +1,13 @@
1
1
  class HttpRouter
2
2
  class Glob < Variable
3
- def matches?(parts, whole_path)
4
- @matches_with.nil? or (!parts.empty? and match = @matches_with.match(parts.first) and match.begin(0))
3
+ def matches?(parts)
4
+ @matches_with.nil? or (!parts.empty? and match = @matches_with.match(parts.first) and match.begin(0)) ? match : nil
5
5
  end
6
6
 
7
- def consume(parts, whole_path)
7
+ def consume(match, parts)
8
8
  if @matches_with
9
9
  params = [parts.shift]
10
- params << parts.shift while matches?(parts, whole_path)
10
+ params << parts.shift while matches?(parts)
11
11
  params
12
12
  else
13
13
  params = parts.dup
@@ -139,7 +139,6 @@ class HttpRouter
139
139
 
140
140
  def find_on_parts(request, parts, params)
141
141
  if parts and !parts.empty?
142
- whole_path = parts.join('/')
143
142
  if parts.size == 1 and parts.first == ''
144
143
  potential_match = find_on_parts(request, [], params)
145
144
  if potential_match and (router.ignore_trailing_slash? or potential_match.value && potential_match.value.route.trailing_slash_ignore?)
@@ -151,12 +150,12 @@ class HttpRouter
151
150
  response = nil
152
151
  dupped_parts = nil
153
152
  next_node = @linear.find do |(tester, node)|
154
- if tester.respond_to?(:matches?) and tester.matches?(parts, whole_path)
153
+ if tester.respond_to?(:matches?) and match = tester.matches?(parts)
155
154
  dupped_parts = parts.dup
156
- params << tester.consume(dupped_parts, whole_path)
155
+ params << tester.consume(match, dupped_parts)
157
156
  parts.replace(dupped_parts) if response = node.find_on_parts(request, dupped_parts, params)
158
- elsif tester.respond_to?(:match) and match = tester.match(whole_path) and match.begin(0) == 0
159
- dupped_parts = router.split(whole_path[match[0].size, whole_path.size])
157
+ elsif tester.respond_to?(:match) and match = tester.match(parts.whole_path) and match.begin(0) == 0
158
+ dupped_parts = router.split(parts.whole_path[match[0].size, parts.whole_path.size])
160
159
  parts.replace(dupped_parts) if response = node.find_on_parts(request, dupped_parts, params)
161
160
  else
162
161
  nil
@@ -168,7 +167,7 @@ class HttpRouter
168
167
  parts.shift
169
168
  return match.find_on_parts(request, parts, params)
170
169
  elsif @catchall
171
- params << @catchall.variable.consume(parts, whole_path)
170
+ params << @catchall.variable.consume(nil, parts)
172
171
  return @catchall.find_on_parts(request, parts, params)
173
172
  end
174
173
  end
@@ -0,0 +1,21 @@
1
+ class HttpRouter
2
+ class Parts < Array
3
+ def initialize(path)
4
+ super((path[0] == ?/ ? path[1, path.size] : path).split('/'))
5
+ end
6
+
7
+ def whole_path
8
+ @whole_path ||= join('/')
9
+ end
10
+
11
+ def shift
12
+ @whole_path = nil
13
+ super
14
+ end
15
+
16
+ def replace(ary)
17
+ @whole_path = nil
18
+ super
19
+ end
20
+ end
21
+ end
@@ -1,4 +1,3 @@
1
- require 'cgi'
2
1
  class HttpRouter
3
2
  class Path
4
3
  attr_reader :parts, :route
@@ -58,9 +57,9 @@ class HttpRouter
58
57
  params.each do |k,v|
59
58
  case v
60
59
  when Array
61
- v.each { |v_part| uri << '&' << CGI.escape(k.to_s) << '%5B%5D=' << CGI.escape(v_part.to_s) }
60
+ v.each { |v_part| uri << '&' << Rack::Utils.escape(k.to_s) << '%5B%5D=' << Rack::Utils.escape(v_part.to_s) }
62
61
  else
63
- uri << '&' << CGI.escape(k.to_s) << '=' << CGI.escape(v.to_s)
62
+ uri << '&' << Rack::Utils.escape(k.to_s) << '=' << Rack::Utils.escape(v.to_s)
64
63
  end
65
64
  end
66
65
  uri[uri_size] = ??
@@ -30,8 +30,8 @@ class HttpRouter
30
30
  end
31
31
 
32
32
  # Creates a deep uncompiled copy of this route.
33
- def clone
34
- Route.new(@router, @original_path.dup).with_options(as_options)
33
+ def clone(new_router)
34
+ Route.new(new_router, @original_path.dup).with_options(as_options)
35
35
  end
36
36
 
37
37
  # Uses an option hash to apply conditions to a Route.
@@ -75,27 +75,27 @@ class HttpRouter
75
75
  def get
76
76
  request_method('GET')
77
77
  end
78
-
78
+
79
79
  # Causes this route to recognize the POST request method. Returns +self+.
80
80
  def post
81
81
  request_method('POST')
82
82
  end
83
-
83
+
84
84
  # Causes this route to recognize the HEAD request method. Returns +self+.
85
85
  def head
86
86
  request_method('HEAD')
87
87
  end
88
-
88
+
89
89
  # Causes this route to recognize the PUT request method. Returns +self+.
90
90
  def put
91
91
  request_method('PUT')
92
92
  end
93
-
93
+
94
94
  # Causes this route to recognize the DELETE request method. Returns +self+.
95
95
  def delete
96
96
  request_method('DELETE')
97
97
  end
98
-
98
+
99
99
  # Sets a request condition for the route
100
100
  # Returns +self+.
101
101
  #
@@ -147,6 +147,11 @@ class HttpRouter
147
147
  def to(dest = nil, &block)
148
148
  compile
149
149
  @dest = dest || block
150
+ if @dest.respond_to?(:url_mount=)
151
+ urlmount = UrlMount.new(@original_path, @default_values)
152
+ urlmount.url_mount = router.url_mount if router.url_mount
153
+ @dest.url_mount = urlmount
154
+ end
150
155
  self
151
156
  end
152
157
 
@@ -161,12 +166,12 @@ class HttpRouter
161
166
  @arbitrary << (proc || block)
162
167
  self
163
168
  end
164
-
169
+
165
170
  # Compile state for route. Returns +true+ or +false+.
166
171
  def compiled?
167
172
  !@paths.nil?
168
173
  end
169
-
174
+
170
175
  # Compiles the route and inserts it into the tree. This is called automatically when you add a destination via #to to the route. Until a route
171
176
  # is compiled, it will not be recognized.
172
177
  def compile
@@ -189,7 +194,7 @@ class HttpRouter
189
194
  end
190
195
  self
191
196
  end
192
-
197
+
193
198
  # Sets the destination of this route to redirect to an arbitrary URL.
194
199
  def redirect(path, status = 302)
195
200
  raise(ArgumentError, "Status has to be an integer between 300 and 399") unless (300..399).include?(status)
@@ -201,7 +206,7 @@ class HttpRouter
201
206
  }
202
207
  self
203
208
  end
204
-
209
+
205
210
  # Sets the destination of this route to serve static files from either a directory or a single file.
206
211
  def static(root)
207
212
  if File.directory?(root)
@@ -233,11 +238,18 @@ class HttpRouter
233
238
  matching_path(args, options)
234
239
  end
235
240
  raise UngeneratableRouteException.new unless path
236
- path.url(args, options)
241
+
242
+ mount_point = nil
243
+ if !router.url_mount.nil?
244
+ mount_point = router.url_mount.url(options)
245
+ end
246
+
247
+ result = path.url(args, options)
248
+ mount_point.nil? ? result : File.join(mount_point, result)
237
249
  end
238
250
 
239
251
  private
240
-
252
+
241
253
  attr_reader :router
242
254
 
243
255
  def matching_path(params, other_hash = nil)
@@ -246,7 +258,7 @@ class HttpRouter
246
258
  else
247
259
  if params.is_a?(Array)
248
260
  significant_keys = other_hash && significant_variable_names & other_hash.keys
249
- @paths.find { |path|
261
+ @paths.find { |path|
250
262
  var_count = significant_keys ? params.size + significant_keys.size : params.size
251
263
  path.variables.size == var_count
252
264
  }
@@ -262,7 +274,7 @@ class HttpRouter
262
274
  end
263
275
  end
264
276
  end
265
-
277
+
266
278
  def extract_partial_match(path)
267
279
  path[-1] == ?* && path.slice!(-1)
268
280
  end
@@ -8,14 +8,13 @@ class HttpRouter
8
8
  @matches_with = matches_with
9
9
  end
10
10
 
11
- def matches?(parts, whole_path)
12
- @matches_with.nil? or (@matches_with and match = @matches_with.match(whole_path) and match.begin(0) == 0)
11
+ def matches?(parts)
12
+ @matches_with.nil? or (@matches_with and match = @matches_with.match(parts.whole_path) and match.begin(0) == 0) ? match : nil
13
13
  end
14
14
 
15
- def consume(parts, whole_path)
15
+ def consume(match, parts)
16
16
  if @matches_with
17
- match = @matches_with.match(whole_path)
18
- parts.replace(router.split(whole_path[match.end(0), whole_path.size]))
17
+ parts.replace(router.split(parts.whole_path[match.end(0), parts.whole_path.size]))
19
18
  match[0]
20
19
  else
21
20
  parts.shift
data/lib/http_router.rb CHANGED
@@ -1,5 +1,7 @@
1
- $LOAD_PATH << File.dirname(__FILE__)
1
+ $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
2
+
2
3
  require 'rack'
4
+ require 'url_mount'
3
5
  require 'ext/rack/uri_escape'
4
6
  require 'http_router/node'
5
7
  require 'http_router/root'
@@ -9,6 +11,7 @@ require 'http_router/route'
9
11
  require 'http_router/response'
10
12
  require 'http_router/path'
11
13
  require 'http_router/optional_compiler'
14
+ require 'http_router/parts'
12
15
 
13
16
  class HttpRouter
14
17
  # Raised when a Route is not able to be generated.
@@ -29,6 +32,7 @@ class HttpRouter
29
32
  AmbiguousVariableException = Class.new(RuntimeError)
30
33
 
31
34
  attr_reader :named_routes, :routes, :root
35
+ attr_accessor :url_mount
32
36
 
33
37
  # Monkey-patches Rack::Builder to use HttpRouter.
34
38
  # See examples/rack_mapper.rb
@@ -83,7 +87,7 @@ class HttpRouter
83
87
  def default(app)
84
88
  @default_app = app
85
89
  end
86
-
90
+
87
91
  # Adds a path to be recognized.
88
92
  #
89
93
  # To assign a part of the path to a specific variable, use :variable_name within the route.
@@ -93,7 +97,7 @@ class HttpRouter
93
97
  # For example, <tt>add('/path/*id')</tt> would match <tt>/path/123/456/789</tt>, with the variable <tt>:id</tt> having the value <tt>["123", "456", "789"]</tt>.
94
98
  #
95
99
  # As well, paths can end with two optional parts, <tt>*</tt> and <tt>/?</tt>. If it ends with a <tt>*</tt>, it will match partially, returning the part of the path unmatched in the PATH_INFO value of the env. The part matched to will be returned in the SCRIPT_NAME. If it ends with <tt>/?</tt>, then a trailing / on the path will be optionally matched for that specific route. As trailing /'s are ignored by default, you probably don't actually want to use this option that frequently.
96
- #
100
+ #
97
101
  # Routes can also contain optional parts. There are surrounded with <tt>( )</tt>'s. If you need to match on a bracket in the route itself, you can escape the parentheses with a backslash.
98
102
  #
99
103
  # The second argument, options, is an optional hash that can modify the route in further ways. See HttpRouter::Route#with_options for details. Typically, you want to add further options to the route by calling additional methods on it. See HttpRouter::Route for further details.
@@ -144,7 +148,7 @@ class HttpRouter
144
148
 
145
149
  # Generate a URL for a specified route. This will accept a list of variable values plus any other variable names named as a hash.
146
150
  # This first value must be either the Route object or the name of the route.
147
- #
151
+ #
148
152
  # Example:
149
153
  # router = HttpRouter.new
150
154
  # router.add('/:foo.:format).name(:test).compile
@@ -167,7 +171,7 @@ class HttpRouter
167
171
  end
168
172
  end
169
173
 
170
- # Rack compatible #call. If matching route is found, and +dest+ value responds to #call, processing will pass to the matched route. Otherwise,
174
+ # Rack compatible #call. If matching route is found, and +dest+ value responds to #call, processing will pass to the matched route. Otherwise,
171
175
  # the default application will be called. The router will be available in the env under the key <tt>router</tt>. And parameters matched will
172
176
  # be available under the key <tt>router.params</tt>. The HttpRouter::Response object will be available under the key <tt>router.response</tt> if
173
177
  # a response is available.
@@ -192,7 +196,7 @@ class HttpRouter
192
196
  @default_app.call(env)
193
197
  end
194
198
  end
195
-
199
+
196
200
  # Returns a new node
197
201
  def node(*args)
198
202
  Node.new(self, *args)
@@ -211,7 +215,7 @@ class HttpRouter
211
215
  def variable(*args)
212
216
  Variable.new(self, *args)
213
217
  end
214
-
218
+
215
219
  # Returns a new glob
216
220
  def glob(*args)
217
221
  Glob.new(self, *args)
@@ -219,16 +223,25 @@ class HttpRouter
219
223
 
220
224
  # Creates a deep-copy of the router.
221
225
  def clone
222
- cloned_router = HttpRouter.new(@default_app, @options, &@init_block)
226
+ cloned_router = HttpRouter.new(@default_app, @options)
223
227
  @routes.each do |route|
224
- new_route = route.clone
225
- new_route.instance_variable_set(:@router, cloned_router)
228
+ new_route = route.clone(cloned_router)
229
+ cloned_router.add_route(new_route).compile
230
+ new_route.name(route.named) if route.named
231
+
232
+ if route.dest
233
+ begin
234
+ new_route.to route.dest.clone
235
+ rescue
236
+ new_route.to route.dest
237
+ end
238
+ end
226
239
  end
227
240
  cloned_router
228
241
  end
229
242
 
230
243
  def split(path)
231
- (path[0] == ?/ ? path[1, path.size] : path).split('/')
244
+ Parts.new(path)
232
245
  end
233
246
 
234
247
  private
@@ -1,3 +1,4 @@
1
+ require 'spec_helper'
1
2
  describe "HttpRouter#generate" do
2
3
  before(:each) do
3
4
  @router = HttpRouter.new
@@ -32,7 +33,7 @@ describe "HttpRouter#generate" do
32
33
  @router.add("/:var").name(:test).compile
33
34
  @router.url(:test, 'test', :query => 'string').should == '/test?query=string'
34
35
  end
35
-
36
+
36
37
  it "should generate with multiple dynamics" do
37
38
  @router.add("/:var/:baz").name(:test).compile
38
39
  @router.url(:test, 'one', 'two').should == '/one/two'
@@ -49,7 +50,7 @@ describe "HttpRouter#generate" do
49
50
  @router.add("/test.:format").name(:test).compile
50
51
  @router.url(:test, :format => 'html').should == '/test.html'
51
52
  end
52
-
53
+
53
54
  it "should generate with format as a symbol" do
54
55
  @router.add("/test.:format").name(:test).compile
55
56
  @router.url(:test, :format => :html).should == '/test.html'
@@ -60,24 +61,24 @@ describe "HttpRouter#generate" do
60
61
  @router.url(:test, 'html').should == '/test.html'
61
62
  @router.url(:test).should == '/test'
62
63
  end
63
-
64
+
64
65
  it "should generate a dynamic path and a format" do
65
66
  @router.add("/:var1.:format").name(:test).compile
66
67
  @router.url(:test, 'var', :format => 'html').should == '/var.html'
67
68
  end
68
-
69
+
69
70
  it "should generate a dynamic path and an optional format" do
70
71
  @router.add("/:var1(.:format)").name(:test).compile
71
72
  @router.url(:test, 'var').should == '/var'
72
73
  @router.url(:test, 'var', :format => 'html').should == '/var.html'
73
74
  end
74
-
75
+
75
76
  it "should generate multiple dynamics and a format" do
76
77
  @router.add("/:foo/:bar.:format").name(:test).compile
77
78
  @router.url(:test, 'var', 'baz', 'html').should == '/var/baz.html'
78
79
  @router.url(:test, :foo => 'var', :bar => 'baz', :format => 'html').should == '/var/baz.html'
79
80
  end
80
-
81
+
81
82
  it "should generate multiple dynamics and an optional format" do
82
83
  @router.add("/:foo/:bar(.:format)").name(:test).compile
83
84
  @router.url(:test, 'var', 'baz').should == '/var/baz'
@@ -137,5 +138,90 @@ describe "HttpRouter#generate" do
137
138
  end
138
139
  end
139
140
 
141
+ context "url mounting" do
142
+ context "nested routes" do
143
+ before(:each) do
144
+ @r1 = HttpRouter.new
145
+ @r2 = HttpRouter.new
146
+ @r2.add("/bar").name(:test).compile
147
+ end
148
+
149
+ it "should set the url mount on a child route" do
150
+ route = @r1.add("/foo").to(@r2)
151
+ @r2.url_mount.url.should == "/foo"
152
+ @r2.url(:test).should == "/foo/bar"
153
+ end
154
+
155
+ it "should set any default values on the url mount" do
156
+ route = @r1.add("/foo/:bar").default(:bar => "baz").to(@r2)
157
+ @r2.url(:test).should == "/foo/baz/bar"
158
+ @r2.url(:test, :bar => "haha").should == "/foo/haha/bar"
159
+ end
160
+
161
+ it "should use multiple variables" do
162
+ @r1.add("/foo/:bar/:baz").default(:bar => "bar").to(@r2)
163
+ @r2.url(:test, :baz => "baz").should == "/foo/bar/baz/bar"
164
+ end
165
+
166
+ it "should not steal parameters from the defaults it doesn't need" do
167
+ route = @r1.add("/foo/:bar").default(:bar => "baz").to(@r2)
168
+ @r2.url(:test, :bang => "ers").should == "/foo/baz/bar?bang=ers"
169
+ @r2.url(:test, :bar => "haha", :bang => "ers").should == "/foo/haha/bar?bang=ers"
170
+ end
171
+
172
+ it "should generate on a path with an optional variable" do
173
+ @r1.add("/foo(/:bar)").to(@r2)
174
+ @r2.add("/hey(/:there)").name(:test).compile
175
+ @r2.url(:test).should == "/foo/hey"
176
+ @r2.url(:test, :bar => "bar").should == "/foo/bar/hey"
177
+ @r2.url(:test, :bar => "bar", :there => "there").should == "/foo/bar/hey/there"
178
+ end
179
+
180
+ it "should nest 3 times deeply" do
181
+ @r3 = HttpRouter.new
182
+ @r1.add("/foo(/:bar)").default(:bar => "barry").to(@r2)
183
+ @r2.add("/hi").name(:hi).compile
184
+ @r2.add("/mounted").to(@r3)
185
+ @r3.add("/endpoint").name(:endpoint).compile
186
+
187
+ @r2.url(:hi).should == "/foo/barry/hi"
188
+ @r3.url(:endpoint).should == "/foo/barry/mounted/endpoint"
189
+ @r3.url(:endpoint, :bar => "flower").should == "/foo/flower/mounted/endpoint"
190
+ end
191
+
192
+ it "should allow me to set the host via a default" do
193
+ @r1.add("/mounted").default(:host => "example.com").to(@r2)
194
+ @r2.url(:test).should == "http://example.com/mounted/bar"
195
+ end
196
+
197
+ it "should allow me to set the host via an option" do
198
+ @r1.add("/mounted").to(@r2)
199
+ @r2.url(:test).should == "/mounted/bar"
200
+ @r2.url(:test, :host => "example.com").should == "http://example.com/mounted/bar"
201
+ end
202
+
203
+ it "should allow me to set the scheme via an option" do
204
+ @r1.add("/mounted").to(@r2)
205
+ @r2.url(:test).should == "/mounted/bar"
206
+ @r2.url(:test, :scheme => "https", :host => "example.com").should == "https://example.com/mounted/bar"
207
+ end
208
+
209
+ it "should clone my nested structure" do
210
+ @r3 = HttpRouter.new
211
+ @r1.add("/first").to(@r2)
212
+ @r2.add("/second").to(@r3)
213
+ r1 = @r1.clone
214
+ @r1.routes.first.should_not be_nil
215
+ r2 = r1.routes.first.dest
216
+ r2.should_not be_nil
217
+ @r1.routes.first.dest.object_id.should == @r2.object_id
218
+ r2.object_id.should_not == @r2.object_id
219
+ r2.routes.should have(2).route
220
+ r3 = r2.routes.last.dest
221
+ r3.should be_an_instance_of(HttpRouter)
222
+ r3.object_id.should_not == @r3.object_id
223
+ end
224
+ end
225
+ end
140
226
  end
141
227
  end
data/spec/misc_spec.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'spec_helper'
1
2
  describe "HttpRouter" do
2
3
  before(:each) do
3
4
  @router = HttpRouter.new
@@ -43,17 +44,17 @@ describe "HttpRouter" do
43
44
  add('/test').name(:test_route).to :test
44
45
  }
45
46
  r2 = r1.clone
46
-
47
+
47
48
  r2.add('/test2').name(:test).to(:test2)
48
49
  r2.routes.size.should == 2
49
-
50
+
50
51
  r1.recognize(Rack::MockRequest.env_for('/test2')).should be_nil
51
52
  r2.recognize(Rack::MockRequest.env_for('/test2')).should_not be_nil
52
53
  r1.named_routes[:test_route].should == r1.routes.first
53
54
  r2.named_routes[:test_route].should == r2.routes.first
54
55
 
55
56
  r1.add('/another').name(:test).to(:test2)
56
-
57
+
57
58
  r1.routes.size.should == r2.routes.size
58
59
  r1.url(:test).should == '/another'
59
60
  r2.url(:test).should == '/test2'
@@ -61,4 +62,4 @@ describe "HttpRouter" do
61
62
  r2.routes.first.dest.should == :test
62
63
  end
63
64
  end
64
- end
65
+ end
@@ -0,0 +1,5 @@
1
+ describe "mounting" do
2
+ before(:each) do
3
+ @router = HttpRouter.new
4
+ end
5
+ end
@@ -1,6 +1,3 @@
1
- route_set = HttpRouter.new
2
- route_set.extend(CallWithMockRequestMixin)
3
-
4
1
  describe "HttpRouter route dispatching with redirect_on_trailing_delimiters" do
5
2
  before(:each) do
6
3
  @route_set = HttpRouter.new(:redirect_trailing_slash => true)
@@ -18,78 +15,79 @@ end
18
15
 
19
16
  describe "HttpRouter route dispatching" do
20
17
  before(:each) do
21
- route_set.reset!
18
+ @route_set = HttpRouter.new(:redirect_trailing_slash => true)
19
+ @route_set.extend(CallWithMockRequestMixin)
22
20
  @app = MockApp.new("Hello World!")
23
21
  end
24
22
 
25
23
  describe "HTTP GET" do
26
24
  before(:each) do
27
- route_set.reset!
28
- route_set.add('/sample').request_method('GET').to(@app)
25
+ @route_set.reset!
26
+ @route_set.add('/sample').request_method('GET').to(@app)
29
27
  end
30
28
 
31
29
  it "should dispatch a request" do
32
- response = route_set.call_with_mock_request
30
+ response = @route_set.call_with_mock_request
33
31
  response.body.should eql("Hello World!")
34
32
  end
35
33
 
36
34
  it "should write router.params" do
37
- response = route_set.call_with_mock_request
35
+ response = @route_set.call_with_mock_request
38
36
  @app.env["router.params"].should == {}
39
37
  end
40
38
  end
41
39
 
42
40
  describe "HTTP POST" do
43
41
  before(:each) do
44
- route_set.reset!
45
- route_set.add('/sample').post.to(@app)
46
- route_set.add('/sample').to(MockApp.new("You shouldn't get here if you are using POST"))
42
+ @route_set.reset!
43
+ @route_set.add('/sample').post.to(@app)
44
+ @route_set.add('/sample').to(MockApp.new("You shouldn't get here if you are using POST"))
47
45
  end
48
46
 
49
47
  it "should dispatch a POST request" do
50
- response = route_set.call_with_mock_request('/sample', 'POST')
48
+ response = @route_set.call_with_mock_request('/sample', 'POST')
51
49
  response.body.should eql("Hello World!")
52
50
  end
53
51
 
54
52
  it "shouldn't dispatch a GET request" do
55
- response = route_set.call_with_mock_request('/sample', 'GET')
53
+ response = @route_set.call_with_mock_request('/sample', 'GET')
56
54
  response.body.should eql("You shouldn't get here if you are using POST")
57
55
  end
58
56
 
59
57
  it "should write router.params" do
60
- response = route_set.call_with_mock_request("/sample", 'POST')
58
+ response = @route_set.call_with_mock_request("/sample", 'POST')
61
59
  @app.env["router.params"].should == {}
62
60
  end
63
61
  end
64
62
 
65
63
  it "should returns HTTP 405 if the method mis-matches" do
66
- route_set.reset!
67
- route_set.post('/sample').to(@app)
68
- route_set.put('/sample').to(@app)
69
- response = route_set.call_with_mock_request('/sample', 'GET')
64
+ @route_set.reset!
65
+ @route_set.post('/sample').to(@app)
66
+ @route_set.put('/sample').to(@app)
67
+ response = @route_set.call_with_mock_request('/sample', 'GET')
70
68
  response.status.should eql(405)
71
69
  response['Allow'].should == 'POST, PUT'
72
70
  end
73
71
 
74
72
  it "should returns HTTP 404 if route doesn't exist" do
75
- response = route_set.call_with_mock_request("/not-existing-url")
73
+ response = @route_set.call_with_mock_request("/not-existing-url")
76
74
  response.status.should eql(404)
77
75
  end
78
76
 
79
77
  describe "shortcuts" do
80
78
  describe "get" do
81
79
  before(:each) do
82
- route_set.reset!
83
- route_set.get('/sample').head.to(@app)
80
+ @route_set.reset!
81
+ @route_set.get('/sample').head.to(@app)
84
82
  end
85
83
 
86
84
  it "should dispatch a GET request" do
87
- response = route_set.call_with_mock_request("/sample", "GET")
85
+ response = @route_set.call_with_mock_request("/sample", "GET")
88
86
  response.body.should eql("Hello World!")
89
87
  end
90
88
 
91
89
  it "should dispatch a HEAD request" do
92
- response = route_set.call_with_mock_request("/sample", "HEAD")
90
+ response = @route_set.call_with_mock_request("/sample", "HEAD")
93
91
  response.body.should eql("Hello World!")
94
92
  end
95
93
  end
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  route_set = HttpRouter.new
2
4
  route_set.extend(CallWithMockRequestMixin)
3
5
 
@@ -9,12 +11,12 @@ describe "HttpRouter route generation" do
9
11
  route_set.add("/named/simple/:named_simple_var").name(:simple).compile
10
12
  route_set.add("/named/optional(/:named_optional_var)").name(:optional).compile
11
13
  end
12
-
14
+
13
15
  describe "named routes" do
14
16
  it "should generate a fixed path" do
15
17
  route_set.url(:fixed).should == "/fixed"
16
18
  end
17
-
19
+
18
20
  it "should generate a named path route" do
19
21
  route_set.url(:simple, :named_simple_var => "the_var").should == "/named/simple/the_var"
20
22
  end
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe "HttpRouter as middleware" do
2
4
  before(:each) do
3
5
  @builder = Rack::Builder.new do
@@ -17,4 +19,4 @@ describe "HttpRouter as middleware" do
17
19
  @builder.call(Rack::MockRequest.env_for('/test')).last.join.should == 'test'
18
20
  end
19
21
  end
20
-
22
+
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe "Rack interface extensions for Usher::Route" do
2
4
  before(:each) do
3
5
  @route_set = HttpRouter.new
@@ -1,3 +1,4 @@
1
+ require 'spec_helper'
1
2
  describe "HttpRouter#recognize" do
2
3
  before(:each) do
3
4
  @router = HttpRouter.new
@@ -10,7 +11,7 @@ describe "HttpRouter#recognize" do
10
11
  @router.recognize(Rack::MockRequest.env_for(path)).route.should == route
11
12
  end
12
13
  end
13
-
14
+
14
15
  context("with optional parts") do
15
16
  it "work either way" do
16
17
  route = @router.add("/test(/optional)").to(:test)
@@ -199,7 +200,7 @@ describe "HttpRouter#recognize" do
199
200
  response.params_as_hash[:format].should be_nil
200
201
  response.params_as_hash[:test].should == 'hey'
201
202
  end
202
-
203
+
203
204
  context "with globs" do
204
205
  it "should recognize" do
205
206
  route = @router.add('/test/*variable').to(:test)
@@ -216,9 +217,9 @@ describe "HttpRouter#recognize" do
216
217
  response.should be_nil
217
218
  end
218
219
  end
219
-
220
+
220
221
  end
221
-
222
+
222
223
  context("with interstitial variables") do
223
224
  it "should recognize" do
224
225
  route = @router.add('/one-:variable-time').to(:test)
@@ -234,14 +235,14 @@ describe "HttpRouter#recognize" do
234
235
  response.route.should == route
235
236
  response.params_as_hash[:variable].should == '123'
236
237
  end
237
-
238
+
238
239
  it "should recognize when there is an extension" do
239
240
  route = @router.add('/hey.:greed.html').to(:test)
240
241
  response = @router.recognize(Rack::MockRequest.env_for('/hey.greedyboy.html'))
241
242
  response.route.should == route
242
243
  response.params_as_hash[:greed].should == 'greedyboy'
243
244
  end
244
-
245
+
245
246
  end
246
247
 
247
248
  context("with dynamic greedy paths") do
@@ -1,3 +1,4 @@
1
+ require 'spec_helper'
1
2
  require "sinatra"
2
3
  require "http_router/interface/sinatra"
3
4
 
@@ -79,28 +80,28 @@ describe "HttpRouter (for Sinatra) route recognition" do
79
80
  describe "mapping functionality" do
80
81
 
81
82
  it "should map a basic route" do
82
- @app.get('/hi', :name => :hi) { generate(:hi) }
83
+ @app.get('/hi', :name => :hi) { generate(:hi) }
83
84
  response = @app.call_with_mock_request('/hi')
84
85
  response.status.should == 200
85
86
  response.body.should == "/hi"
86
87
  end
87
88
 
88
89
  it "should map a basic route ignoring trailing delimiters" do
89
- @app.get('/hi', :name => :hi) { generate(:hi) }
90
+ @app.get('/hi', :name => :hi) { generate(:hi) }
90
91
  response = @app.call_with_mock_request('/hi/')
91
92
  response.status.should == 200
92
93
  response.body.should == "/hi"
93
94
  end
94
95
 
95
96
  it "should map a basic route with params" do
96
- @app.get('/hi/:id', :name => :hi) { generate(:hi, :id => 18) }
97
+ @app.get('/hi/:id', :name => :hi) { generate(:hi, :id => 18) }
97
98
  response = @app.call_with_mock_request('/hi/1')
98
99
  response.status.should == 200
99
100
  response.body.should == "/hi/18"
100
101
  end
101
102
 
102
103
  it "should map route with params" do
103
- @app.get('/hi-:id', :name => :hi) { generate(:hi, :id => 18) }
104
+ @app.get('/hi-:id', :name => :hi) { generate(:hi, :id => 18) }
104
105
  response = @app.call_with_mock_request('/hi-1')
105
106
  response.status.should == 200
106
107
  response.body.should == "/hi-18"
@@ -137,4 +138,4 @@ describe "HttpRouter (for Sinatra) route recognition" do
137
138
  response.body.should_not match(/__sinatra__/)
138
139
  end
139
140
  end
140
- end
141
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,8 @@
1
- require 'rack'
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'rubygems'
3
+ require 'http_router'
4
+ require 'spec'
5
+ require 'spec/autorun'
2
6
 
3
7
  module CallWithMockRequestMixin
4
8
  def call_with_mock_request(url = "/sample", method = "GET", params = Hash.new)
@@ -21,4 +25,4 @@ class MockApp
21
25
  @headers.merge("Content-Length" => @body.length.to_s)
22
26
  [@status, @headers, [@body]]
23
27
  end
24
- end
28
+ 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: 17
4
+ hash: 31
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 3
10
- version: 0.2.3
9
+ - 4
10
+ version: 0.2.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Joshua Hull
@@ -34,6 +34,21 @@ dependencies:
34
34
  version: 1.0.0
35
35
  type: :runtime
36
36
  version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: url_mount
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 15
46
+ segments:
47
+ - 0
48
+ - 2
49
+ version: "0.2"
50
+ type: :runtime
51
+ version_requirements: *id002
37
52
  description: A kick-ass HTTP router for use in Rack & Sinatra
38
53
  email: joshbuddy@gmail.com
39
54
  executables: []
@@ -44,6 +59,8 @@ extra_rdoc_files:
44
59
  - README.rdoc
45
60
  files:
46
61
  - .gitignore
62
+ - Gemfile
63
+ - Gemfile.lock
47
64
  - README.rdoc
48
65
  - Rakefile
49
66
  - VERSION
@@ -67,6 +84,7 @@ files:
67
84
  - lib/http_router/interface/sinatra.rb
68
85
  - lib/http_router/node.rb
69
86
  - lib/http_router/optional_compiler.rb
87
+ - lib/http_router/parts.rb
70
88
  - lib/http_router/path.rb
71
89
  - lib/http_router/response.rb
72
90
  - lib/http_router/root.rb
@@ -74,6 +92,7 @@ files:
74
92
  - lib/http_router/variable.rb
75
93
  - spec/generate_spec.rb
76
94
  - spec/misc_spec.rb
95
+ - spec/mounting_spec.rb
77
96
  - spec/rack/dispatch_spec.rb
78
97
  - spec/rack/generate_spec.rb
79
98
  - spec/rack/middleware_spec.rb
@@ -119,6 +138,7 @@ summary: A kick-ass HTTP router for use in Rack & Sinatra
119
138
  test_files:
120
139
  - spec/generate_spec.rb
121
140
  - spec/misc_spec.rb
141
+ - spec/mounting_spec.rb
122
142
  - spec/rack/dispatch_spec.rb
123
143
  - spec/rack/generate_spec.rb
124
144
  - spec/rack/middleware_spec.rb