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