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.
- 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
|