usher 0.5.4 → 0.5.5

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.rdoc CHANGED
@@ -1,4 +1,4 @@
1
- = Usher
1
+ = Usher
2
2
 
3
3
  Tree-based router library. Useful for (specifically) for Rails and Rack, but probably generally useful for anyone interested in doing routing. Based on Ilya Grigorik suggestion, turns out looking up in a hash and following a tree is faster than Krauter's massive regex approach.
4
4
 
@@ -19,13 +19,13 @@ Tree-based router library. Useful for (specifically) for Rails and Rack, but pro
19
19
  From the rdoc:
20
20
 
21
21
  Creates a route from +path+ and +options+
22
-
22
+
23
23
  === +path+
24
24
  A path consists a mix of dynamic and static parts delimited by <tt>/</tt>
25
25
 
26
26
  ==== Dynamic
27
27
  Dynamic parts are prefixed with either :, *. :variable matches only one part of the path, whereas *variable can match one or
28
- more parts.
28
+ more parts.
29
29
 
30
30
  <b>Example:</b>
31
31
  <tt>/path/:variable/path</tt> would match
@@ -35,7 +35,7 @@ more parts.
35
35
  * <tt>/path/one_more/path</tt>
36
36
 
37
37
  In the above examples, 'test', 'something_else' and 'one_more' respectively would be bound to the key <tt>:variable</tt>.
38
- However, <tt>/path/test/one_more/path</tt> would not be matched.
38
+ However, <tt>/path/test/one_more/path</tt> would not be matched.
39
39
 
40
40
  <b>Example:</b>
