joshbuddy-usher 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +0 -1
- data/VERSION.yml +1 -1
- data/lib/usher.rb +7 -5
- data/lib/usher/interface/rack_interface.rb +2 -0
- data/lib/usher/node.rb +11 -53
- data/lib/usher/route.rb +1 -2
- data/lib/usher/route/variable.rb +2 -16
- data/lib/usher/splitter.rb +8 -17
- data/spec/private/email/recognize_spec.rb +0 -2
- data/spec/private/generate_spec.rb +5 -0
- data/spec/private/rack/dispatch_spec.rb +0 -2
- data/spec/private/recognize_spec.rb +6 -16
- data/spec/private/split_spec.rb +1 -1
- metadata +2 -2
data/README.rdoc
CHANGED
@@ -74,7 +74,6 @@ For instance, the path, <tt>/path/something(.xml|.html)</tt> would only match <t
|
|
74
74
|
<tt>/path/something.html</tt>. Generally its more efficent to use one and only sections over using regex.
|
75
75
|
|
76
76
|
=== +options+
|
77
|
-
* +transformers+ - Transforms a variable before it gets to the requirements. Takes either a +proc+ or a +symbol+. If its a +symbol+, calls the method on the incoming parameter. If its a +proc+, its called with the variable.
|
78
77
|
* +requirements+ - After transformation, tests the condition using ===. If it returns false, it raises an <tt>Usher::ValidationException</tt>
|
79
78
|
* +conditions+ - Accepts any of the +request_methods+ specificied in the construction of Usher. This can be either a <tt>string</tt> or a regular expression.
|
80
79
|
* Any other key is interpreted as a requirement for the variable of its name.
|
data/VERSION.yml
CHANGED
data/lib/usher.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
$:.unshift File.dirname(__FILE__)
|
2
2
|
|
3
3
|
require 'cgi'
|
4
|
+
require 'uri'
|
4
5
|
require 'usher/node'
|
5
6
|
require 'usher/route'
|
6
7
|
require 'usher/grapher'
|
@@ -135,12 +136,10 @@ class Usher
|
|
135
136
|
# <tt>/path/something.html</tt>. Generally its more efficent to use one and only sections over using regex.
|
136
137
|
#
|
137
138
|
# === +options+
|
138
|
-
# * +transformers+ - Transforms a variable before it gets to the requirements. Takes either a +proc+ or a +symbol+. If its a +symbol+, calls the method on the incoming parameter. If its a +proc+, its called with the variable.
|
139
139
|
# * +requirements+ - After transformation, tests the condition using ===. If it returns false, it raises an <tt>Usher::ValidationException</tt>
|
140
140
|
# * +conditions+ - Accepts any of the +request_methods+ specificied in the construction of Usher. This can be either a <tt>string</tt> or a regular expression.
|
141
141
|
# * Any other key is interpreted as a requirement for the variable of its name.
|
142
142
|
def add_route(path, options = nil)
|
143
|
-
transformers = options && options.delete(:transformers) || {}
|
144
143
|
conditions = options && options.delete(:conditions) || {}
|
145
144
|
requirements = options && options.delete(:requirements) || {}
|
146
145
|
if options
|
@@ -151,7 +150,7 @@ class Usher
|
|
151
150
|
end
|
152
151
|
end
|
153
152
|
end
|
154
|
-
route = Route.new(path, self, {:
|
153
|
+
route = Route.new(path, self, {:conditions => conditions, :requirements => requirements})
|
155
154
|
route.to(options) if options && !options.empty?
|
156
155
|
|
157
156
|
@tree.add(route)
|
@@ -224,15 +223,18 @@ class Usher
|
|
224
223
|
case p.type
|
225
224
|
when :*
|
226
225
|
param_list.first.each {|dp| p.valid!(dp.to_s) } if check_variables
|
227
|
-
generated_path << param_list.shift.collect{|dp| dp.to_s} * delimiter
|
226
|
+
generated_path << param_list.shift.collect{|dp| URI.escape(dp.to_s)} * delimiter
|
228
227
|
else
|
229
228
|
p.valid!(param_list.first.to_s) if check_variables
|
230
|
-
|
229
|
+
if dp = param_list.shift
|
230
|
+
generated_path << URI.escape(dp.to_s)
|
231
|
+
end
|
231
232
|
end
|
232
233
|
else
|
233
234
|
generated_path << p.to_s
|
234
235
|
end
|
235
236
|
end
|
237
|
+
|
236
238
|
unless params_hash.empty?
|
237
239
|
has_query = generated_path[??]
|
238
240
|
params_hash.each do |k,v|
|
data/lib/usher/node.rb
CHANGED
@@ -90,37 +90,27 @@ class Usher
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def find(request, path, params = [])
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
[@lookup[request.send(@exclusive_type)], @lookup[nil]].each do |n|
|
98
|
-
ret = n.find(request, path.dup, params.dup) if n
|
99
|
-
ret and return ret
|
100
|
-
end
|
101
|
-
elsif path.size.zero? && !part
|
102
|
-
if terminates?
|
103
|
-
Response.new(terminates, params)
|
104
|
-
elsif params.last.is_a?(Array) && @lookup[nil]
|
105
|
-
if @lookup[nil].exclusive_type
|
106
|
-
@lookup[nil].find(request, path, params)
|
107
|
-
else
|
108
|
-
Response.new(@lookup[nil].terminates, params)
|
93
|
+
if exclusive_type
|
94
|
+
[lookup[request.send(exclusive_type)], lookup[nil]].each do |n|
|
95
|
+
if n && (ret = n.find(request, path.dup, params.dup))
|
96
|
+
return ret
|
109
97
|
end
|
110
98
|
end
|
111
|
-
elsif
|
112
|
-
|
99
|
+
elsif path.size.zero? && terminates?
|
100
|
+
Response.new(terminates, params)
|
101
|
+
elsif next_part = lookup[part = path.shift] || lookup[nil]
|
102
|
+
case next_part.value
|
103
|
+
when Route::Variable
|
113
104
|
case next_part.value.type
|
114
105
|
when :*
|
115
106
|
params << [next_part.value.name, []] unless params.last && params.last.first == next_part.value.name
|
116
107
|
loop do
|
117
|
-
if (next_part.value.look_ahead === part || (!part.is_a?(Symbol) && !next_part.value.regex_matcher.match(part)))
|
108
|
+
if (next_part.value.look_ahead === part || (!part.is_a?(Symbol) && next_part.value.regex_matcher && !next_part.value.regex_matcher.match(part)))
|
118
109
|
path.unshift(part)
|
119
110
|
path.unshift(next_part.parent.value) if next_part.parent.value.is_a?(Symbol)
|
120
111
|
break
|
121
112
|
else
|
122
113
|
unless part.is_a?(Symbol) && !next_part.value.globs_capture_separators
|
123
|
-
part = next_part.value.transform!(part)
|
124
114
|
next_part.value.valid!(part)
|
125
115
|
params.last.last << part
|
126
116
|
end
|
@@ -131,48 +121,16 @@ class Usher
|
|
131
121
|
part = path.shift
|
132
122
|
end
|
133
123
|
end
|
134
|
-
next_part.find(request, path, params)
|
135
|
-
when :':'
|
136
|
-
part = next_part.value.transform!(part)
|
137
|
-
next_part.value.valid!(part)
|
138
|
-
var = next_part.value
|
139
|
-
params << [next_part.value.name, part]
|
140
|
-
until (path.first == var.look_ahead) || path.empty?
|
141
|
-
params.last.last << path.shift.to_s
|
142
|
-
end
|
143
|
-
next_part.find(request, path, params)
|
144
|
-
end
|
145
|
-
else
|
146
|
-
next_part.find(request, path, params)
|
147
|
-
end
|
148
|
-
elsif next_part = @lookup[nil]
|
149
|
-
if next_part.value.is_a?(Route::Variable)
|
150
|
-
case next_part.value.type
|
151
|
-
when :*
|
152
|
-
params << [next_part.value.name, []] unless params.last && params.last.first == next_part.value.name
|
153
|
-
if next_part.value.look_ahead === part
|
154
|
-
path.unshift(part)
|
155
|
-
path.unshift(next_part.parent.value) if next_part.parent.value.is_a?(Symbol)
|
156
|
-
next_part.find(request, path, params)
|
157
|
-
else
|
158
|
-
unless part.is_a?(Symbol) && !next_part.value.globs_capture_separators
|
159
|
-
part = next_part.value.transform!(part)
|
160
|
-
next_part.value.valid!(part)
|
161
|
-
params.last.last << part
|
162
|
-
end
|
163
|
-
find(request, path, params)
|
164
|
-
end
|
165
124
|
when :':'
|
166
|
-
part = next_part.value.transform!(part)
|
167
125
|
next_part.value.valid!(part)
|
168
126
|
var = next_part.value
|
169
127
|
params << [next_part.value.name, part]
|
170
128
|
until (path.first == var.look_ahead) || path.empty?
|
171
129
|
params.last.last << path.shift.to_s
|
172
130
|
end
|
173
|
-
next_part.find(request, path, params)
|
174
131
|
end
|
175
132
|
end
|
133
|
+
next_part.find(request, path, params)
|
176
134
|
else
|
177
135
|
nil
|
178
136
|
end
|
data/lib/usher/route.rb
CHANGED
@@ -13,8 +13,7 @@ class Usher
|
|
13
13
|
@router = router
|
14
14
|
@requirements = options && options.delete(:requirements)
|
15
15
|
@conditions = options && options.delete(:conditions)
|
16
|
-
@
|
17
|
-
@paths = @router.splitter.split(@original_path, @requirements, @transformers).collect {|path| Path.new(self, path)}
|
16
|
+
@paths = @router.splitter.split(@original_path, @requirements).collect {|path| Path.new(self, path)}
|
18
17
|
@primary_path = @paths.first
|
19
18
|
#FIXME params is poorly named. this shouldn't be an array
|
20
19
|
@params = []
|
data/lib/usher/route/variable.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
class Usher
|
2
2
|
class Route
|
3
3
|
class Variable
|
4
|
-
attr_reader :type, :name, :validator, :
|
4
|
+
attr_reader :type, :name, :validator, :regex_matcher
|
5
5
|
attr_accessor :look_ahead, :globs_capture_separators
|
6
6
|
|
7
|
-
def initialize(type, name, validator = nil,
|
7
|
+
def initialize(type, name, validator = nil, regex_matcher = nil, globs_capture_separators = false)
|
8
8
|
@type = type
|
9
9
|
@name = :"#{name}"
|
10
10
|
@validator = validator
|
11
|
-
@transformer = transformer
|
12
11
|
@regex_matcher = regex_matcher
|
13
12
|
@globs_capture_separators = globs_capture_separators
|
14
13
|
end
|
@@ -17,19 +16,6 @@ class Usher
|
|
17
16
|
"#{type}#{name}"
|
18
17
|
end
|
19
18
|
|
20
|
-
def transform!(val)
|
21
|
-
return val unless @transformer
|
22
|
-
|
23
|
-
case @transformer
|
24
|
-
when Proc
|
25
|
-
@transformer.call(val)
|
26
|
-
when Symbol
|
27
|
-
val.send(@transformer)
|
28
|
-
end
|
29
|
-
rescue Exception => e
|
30
|
-
raise ValidationException.new("#{val} could not be successfully transformed by #{@transformer}, root cause #{e.inspect}")
|
31
|
-
end
|
32
|
-
|
33
19
|
def valid!(val)
|
34
20
|
case @validator
|
35
21
|
when Proc
|
data/lib/usher/splitter.rb
CHANGED
@@ -19,27 +19,18 @@ class Usher
|
|
19
19
|
def initialize(delimiters, split_regex, url_split_regex)
|
20
20
|
@delimiters = delimiters
|
21
21
|
@delimiter_chars = delimiters.collect{|d| d[0]}
|
22
|
+
@delimiter_chars_map = Hash[*@delimiter_chars.map{|c| [c, c.chr.to_sym]}.flatten]
|
22
23
|
@split_regex = split_regex
|
23
24
|
@url_split_regex = url_split_regex
|
24
25
|
end
|
25
26
|
|
26
27
|
def url_split(path)
|
27
|
-
parts =
|
28
|
-
|
29
|
-
while !ss.eos?
|
30
|
-
if part = ss.scan(@url_split_regex)
|
31
|
-
parts << case part[0]
|
32
|
-
when *@delimiter_chars
|
33
|
-
part.to_sym
|
34
|
-
else
|
35
|
-
part
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end if path && !path.empty?
|
28
|
+
parts = path.scan(@url_split_regex)
|
29
|
+
parts.map!{ |part| @delimiter_chars_map[part[0]] || part}
|
39
30
|
parts
|
40
31
|
end
|
41
32
|
|
42
|
-
def split(path, requirements = nil
|
33
|
+
def split(path, requirements = nil)
|
43
34
|
parts = Group.new(:all, nil)
|
44
35
|
ss = StringScanner.new(path)
|
45
36
|
current_group = parts
|
@@ -48,9 +39,9 @@ class Usher
|
|
48
39
|
case part[0]
|
49
40
|
when ?*, ?:
|
50
41
|
type = part.slice!(0).chr.to_sym
|
51
|
-
current_group << Usher::Route::Variable.new(type, part, requirements && requirements[part.to_sym]
|
42
|
+
current_group << Usher::Route::Variable.new(type, part, requirements && requirements[part.to_sym])
|
52
43
|
when ?{
|
53
|
-
pattern = '
|
44
|
+
pattern = ''
|
54
45
|
count = 1
|
55
46
|
variable = ss.scan(/[:\*]([^,]+),/)
|
56
47
|
until count.zero?
|
@@ -63,12 +54,12 @@ class Usher
|
|
63
54
|
end
|
64
55
|
pattern << regex_part
|
65
56
|
end
|
66
|
-
pattern
|
57
|
+
pattern.slice!(pattern.length - 1)
|
67
58
|
regex = Regexp.new(pattern)
|
68
59
|
if variable
|
69
60
|
variable_type = variable.slice!(0).chr.to_sym
|
70
61
|
variable_name = variable[0, variable.size - 1].to_sym
|
71
|
-
current_group << Usher::Route::Variable.new(variable_type, variable_name, requirements && requirements[variable_name],
|
62
|
+
current_group << Usher::Route::Variable.new(variable_type, variable_name, requirements && requirements[variable_name], regex)
|
72
63
|
else
|
73
64
|
current_group << regex
|
74
65
|
end
|
@@ -18,6 +18,11 @@ describe "Usher URL generation" do
|
|
18
18
|
route_set.generate_url(:sample, {:action => 'action'}).should == '/sample/action'
|
19
19
|
end
|
20
20
|
|
21
|
+
it "should generate a simple URL with a single variable (and escape)" do
|
22
|
+
route_set.add_named_route(:sample, '/sample/:action', :controller => 'sample')
|
23
|
+
route_set.generate_url(:sample, {:action => 'action time'}).should == '/sample/action%20time'
|
24
|
+
end
|
25
|
+
|
21
26
|
it "should generate a simple URL with a single variable (thats not a string)" do
|
22
27
|
route_set.add_named_route(:sample, '/sample/:action/:id', :controller => 'sample')
|
23
28
|
route_set.generate_url(:sample, {:action => 'action', :id => 123}).should == '/sample/action/123'
|
@@ -10,8 +10,6 @@ def build_request(opts)
|
|
10
10
|
request
|
11
11
|
end
|
12
12
|
|
13
|
-
SampleController = Object.new
|
14
|
-
|
15
13
|
describe "Usher route recognition" do
|
16
14
|
|
17
15
|
before(:each) do
|
@@ -83,6 +81,12 @@ describe "Usher route recognition" do
|
|
83
81
|
route_set.recognize(build_request({:method => 'get', :path => '/test/part/hello/again/123/hello/again/onemore'})).params.should == [[:test, ['hello', 'again', '123', 'hello', 'again']]]
|
84
82
|
end
|
85
83
|
|
84
|
+
it "should recgonize a regex glob variable terminated by a single regex variable" do
|
85
|
+
target_route = route_set.add_route('/test/part/{*test,^(hello|again|\d+)$}/{:party,onemore}')
|
86
|
+
route_set.recognize(build_request({:method => 'get', :path => '/test/part/hello/again/123/hello/again/onemore'})).path.route.should == target_route
|
87
|
+
route_set.recognize(build_request({:method => 'get', :path => '/test/part/hello/again/123/hello/again/onemore'})).params.should == [[:test, ['hello', 'again', '123', 'hello', 'again']], [:party, 'onemore']]
|
88
|
+
end
|
89
|
+
|
86
90
|
it "should recgonize two glob-style variables separated by a static part" do
|
87
91
|
target_route = route_set.add_route('/*format/innovate/*onemore')
|
88
92
|
response = route_set.recognize(build_request({:method => 'get', :path => '/sample/html/innovate/apple'}))
|
@@ -143,11 +147,6 @@ describe "Usher route recognition" do
|
|
143
147
|
www_product_show_route.should == route_set.recognize(build_request({:method => 'get', :path => '/products/show/123', :subdomains => ['www'], :user_agent => false})).path.route
|
144
148
|
end
|
145
149
|
|
146
|
-
it "should use a transformer (proc) on incoming variables" do
|
147
|
-
route_set.add_route('/:controller/:action/:id', :transformers => {:id => proc{|v| v.to_i}})
|
148
|
-
route_set.recognize(build_request({:method => 'get', :path => '/products/show/123asd', :domain => 'admin.host.com'})).params.rassoc(123).first.should == :id
|
149
|
-
end
|
150
|
-
|
151
150
|
it "should use a requirement (proc) on incoming variables" do
|
152
151
|
route_set.add_route('/:controller/:action/:id', :id => proc{|v| Integer(v)})
|
153
152
|
proc {route_set.recognize(build_request({:method => 'get', :path => '/products/show/123', :domain => 'admin.host.com'}))}.should_not raise_error Usher::ValidationException
|
@@ -164,19 +163,10 @@ describe "Usher route recognition" do
|
|
164
163
|
route_set.recognize(build_request({:method => 'get', :path => '/testing/asd.qwe/testing2/poi.zxc/oiu.asd'})).params.should == [[:id, 'asd.qwe'], [:id2, 'poi.zxc'], [:id3, 'oiu.asd']]
|
165
164
|
end
|
166
165
|
|
167
|
-
it "should use a transformer (symbol) on incoming variables" do
|
168
|
-
route_set.add_route('/:controller/:action/:id', :transformers => {:id => :to_i})
|
169
|
-
route_set.recognize(build_request({:method => 'get', :path => '/products/show/123asd', :domain => 'admin.host.com'})).params.rassoc(123).first.should == :id
|
170
|
-
end
|
171
|
-
|
172
166
|
it "should should raise if malformed variables are used" do
|
173
167
|
route_set.add_route('/products/show/:id', :id => /\d+/, :conditions => {:method => 'get'})
|
174
168
|
proc {route_set.recognize(build_request({:method => 'get', :path => '/products/show/qweasd', :domain => 'admin.host.com'}))}.should raise_error
|
175
169
|
end
|
176
170
|
|
177
|
-
it "should should raise if transformer proc raises (anything)" do
|
178
|
-
route_set.add_route('/products/show/:id', :transformers => {:id => proc{|v| Integer(v)}})
|
179
|
-
proc {route_set.recognize(build_request({:method => 'get', :path => '/products/show/qweasd', :domain => 'admin.host.com'}))}.should raise_error(Usher::ValidationException)
|
180
|
-
end
|
181
171
|
|
182
172
|
end
|
data/spec/private/split_spec.rb
CHANGED
@@ -9,7 +9,7 @@ describe "Usher route tokenizing" do
|
|
9
9
|
|
10
10
|
it "should split / delimited routes with a regex in it" do
|
11
11
|
Usher::Splitter.for_delimiters(['/', '.'], '[0-9A-Za-z\$\-_\+!\*\',]+').
|
12
|
-
split('/test/{this}/split').should == [[:/, 'test', :/,
|
12
|
+
split('/test/{this}/split').should == [[:/, 'test', :/, /this/, :/, 'split']]
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should split on ' ' delimited routes as well" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: joshbuddy-usher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Hull
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-05-
|
12
|
+
date: 2009-05-15 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|