usher 0.5.11 → 0.5.12

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.
Files changed (39) hide show
  1. data/Rakefile +48 -53
  2. data/VERSION.yml +2 -2
  3. data/lib/usher.rb +12 -12
  4. data/lib/usher/delimiters.rb +7 -7
  5. data/lib/usher/interface.rb +19 -30
  6. data/lib/usher/interface/{email_interface.rb → email.rb} +1 -1
  7. data/lib/usher/interface/{merb_interface.rb → merb.rb} +1 -1
  8. data/lib/usher/interface/{rack_interface.rb → rack.rb} +14 -10
  9. data/lib/usher/interface/{rack_interface → rack}/mapper.rb +0 -0
  10. data/lib/usher/interface/rack/route.rb +16 -0
  11. data/lib/usher/interface/rails20.rb +7 -0
  12. data/lib/usher/interface/{rails2_2_interface.rb → rails22.rb} +18 -18
  13. data/lib/usher/interface/{rails2_2_interface → rails22}/mapper.rb +2 -2
  14. data/lib/usher/interface/{rails2_3_interface.rb → rails23.rb} +1 -1
  15. data/lib/usher/interface/rails23/mapper.rb +44 -0
  16. data/lib/usher/interface/{rails3_interface.rb → rails3.rb} +1 -1
  17. data/lib/usher/interface/{text_interface.rb → text.rb} +1 -1
  18. data/lib/usher/node.rb +71 -57
  19. data/lib/usher/route.rb +10 -5
  20. data/lib/usher/route/static.rb +9 -0
  21. data/lib/usher/route/variable.rb +28 -9
  22. data/lib/usher/util.rb +3 -3
  23. data/lib/usher/util/generate.rb +55 -18
  24. data/lib/usher/util/parser.rb +25 -18
  25. data/rails/init.rb +24 -8
  26. data/spec/private/generate_spec.rb +116 -1
  27. data/spec/private/generate_with_spec.rb +28 -0
  28. data/spec/private/rack/dispatch_spec.rb +23 -2
  29. data/spec/private/rack/route_spec.rb +50 -0
  30. data/spec/private/rails2_2/generate_spec.rb +1 -1
  31. data/spec/private/rails2_2/path_spec.rb +1 -1
  32. data/spec/private/rails2_2/recognize_spec.rb +1 -1
  33. data/spec/private/rails2_3/generate_spec.rb +1 -1
  34. data/spec/private/rails2_3/path_spec.rb +1 -1
  35. data/spec/private/rails2_3/recognize_spec.rb +1 -1
  36. data/spec/private/recognize_spec.rb +4 -5
  37. data/spec/private/url_parts_spec.rb +116 -0
  38. metadata +26 -12
  39. data/lib/usher/interface/rack_interface/route.rb +0 -16
@@ -1,6 +1,6 @@
1
1
  class Usher
2
2
  module Interface
3
- class Rails3Interface
3
+ class Rails3
4
4
 
5
5
  @@instance = nil
6
6
 
@@ -1,6 +1,6 @@
1
1
  class Usher
2
2
  module Interface
3
- class TextInterface
3
+ class Text
4
4
 
5
5
  def initialize(&blk)
6
6
  @usher = Usher.new(:delimiters => [' '], :generator => Usher::Util::Generators::Generic.new)
@@ -51,7 +51,7 @@ class Usher
51
51
  end
52
52
 
53
53
  def greedy?
54
- @greedy && !@greedy.empty?
54
+ @greedy
55
55
  end
56
56
 
57
57
  def self.root(route_set, request_methods)
@@ -116,17 +116,32 @@ class Usher
116
116
  terminates.route.partial_match? ?
117
117
  Response.new(terminates, params, original_path[position, original_path.size], original_path[0, position]) :
118
118
  Response.new(terminates, params, nil, original_path)
119
- elsif !path.empty? && (greedy? && (match_with_result_output = greedy.match_with_result(whole_path = original_path[position, original_path.size])))
119
+ elsif !path.empty? and greedy and match_with_result_output = greedy.match_with_result(whole_path = original_path[position, original_path.size])
120
120
  next_path, matched_part = match_with_result_output
121
121
  position += matched_part.size
122
- params << [next_path.value.name, whole_path.slice!(0, matched_part.size)]
122
+ whole_path.slice!(0, matched_part.size)
123
+ params << [next_path.value.name, matched_part] if next_path.value.is_a?(Route::Variable)
123
124
  next_path.find(usher, request_object, original_path, whole_path.empty? ? whole_path : usher.splitter.url_split(whole_path), params, position)
