usher 0.5.11 → 0.5.12
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +48 -53
- data/VERSION.yml +2 -2
- data/lib/usher.rb +12 -12
- data/lib/usher/delimiters.rb +7 -7
- data/lib/usher/interface.rb +19 -30
- data/lib/usher/interface/{email_interface.rb → email.rb} +1 -1
- data/lib/usher/interface/{merb_interface.rb → merb.rb} +1 -1
- data/lib/usher/interface/{rack_interface.rb → rack.rb} +14 -10
- data/lib/usher/interface/{rack_interface → rack}/mapper.rb +0 -0
- data/lib/usher/interface/rack/route.rb +16 -0
- data/lib/usher/interface/rails20.rb +7 -0
- data/lib/usher/interface/{rails2_2_interface.rb → rails22.rb} +18 -18
- data/lib/usher/interface/{rails2_2_interface → rails22}/mapper.rb +2 -2
- data/lib/usher/interface/{rails2_3_interface.rb → rails23.rb} +1 -1
- data/lib/usher/interface/rails23/mapper.rb +44 -0
- data/lib/usher/interface/{rails3_interface.rb → rails3.rb} +1 -1
- data/lib/usher/interface/{text_interface.rb → text.rb} +1 -1
- data/lib/usher/node.rb +71 -57
- data/lib/usher/route.rb +10 -5
- data/lib/usher/route/static.rb +9 -0
- data/lib/usher/route/variable.rb +28 -9
- data/lib/usher/util.rb +3 -3
- data/lib/usher/util/generate.rb +55 -18
- data/lib/usher/util/parser.rb +25 -18
- data/rails/init.rb +24 -8
- data/spec/private/generate_spec.rb +116 -1
- data/spec/private/generate_with_spec.rb +28 -0
- data/spec/private/rack/dispatch_spec.rb +23 -2
- data/spec/private/rack/route_spec.rb +50 -0
- data/spec/private/rails2_2/generate_spec.rb +1 -1
- data/spec/private/rails2_2/path_spec.rb +1 -1
- data/spec/private/rails2_2/recognize_spec.rb +1 -1
- data/spec/private/rails2_3/generate_spec.rb +1 -1
- data/spec/private/rails2_3/path_spec.rb +1 -1
- data/spec/private/rails2_3/recognize_spec.rb +1 -1
- data/spec/private/recognize_spec.rb +4 -5
- data/spec/private/url_parts_spec.rb +116 -0
- metadata +26 -12
- data/lib/usher/interface/rack_interface/route.rb +0 -16
data/lib/usher/node.rb
CHANGED
@@ -51,7 +51,7 @@ class Usher
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def greedy?
|
54
|
-
@greedy
|
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?
|
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
|
-
|
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?
|
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, []]
|
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
|
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
|
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
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
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(
|
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
|
-
|
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[
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
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
|
-
|
223
|
-
|
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
|
-
|
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
|
-
|
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)
|
data/lib/usher/route.rb
CHANGED
@@ -1,14 +1,19 @@
|
|
1
|
-
require File.join(
|
2
|
-
require File.join(
|
3
|
-
require File.join(
|
4
|
-
require File.join(
|
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
|
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
|
data/lib/usher/route/variable.rb
CHANGED
@@ -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
|
-
|
16
|
-
|
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 #{@
|
31
|
+
raise ValidationException.new("#{val} does not conform to #{@validator}, root cause #{e.inspect}")
|
21
32
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
58
|
+
class Greedy < Variable
|
59
|
+
def to_s
|
60
|
+
"!#{name}"
|
61
|
+
end
|
62
|
+
end
|
44
63
|
|
45
64
|
end
|
46
65
|
|
data/lib/usher/util.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Usher
|
2
2
|
module Util
|
3
|
-
autoload :Generators, File.join(
|
4
|
-
autoload :Parser,
|
5
|
-
autoload :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
|
data/lib/usher/util/generate.rb
CHANGED
@@ -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(
|
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
|
-
|
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(
|
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(
|
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
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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]
|
data/lib/usher/util/parser.rb
CHANGED
@@ -39,25 +39,30 @@ class Usher
|
|
39
39
|
|
40
40
|
paths = Route::Util.expand_path(unprocessed_path)
|
41
41
|
|
42
|
-
paths.
|
43
|
-
path.
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
if part.
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|