usher 0.6.2 → 0.6.3

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/VERSION.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
+ :patch: 3
2
3
  :build:
3
- :patch: 2
4
4
  :major: 0
5
5
  :minor: 6
@@ -2,4 +2,5 @@ class Usher
2
2
  class UnrecognizedException < RuntimeError; end
3
3
  class ValidationException < RuntimeError; end
4
4
  class MissingParameterException < RuntimeError; end
5
+ class MultipleParameterException < RuntimeError; end
5
6
  end
@@ -57,7 +57,7 @@ class Usher
57
57
 
58
58
  def initialize(app = nil, options = nil, &blk)
59
59
  @_app = app || lambda { |env| ::Rack::Response.new("No route found", 404).finish }
60
- @router = Usher.new(:request_methods => [:request_method, :host, :port, :scheme], :generator => Usher::Util::Generators::URL.new)
60
+ @router = Usher.new(:request_methods => [:request_method, :host, :port, :scheme], :generator => Usher::Util::Generators::URL.new, :allow_identical_variable_names => false)
61
61
  @use_destinations = options && options.key?(:use_destinations) ? options[:use_destinations] : true
62
62
  instance_eval(&blk) if blk
63
63
  end
data/lib/usher/node.rb CHANGED
@@ -122,14 +122,14 @@ class Usher
122
122
  def find(usher, request_object, original_path, path, params = [], position = 0)
123
123
  if terminates? && (path.empty? || terminates.route.partial_match? || (usher.ignore_trailing_delimiters? && path.all?{|p| usher.delimiters.include?(p)}))
124
124
  terminates.route.partial_match? ?
125
- Response.new(terminates, params, original_path[position, original_path.size], original_path[0, position]) :
126
- Response.new(terminates, params, nil, original_path)
125
+ Response.new(terminates, terminates.convert_params_array(params), original_path[position, original_path.size], original_path[0, position]) :
126
+ Response.new(terminates, terminates.convert_params_array(params), nil, original_path)
127
127
  elsif !path.empty? and greedy and match_with_result_output = greedy.match_with_result(whole_path = original_path[position, original_path.size])
128
128
  next_path, matched_part = match_with_result_output
129
129
  position += matched_part.size
130
130
  whole_path.slice!(0, matched_part.size)
131
- params << [next_path.value.name, matched_part] if next_path.value.is_a?(Route::Variable)
132
- next_path.find(usher, request_object, original_path, whole_path.empty? ? whole_path : usher.splitter.url_split(whole_path), params, position)
131
+ params << matched_part if next_path.value.is_a?(Route::Variable)
132
+ next_path.find(usher, request_object, original_path, whole_path.empty? ? whole_path : usher.splitter.split(whole_path), params, position)
133
133
  elsif !path.empty? and normal and next_part = normal[path.first] || normal[nil]
134
134
  part = path.shift
135
135
  position += part.size
@@ -142,14 +142,14 @@ class Usher
142
142
  # do a validity check
143
143
  var.valid!(part)
144
144
  # because its a variable, we need to add it to the params array
145
- params << [var.name, part]
145
+ params << part
146
146
  until path.empty? || (var.look_ahead === path.first) # variables have a look ahead notion,
147
147
  next_path_part = path.shift # and until they are satified,
148
148
  position += next_path_part.size # keep appending to the value in params
149
- params.last.last << next_path_part
149
+ params.last << next_path_part
150
150
  end if var.look_ahead && usher.delimiters.size > 1
151
151
  when Route::Variable::Glob
152
- params << [next_part.value.name, []]
152
+ params << []
153
153
  while true
154
154
  if (next_part.value.look_ahead === part || (!usher.delimiters.unescaped.include?(part) && next_part.value.regex_matcher && !next_part.value.regex_matcher.match(part)))
155
155
  path.unshift(part)
@@ -161,7 +161,7 @@ class Usher
161
161
  break
162
162
  elsif !usher.delimiters.unescaped.include?(part)
163
163
  next_part.value.valid!(part)
164
- params.last.last << part
164
+ params.last << part
165
165
  end
166
166
  if path.empty?
167
167
  break
@@ -9,7 +9,11 @@ class Usher
9
9
  self.route = route
10
10
  self.parts = parts
11
11
  end
12
-
12
+
13
+ def convert_params_array(ary)
14
+ ary.empty? ? ary : dynamic_keys.zip(ary)
15
+ end
16
+
13
17
  def dynamic_indicies