124
- elsif !path.empty? && normal && (next_part = normal[path.first] || normal[nil])
125
+ elsif !path.empty? and normal and next_part = normal[path.first] || normal[nil]
125
126
  part = path.shift
126
127
  position += part.size
127
128
  case next_part.value
129
+ when String
130
+ # do nothing
131
+ when Route::Variable::Single
132
+ # get the variable
133
+ var = next_part.value
134
+ # do a validity check
135
+ var.valid!(part)
136
+ # because its a variable, we need to add it to the params array
137
+ params << [var.name, part]
138
+ until path.empty? || (var.look_ahead === path.first) # variables have a look ahead notion,
139
+ next_path_part = path.shift # and until they are satified,
140
+ position += next_path_part.size # keep appending to the value in params
141
+ params.last.last << next_path_part
142
+ end if var.look_ahead && usher.delimiters.size > 1
128
143
  when Route::Variable::Glob
129
- params << [next_part.value.name, []] unless params.last && params.last.first == next_part.value.name
144
+ params << [next_part.value.name, []]
130
145
  while true
131
146
  if (next_part.value.look_ahead === part || (!usher.delimiters.unescaped.include?(part) && next_part.value.regex_matcher && !next_part.value.regex_matcher.match(part)))
132
147
  path.unshift(part)
@@ -146,21 +161,12 @@ class Usher
146
161
  part = path.shift
147
162
  end
148
163
  end
149
- when Route::Variable::Single
150
- var = next_part.value
151
- var.valid!(part)
152
- params << [var.name, part]
153
- until path.empty? || (var.look_ahead === path.first)
154
- next_path_part = path.shift
155
- position += next_path_part.size
156
- params.last.last << next_path_part
157
- end if var.look_ahead && usher.delimiters.size > 1
158
164
  end
159
165
  next_part.find(usher, request_object, original_path, path, params, position)
160
166
  elsif request_method_type
161
- if (specific_node = request[request_object.send(request_method_type)]) && (ret = specific_node.find(usher, request_object, original_path, path.dup, params.dup, position))
167
+ if specific_node = request[request_object.send(request_method_type)] and ret = specific_node.find(usher, request_object, original_path, path.dup, params.dup, position)
162
168
  ret
163
- elsif (general_node = request[nil]) && (ret = general_node.find(usher, request_object, original_path, path.dup, params.dup, position))
169
+ elsif general_node = request[nil] and ret = general_node.find(usher, request_object, original_path, path.dup, params.dup, position)
164
170
  ret
165
171
  else
166
172
  nil
@@ -173,65 +179,69 @@ class Usher
173
179
  private
174
180
 
175
181
  def set_path_with_destination(path, destination = path)
176
- node = path.parts.inject(self){ |node, key| process_path_part(node, key) }
177
- node = process_request_parts(node, request_methods_for_path(path)) if request_methods
178
-
179
- while node.request_method_type
180
- node = (node.request[nil] ||= Node.new(node, Route::RequestMethod.new(node.request_method_type, nil)))
182
+ nodes = [path.parts.inject(self){ |node, key| process_path_part(node, key) }]
183
+ nodes = process_request_parts(nodes, request_methods_for_path(path)) if request_methods
184
+
185
+ nodes.each do |node|
186
+ while node.request_method_type
187
+ node = (node.request[nil] ||= Node.new(node, Route::RequestMethod.new(node.request_method_type, nil)))
188
+ end
189
+ node.terminates = destination
181
190
  end
182
-
183
- node.terminates = destination
184
191
  end
185
192
 
186
193
  def request_method_index(type)
187
194
  request_methods.index(type)
188
195
  end
189
196
 
190
- def process_request_parts(node, parts)
197
+ def process_request_parts(nodes, parts)
191
198
  while parts.any?{ |p| !p.trivial? }
192
199
  key = parts.shift
193
200
 
194
201
  next if key.trivial?
202
+
203
+ nodes.map! do |node|
204
+
205
+ node.activate_request!
195
206
 
196
- node.activate_request!
197
-
198
- node = if node.request_method_type.nil?
199
- node.request_method_type = key.type
200
- node.upgrade_request! if key.value.is_a?(Regexp)
201
- node.request[key.value] ||= Node.new(node, key)
202
- else
203
- case request_method_index(node.request_method_type) <=> request_method_index(key.type)
204
- when -1
205
- parts.unshift(key)
206
- node.request[key.value] ||= Node.new(node, Route::RequestMethod.new(node.request_method_type, nil))
207
- when 0
207
+ if node.request_method_type.nil?
208
+ node.request_method_type = key.type
208
209
  node.upgrade_request! if key.value.is_a?(Regexp)