41
41
  <tt>/path/*variable/path</tt> would match
@@ -59,7 +59,7 @@ But not
59
59
  As well, the same logic applies for * variables as well, where only parts matchable by the supplied regex will
60
60
  actually be bound to the variable
61
61
 
62
- Variables can also have a greedy regex matcher. These matchers ignore all delimiters, and continue matching for as long as much as their
62
+ Variables can also have a greedy regex matcher. These matchers ignore all delimiters, and continue matching for as long as much as their
63
63
  regex allows.
64
64
 
65
65
  <b>Example:</b>
@@ -110,7 +110,7 @@ For instance, the path, <tt>/path/something(.xml|.html)</tt> would only match <t
110
110
  [body] # Response body
111
111
  ]
112
112
  end
113
-
113
+
114
114
  routes = Usher::Interface.for(:rack) do
115
115
  add('/hello/:name').to(app)
116
116
  end
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 4
3
- :major: 0
4
2
  :minor: 5
3
+ :patch: 5
4
+ :major: 0
data/lib/usher.rb CHANGED
@@ -7,12 +7,12 @@ require File.join(File.dirname(__FILE__), 'usher', 'exceptions')
7
7
  require File.join(File.dirname(__FILE__), 'usher', 'util')
8
8
 
9
9
  class Usher
10
- attr_reader :root, :named_routes, :routes, :splitter,
11
- :delimiters, :delimiter_chars, :delimiters_regex,
10
+ attr_reader :root, :named_routes, :routes, :splitter,
11
+ :delimiters, :delimiter_chars, :delimiters_regex,
12
12
  :parent_route, :generator
13
-
13
+
14
14
  # Returns whether the route set is empty
15
- #
15
+ #
16
16
  # set = Usher.new
17
17
  # set.empty? => true
18
18
  # set.add_route('/test')
@@ -20,13 +20,13 @@ class Usher
20
20
  def empty?
21
21
  @routes.empty?
22
22
  end
23
-
23
+
24
24
  def route_count
25
25
  @routes.size
26
26
  end
27
-
27
+
28
28
  # Resets the route set back to its initial state
29
- #
29
+ #
30
30
  # set = Usher.new
31
31
  # set.add_route('/test')
32
32
  # set.empty? => false
@@ -39,14 +39,14 @@ class Usher
39
39
  @grapher = Grapher.new
40
40
  end
41
41
  alias clear! reset!
42
-
42
+
43
43
  # Creates a route set, with options
44
- #
44
+ #
45
45
  # <tt>:delimiters</tt>: Array of Strings. (default <tt>['/', '.']</tt>). Delimiters used in path separation. Array must be single character strings.
46
- #
46
+ #
47
47
  # <tt>:valid_regex</tt>: String. (default <tt>'[0-9A-Za-z\$\-_\+!\*\',]+'</tt>). String that can be interpolated into regex to match
48
48
  # valid character sequences within path.
49
- #
49
+ #
50
50
  # <tt>:request_methods</tt>: Array of Symbols. (default <tt>[:protocol, :domain, :port, :query_string, :remote_ip, :user_agent, :referer, :method, :subdomains]</tt>)
51
51
  # Array of methods called against the request object for the purposes of matching route requirements.
52
52
  def initialize(options = nil)
@@ -64,13 +64,13 @@ class Usher
64
64
  def can_generate?
65
65
  !@generator.nil?
66
66
  end
67
-
67
+
68
68
  def generator
69
69
  @generator
70
70
  end
71
-
71
+
72
72
  # Adds a route referencable by +name+. See add_route for format +path+ and +options+.
73
- #
73
+ #
74
74
  # set = Usher.new
75
75
  # set.add_named_route(:test_route, '/test')
76
76
  def add_named_route(name, path, options = nil)
@@ -78,7 +78,7 @@ class Usher
78
78
  end
79
79
 
80
80
  # Deletes a route referencable by +name+. At least the path and conditions have to match the route you intend to delete.
81
- #
81
+ #
82
82
  # set = Usher.new
83
83
  # set.delete_named_route(:test_route, '/test')
84
84
  def delete_named_route(name, path, options = nil)
@@ -87,7 +87,7 @@ class Usher
87
87
  end
88
88
 
89
89
  # Attaches a +route+ to a +name+
90
- #
90
+ #
91
91
  # set = Usher.new
92
92
  # route = set.add_route('/test')
93
93
  # set.name(:test, route)
@@ -97,13 +97,13 @@ class Usher
97
97
  end
98
98
 
99
99
  # Creates a route from +path+ and +options+
100
- #
100
+ #
101
101
  # === +path+
102
102
  # A path consists a mix of dynamic and static parts delimited by <tt>/</tt>
103
103
  #
104
104
  # ==== Dynamic
105
105
  # Dynamic parts are prefixed with either :, *. :variable matches only one part of the path, whereas *variable can match one or
106
- # more parts.
106
+ # more parts.
107
107
  #
108
108
  # <b>Example:</b>
109
109
  # <tt>/path/:variable/path</tt> would match
@@ -113,7 +113,7 @@ class Usher
113
113
  # * <tt>/path/one_more/path</tt>
114
114
  #
115
115
  # In the above examples, 'test', 'something_else' and 'one_more' respectively would be bound to the key <tt>:variable</tt>.
116
- # However, <tt>/path/test/one_more/path</tt> would not be matched.
116
+ # However, <tt>/path/test/one_more/path</tt> would not be matched.
117
117
  #
118
118
  # <b>Example:</b>
119
119
  # <tt>/path/*variable/path</tt> would match
@@ -127,22 +127,22 @@ class Usher
127
127
  #
128
128
  # <b>Example:</b>
129
129
  # <tt>/product/{:id,\d+}</tt> would match
130
- #
130
+ #
131
131
  # * <tt>/product/123</tt>
132
132
  # * <tt>/product/4521</tt>
133
- #
133
+ #
134
134
  # But not
135
135
  # * <tt>/product/AE-35</tt>
136
- #
136
+ #
137
137
  # As well, the same logic applies for * variables as well, where only parts matchable by the supplied regex will
138
138
  # actually be bound to the variable
139
139
  #
140
- # Variables can also have a greedy regex matcher. These matchers ignore all delimiters, and continue matching for as long as much as their
140
+ # Variables can also have a greedy regex matcher. These matchers ignore all delimiters, and continue matching for as long as much as their
141
141
  # regex allows.
142
142
  #
143
143
  # <b>Example:</b>
144
144
  # <tt>/product/{!id,hello/world|hello}</tt> would match
145
- #
145
+ #
146
146
  # * <tt>/product/hello/world</tt>
147
147
  # * <tt>/product/hello</tt>
148
148
  #
@@ -176,7 +176,7 @@ class Usher
176
176
  end
177
177
 
178
178
  # Deletes a route. At least the path and conditions have to match the route you intend to delete.
179
- #
179
+ #
180
180
  # set = Usher.new
181
181
  # set.delete_route('/test')
182
182
  def delete_route(path, options = nil)
@@ -188,7 +188,7 @@ class Usher
188
188
  end
189
189
 
190
190
  # Recognizes a +request+ and returns +nil+ or an Usher::Node::Response, which is a struct containing a Usher::Route::Path and an array of arrays containing the extracted parameters.
191
- #
191
+ #
192
192
  # Request = Struct.new(:path)
193
193
  # set = Usher.new
194
194
  # route = set.add_route('/test')
@@ -198,7 +198,7 @@ class Usher
198
198
  end
199
199
 
200
200
  # Recognizes a +path+ and returns +nil+ or an Usher::Node::Response, which is a struct containing a Usher::Route::Path and an array of arrays containing the extracted parameters. Convenience method for when recognizing on the request object is unneeded.
201
- #
201
+ #
202
202
  # Request = Struct.new(:path)
203
203
  # set = Usher.new
204
204
  # route = set.add_route('/test')
@@ -208,35 +208,43 @@ class Usher
208
208
  end
209
209
 
210
210
  # Recognizes a set of +parameters+ and gets the closest matching Usher::Route::Path or +nil+ if no route exists.
211
- #
211
+ #
212
212
  # set = Usher.new
213
213
  # route = set.add_route('/:controller/:action')
214
214
  # set.path_for_options({:controller => 'test', :action => 'action'}) == path.route => true
215
215
  def path_for_options(options)
216
216
  @grapher.find_matching_path(options)
217
217
  end
218
-
218
+
219
219
  def parent_route=(route)
220
220
  @parent_route = route
221
221
  routes.each{|r| r.parent_route = route}
222
222
  end
223
-
223
+
224
224
  def dup
225
225
  replacement = super
226
226
  original = self
227
+ inverted_named_routes = original.named_routes.invert
227
228
  replacement.instance_eval do
229
+ @parser = nil
228
230
  reset!
229
231
  original.routes.each do |route|
230
- @root.add(route)
231
- @routes << route
232
+ new_route = route.dup
233
+ new_route.router = self
234
+ @root.add(new_route)
235
+ @routes << new_route
236
+ if name = inverted_named_routes[route]
237
+ @named_routes[name] = new_route
238
+ end
232
239
  end
240
+ send(:generator=, original.generator.class.new) if original.can_generate?
233
241
  rebuild_grapher!
234
242
  end
235
243
  replacement
236
244
  end
237
-
245
+
238
246
  private
239
-
247
+
240
248
  attr_accessor :request_methods
241
249
  attr_reader :valid_regex
242
250
 
@@ -247,7 +255,7 @@ class Usher
247
255
  end
248
256
  @generator
249
257
  end
250
-
258
+
251
259
  def delimiters=(delimiters)
252
260
  @delimiters = delimiters
253
261
  @delimiter_chars = @delimiters.collect{|d| d[0]}
@@ -274,12 +282,12 @@ class Usher
274
282
  end
275
283
  end
276
284
  end
277
-
285
+
278
286
  route = parser.generate_route(path, conditions, requirements, default_values, generate_with)
279
287
  route.to(options) if options && !options.empty?
280
288
  route
281
289
  end
282
-
290
+
283
291
  def rebuild_grapher!
284
292
  @grapher = Grapher.new
285
293
  @routes.each{|r| @grapher.add_route(r)}
@@ -6,6 +6,7 @@ class Usher
6
6
  autoload :RackInterface, File.join(File.dirname(__FILE__), 'interface', 'rack_interface')
7
7
  autoload :EmailInterface, File.join(File.dirname(__FILE__), 'interface', 'email_interface')
8
8
  autoload :Rails3Interface, File.join(File.dirname(__FILE__), 'interface', 'rails3_interface')
9
+ autoload :TextInterface, File.join(File.dirname(__FILE__), 'interface', 'text_interface')
9
10
 
10
11
  def self.for(type, &blk)
11
12
  class_for(type).new(&blk)
@@ -25,6 +26,8 @@ class Usher
25
26
  EmailInterface
26
27
  when :rails3
27
28
  Rails3Interface
29
+ when :text
30
+ TextInterface
28
31
  end
29
32
 
30
33
  end
@@ -3,28 +3,34 @@ require 'rack'
3
3
  class Usher
4
4
  module Interface
5
5
  class RackInterface
6
-
6
+
7
7
  attr_reader :router
8
-
8
+ attr_accessor :app
9
+
10
+ DEFAULT_APPLICATION = lambda do |env|
11
+ Rack::Response.new("No route found", 404).finish
12
+ end
13
+
9
14
  class Builder < Rack::Builder
10
-
15
+
11
16
  def initialize(&block)
12
17
  @usher = Usher::Interface::RackInterface.new
13
18
  super
14
19
  end
15
-
20
+
16
21
  def map(path, options = nil, &block)
17
22
  @usher.add(path, options).to(&block)
18
23
  @ins << @usher unless @ins.last == @usher
19
24
  end
20
-
25
+
21
26
  end
22
-
23
- def initialize(&blk)
27
+
28
+ def initialize(app = nil, &blk)
29
+ @app = app || DEFAULT_APPLICATION
24
30
  @router = Usher.new(:request_methods => [:request_method, :host, :port, :scheme], :generator => Usher::Util::Generators::URL.new)
25
31
  instance_eval(&blk) if blk
26
32
  end
27
-
33
+
28
34
  def dup
29
35
  new_one = super
30
36
  original = self
@@ -33,15 +39,15 @@ class Usher
33
39
  end
34
40
  new_one
35
41
  end
36
-
42
+
37
43
  def add(path, options = nil)
38
44
  @router.add_route(path, options)
39
- end
40
-
45
+ end
46
+
41
47
  def parent_route=(route)
42
48
  @router.parent_route = route
43
49
  end
44
-
50
+
45
51
  def parent_route
46
52
  @router.parent_route
47
53
  end
@@ -51,30 +57,43 @@ class Usher
51
57
  end
52
58
 
53
59
  def call(env)
54
- env['usher.params'] ||= {}
55
60
  response = @router.recognize(request = Rack::Request.new(env), request.path_info)
56
- if response.nil?
57
- body = "No route found"
58
- headers = {"Content-Type" => "text/plain", "Content-Length" => body.length.to_s}
59
- [404, headers, [body]]
60
- else
61
- params = response.path.route.default_values || {}
62
- response.params.each{ |hk| params[hk.first] = hk.last}
63
-
64
- # consume the path_info to the script_name response.remaining_path
65
- env["SCRIPT_NAME"] << response.matched_path || ""
66
- env["PATH_INFO"] = response.remaining_path || ""
67
-
68
- env['usher.params'].merge!(params)
69
-
70
- response.path.route.destination.call(env)
71
- end
61
+ after_match(env, response) if response
62
+ determine_respondant(response).call(env)
72
63
  end
73
64
 
74
65
  def generate(route, params = nil, options = nil)
75
66
  @usher.generator.generate(route, params, options)
76
67
  end
77
68
 
69
+ # Allows a hook to be placed for sub classes to make use of between matching
70
+ # and calling the application
71
+ #
72
+ # @api plugin
73
+ def after_match(env, response)
74
+ env['usher.params'] ||= {}
75
+ params = response.path.route.default_values || {}
76
+ response.params.each{|hk| params[hk.first] = hk.last}
77
+ env['usher.params'].merge!(params)
78
+
79
+ # consume the path_info to the script_name response.remaining_path
80
+ env["SCRIPT_NAME"] << response.matched_path || ""
81
+ env["PATH_INFO"] = response.remaining_path || ""
82
+ end
83
+
84
+ # Determines which application to respond with.
85
+ #
86
+ # Within the request when determine respondant is called
87
+ # If there is a matching route to an application, that
88
+ # application is called, Otherwise the middleware application is called.
89
+ #
90
+ # @api private
91
+ def determine_respondant(response)
92
+ return app if response.nil?
93
+ respondant = response.path.route.destination
94
+ respondant = app unless respondant.respond_to?(:call)
95
+ respondant
96
+ end
78
97
  end
79
98
  end
80
99
  end
@@ -0,0 +1,44 @@
1
+ class Usher
2
+ module Interface
3
+ class TextInterface
4
+
5
+ def initialize(&blk)
6
+ @usher = Usher.new(:delimiters => [' '], :generator => Usher::Util::Generators::Generic.new)
7
+ instance_eval(&blk) if blk
8
+ end
9
+
10
+ def generate(name, params = nil)
11
+ @usher.generator.generate(name, params)
12
+ end
13
+
14
+ def on(text, name = nil, &blk)
15
+ r = @usher.add_route(text).to(:block => blk, :arg_type => :array)
16
+ r.name(name) if name
17
+ end
18
+
19
+ def unrecognized(&blk)
20
+ @unrecognize_block = blk
21
+ end
22
+
23
+ def on_with_hash(text, name = nil, &blk)
24
+ r = @usher.add_route(text).to(:block => blk, :arg_type => :hash)
25
+ r.name(name) if name
26
+ end
27
+
28
+ def run(text)
29
+ response = @usher.recognize_path(text.strip)
30
+ if response
31
+ case response.path.route.destination[:arg_type]
32
+ when :hash
33
+ response.path.route.destination[:block].call(response.params.inject({}){|h,(k,v)| h[k]=v; h })
34
+ when :array
35
+ response.path.route.destination[:block].call(*response.params.collect{|p| p.last})
36
+ end
37
+ else
38
+ @unrecognize_block ? @unrecognize_block.call(text) : nil
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end
data/lib/usher/node.rb CHANGED
@@ -108,15 +108,7 @@ class Usher
108
108
  end
109
109
 
110
110
  def find(usher, request_object, original_path, path, params = [], position = 0)
111
- if request_method_type
112
- if (specific_node = request[request_object.send(request_method_type)]) && (ret = specific_node.find(usher, request_object, original_path, path.dup, params.dup, position))
113
- ret
114
- elsif (general_node = request[nil]) && (ret = general_node.find(usher, request_object, original_path, path.dup, params.dup, position))
115
- ret
116
- else
117
- nil
118
- end
119
- elsif terminates? && (path.empty? || terminates.route.partial_match?)
111
+ if terminates? && (path.empty? || terminates.route.partial_match?)
120
112
  terminates.route.partial_match? ?
121
113
  Response.new(terminates, params, original_path[position, original_path.size], original_path[0, position]) :
122
114
  Response.new(terminates, params, nil, original_path)
@@ -130,7 +122,7 @@ class Usher
130
122
  case next_part.value
131
123
  when Route::Variable::Glob
132
124
  params << [next_part.value.name, []] unless params.last && params.last.first == next_part.value.name
133
- loop do
125
+ while true
134
126
  if (next_part.value.look_ahead === part || (!usher.delimiter_chars.include?(part[0]) && next_part.value.regex_matcher && !next_part.value.regex_matcher.match(part)))
135
127
  path.unshift(part)
136
128
  position -= part.size
@@ -153,13 +145,21 @@ class Usher
153
145
  var = next_part.value
154
146
  var.valid!(part)
155
147
  params << [var.name, part]
156
- until (var.look_ahead === path.first) || path.empty?
148
+ until path.empty? || (var.look_ahead === path.first)
157
149
  next_path_part = path.shift
158
150
  position += next_path_part.size
159
151
  params.last.last << next_path_part
160
- end
152
+ end if var.look_ahead && usher.delimiter_chars.size > 1
161
153
  end
162
154
  next_part.find(usher, request_object, original_path, path, params, position)
155
+ elsif request_method_type
156
+ if (specific_node = request[request_object.send(request_method_type)]) && (ret = specific_node.find(usher, request_object, original_path, path.dup, params.dup, position))
157
+ ret
158
+ elsif (general_node = request[nil]) && (ret = general_node.find(usher, request_object, original_path, path.dup, params.dup, position))
159
+ ret
160
+ else
161
+ nil
162
+ end
163
163
  else
164
164
  nil
165
165
  end
data/lib/usher/route.rb CHANGED
@@ -8,16 +8,16 @@ class Usher
8
8
  attr_reader :paths, :requirements, :conditions,
9
9
  :destination, :named, :generate_with,
10
10
  :default_values, :match_partially
11
- attr_accessor :parent_route
12
-
11
+ attr_accessor :parent_route, :router
12
+
13
13
  GenerateWith = Struct.new(:scheme, :port, :host)
14
-
15
- def initialize(parsed_paths, router, conditions, requirements, default_values, generate_with, match_partially)
14
+
15
+ def initialize(parsed_paths, router, conditions, requirements, default_values, generate_with, match_partially)
16
16
  @paths = parsed_paths.collect {|path| Path.new(self, path)}
17
17
  @router, @requirements, @conditions, @default_values, @match_partially = router, requirements, conditions, default_values, match_partially
18
18
  @generate_with = GenerateWith.new(generate_with[:scheme], generate_with[:port], generate_with[:host]) if generate_with
19
19
  end
20
-
20
+
21
21
  def grapher
22
22
  unless @grapher
23
23
  @grapher = Grapher.new
@@ -28,7 +28,9 @@ class Usher
28
28
 
29
29
  def dup
30
30
  result = super
31
- result.grapher = nil
31
+ result.instance_eval do
32
+ @grapher = nil
33
+ end
32
34
  result
33
35
  end
34
36
 
@@ -38,17 +40,17 @@ class Usher
38
40
  else
39
41
  matching_path = @paths.size == 1 ? @paths.first : grapher.find_matching_path(params)
40
42
  end
41
-
43
+
42
44
  if parent_route
43
45
  matching_path = parent_route.find_matching_path(params).merge(matching_path)
44
46
  matching_path.route = self
45
47
  end
46
-
48
+
47
49
  matching_path
48
50
  end
49
-
51
+
50
52
  # Sets +options+ on a route. Returns +self+.
51
- #
53
+ #
52
54
  # Request = Struct.new(:path)
53
55
  # set = Usher.new
54
56
  # route = set.add_route('/test')
@@ -66,7 +68,7 @@ class Usher
66
68
  end
67
69
 
68
70
  # Sets route as referenceable from +name+. Returns +self+.
69
- #
71
+ #
70
72
  # set = Usher.new
71
73
  # route = set.add_route('/test').name(:route)
72
74
  # set.generate_url(:route) => '/test'
@@ -75,16 +77,16 @@ class Usher
75
77
  @router.name(name, self)
76
78
  self
77
79
  end
78
-
80
+
79
81
  def match_partially!
80
82
  @match_partially = true
81
83
  self
82
84
  end
83
-
85
+
84
86
  def partial_match?
85
87
  @match_partially
86
88
  end
87
-
89
+
88
90
  private
89
91
  attr_writer :grapher
90
92
 
@@ -1,35 +1,46 @@
1
- require 'rack'
1
+ class Usher
2
+ module Util
3
+ class Generators
2
4
 
3
- unless Rack::Utils.respond_to?(:uri_escape)
4
- module Rack
5
+ class Generic
5
6
 
6
- module Utils
7
+ attr_accessor :usher
7
8
 
8
- def uri_escape(s)
9
- s.to_s.gsub(/([^:\/?\[\]\-_~\.!\$&'\(\)\*\+,;=@a-zA-Z0-9]+)/n) {
10
- '%'<<$1.unpack('H2'*$1.size).join('%').upcase
11
- }.tr(' ', '+')
12
- end
13
- module_function :uri_escape
9
+ def generate(name, params)
10
+ generate_path_for_base_params(@usher.named_routes[name].find_matching_path(params), params)
11
+ end
12
+
13
+ def generate_path_for_base_params(path, params)
14
+ raise UnrecognizedException.new unless path
15
+
16
+ result = ''
17
+ path.parts.each do |part|
18
+ case part
19
+ when Route::Variable::Glob
20
+ value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new)
21
+ value.each_with_index do |current_value, index|
22
+ part.valid!(current_value)
23
+ result << current_value.to_s
24
+ result << usher.delimiters.first if index != value.size - 1
25
+ end
26
+ when Route::Variable
27
+ value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new)
28
+ part.valid!(value)
29
+ result << value.to_s
30
+ else
31
+ result << part
32
+ end
33
+ end
34
+ result
35
+ end
14
36
 
15
- def uri_unescape(s)
16
- gsub(/((?:%[0-9a-fA-F]{2})+)/n){
17
- [$1.delete('%')].pack('H*')
18
- }
19
37
  end
20
- module_function :uri_unescape
21
38
 
22
- end
23
- end
24
- end
39
+ class URL < Generic
25
40
 
26
- class Usher
27
- module Util
28
- class Generators
29
-
30
- class URL
31
-
32
- attr_accessor :usher
41
+ def initialize
42
+ require File.join(File.dirname(__FILE__), 'rack-mixins')
43
+ end
33
44
 
34
45
  def generate_full(routing_lookup, request, params = nil)
35
46
  path = path_for_routing_lookup(routing_lookup, params)
@@ -37,10 +48,75 @@ class Usher
37
48
  result << generate_path(path, params)
38
49
  end
39
50
 
51
+ # Generates a completed URL based on a +route+ or set of optional +params+
52
+ #
53
+ # set = Usher.new
54
+ # route = set.add_named_route(:test_route, '/:controller/:action')
55
+ # set.generator.generate(nil, {:controller => 'c', :action => 'a'}) == '/c/a' => true
56
+ # set.generator.generate(:test_route, {:controller => 'c', :action => 'a'}) == '/c/a' => true
57
+ # set.generator.generate(route.primary_path, {:controller => 'c', :action => 'a'}) == '/c/a' => true
40
58
  def generate(routing_lookup, params = nil)
41
59
  generate_path(path_for_routing_lookup(routing_lookup, params), params)
42
60
  end
43
61
 
62
+ def generate_path(path, params = nil)
63
+ params = Array(params) if params.is_a?(String)
64
+ if params.is_a?(Array)
65
+ given_size = params.size
66
+ extra_params = params.last.is_a?(Hash) ? params.pop : nil
67
+ params = Hash[*path.dynamic_parts.inject([]){|a, dynamic_part| a.concat([dynamic_part.name, params.shift || raise(MissingParameterException.new("got #{given_size}, expected #{path.dynamic_parts.size} parameters"))]); a}]
68
+ params.merge!(extra_params) if extra_params
69
+ end
70
+
71
+ result = Rack::Utils.uri_escape(generate_path_for_base_params(path, params))
72
+ unless params.nil? || params.empty?
73
+ extra_params = generate_extra_params(params, result[??])
74
+ result << extra_params
75
+ end
76
+ result
77
+ end
78
+
79
+ def generation_module
80
+ build_module!
81
+ @generation_module
82
+ end
83
+
84
+ def build_module!
85
+ unless @generation_module
86
+ @generation_module = Module.new
87
+ @generation_module.module_eval <<-END_EVAL
88
+ @@generator = nil
89
+ def self.generator=(generator)
90
+ @@generator = generator
91
+ end
92
+ END_EVAL
93
+ @generation_module.generator = self
94
+
95
+ @generation_module.module_eval <<-END_EVAL
96
+ def respond_to?(method_name)
97
+ if match = Regexp.new('^(.*?)_(path|url)$').match(method_name.to_s)
98
+ @@generator.usher.named_routes.key?(match.group(1))
99
+ else
100
+ super
101
+ end
102
+ end
103
+ END_EVAL
104
+
105
+
106
+ usher.named_routes.each do |name, route|
107
+ @generation_module.module_eval <<-END_EVAL
108
+ def #{name}_url(name, request, params = nil)
109
+ @@generator.generate_full(name, request, options)
110
+ end
111
+
112
+ def #{name}_path(name, params = nil)
113
+ @@generator.generate(name, options)
114
+ end
115
+ END_EVAL
116
+ end
117
+ end
118
+ end
119
+
44
120
  def generate_start(path, request)
45
121
  result = (path.route.generate_with && path.route.generate_with.scheme || request.scheme).dup
46
122
  result << '://'
@@ -53,77 +129,42 @@ class Usher
53
129
  end
54
130
  result
55
131
  end
56
-
132
+
57
133
  def path_for_routing_lookup(routing_lookup, params = {})
58
134
  path = case routing_lookup
59
135
  when Symbol
60
- route = @usher.named_routes[routing_lookup]
136
+ route = @usher.named_routes[routing_lookup]
137
+ raise UnrecognizedException unless route
61
138
  route.find_matching_path(params || {})
62
139
  when Route
63
- routing_lookup.find_matching_path(params || {})
140
+ routing_lookup.find_matching_path(params)
64
141
  when nil
65
- params.is_a?(Hash) ? @usher.path_for_options(params) : raise
142
+ params.is_a?(Hash) ? usher.path_for_options(params) : raise
66
143
  when Route::Path
67
144
  routing_lookup
68
145
  end
69
146
  end
70
-
71
- # Generates a completed URL based on a +route+ or set of optional +params+
72
- #
73
- # set = Usher.new
74
- # route = set.add_named_route(:test_route, '/:controller/:action')
75
- # set.generate_url(nil, {:controller => 'c', :action => 'a'}) == '/c/a' => true
76
- # set.generate_url(:test_route, {:controller => 'c', :action => 'a'}) == '/c/a' => true
77
- # set.generate_url(route.primary_path, {:controller => 'c', :action => 'a'}) == '/c/a' => true
78
- def generate_path(path, params = nil)
79
- raise UnrecognizedException.new unless path
80
147
 
81
- params = Array(params) if params.is_a?(String)
82
- if params.is_a?(Array)
83
- given_size = params.size
84
- extra_params = params.last.is_a?(Hash) ? params.pop : nil
85
- params = Hash[*path.dynamic_parts.inject([]){|a, dynamic_part| a.concat([dynamic_part.name, params.shift || raise(MissingParameterException.new("got #{given_size}, expected #{path.dynamic_parts.size} parameters"))]); a}]
86
- params.merge!(extra_params) if extra_params
87
- end
88
148
 
89
- result = ''
90
- path.parts.each do |part|
91
- case part
92
- when Route::Variable::Glob
93
- value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new)
94
- value.each_with_index do |current_value, index|
95
- part.valid!(current_value)
96
- result << current_value.to_s
97
- result << '/' if index != value.size - 1
98
- end
99
- when Route::Variable
100
- value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new)
101
- part.valid!(value)
102
- result << value.to_s
103
- else
104
- result << part
105
- end
106
- end
107
- result = Rack::Utils.uri_escape(result)
149
+ def generate_extra_params(params, has_question_mark)
150
+ extra_params_result = ''
108
151
 
109
- unless params.nil? || params.empty?
110
- has_query = result[??]
111
- params.each do |k,v|
112
- case v
113
- when Array
114
- v.each do |v_part|
115
- result << (has_query ? '&' : has_query = true && '?') << Rack::Utils.escape("#{k.to_s}[]") << '=' << Rack::Utils.escape(v_part.to_s)
116
- end
117
- else
118
- result << (has_query ? '&' : has_query = true && '?') << Rack::Utils.escape(k.to_s) << '=' << Rack::Utils.escape(v.to_s)
152
+ params.each do |k,v|
153
+ case v
154
+ when Array
155
+ v.each do |v_part|
156
+ extra_params_result << (has_question_mark ? '&' : has_question_mark = true && '?') << Rack::Utils.escape("#{k.to_s}[]") << '=' << Rack::Utils.escape(v_part.to_s)
119
157
  end
158
+ else
159
+ extra_params_result << (has_question_mark ? '&' : has_question_mark = true && '?') << Rack::Utils.escape(k.to_s) << '=' << Rack::Utils.escape(v.to_s)
120
160
  end
121
161
  end
122
- result
162
+ extra_params_result
123
163
  end
124
-
164
+
125
165
  end
126
-
166
+
127
167
  end
128
168
  end
129
- end
169
+ end
170
+
@@ -0,0 +1,24 @@
1
+ require 'rack'
2
+
3
+ unless Rack::Utils.respond_to?(:uri_escape)
4
+ module Rack
5
+
6
+ module Utils
7
+
8
+ def uri_escape(s)
9
+ s.to_s.gsub(/([^:\/?\[\]\-_~\.!\$&'\(\)\*\+,;=@a-zA-Z0-9]+)/n) {
10
+ '%'<<$1.unpack('H2'*$1.size).join('%').upcase
11
+ }.tr(' ', '+')
12
+ end
13
+ module_function :uri_escape
14
+
15
+ def uri_unescape(s)
16
+ gsub(/((?:%[0-9a-fA-F]{2})+)/n){
17
+ [$1.delete('%')].pack('H*')
18
+ }
19
+ end
20
+ module_function :uri_unescape
21
+
22
+ end
23
+ end
24
+ end
@@ -2,37 +2,37 @@ require 'lib/usher'
2
2
  require 'rack'
3
3
 
4
4
  describe "Usher URL generation" do
5
-
5
+
6
6
  before(:each) do
7
7
  @route_set = Usher.new(:generator => Usher::Util::Generators::URL.new)
8
8
  @route_set.reset!
9
9
  end
10
-
10
+
11
11
  it "should generate a simple URL" do
12
12
  @route_set.add_named_route(:sample, '/sample', :controller => 'sample', :action => 'action')
13
13
  @route_set.generator.generate(:sample, {}).should == '/sample'
14
14
  end
15
-
15
+
16
16
  it "should generate a simple URL with a single variable" do
17
17
  @route_set.add_named_route(:sample, '/sample/:action', :controller => 'sample')
18
18
  @route_set.generator.generate(:sample, {:action => 'action'}).should == '/sample/action'
19
19
  end
20
-
20
+
21
21
  it "should generate a simple URL with a single variable (and escape)" do
22
22
  @route_set.add_named_route(:sample, '/sample/:action', :controller => 'sample')
23
23
  @route_set.generator.generate(:sample, {:action => 'action time'}).should == '/sample/action%20time'
24
24
  end
25
-
25
+
26
26
  it "should generate a simple URL with a single variable (thats not a string)" do
27
27
  @route_set.add_named_route(:sample, '/sample/:action/:id', :controller => 'sample')
28
28
  @route_set.generator.generate(:sample, {:action => 'action', :id => 123}).should == '/sample/action/123'
29
29
  end
30
-
30
+
31
31
  it "should generate a simple URL with a glob variable" do
32
32
  @route_set.add_named_route(:sample, '/sample/*action', :controller => 'sample')
33
33
  @route_set.generator.generate(:sample, {:action => ['foo', 'baz']}).should == '/sample/foo/baz'
34
34
  end
35
-
35
+
36
36
  it "should generate a mutliple vairable URL from a hash" do
37
37
  @route_set.add_named_route(:sample, '/sample/:first/:second', :controller => 'sample')
38
38
  @route_set.generator.generate(:sample, {:first => 'zoo', :second => 'maz'}).should == '/sample/zoo/maz'
@@ -125,7 +125,7 @@ describe "Usher URL generation" do
125
125
  @route_set.add_named_route(:name, '/:one/:two/:three', {:default_values => {:one => 'one', :two => 'two', :three => 'three'}})
126
126
  @route_set.generator.generate(:name).should == '/one/two/three'
127
127
  end
128
-
128
+
129
129
  it "should generate a route using defaults and optionals using the last parameter" do
130
130
  @route_set.add_named_route(:opts_with_defaults, '/:one(/:two(/:three))', {:default_values => {:one => '1', :two => '2', :three => '3'}})
131
131
  @route_set.generator.generate(:opts_with_defaults, {:three => 'three'}).should == '/1/2/three'
@@ -135,61 +135,114 @@ describe "Usher URL generation" do
135
135
  @route_set.add_named_route(:optionals, '/:controller(/:action(/:id))(.:format)')
136
136
  @route_set.generator.generate(:optionals, {:controller => "foo", :action => "bar"}).should == '/foo/bar'
137
137
  end
138
-
138
+
139
139
  describe "nested generation" do
140
140
  before do
141
141
  @route_set2 = Usher.new(:generator => Usher::Util::Generators::URL.new)
142
142
  @route_set3 = Usher.new(:generator => Usher::Util::Generators::URL.new)
143
143
  @route_set4 = Usher.new(:generator => Usher::Util::Generators::URL.new)
144
-
144
+
145
145
  @route_set.add_named_route(:simple, "/mount_point").match_partially!.to(@route_set2)
146
146
  @route_set.add_route("/third/:foo", :default_values => {:foo => "foo"}).match_partially!.to(@route_set3)
147
147
  @route_set.add_route("/fourth/:bar").match_partially!.to(@route_set4)
148
-
148
+
149
149
  @route_set2.add_named_route(:nested_simple, "/nested/simple", :controller => "nested", :action => "simple")
150
150
  @route_set2.add_named_route(:nested_complex, "/another_nested(/:complex)", :controller => "nested", :action => "complex")
151
-
151
+
152
152
  @route_set3.add_named_route(:nested_simple, "/nested/simple", :controller => "nested", :action => "simple")
153
153
  @route_set3.add_named_route(:nested_complex, "/another_nested(/:complex)", :controller => "nested", :action => "complex")
154
-
154
+
155
155
  @route_set4.add_named_route(:nested_simple, "/nested/simple", :controller => "nested", :action => "simple")
156
156
  end
157
-
157
+
158
158
  it "should generate a route for the simple nested route" do
159
159
  @route_set2.generator.generate(:nested_simple).should == "/mount_point/nested/simple"
160
160
  end
161
-
161
+
162
162
  it "should generate a simple route without optional segments" do
163
163
  @route_set2.generator.generate(:nested_complex).should == "/mount_point/another_nested"
164
164
  end
165
-
165
+
166
166
  it "should generate a route with optional segements" do
167
167
  @route_set2.generator.generate(:nested_complex, :complex => "foo").should == "/mount_point/another_nested/foo"
168
168
  end
169
-
169
+
170
170
  it "should genearte a route with the specified value for the parent route" do
171
171
  @route_set3.generator.generate(:nested_simple, :foo => "bar").should == "/third/bar/nested/simple"
172
172
  end
173
-
173
+
174
174
  it "should generate a route with the default value from the parent route" do
175
175
  @route_set3.generator.generate(:nested_simple).should == "/third/foo/nested/simple"
176
176
  end
177
-
177
+
178
178
  it "should generate a route with an optional segement in the parent and child" do
179
179
  @route_set3.generator.generate(:nested_complex, :complex => "complex").should == "/third/foo/another_nested/complex"
180
180
  end
181
-
181
+
182
182
  it "should generate a route without the optional value from the child" do
183
183
  @route_set3.generator.generate(:nested_complex).should == "/third/foo/another_nested"
184
184
  end
185
-
185
+
186
186
  it "should raise an exception when trying to generate a route where the parent variable is not defined and does not have a default value" do
187
187
  lambda do
188
188
  @route_set4.generator.generate(:nested_simple)
189
189
  end.should raise_error(Usher::MissingParameterException)
190
190
  end
191
-
192
-
193
-
194
191
  end
195
- end
192
+
193
+ describe "dupped generation" do
194
+ before(:each) do
195
+ @r1 = Usher.new(:generator => Usher::Util::Generators::URL.new)
196
+ @r2 = Usher.new(:generator => Usher::Util::Generators::URL.new)
197
+ @r3 = Usher.new(:generator => Usher::Util::Generators::URL.new)
198
+
199
+ @r1.add_route("/r1", :router => "r1").name(:route)
200
+ @r2.add_route("/r2", :router => "r2").name(:route)
201
+ @r3.add_route("/r3", :router => "r3").name(:route)
202
+ end
203
+
204
+ it "should generate dupped routes" do
205
+ @r1.generator.generate(:route).should == "/r1"
206
+ r1 = @r1.dup
207
+ r1.generator.generate(:route).should == "/r1"
208
+ end
209
+
210
+ it "should not generate new routes added to a dup on the original" do
211
+ r1 = @r1.dup
212
+ r1.add_route("/new_r1", :router => "r4").name(:new_route)
213
+ lambda do
214
+ @r1.generator.generate(:new_route).should be_nil
215
+ end
216
+ end
217
+
218
+ it "should generate new routes added to a dup" do
219
+ r1 = @r1.dup
220
+ r1.add_route("/new_r1", :router => "r4").name(:new_route)
221
+ r1.generator.generate(:new_route).should == "/new_r1"
222
+ end
223
+
224
+ it "should generate a route for a nested usher" do
225
+ @r1.add_route("/mounted").match_partially!.to(@r2)
226
+ @r2.generator.generate(:route).should == "/mounted/r2"
227
+ end
228
+
229
+ it "should generate a route for a dupped nested usher" do
230
+ r3 = @r3.dup
231
+ @r1.add_route("/mounted").match_partially!.to(r3)
232
+ r3.generator.generate(:route).should == "/mounted/r3"
233
+ end
234
+
235
+ it "should generate a route for 2 differently mounted dupped ushers" do
236
+ r21 = @r2.dup
237
+ r22 = @r2.dup
238
+
239
+ @r1.add_route("/mounted").match_partially!.to(r21)
240
+ @r1.add_route("/other_mount").match_partially!.to(r22)
241
+
242
+ r21.generator.generate(:route).should == "/mounted/r2"
243
+ r22.generator.generate(:route).should == "/other_mount/r2"
244
+ @r2.generator.generate(:route).should == "/r2"
245
+ end
246
+ end
247
+
248
+ end
@@ -55,40 +55,40 @@ describe "Usher (for rack) route dispatching" do
55
55
  response = route_set.call_with_mock_request("/not-existing-url")
56
56
  response.status.should eql(404)
57
57
  end
58
-
58
+
59
59
  describe "mounted rack instances" do
60
60
  before do
61
61
  @bad_app = mock("bad_app")
62
-
62
+
63
63
  @usher2 = Usher::Interface.for(:rack)
64
64
  @usher2.add("/good" ).to(@app)
65
65
  @usher2.add("/bad" ).match_partially!.to(@bad_app)
66
66
  @usher2.add("/some(/:foo)").to(@app)
67
-
67
+
68
68
  route_set.add("/foo/:bar", :default_values => {:foo => "foo"}).match_partially!.to(@usher2)
69
69
  route_set.add("/foo", :default_values => {:controller => :foo}).to(@app)
70
70
  end
71
-
71
+
72
72
  it "should match the route without nesting" do
73
73
  @app.should_receive(:call).once.with{ |e| e['usher.params'].should == {:controller => :foo}}
74
74
  route_set.call(Rack::MockRequest.env_for("/foo"))
75
75
  end
76
-
76
+
77
77
  it "should route through the first route, and the second to the app" do
78
78
  @app.should_receive(:call).once.with{|e| e['usher.params'].should == {:bar => "bar", :foo => "foo"}}
79
79
  result = route_set.call(Rack::MockRequest.env_for("/foo/bar/good"))
80
80
  end
81
-
81
+
82
82
  it "should go through to the bad app" do
83
83
  @bad_app.should_receive(:call).once.with{|e| e['usher.params'].should == {:bar => "some_bar", :foo => "foo"}}
84
84
  result = route_set.call(Rack::MockRequest.env_for("/foo/some_bar/bad"))
85
85
  end
86
-
86
+
87
87
  it "should match optional routes paramters" do
88
88
  @app.should_receive(:call).once.with{|e| e['usher.params'].should == {:bar => "bar", :foo => "a_different_foo"}}
89
89
  route_set.call(Rack::MockRequest.env_for("/foo/bar/some/a_different_foo"))
90
90
  end
91
-
91
+
92
92
  describe "SCRIPT_NAME & PATH_INFO" do
93
93
  it "should update the script name for a fully consumed route" do
94
94
  @app.should_receive(:call).once.with do |e|
@@ -97,48 +97,48 @@ describe "Usher (for rack) route dispatching" do
97
97
  end
98
98
  route_set.call(Rack::MockRequest.env_for("/foo"))
99
99
  end
100
-
100
+
101
101
  it "should update the script name and path info for a partially consumed route" do
102
102
  @app.should_receive(:call).once.with do |e|
103
103
  e['SCRIPT_NAME'].should == "/partial"
104
104
  e['PATH_INFO'].should == "/bar/baz"
105
105
  end
106
-
106
+
107
107
  route_set.add("/partial").match_partially!.to(@app)
108
108
  route_set.call(Rack::MockRequest.env_for("/partial/bar/baz"))
109
109
  end
110
-
110
+
111
111
  it "should consume the path through a mounted usher" do
112
112
  @bad_app.should_receive(:call).once.with do |e|
113
113
  e['SCRIPT_NAME'].should == "/foo/bar/bad"
114
114
  e['PATH_INFO'].should == "/leftovers"
115
115
  end
116
-
116
+
117
117
  route_set.call(Rack::MockRequest.env_for("/foo/bar/bad/leftovers"))
118
118
  end
119
-
119
+
120
120
  end
121
-
121
+
122
122
  describe "dupping" do
123
- before do
123
+ before do
124
124
  @app = mock("app")
125
125
  @u1 = Usher::Interface.for(:rack)
126
126
  @u2 = Usher::Interface.for(:rack)
127
-
127
+
128
128
  @u1.add("/one", :default_values => {:one => :one}).to(@app)
129
129
  @u1.add("/mount").match_partially!.to(@u2)
130
-
130
+
131
131
  @u2.add("/app", :default_values => {:foo => :bar}).to(@app)
132
-
132
+
133
133
  end
134
-
134
+
135
135
  it "should allow me to dup the router" do
136
136
  @app.should_receive(:call).twice.with{|e| e['usher.params'].should == {:one => :one}}
137
137
  @u1.call(Rack::MockRequest.env_for("/one"))
138
138
  u1_dash = @u1.dup
139
139
  u1_dash.call(Rack::MockRequest.env_for("/one"))
140
140
  end
141
-
141
+
142
142
  it "should allow me to dup the router and add a new route without polluting the original" do
143
143
  @app.should_receive(:call).with{|e| e['usher.params'].should == {:foo => :bar}}
144
144
  u1_dash = @u1.dup
@@ -147,27 +147,53 @@ describe "Usher (for rack) route dispatching" do
147
147
  @app.should_not_receive(:call)
148
148
  @u1.call(Rack::MockRequest.env_for("/foo"))
149
149
  end
150
-
150
+
151
151
  it "should allow me to dup the router and nested routers should remain intact" do
152
152
  @app.should_receive(:call).with{|e| e['usher.params'].should == {:foo => :bar}}
153
153
  u1_dash = @u1.dup
154
154
  u1_dash.call(Rack::MockRequest.env_for("/mount/app"))
155
155
  end
156
-
156
+
157
157
  it "should allow me to dup the router and add more routes" do
158
158
  @app.should_receive(:call).with{|e| e['usher.params'].should == {:another => :bar}}
159
-
159
+
160
160
  u3 = Usher::Interface.for(:rack)
161
161
  u1_dash = @u1.dup
162
-
162
+
163
163
  u3.add("/another_bar", :default_values => {:another => :bar}).to(@app)
164
164
  u1_dash.add("/some/mount").match_partially!.to(u3)
165
-
165
+
166
166
  u1_dash.call(Rack::MockRequest.env_for("/some/mount/another_bar"))
167
-
167
+
168
168
  @app.should_not_receive(:call)
169
169
  @u1.call(Rack::MockRequest.env_for("/some/mount/another_bar"))
170
170
  end
171
171
  end
172
172
  end
173
+
174
+ describe "use as middlware" do
175
+ it "should allow me to set a default application to use" do
176
+ @app.should_receive(:call).with{|e| e['usher.params'].should == {:middle => :ware}}
177
+
178
+ u = Usher::Interface::RackInterface.new(@app)
179
+ u.add("/foo", :default_values => {:middle => :ware}).name(:foo)
180
+
181
+ u.call(Rack::MockRequest.env_for("/foo"))
182
+ end
183
+
184
+ it "should use the default application when no routes match" do
185
+ env = Rack::MockRequest.env_for("/not_a_route")
186
+ @app.should_receive(:call).with(env)
187
+ u = Usher::Interface::RackInterface.new(@app)
188
+ u.call(env)
189
+ end
190
+
191
+ it "should allow me to set the application after initialization" do
192
+ @app.should_receive(:call).with{|e| e['usher.params'].should == {:after => :stuff}}
193
+ u = Usher::Interface.for(:rack)
194
+ u.app = @app
195
+ u.add("/foo", :default_values => {:after => :stuff})
196
+ u.call(Rack::MockRequest.env_for("/foo"))
197
+ end
198
+ end
173
199
  end
@@ -195,6 +195,14 @@ describe "Usher route recognition" do
195
195
  route_set.recognize(build_request({:method => 'get', :path => '/testing/asd.qwe/testing2/poi.zxc/oiu.asd'})).params.should == [[:id, 'asd.qwe'], [:id2, 'poi.zxc'], [:id3, 'oiu.asd']]
196
196
  end
197
197
 
198
+ it "should recognize a path with an optional compontnet" do
199
+ route_set.add_route("/:name(/:surname)", :conditions => {:method => 'get'})
200
+ result = route_set.recognize(build_request({:method => 'get', :path => '/homer'}))
201
+ result.params.should == [[:name, "homer"]]
202
+ result = route_set.recognize(build_request({:method => 'get', :path => "/homer/simpson"}))
203
+ result.params.should == [[:name, "homer"],[:surname, "simpson"]]
204
+ end
205
+
198
206
  it "should should raise if malformed variables are used" do
199
207
  route_set.add_route('/products/show/:id', :id => /\d+/, :conditions => {:method => 'get'})
200
208
  proc {route_set.recognize(build_request({:method => 'get', :path => '/products/show/qweasd', :domain => 'admin.host.com'}))}.should raise_error
@@ -228,8 +236,8 @@ describe "Usher route recognition" do
228
236
  end
229
237
 
230
238
  it "should recognize the originals routes in the dup" do
231
- route_set.recognize(build_request(:path => "/foo")).path.route.destination.should == {:foo =>"foo"}
232
- @r2.recognize( build_request(:path => "/foo")).path.route.destination.should == {:foo =>"foo"}
239
+ route_set.recognize( build_request(:path => "/foo")).path.route.destination.should == {:foo =>"foo"}
240
+ @r2.recognize( build_request(:path => "/foo")).path.route.destination.should == {:foo =>"foo"}
233
241
  end
234
242
 
235
243
  it "should not add routes added to the dup to the original" do
@@ -240,8 +248,8 @@ describe "Usher route recognition" do
240
248
 
241
249
  it "should not delete routes added to the dup to the original" do
242
250
  @r2.delete_route("/foo")
243
- route_set.recognize( build_request(:path => "/foo")).path.route.destination.should == {:foo => "foo"}
244
- @r2.recognize( build_request(:path => "/foo")).should == nil
251
+ route_set.recognize( build_request(:path => "/foo")).path.route.destination.should == {:foo => "foo"}
252
+ @r2.recognize( build_request(:path => "/foo")).should == nil
245
253
  end
246
254
 
247
255
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: usher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.4
4
+ version: 0.5.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Hull
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-08-28 00:00:00 -04:00
12
+ date: 2009-09-05 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -49,6 +49,7 @@ files:
49
49
  - lib/usher/interface/rails2_2_interface/mapper.rb
50
50
  - lib/usher/interface/rails2_3_interface.rb
51
51
  - lib/usher/interface/rails3_interface.rb
52
+ - lib/usher/interface/text_interface.rb
52
53
  - lib/usher/node.rb
53
54
  - lib/usher/route.rb
54
55
  - lib/usher/route/path.rb
@@ -59,6 +60,7 @@ files:
59
60
  - lib/usher/util.rb
60
61
  - lib/usher/util/generate.rb
61
62
  - lib/usher/util/parser.rb
63
+ - lib/usher/util/rack-mixins.rb
62
64
  - rails/init.rb
63
65
  - spec/private/email/recognize_spec.rb
64
66
  - spec/private/generate_spec.rb