14
18
  unless dynamic? && @dynamic_indicies
15
19
  @dynamic_indicies = []
@@ -31,7 +35,7 @@ class Usher
31
35
  end
32
36
 
33
37
  def dynamic_keys
34
- @dynamic_keys ||= dynamic_map.keys if dynamic?
38
+ @dynamic_keys ||= dynamic_parts.map{|dp| dp.name} if dynamic?
35
39
  end
36
40
 
37
41
  def dynamic_required_keys
@@ -1,6 +1,23 @@
1
1
  class Usher
2
2
  class Route
3
3
  class Variable
4
+
5
+ module ProcValidator
6
+ def valid!(val)
7
+ begin
8
+ @validator.call(val)
9
+ rescue Exception => e
10
+ raise ValidationException.new("#{val} does not conform to #{@validator}, root cause #{e.inspect}")
11
+ end
12
+ end
13
+ end
14
+
15
+ module CaseEqualsValidator
16
+ def valid!(val)
17
+ @validator === val or raise(ValidationException.new("#{val} does not conform to #{@validator}"))
18
+ end
19
+ end
20
+
4
21
  attr_reader :type, :name, :validator, :regex_matcher
5
22
  attr_accessor :look_ahead, :default_value, :look_ahead_priority
6
23
 
@@ -10,12 +27,12 @@ class Usher
10
27
  @regex_matcher = regex_matcher
11
28
 
12
29
  case @validator
13
- when Proc
14
- self.extend(ProcValidator)
15
30
  when nil
16
31
  # do nothing
32
+ when Proc
33
+ extend(ProcValidator)
17
34
  else
18
- self.extend(CaseEqualsValidator)
35
+ extend(CaseEqualsValidator)
19
36
  end
20
37
  end
21
38
  private :initialize
@@ -23,22 +40,6 @@ class Usher
23
40
  def valid!(val)
24
41
  end
25
42
 
26
- module ProcValidator
27
- def valid!(val)
28
- begin
29
- @validator.call(val)
30
- rescue Exception => e
31
- raise ValidationException.new("#{val} does not conform to #{@validator}, root cause #{e.inspect}")
32
- end
33
- end
34
- end
35
-
36
- module CaseEqualsValidator
37
- def valid!(val)
38
- @validator === val or raise(ValidationException.new("#{val} does not conform to #{@validator}"))
39
- end
40
- end
41
-
42
43
  def ==(o)
43
44
  o && (o.class == self.class && o.name == @name && o.validator == @validator)
44
45
  end
@@ -14,10 +14,9 @@ class Usher
14
14
  @url_split_regex = Regexp.new("[#{delimiters.collect{|d| Regexp.quote(d)}.join}]|[^#{delimiters.collect{|d| Regexp.quote(d)}.join}]+")
15
15
  end
16
16
 
17
- def url_split(path)
17
+ def split(path)
18
18
  path.scan(@url_split_regex)
19
19
  end
20
- alias split url_split
21
20
  end
22
21
 
23
22
  class MultiCharacterSplitterInstance
@@ -26,12 +25,11 @@ class Usher
26
25
  @delimiters = delimiters
27
26
  end
28
27
 
29
- def url_split(path)
28
+ def split(path)
30
29
  split_path = path.split(delimiters_regexp)
31
- split_path.reject!{|s| s.size.zero? }
30
+ split_path.reject!{|s| s.empty? }
32
31
  split_path
33
32
  end
34
- alias split url_split
35
33
 
36
34
  protected
37
35
 
@@ -12,28 +12,50 @@ class Usher
12
12
 
13
13
  def generate_path_for_base_params(path, params)
14
14
  raise UnrecognizedException.new unless path
15
-
16
15
  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
16
+
17
+ case params
18
+ when nil, Hash
19
+ path.parts.each do |part|
20
+ case part
21
+ when String
22
+ result << part
23
+ when Route::Variable::Glob
24
+ value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new)
25
+ value.each_with_index do |current_value, index|
26
+ part.valid!(current_value)
27
+ result << current_value.to_s
28
+ result << usher.delimiters.first if index != value.size - 1
29
+ end
30
+ when Route::Variable
31
+ value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new)
32
+ part.valid!(value)
33
+ result << value.to_s
34
+ end
35
+ end
36
+ else
37
+ params = Array(params)
38
+ path.parts.each do |part|
39
+ case part
40
+ when String
41
+ result << part
42
+ when Route::Variable::Glob
43
+ value = (params && params.shift) || part.default_value || raise(MissingParameterException.new)
44
+ value.each_with_index do |current_value, index|
45
+ part.valid!(current_value)
46
+ result << current_value.to_s
47
+ result << usher.delimiters.first if index != value.size - 1
48
+ end
49
+ when Route::Variable
50
+ value = (params && params.shift) || part.default_value || raise(MissingParameterException.new)
51
+ part.valid!(value)
52
+ result << value.to_s
25
53
  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