209
- node.request[key.value] ||= Node.new(node, key)
210
- when 1
211
- previous_node = node.parent
212
- current_node_entry_key = nil
213
- current_node_entry_lookup = nil
214
- [previous_node.normal, previous_node.greedy, previous_node.request].compact.each do |l|
215
- current_node_entry_key = l.each{|k,v| break k if node == v}
216
- current_node_entry_lookup = l and break if current_node_entry_key
217
- end
218
-
219
- current_node_entry_lookup.respond_to?(:delete_value) ?
220
- current_node_entry_lookup.delete_value(node) : current_node_entry_lookup.delete_if{|k,v| v == node}
210
+ Array(key.value).map{|k| node.request[k] ||= Node.new(node, key) }
211
+ else
212
+ case request_method_index(node.request_method_type) <=> request_method_index(key.type)
213
+ when -1
214
+ parts.unshift(key)
215
+ Array(key.value).map{|k| node.request[k] ||= Node.new(node, Route::RequestMethod.new(node.request_method_type, nil)) }
216
+ when 0
217
+ node.upgrade_request! if key.value.is_a?(Regexp)
218
+ Array(key.value).map{|k| node.request[k] ||= Node.new(node, key) }
219
+ when 1
220
+ previous_node = node.parent
221
+ current_node_entry_key = nil
222
+ current_node_entry_lookup = nil
223
+ [previous_node.normal, previous_node.greedy, previous_node.request].compact.each do |l|
224
+ current_node_entry_key = l.each{|k,v| break k if node == v}
225
+ current_node_entry_lookup = l and break if current_node_entry_key
226
+ end
221
227
 
222
- new_node = Node.new(previous_node, Route::RequestMethod.new(key.type, nil))
223
- new_node.activate_request!
224
- new_node.request_method_type = key.type
225
- current_node_entry_lookup[current_node_entry_key] = new_node
226
- node.parent = new_node
227
- new_node.request[nil] = node
228
- parts.unshift(key)
228
+ current_node_entry_lookup.respond_to?(:delete_value) ?
229
+ current_node_entry_lookup.delete_value(node) : current_node_entry_lookup.delete_if{|k,v| v == node}
229
230
 
230
- new_node
231
+ new_node = Node.new(previous_node, Route::RequestMethod.new(key.type, nil))
232
+ new_node.activate_request!
233
+ new_node.request_method_type = key.type
234
+ current_node_entry_lookup[current_node_entry_key] = new_node
235
+ node.parent = new_node
236
+ new_node.request[nil] = node
237
+ parts.unshift(key)
238
+ new_node
239
+ end
231
240
  end
232
241
  end
242
+ nodes.flatten!
233
243
  end
234
- node
244
+ nodes
235
245
  end
236
246
 
237
247
 
@@ -253,6 +263,10 @@ class Usher
253
263
  else
254
264
  node.normal[nil] ||= Node.new(node, key)
255
265
  end
266
+ when Route::Static::Greedy
267
+ node.activate_greedy!
268
+ node.upgrade_greedy!
269
+ node.greedy[key] ||= Node.new(node, key)
256
270
  else
257
271
  node.activate_normal!
258
272
  node.upgrade_normal! if key.is_a?(Regexp)
@@ -1,14 +1,19 @@
1
- require File.join(File.dirname(__FILE__), 'route', 'path')
2
- require File.join(File.dirname(__FILE__), 'route', 'util')
3
- require File.join(File.dirname(__FILE__), 'route', 'variable')
4
- require File.join(File.dirname(__FILE__), 'route', 'request_method')
1
+ require File.join('usher', 'route', 'path')
2
+ require File.join('usher', 'route', 'util')
3
+ require File.join('usher', 'route', 'variable')
4
+ require File.join('usher', 'route', 'static')
5
+ require File.join('usher', 'route', 'request_method')
5
6
 
6
7
  class Usher
7
8
  class Route
8
9
  attr_reader :paths, :requirements, :conditions, :named, :generate_with, :default_values, :match_partially, :destination
9
10
  attr_accessor :parent_route, :router, :recognizable
10
11
 
11
- GenerateWith = Struct.new(:scheme, :port, :host)
12
+ class GenerateWith < Struct.new(:scheme, :port, :host)
13
+ def empty?
14
+ scheme.nil? and port.nil? and host.nil?
15
+ end
16
+ end
12
17
 
13
18
  def initialize(parsed_paths, router, conditions, requirements, default_values, generate_with, match_partially)
14
19
  @router, @requirements, @conditions, @default_values, @match_partially = router, requirements, conditions, default_values, match_partially
