usher 0.5.4 → 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
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