54
  end
33
55
  end
34
56
  result
35
57
  end
36
-
58
+
37
59
  end
38
60
 
39
61
  class URL < Generic
@@ -104,26 +126,30 @@ class Usher
104
126
 
105
127
  def generate_path(path, params = nil, generate_extra = true)
106
128
  params = Array(params) if params.is_a?(String)
129
+ extra_params = nil
107
130
  case params
108
131
  when nil
109
132
  params = path && path.route.default_values
110
133
  when Hash
111
134
  params = path.route.default_values.merge(params) if path && path.route.default_values
112
- when String, Array
135
+ else
113
136
  params = Array(params)
114
- given_size = params.size
115
137
  extra_params = params.last.is_a?(Hash) ? params.pop : nil
116
- params = Hash[*path.dynamic_parts.inject(path.route.default_values ? path.route.default_values.to_a : []){|a, dynamic_part| a.concat([dynamic_part.name, params.shift || raise(MissingParameterException.new("got #{given_size}, expected #{path.dynamic_parts.size} parameters"))]); a}]
117
- params.merge!(extra_params) if extra_params
138
+ raise MissingParameterException.new("got #{params.size}, expected #{path.dynamic_parts.size} parameters") unless path.dynamic_parts.size == params.size
118
139
  end
119
140
 
120
141
  result = Rack::Utils.uri_escape(generate_path_for_base_params(path, params))
142
+
143
+ params = extra_params if extra_params
144
+
121
145
  unless !generate_extra || params.nil? || params.empty?
122
146
  if usher.consider_destination_keys? && path.route.destination_keys
123
147
  params.delete_if{|k, v| path.route.destination_keys.include?(k)}
124
148
  end
125
- extra_params = generate_extra_params(params, result[??])
126
- result << extra_params
149
+ unless params.empty?
150
+ result << '?' unless result[??]
151
+ result << generate_extra_params(params)
152
+ end
127
153
  end
128
154
  result
129
155
  end
@@ -188,6 +214,8 @@ class Usher
188
214
 
189
215
  def path_for_routing_lookup(routing_lookup, params = {})
190
216
  path = case routing_lookup
217
+ when Route::Path
218
+ routing_lookup
191
219
  when Symbol
192
220
  route = @usher.named_routes[routing_lookup]
193
221
  raise UnrecognizedException unless route
@@ -196,23 +224,23 @@ class Usher
196
224
  routing_lookup.find_matching_path(params)
197
225
  when nil
198
226
  params.is_a?(Hash) ? usher.path_for_options(params) : raise
199
- when Route::Path
200
- routing_lookup
201
227
  end
202
228
  end
203
229
 
204
230
 
205
- def generate_extra_params(params, has_question_mark)
231
+ def generate_extra_params(params)
206
232
  extra_params_result = ''
207
233
 
208
234
  params.each do |k,v|
209
235
  case v
210
236
  when Array
211
237
  v.each do |v_part|
212
- extra_params_result << (has_question_mark ? '&' : has_question_mark = true && '?') << Rack::Utils.escape("#{k.to_s}[]") << '=' << Rack::Utils.escape(v_part.to_s)
238
+ extra_params_result << '&' unless extra_params_result.empty?
239
+ extra_params_result << Rack::Utils.escape("#{k.to_s}[]") << '=' << Rack::Utils.escape(v_part.to_s)
213
240
  end
214
241
  else
215
- extra_params_result << (has_question_mark ? '&' : has_question_mark = true && '?') << Rack::Utils.escape(k.to_s) << '=' << Rack::Utils.escape(v.to_s)
242
+ extra_params_result << '&' unless extra_params_result.empty?
243
+ extra_params_result << Rack::Utils.escape(k.to_s) << '=' << Rack::Utils.escape(v.to_s)
216
244
  end
217
245
  end
218
246
  extra_params_result