@@ -0,0 +1,9 @@
1
+ class Usher
2
+ class Route
3
+ class Static
4
+ class Greedy < String
5
+
6
+ end
7
+ end
8
+ end
9
+ end
@@ -6,22 +6,37 @@ class Usher
6
6
 
7
7
  def initialize(name, regex_matcher = nil, validator = nil)
8
8
  @name = name.to_s.to_sym
9
- @validator = validator
9
+ @validator = validator || regex_matcher
10
10
  @regex_matcher = regex_matcher
11
+
12
+ case @validator
13
+ when Proc
14
+ self.extend(ProcValidator)
15
+ when nil
16
+ # do nothing
17
+ else
18
+ self.extend(CaseEqualsValidator)
19
+ end
11
20
  end
12
21
  private :initialize
13
-
22
+
14
23
  def valid!(val)
15
- case @validator
16
- when Proc
24
+ end
25
+
26
+ module ProcValidator
27
+ def valid!(val)
17
28
  begin
18
29
  @validator.call(val)
19
30
  rescue Exception => e
20
- raise ValidationException.new("#{val} does not conform to #{@g}, root cause #{e.inspect}")
31
+ raise ValidationException.new("#{val} does not conform to #{@validator}, root cause #{e.inspect}")
21
32
  end
22
- else
23
- @validator === val or raise(ValidationException.new("#{val} does not conform to #{@validator}, root cause #{e.inspect}"))
24
- end if @validator
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
25
40
  end
26
41
 
27
42
  def ==(o)
@@ -40,7 +55,11 @@ class Usher
40
55
  end
41
56
  end
42
57
 
43
- Greedy = Class.new(Variable)
58
+ class Greedy < Variable
59
+ def to_s
60
+ "!#{name}"
61
+ end
62
+ end
44
63
 
45
64
  end
46
65
 
@@ -1,7 +1,7 @@
1
1
  class Usher
2
2
  module Util
3
- autoload :Generators, File.join(File.dirname(__FILE__), 'util', 'generate')
4
- autoload :Parser, File.join(File.dirname(__FILE__), 'util', 'parser')
5
- autoload :Graph, File.join(File.dirname(__FILE__), 'util', 'graph')
3
+ autoload :Generators, File.join('usher', 'util', 'generate')
4
+ autoload :Parser, File.join('usher', 'util', 'parser')
5
+ autoload :Graph, File.join('usher', 'util', 'graph')
6
6
  end
7
7
  end
@@ -38,13 +38,56 @@ class Usher
38
38
 
39
39
  class URL < Generic
40
40
 
41
+ class UrlParts < Struct.new(:path, :request)
42
+ def scheme
43
+ @scheme ||= generate_with(:scheme) || (request.respond_to?(:scheme) and request.scheme)
44
+ end
45
+
46
+ def protocol
47
+ @protocol ||= scheme ? "#{scheme}://" : request.protocol
48
+ end
49
+
50
+ def host
51
+ @host ||= generate_with(:host) || request.host
52
+ end
53
+
54
+ def port
55
+ @port ||= generate_with(:port) || request.port
56
+ end
57
+
58
+ def port_string
59
+ @port_string ||= standard_port? ? '' : ":#{port}"
60
+ end
61
+
62
+ def url
63
+ path.route.generate_with.nil? || path.route.generate_with.empty? ?
64
+ request.url :
65
+ protocol + host + port_string
66
+ end
67
+
68
+ protected
69
+
70
+ def generate_with(property)
71
+ path.route.generate_with and path.route.generate_with[property]
72
+ end
73
+
74
+ def standard_port?
75
+ ssl? ? port == 443 : port == 80
76
+ end
77
+
78
+ def ssl?
79
+ protocol[0..4] == 'https'
80
+ end
81
+ end
82
+
41
83
  def initialize
42
- require File.join(File.dirname(__FILE__), 'rack-mixins')
84
+ require File.join('usher', 'util', 'rack-mixins')
43
85
  end
44
86
 
45
87
  def generate_full(routing_lookup, request, params = nil)
46
88
  path = path_for_routing_lookup(routing_lookup, params)
47
- result = generate_start(path, request)
89
+
90
+ result = generate_start(path, request)
48
91
  result << generate_path(path, params)
49
92
  end
50
93
 
@@ -105,12 +148,12 @@ class Usher
105
148
 
106
149
  usher.named_routes.each do |name, route|
107
150
  @generation_module.module_eval <<-END_EVAL
108
- def #{name}_url(name, request, params = nil)
109
- @@generator.generate_full(name, request, options)
151
+ def #{name}_url(options={})
152
+ @@generator.generate_full('#{name}'.to_sym, request, options)
110
153
  end
