usher 0.5.11 → 0.5.12

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