data/lib/usher.rb CHANGED
@@ -67,15 +67,20 @@ class Usher
67
67
  # Example, you create a route with a destination of :controller => 'test', :action => 'action'. If you made a call to generator with :controller => 'test',
68
68
  # :action => 'action', it would pick that route to use for generation.
69
69
  def initialize(options = nil)
70
- self.generator = options && options.delete(:generator)
71
- self.delimiters = Delimiters.new(options && options.delete(:delimiters) || ['/', '.'])
72
- self.valid_regex = options && options.delete(:valid_regex) || '[0-9A-Za-z\$\-_\+!\*\',]+'
73
- self.request_methods = options && options.delete(:request_methods)
74
- self.ignore_trailing_delimiters = options && options.key?(:ignore_trailing_delimiters) ? options.delete(:ignore_trailing_delimiters) : false
75
- self.consider_destination_keys = options && options.key?(:consider_destination_keys) ? options.delete(:consider_destination_keys) : false
70
+ self.generator = options && options.delete(:generator)
71
+ self.delimiters = Delimiters.new(options && options.delete(:delimiters) || ['/', '.'])
72
+ self.valid_regex = options && options.delete(:valid_regex) || '[0-9A-Za-z\$\-_\+!\*\',]+'
73
+ self.request_methods = options && options.delete(:request_methods)
74
+ self.ignore_trailing_delimiters = options && options.key?(:ignore_trailing_delimiters) ? options.delete(:ignore_trailing_delimiters) : false
75
+ self.consider_destination_keys = options && options.key?(:consider_destination_keys) ? options.delete(:consider_destination_keys) : false
76
+ self.allow_identical_variable_names = options && options.key?(:allow_identical_variable_names) ? options.delete(:allow_identical_variable_names) : true
76
77
  reset!
77
78
  end
78
79
 
80
+ def allow_identical_variable_names?
81
+ @allow_identical_variable_names
82
+ end
83
+
79
84
  def ignore_trailing_delimiters?
80
85
  @ignore_trailing_delimiters
81
86
  end
@@ -223,7 +228,7 @@ class Usher
223
228
  # route = set.add_route('/test')
224
229
  # set.recognize(Request.new('/test')).path.route == route => true
225
230
  def recognize(request, path = request.path)
226
- @root.find(self, request, path, @splitter.url_split(path))
231
+ @root.find(self, request, path, @splitter.split(path))
227
232
  end
228
233
 
229
234
  # 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.
@@ -282,7 +287,7 @@ class Usher
282
287
 
283
288
  private
284
289
 
285
- attr_accessor :request_methods, :ignore_trailing_delimiters, :consider_destination_keys
290
+ attr_accessor :request_methods, :ignore_trailing_delimiters, :consider_destination_keys, :allow_identical_variable_names
286
291
  attr_reader :valid_regex
287
292
 
288
293
  def generator=(generator)
@@ -333,6 +338,7 @@ class Usher
333
338
  end
334
339
 
335
340
  route = parser.generate_route(path, conditions, requirements, default_values, generate_with, priority)
341
+ raise(MultipleParameterException.new) if !allow_identical_variable_names? and route.paths.first.dynamic? and route.paths.first.dynamic_keys.uniq.size != route.paths.first.dynamic_keys.size
336
342
  route.to(options) if options && !options.empty?
337
343
  route
338
344
  end
@@ -273,6 +273,13 @@ describe "Usher route recognition" do
273
273
  @route_set.recognize(build_request({:path => '/id1/one/id2.html'})).params.should == [[:id1, 'id1'], [:id2, 'id2'], [:format, 'html']]
274
274
  end
275
275
 
276
+ it "should pick the correct variable name when there are two variable names that could be represented" do
277
+ @route_set.add_route('/:var1')
278
+ @route_set.add_route('/:var2/foo')
279
+ @route_set.recognize(build_request({:path => '/foo1'})).params.should == [[:var1, 'foo1']]
280
+ @route_set.recognize(build_request({:path => '/foo2/foo'})).params.should == [[:var2, 'foo2']]
281
+ end
282
+
276
283
  it "should recognize a path with an optional compontnet" do
277
284
  @route_set.add_route("/:name(/:surname)", :conditions => {:method => 'get'})
278
285
  result = @route_set.recognize(build_request({:method => 'get', :path => '/homer'}))
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.6.2
4
+ version: 0.6.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Neighman
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-01-05 00:00:00 -05:00
17
+ date: 2010-01-09 00:00:00 -05:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency