usher 0.5.13 → 0.6.0
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/README.rdoc +16 -0
- data/Rakefile +1 -1
- data/VERSION.yml +3 -3
- data/lib/usher.rb +31 -9
- data/lib/usher/delimiters.rb +16 -14
- data/lib/usher/grapher.rb +45 -22
- data/lib/usher/interface.rb +8 -4
- data/lib/usher/interface/sinatra.rb +66 -0
- data/lib/usher/node.rb +9 -1
- data/lib/usher/route.rb +45 -12
- data/lib/usher/route/path.rb +11 -2
- data/lib/usher/route/static.rb +1 -1
- data/lib/usher/util/generate.rb +6 -4
- data/lib/usher/util/parser.rb +133 -142
- data/spec/private/delimiters_spec.rb +5 -5
- data/spec/private/destination_spec.rb +32 -0
- data/spec/private/generate_spec.rb +28 -0
- data/spec/private/generate_with_spec.rb +2 -2
- data/spec/private/rack/dispatch_spec.rb +38 -1
- data/spec/private/recognize_spec.rb +27 -0
- data/spec/private/url_parts_spec.rb +4 -4
- metadata +6 -6
- data/lib/usher/spinoffs/strscan_additions.rb +0 -31
- data/spec/private/string_scanner_spec.rb +0 -39
data/lib/usher/route/path.rb
CHANGED
@@ -42,8 +42,17 @@ class Usher
|
|
42
42
|
@dynamic
|
43
43
|
end
|
44
44
|
|
45
|
-
def
|
46
|
-
|
45
|
+
def can_generate_from_keys?(keys)
|
46
|
+
if dynamic?
|
47
|
+
(dynamic_required_keys - keys).size.zero? ? keys : nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def can_generate_from_params?(params)
|
52
|
+
if route.router.consider_destination_keys?
|
53
|
+
route.destination.to_a - params.to_a
|
54
|
+
(route.destination.to_a - params.to_a).size.zero?
|
55
|
+
end
|
47
56
|
end
|
48
57
|
|
49
58
|
# Merges paths for use in generation
|
data/lib/usher/route/static.rb
CHANGED
data/lib/usher/util/generate.rb
CHANGED
@@ -117,9 +117,11 @@ class Usher
|
|
117
117
|
params.merge!(extra_params) if extra_params
|
118
118
|
end
|
119
119
|
|
120
|
-
|
121
120
|
result = Rack::Utils.uri_escape(generate_path_for_base_params(path, params))
|
122
121
|
unless !generate_extra || params.nil? || params.empty?
|
122
|
+
if usher.consider_destination_keys? && path.route.destination_keys
|
123
|
+
params.delete_if{|k, v| path.route.destination_keys.include?(k)}
|
124
|
+
end
|
123
125
|
extra_params = generate_extra_params(params, result[??])
|
124
126
|
result << extra_params
|
125
127
|
end
|
@@ -144,8 +146,8 @@ class Usher
|
|
144
146
|
|
145
147
|
@generation_module.module_eval <<-END_EVAL
|
146
148
|
def respond_to?(method_name)
|
147
|
-
if
|
148
|
-
@@generator.usher.named_routes.key?(
|
149
|
+
if method_name =~ /^(.*?)_(path|url)$/
|
150
|
+
@@generator.usher.named_routes.key?($1)
|
149
151
|
else
|
150
152
|
super
|
151
153
|
end
|
@@ -184,7 +186,7 @@ class Usher
|
|
184
186
|
(url[-1] == ?/) ? url[0..-2] : url
|
185
187
|
end
|
186
188
|
|
187
|
-
def path_for_routing_lookup(routing_lookup, params = {})
|
189
|
+
def path_for_routing_lookup(routing_lookup, params = {})
|
188
190
|
path = case routing_lookup
|
189
191
|
when Symbol
|
190
192
|
route = @usher.named_routes[routing_lookup]
|
data/lib/usher/util/parser.rb
CHANGED
@@ -4,170 +4,161 @@ class Usher
|
|
4
4
|
module Util
|
5
5
|
class Parser
|
6
6
|
|
7
|
+
attr_reader :router
|
8
|
+
|
7
9
|
def self.for_delimiters(router, valid_regex)
|
8
|
-
|
10
|
+
new(
|
9
11
|
router,
|
10
12
|
Regexp.new('((:|\*)?' + valid_regex + '|' + router.delimiters_regex + '|\(|\)|\||\{)')
|
11
13
|
)
|
12
14
|
end
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
16
|
+
def initialize(router, split_regex)
|
17
|
+
@router = router
|
18
|
+
@split_regex = split_regex
|
19
|
+
@delimiters_regex = Regexp.new(router.delimiters_regex)
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
false
|
29
|
-
end
|
30
|
-
else
|
31
|
-
false
|
32
|
-
end
|
33
|
-
|
34
|
-
unless unprocessed_path.first.is_a?(Route::Util::Group)
|
35
|
-
group = Usher::Route::Util::Group.new(:all, nil)
|
36
|
-
unprocessed_path.each{|p| group << p}
|
37
|
-
unprocessed_path = group
|
22
|
+
def generate_route(unprocessed_path, conditions, requirements, default_values, generate_with, priority)
|
23
|
+
match_partially = false
|
24
|
+
case unprocessed_path
|
25
|
+
when String
|
26
|
+
if unprocessed_path[-1] == ?*
|
27
|
+
unprocessed_path.slice!(-1)
|
28
|
+
match_partially = true
|
38
29
|
end
|
30
|
+
unprocessed_path = parse(unprocessed_path, requirements, default_values)
|
31
|
+
when Regexp
|
32
|
+
unprocessed_path = [Route::Static::Greedy.new(unprocessed_path)]
|
33
|
+
else
|
34
|
+
match_partially = false
|
35
|
+
end
|
36
|
+
|
37
|
+
unless unprocessed_path.first.is_a?(Route::Util::Group)
|
38
|
+
group = Usher::Route::Util::Group.new(:all, nil)
|
39
|
+
unprocessed_path.each{|p| group << p}
|
40
|
+
unprocessed_path = group
|
41
|
+
end
|
42
|
+
paths = Route::Util.expand_path(unprocessed_path)
|
39
43
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
part.
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
63
|
-
end
|
44
|
+
paths.each do |path|
|
45
|
+
path.each_with_index do |part, index|
|
46
|
+
part.default_value = default_values[part.name] if part.is_a?(Usher::Route::Variable) && default_values && default_values[part.name]
|
47
|
+
case part
|
48
|
+
when Usher::Route::Variable::Glob
|
49
|
+
if part.look_ahead && !part.look_ahead_priority
|
50
|
+
part.look_ahead = nil
|
51
|
+
part.look_ahead_priority = true
|
52
|
+
else
|
53
|
+
part.look_ahead = path[index + 1, path.size].find{|p| !p.is_a?(Usher::Route::Variable) && !router.delimiters.unescaped.include?(p)} || nil
|
54
|
+
end
|
55
|
+
when Usher::Route::Variable
|
56
|
+
if part.look_ahead && !part.look_ahead_priority
|
57
|
+
part.look_ahead = nil
|
58
|
+
part.look_ahead_priority = true
|
59
|
+
else
|
60
|
+
part.look_ahead = router.delimiters.first_in(path[index + 1, path.size]) || router.delimiters.unescaped.first
|
64
61
|
end
|
65
|
-
path
|
66
62
|
end
|
67
63
|
end
|
68
|
-
|
69
|
-
Route.new(
|
70
|
-
paths,
|
71
|
-
router,
|
72
|
-
conditions,
|
73
|
-
requirements,
|
74
|
-
default_values,
|
75
|
-
generate_with,
|
76
|
-
match_partially,
|
77
|
-
priority
|
78
|
-
)
|
79
|
-
|
80
64
|
end
|
81
65
|
|
82
|
-
|
83
|
-
|
84
|
-
|
66
|
+
Route.new(
|
67
|
+
paths,
|
68
|
+
router,
|
69
|
+
conditions,
|
70
|
+
requirements,
|
71
|
+
default_values,
|
72
|
+
generate_with,
|
73
|
+
match_partially,
|
74
|
+
priority
|
75
|
+
)
|
76
|
+
end
|
85
77
|
|
86
|
-
|
87
|
-
|
88
|
-
|
78
|
+
def parse_and_expand(path, requirements = nil, default_values = nil)
|
79
|
+
Usher::Route::Util.expand_path(parse(path, requirements, default_values))
|
80
|
+
end
|
89
81
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
part ?
|
94
|
-
(part << scanner.scan(@split_regex)) :
|
95
|
-
(part = scanner.scan(@split_regex))
|
82
|
+
def parse(path, requirements = nil, default_values = nil)
|
83
|
+
parts = Usher::Route::Util::Group.new(:all, nil)
|
84
|
+
scanner = StringScanner.new(path)
|
96
85
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
86
|
+
current_group = parts
|
87
|
+
part = nil
|
88
|
+
while !scanner.eos?
|
89
|
+
part ?
|
90
|
+
(part << scanner.scan(@split_regex)) :
|
91
|
+
(part = scanner.scan(@split_regex))
|
102
92
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
variable_type = variable.slice!(0).chr.to_sym
|
129
|
-
variable_class = case variable_type
|
130
|
-
when :'!' then Usher::Route::Variable::Greedy
|
131
|
-
when :* then Usher::Route::Variable::Glob
|
132
|
-
when :':' then Usher::Route::Variable::Single
|
133
|
-
end
|
134
|
-
variable_name = variable[0, variable.size - 1].to_sym
|
135
|
-
current_group << variable_class.new(variable_name, regex, requirements && requirements[variable_name])
|
136
|
-
elsif simple
|
137
|
-
current_group << Usher::Route::Static::Greedy.new(pattern)
|
138
|
-
else
|
139
|
-
current_group << regex
|
93
|
+
if scanner.match?(/\\/) and !scanner.match?(@delimiters_regex)
|
94
|
+
scanner.skip(/\\/)
|
95
|
+
part << scanner.getch
|
96
|
+
next
|
97
|
+
end
|
98
|
+
|
99
|
+
case part[0]
|
100
|
+
when ?*
|
101
|
+
var_name = part[1, part.size - 1].to_sym
|
102
|
+
current_group << Usher::Route::Variable::Glob.new(part[1, part.size - 1], nil, requirements && requirements[var_name])
|
103
|
+
when ?:
|
104
|
+
var_name = part[1, part.size - 1].to_sym
|
105
|
+
current_group << Usher::Route::Variable::Single.new(part[1, part.size - 1], nil, requirements && requirements[var_name])
|
106
|
+
when ?{
|
107
|
+
pattern = ''
|
108
|
+
count = 1
|
109
|
+
simple = scanner.scan(/~/)
|
110
|
+
variable = scanner.scan(/[!:\*]([^,]+),/)
|
111
|
+
until count.zero?
|
112
|
+
regex_part = scanner.scan(/\{|\}|[^\{\}]+/)
|
113
|
+
case regex_part[0]
|
114
|
+
when ?{
|
115
|
+
count += 1
|
116
|
+
when ?}
|
117
|
+
count -= 1
|
140
118
|
end
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
detached_group.parent = new_group
|
152
|
-
detached_group.group_type = :all
|
153
|
-
new_group << detached_group
|
154
|
-
new_group.parent << new_group
|
119
|
+
pattern << regex_part
|
120
|
+
end
|
121
|
+
pattern.slice!(pattern.length - 1)
|
122
|
+
regex = Regexp.new(pattern)
|
123
|
+
if variable
|
124
|
+
variable_type = variable.slice!(0).chr.to_sym
|
125
|
+
variable_class = case variable_type
|
126
|
+
when :'!' then Usher::Route::Variable::Greedy
|
127
|
+
when :* then Usher::Route::Variable::Glob
|
128
|
+
when :':' then Usher::Route::Variable::Single
|
155
129
|
end
|
156
|
-
|
157
|
-
current_group
|
158
|
-
|
159
|
-
current_group <<
|
130
|
+
variable_name = variable[0, variable.size - 1].to_sym
|
131
|
+
current_group << variable_class.new(variable_name, regex, requirements && requirements[variable_name])
|
132
|
+
elsif simple
|
133
|
+
current_group << Usher::Route::Static::Greedy.new(Regexp.new(pattern))
|
160
134
|
else
|
161
|
-
current_group <<
|
135
|
+
current_group << regex
|
162
136
|
end
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
137
|
+
when ?(
|
138
|
+
new_group = Usher::Route::Util::Group.new(:any, current_group)
|
139
|
+
current_group << new_group
|
140
|
+
current_group = new_group
|
141
|
+
when ?)
|
142
|
+
current_group = current_group.parent.group_type == :one ? current_group.parent.parent : current_group.parent
|
143
|
+
when ?|
|
144
|
+
unless current_group.parent.group_type == :one
|
145
|
+
detached_group = current_group.parent.pop
|
146
|
+
new_group = Usher::Route::Util::Group.new(:one, detached_group.parent)
|
147
|
+
detached_group.parent = new_group
|
148
|
+
detached_group.group_type = :all
|
149
|
+
new_group << detached_group
|
150
|
+
new_group.parent << new_group
|
151
|
+
end
|
152
|
+
current_group.parent << Usher::Route::Util::Group.new(:all, current_group.parent)
|
153
|
+
current_group = current_group.parent.last
|
154
|
+
when ?\\
|
155
|
+
current_group << part[1..-1]
|
156
|
+
else
|
157
|
+
current_group << part
|
158
|
+
end
|
159
|
+
part = nil
|
160
|
+
end unless !path || path.empty?
|
161
|
+
parts
|
171
162
|
end
|
172
163
|
end
|
173
164
|
end
|
@@ -1,17 +1,17 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
|
2
2
|
require 'usher'
|
3
3
|
|
4
|
-
describe Delimiters do
|
4
|
+
describe Usher::Delimiters do
|
5
5
|
describe "#unescaped" do
|
6
6
|
it "should unescape delimiters correctly" do
|
7
|
-
Delimiters.new(['/', '\)', '\\\\']).unescaped.should == ['/', ')', '\\']
|
7
|
+
Usher::Delimiters.new(['/', '\)', '\\\\']).unescaped.should == ['/', ')', '\\']
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
11
|
describe "#first_in" do
|
12
12
|
describe "when there is a complex path with a lot of delimiters occurrences" do
|
13
13
|
before :each do
|
14
|
-
@delimiters = Delimiters.new ['@', '.', '/']
|
14
|
+
@delimiters = Usher::Delimiters.new ['@', '.', '/']
|
15
15
|
@paths = ['var', '.', 'var', '/', 'var', '@']
|
16
16
|
end
|
17
17
|
|
@@ -24,7 +24,7 @@ describe Delimiters do
|
|
24
24
|
|
25
25
|
describe "when there are delimiters with escaped charaters" do
|
26
26
|
before :each do
|
27
|
-
@delimiters = Delimiters.new ['\\(', '\\)']
|
27
|
+
@delimiters = Usher::Delimiters.new ['\\(', '\\)']
|
28
28
|
@paths = ['var', '(', 'var', ')']
|
29
29
|
end
|
30
30
|
|
@@ -35,7 +35,7 @@ describe Delimiters do
|
|
35
35
|
|
36
36
|
describe "when there is no occurence of delimiters in path" do
|
37
37
|
before :each do
|
38
|
-
@delimiters = Delimiters.new ['-', '/']
|
38
|
+
@delimiters = Usher::Delimiters.new ['-', '/']
|
39
39
|
@paths = ['e', '@', 'ma', '.', 'il']
|
40
40
|
end
|
41
41
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
describe "a route destination" do
|
2
|
+
before(:each) do
|
3
|
+
@u = Usher.new
|
4
|
+
end
|
5
|
+
|
6
|
+
it "should return a compound with given var args" do
|
7
|
+
r = @u.add_route('/testsauce').to(:one, :two, :three, :four)
|
8
|
+
r.destination.args.should == [:one, :two, :three, :four]
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should return a compound with given var args and a hash on the end" do
|
12
|
+
r = @u.add_route('/testsauce').to(:one, :two, :three, :four, :five => 'six', :seven => 'heaven')
|
13
|
+
r.destination.args.should == [:one, :two, :three, :four]
|
14
|
+
r.destination.options.should == {:five => 'six', :seven => 'heaven'}
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should never wrap it in a compound if its a simple hash" do
|
18
|
+
r = @u.add_route('/testsauce').to(:five => 'six', :seven => 'heaven')
|
19
|
+
r.destination.should == {:five => 'six', :seven => 'heaven'}
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should never wrap it in a compound if its a simple object" do
|
23
|
+
r = @u.add_route('/testsauce').to(:eighteen)
|
24
|
+
r.destination.should == :eighteen
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should never wrap it in a compound if its a simple block" do
|
28
|
+
p = proc{ puts 'lovetown' }
|
29
|
+
r = @u.add_route('/testsauce').to(&p)
|
30
|
+
r.destination.should == p
|
31
|
+
end
|
32
|
+
end
|
@@ -142,6 +142,21 @@ describe "Usher URL generation" do
|
|
142
142
|
@route_set.generator.generate(:default_values_not_in_path, {:controller => "foo"}).should == '/foo?page=1'
|
143
143
|
end
|
144
144
|
|
145
|
+
describe "with consider_destination_keys enabled" do
|
146
|
+
|
147
|
+
before(:each) do
|
148
|
+
@route_set = Usher.new(:generator => Usher::Util::Generators::URL.new, :consider_destination_keys => true)
|
149
|
+
@route_set.reset!
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should generate direct unnamed paths" do
|
153
|
+
@route_set.add_route('/profiles', :controller => 'profiles', :action => 'edit')
|
154
|
+
@route_set.add_route('/users', :controller => 'users', :action => 'index')
|
155
|
+
@route_set.generator.generate(nil, :controller => 'profiles', :action => 'edit').should == '/profiles'
|
156
|
+
@route_set.generator.generate(nil, :controller => 'users', :action => 'index').should == '/users'
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
145
160
|
describe "when named route was added with string key" do
|
146
161
|
before :each do
|
147
162
|
@route_set.add_named_route 'items', '/items', :controller => 'items', :action => 'index'
|
@@ -258,6 +273,19 @@ describe "Usher URL generation" do
|
|
258
273
|
end
|
259
274
|
end
|
260
275
|
|
276
|
+
describe "#path_for_routing_lookup" do
|
277
|
+
describe "when direct route exists" do
|
278
|
+
before :each do
|
279
|
+
@route_set = Usher.new(:generator => Usher::Util::Generators::URL.new, :consider_destination_keys => true)
|
280
|
+
@route = @route_set.add_named_route(:direct_path, '/some-neat-name', :controller => 'foo', :action => 'bar')
|
281
|
+
end
|
282
|
+
|
283
|
+
it "should return exactly this route" do
|
284
|
+
@route_set.generator.path_for_routing_lookup(nil, :controller => 'foo', :action => 'bar').should == @route.paths.first
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
261
289
|
describe "nested generation" do
|
262
290
|
before do
|
263
291
|
@route_set2 = Usher.new(:generator => Usher::Util::Generators::URL.new)
|