111
154
 
112
- def #{name}_path(name, params = nil)
113
- @@generator.generate(name, options)
155
+ def #{name}_path(options={})
156
+ @@generator.generate('#{name}'.to_sym, options)
114
157
  end
115
158
  END_EVAL
116
159
  end
@@ -127,20 +170,14 @@ class Usher
127
170
  end
128
171
  end
129
172
 
130
- def generate_start(path, request)
131
- result = (path.route.generate_with && path.route.generate_with.scheme || request.scheme).dup
132
- result << '://'
133
- result << (path.route.generate_with && path.route.generate_with.host) ? path.route.generate_with.host : request.host
134
- port = path.route.generate_with && path.route.generate_with.port || request.port
135
- if result[4] == ?s
136
- result << ':' << port.to_s unless port == 443
137
- else
138
- result << ':' << port.to_s unless port == 80
139
- end
140
- result
173
+ def generate_start(path, request)
174
+ url_parts = UrlParts.new(path, request)
175
+ url = url_parts.url
176
+
177
+ (url[-1] == ?/) ? url[0..-2] : url
141
178
  end
142
179
 
143
- def path_for_routing_lookup(routing_lookup, params = {})
180
+ def path_for_routing_lookup(routing_lookup, params = {})
144
181
  path = case routing_lookup
145
182
  when Symbol
146
183
  route = @usher.named_routes[routing_lookup]
@@ -39,25 +39,30 @@ class Usher
39
39
 
40
40
  paths = Route::Util.expand_path(unprocessed_path)
41
41
 
42
- paths.each do |path|
43
- path.each_with_index do |part, index|
44
- part.default_value = default_values[part.name] if part.is_a?(Usher::Route::Variable) && default_values && default_values[part.name]
45
- case part
46
- when Usher::Route::Variable::Glob
47
- if part.look_ahead && !part.look_ahead_priority
48
- part.look_ahead = nil
49
- part.look_ahead_priority = true
50
- else
51
- part.look_ahead = path[index + 1, path.size].find{|p| !p.is_a?(Usher::Route::Variable) && !router.delimiters.unescaped.include?(p)} || nil
52
- end
53
- when Usher::Route::Variable
54
- if part.look_ahead && !part.look_ahead_priority
55
- part.look_ahead = nil
56
- part.look_ahead_priority = true
57
- else
58
- part.look_ahead = router.delimiters.first_in(path[index + 1, path.size]) || router.delimiters.unescaped.first
42
+ paths.map! do |path|
43
+ if path.all?{|part| String === part}
44
+ path #parse "{~#{path.join}}"
45
+ else
46
+ path.each_with_index do |part, index|
47
+ part.default_value = default_values[part.name] if part.is_a?(Usher::Route::Variable) && default_values && default_values[part.name]
48
+ case part
49
+ when Usher::Route::Variable::Glob
50
+ if part.look_ahead && !part.look_ahead_priority
51
+ part.look_ahead = nil
52
+ part.look_ahead_priority = true
53
+ else
54
+ part.look_ahead = path[index + 1, path.size].find{|p| !p.is_a?(Usher::Route::Variable) && !router.delimiters.unescaped.include?(p)} || nil
55
+ end
56
+ when Usher::Route::Variable
57
+ if part.look_ahead && !part.look_ahead_priority
58
+ part.look_ahead = nil
59
+ part.look_ahead_priority = true
60
+ else
61
+ part.look_ahead = router.delimiters.first_in(path[index + 1, path.size]) || router.delimiters.unescaped.first
62
+ end
59
63
  end
60
64
  end
65
+ path
61
66
  end
62
67
  end
63
68
 
@@ -104,6 +109,7 @@ class Usher
104
109
  when ?{
105
110
  pattern = ''
106
111
  count = 1
112
+ simple = scanner.scan(/~/)
107
113
  variable = scanner.scan(/[!:\*]([^,]+),/)
108
114
  until count.zero?
109
115
  regex_part = scanner.scan(/\{|\}|[^\{\}]+/)
@@ -124,9 +130,10 @@ class Usher
124
130
  when :* then Usher::Route::Variable::Glob
125
131
  when :':' then Usher::Route::Variable::Single
126
132
  end
127
-
128
133
  variable_name = variable[0, variable.size - 1].to_sym
129
134
  current_group << variable_class.new(variable_name, regex, requirements && requirements[variable_name])
135
+ elsif simple
136
+ current_group << Usher::Route::Static::Greedy.new(pattern)
130
137
  else
131
138
  current_group << regex
132
139
  end