joshbuddy-usher 0.5.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +2 -0
- data/Rakefile +5 -27
- data/VERSION.yml +2 -2
- data/lib/usher.rb +104 -67
- data/lib/usher/grapher.rb +2 -1
- data/lib/usher/interface.rb +12 -5
- data/lib/usher/interface/rack_interface.rb +31 -13
- data/lib/usher/interface/rails2_2_interface.rb +2 -3
- data/lib/usher/interface/rails2_3_interface.rb +2 -3
- data/lib/usher/interface/rails3_interface.rb +57 -0
- data/lib/usher/node.rb +76 -50
- data/lib/usher/route.rb +44 -10
- data/lib/usher/route/path.rb +22 -9
- data/lib/usher/util/generate.rb +12 -14
- data/lib/usher/util/parser.rb +50 -0
- data/spec/private/generate_spec.rb +86 -32
- data/spec/private/grapher_spec.rb +5 -6
- data/spec/private/path_spec.rb +35 -4
- data/spec/private/rack/dispatch_spec.rb +100 -15
- data/spec/private/recognize_spec.rb +68 -50
- data/spec/spec_helper.rb +22 -0
- metadata +6 -3
@@ -0,0 +1,57 @@
|
|
1
|
+
class Usher
|
2
|
+
module Interface
|
3
|
+
class Rails3Interface
|
4
|
+
|
5
|
+
@@instance = nil
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@usher = Usher.new
|
9
|
+
@controller_paths = []
|
10
|
+
@configurations_files = []
|
11
|
+
|
12
|
+
@@instance = self
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.instance
|
16
|
+
@@instance
|
17
|
+
end
|
18
|
+
|
19
|
+
def draw(&blk)
|
20
|
+
@usher.instance_eval(&blk)
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_accessor :controller_paths
|
24
|
+
|
25
|
+
def add_configuration_file(file)
|
26
|
+
@configurations_files << file
|
27
|
+
end
|
28
|
+
|
29
|
+
def reload!
|
30
|
+
@usher.reset!
|
31
|
+
@configurations_files.each do |c|
|
32
|
+
Kernel.load(c)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def call(env)
|
37
|
+
request = ActionDispatch::Request.new(env)
|
38
|
+
response = @usher.recognize(request, request.path_info)
|
39
|
+
request.parameters.merge!(response.path.route.default_values) if response.path.route.default_values
|
40
|
+
response.params.each{ |hk| request.parameters[hk.first] = hk.last}
|
41
|
+
controller = "#{request.parameters[:controller].to_s.camelize}Controller".constantize
|
42
|
+
controller.action(request.parameters[:action] || 'index').call(env)
|
43
|
+
end
|
44
|
+
|
45
|
+
def recognize(request)
|
46
|
+
params = recognize_path(request.path, extract_request_environment(request))
|
47
|
+
request.path_parameters = params.with_indifferent_access
|
48
|
+
"#{params[:controller].to_s.camelize}Controller".constantize
|
49
|
+
end
|
50
|
+
|
51
|
+
def load(app)
|
52
|
+
@app = app
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/usher/node.rb
CHANGED
@@ -4,7 +4,7 @@ class Usher
|
|
4
4
|
|
5
5
|
class Node
|
6
6
|
|
7
|
-
Response = Struct.new(:path, :params)
|
7
|
+
Response = Struct.new(:path, :params, :remaining_path, :matched_path)
|
8
8
|
|
9
9
|
attr_reader :lookup, :greedy_lookup
|
10
10
|
attr_accessor :terminates, :exclusive_type, :parent, :value, :request_methods
|
@@ -26,7 +26,7 @@ class Usher
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def depth
|
29
|
-
@depth ||= @parent
|
29
|
+
@depth ||= @parent.is_a?(Node) ? @parent.depth + 1 : 0
|
30
30
|
end
|
31
31
|
|
32
32
|
def greedy?
|
@@ -55,52 +55,26 @@ class Usher
|
|
55
55
|
|
56
56
|
def add(route)
|
57
57
|
route.paths.each do |path|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
parts.unshift(key)
|
76
|
-
current_node.lookup[nil] ||= Node.new(current_node, Route::RequestMethod.new(current_node.exclusive_type, nil))
|
77
|
-
end
|
78
|
-
else
|
79
|
-
case key
|
80
|
-
when Route::Variable
|
81
|
-
(upgrade_method, lookup_method) = case key
|
82
|
-
when Route::Variable::Greedy
|
83
|
-
[:upgrade_greedy_lookup, :greedy_lookup]
|
84
|
-
else
|
85
|
-
[:upgrade_lookup, :lookup]
|
86
|
-
end
|
87
|
-
|
88
|
-
if key.regex_matcher
|
89
|
-
current_node.send(upgrade_method)
|
90
|
-
current_node.send(lookup_method)[key.regex_matcher] ||= Node.new(current_node, key)
|
91
|
-
else
|
92
|
-
current_node.send(lookup_method)[nil] ||= Node.new(current_node, key)
|
93
|
-
end
|
94
|
-
else
|
95
|
-
current_node.upgrade_lookup if key.is_a?(Regexp)
|
96
|
-
current_node.lookup[key] ||= Node.new(current_node, key)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
current_node = target_node
|
100
|
-
end
|
101
|
-
current_node.terminates = path
|
58
|
+
set_path_with_destination(path)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def delete(route)
|
63
|
+
route.paths.each do |path|
|
64
|
+
set_path_with_destination(path, nil)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def unique_routes(node = self, routes = [])
|
69
|
+
routes << node.terminates.route if node.terminates
|
70
|
+
node.lookup.values.each do |v|
|
71
|
+
unique_routes(v, routes)
|
72
|
+
end
|
73
|
+
node.greedy_lookup.values.each do |v|
|
74
|
+
unique_routes(v, routes)
|
102
75
|
end
|
103
|
-
|
76
|
+
routes.uniq!
|
77
|
+
routes
|
104
78
|
end
|
105
79
|
|
106
80
|
def find(usher, request, original_path, path, params = [], position = 0)
|
@@ -110,10 +84,16 @@ class Usher
|
|
110
84
|
return ret
|
111
85
|
end
|
112
86
|
end
|
113
|
-
|
114
|
-
|
87
|
+
nil
|
88
|
+
elsif terminates? && (path.size.zero? || terminates.route.partial_match?)
|
89
|
+
if terminates.route.partial_match?
|
90
|
+
Response.new(terminates, params, original_path[position, original_path.size], original_path[0, position])
|
91
|
+
else
|
92
|
+
Response.new(terminates, params, nil, original_path)
|
93
|
+
end
|
94
|
+
|
115
95
|
elsif !path.size.zero? && (greedy? && (match_with_result_output = greedy_lookup.match_with_result(whole_path = original_path[position, original_path.size])))
|
116
|
-
|
96
|
+
next_path, matched_part = match_with_result_output
|
117
97
|
position += matched_part.size
|
118
98
|
params << [next_path.value.name, whole_path.slice!(0, matched_part.size)]
|
119
99
|
next_path.find(usher, request, original_path, whole_path.size.zero? ? whole_path : usher.splitter.url_split(whole_path), params, position)
|
@@ -157,5 +137,51 @@ class Usher
|
|
157
137
|
end
|
158
138
|
end
|
159
139
|
|
140
|
+
private
|
141
|
+
def set_path_with_destination(path, destination = path)
|
142
|
+
parts = path.parts.dup
|
143
|
+
request_methods.each do |type|
|
144
|
+
parts.push(Route::RequestMethod.new(type, path.route.conditions[type])) if path.route.conditions && path.route.conditions.key?(type)
|
145
|
+
end
|
146
|
+
|
147
|
+
current_node = self
|
148
|
+
until parts.size.zero?
|
149
|
+
key = parts.shift
|
150
|
+
target_node = case key
|
151
|
+
when Route::RequestMethod
|
152
|
+
current_node.upgrade_lookup if key.value.is_a?(Regexp)
|
153
|
+
if current_node.exclusive_type == key.type
|
154
|
+
current_node.lookup[key.value] ||= Node.new(current_node, key)
|
155
|
+
elsif current_node.lookup.empty?
|
156
|
+
current_node.exclusive_type = key.type
|
157
|
+
current_node.lookup[key.value] ||= Node.new(current_node, key)
|
158
|
+
else
|
159
|
+
parts.unshift(key)
|
160
|
+
current_node.lookup[nil] ||= Node.new(current_node, Route::RequestMethod.new(current_node.exclusive_type, nil))
|
161
|
+
end
|
162
|
+
when Route::Variable
|
163
|
+
upgrade_method, lookup_method = case key
|
164
|
+
when Route::Variable::Greedy
|
165
|
+
[:upgrade_greedy_lookup, :greedy_lookup]
|
166
|
+
else
|
167
|
+
[:upgrade_lookup, :lookup]
|
168
|
+
end
|
169
|
+
|
170
|
+
if key.regex_matcher
|
171
|
+
current_node.send(upgrade_method)
|
172
|
+
current_node.send(lookup_method)[key.regex_matcher] ||= Node.new(current_node, key)
|
173
|
+
else
|
174
|
+
current_node.send(lookup_method)[nil] ||= Node.new(current_node, key)
|
175
|
+
end
|
176
|
+
else
|
177
|
+
current_node.upgrade_lookup if key.is_a?(Regexp)
|
178
|
+
current_node.lookup[key] ||= Node.new(current_node, key)
|
179
|
+
end
|
180
|
+
current_node = target_node
|
181
|
+
end
|
182
|
+
current_node.terminates = destination
|
183
|
+
end
|
184
|
+
|
185
|
+
|
160
186
|
end
|
161
187
|
end
|
data/lib/usher/route.rb
CHANGED
@@ -5,17 +5,17 @@ require File.join(File.dirname(__FILE__), 'route', 'request_method')
|
|
5
5
|
|
6
6
|
class Usher
|
7
7
|
class Route
|
8
|
-
attr_reader
|
8
|
+
attr_reader :paths, :requirements, :conditions,
|
9
|
+
:destination, :named, :generate_with,
|
10
|
+
:default_values, :match_partially
|
11
|
+
attr_accessor :parent_route
|
9
12
|
|
10
13
|
GenerateWith = Struct.new(:scheme, :port, :host)
|
11
14
|
|
12
|
-
def initialize(parsed_paths, router, conditions, requirements, default_values, generate_with)
|
13
|
-
@router = router
|
14
|
-
@requirements = requirements
|
15
|
-
@conditions = conditions
|
16
|
-
@default_values = default_values
|
17
|
-
@generate_with = GenerateWith.new(generate_with[:scheme], generate_with[:port], generate_with[:host]) if generate_with
|
15
|
+
def initialize(parsed_paths, router, conditions, requirements, default_values, generate_with, match_partially)
|
18
16
|
@paths = parsed_paths.collect {|path| Path.new(self, path)}
|
17
|
+
@router, @requirements, @conditions, @default_values, @match_partially = router, requirements, conditions, default_values, match_partially
|
18
|
+
@generate_with = GenerateWith.new(generate_with[:scheme], generate_with[:port], generate_with[:host]) if generate_with
|
19
19
|
end
|
20
20
|
|
21
21
|
def grapher
|
@@ -26,11 +26,27 @@ class Usher
|
|
26
26
|
@grapher
|
27
27
|
end
|
28
28
|
|
29
|
+
def dup
|
30
|
+
result = super
|
31
|
+
result.grapher = nil
|
32
|
+
result
|
33
|
+
end
|
34
|
+
|
29
35
|
def find_matching_path(params)
|
30
|
-
|
36
|
+
if params.nil? || params.empty?
|
37
|
+
matching_path = @paths.first
|
38
|
+
else
|
39
|
+
matching_path = @paths.size == 1 ? @paths.first : grapher.find_matching_path(params)
|
40
|
+
end
|
41
|
+
|
42
|
+
if parent_route
|
43
|
+
matching_path = parent_route.find_matching_path(params).merge(matching_path)
|
44
|
+
matching_path.route = self
|
45
|
+
end
|
46
|
+
|
47
|
+
matching_path
|
31
48
|
end
|
32
49
|
|
33
|
-
|
34
50
|
# Sets +options+ on a route. Returns +self+.
|
35
51
|
#
|
36
52
|
# Request = Struct.new(:path)
|
@@ -39,7 +55,13 @@ class Usher
|
|
39
55
|
# route.to(:controller => 'testing', :action => 'index')
|
40
56
|
# set.recognize(Request.new('/test')).first.params => {:controller => 'testing', :action => 'index'}
|
41
57
|
def to(options = nil, &block)
|
42
|
-
|
58
|
+
raise "cannot set destintaion as block and argument" if block_given? && options
|
59
|
+
@destination = if block_given?
|
60
|
+
block
|
61
|
+
else
|
62
|
+
options.parent_route = self if options.respond_to?(:parent_route=)
|
63
|
+
options
|
64
|
+
end
|
43
65
|
self
|
44
66
|
end
|
45
67
|
|
@@ -53,6 +75,18 @@ class Usher
|
|
53
75
|
@router.name(name, self)
|
54
76
|
self
|
55
77
|
end
|
78
|
+
|
79
|
+
def match_partially!
|
80
|
+
@match_partially = true
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
def partial_match?
|
85
|
+
@match_partially
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
attr_writer :grapher
|
56
90
|
|
57
91
|
end
|
58
92
|
end
|
data/lib/usher/route/path.rb
CHANGED
@@ -2,16 +2,16 @@ class Usher
|
|
2
2
|
class Route
|
3
3
|
class Path
|
4
4
|
|
5
|
-
|
5
|
+
attr_accessor :route
|
6
|
+
attr_reader :parts
|
6
7
|
|
7
8
|
def initialize(route, parts)
|
8
|
-
|
9
|
-
|
10
|
-
@dynamic = @parts.any?{|p| p.is_a?(Variable)}
|
9
|
+
self.route = route
|
10
|
+
self.parts = parts
|
11
11
|
end
|
12
12
|
|
13
13
|
def dynamic_indicies
|
14
|
-
unless @dynamic_indicies
|
14
|
+
unless dynamic? && @dynamic_indicies
|
15
15
|
@dynamic_indicies = []
|
16
16
|
parts.each_index{|i| @dynamic_indicies << i if parts[i].is_a?(Variable)}
|
17
17
|
end
|
@@ -19,11 +19,11 @@ class Usher
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def dynamic_parts
|
22
|
-
@dynamic_parts ||= parts.values_at(*dynamic_indicies)
|
22
|
+
@dynamic_parts ||= parts.values_at(*dynamic_indicies) if dynamic?
|
23
23
|
end
|
24
24
|
|
25
25
|
def dynamic_map
|
26
|
-
unless @dynamic_map
|
26
|
+
unless dynamic? && @dynamic_map
|
27
27
|
@dynamic_map = {}
|
28
28
|
dynamic_parts.each{|p| @dynamic_map[p.name] = p }
|
29
29
|
end
|
@@ -31,11 +31,11 @@ class Usher
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def dynamic_keys
|
34
|
-
@dynamic_keys ||= dynamic_map.keys
|
34
|
+
@dynamic_keys ||= dynamic_map.keys if dynamic?
|
35
35
|
end
|
36
36
|
|
37
37
|
def dynamic_required_keys
|
38
|
-
@dynamic_required_keys ||= dynamic_parts.select{|dp| !dp.default_value}.map{|dp| dp.name}
|
38
|
+
@dynamic_required_keys ||= dynamic_parts.select{|dp| !dp.default_value}.map{|dp| dp.name} if dynamic?
|
39
39
|
end
|
40
40
|
|
41
41
|
def dynamic?
|
@@ -45,6 +45,19 @@ class Usher
|
|
45
45
|
def can_generate_from?(keys)
|
46
46
|
(dynamic_required_keys - keys).size.zero?
|
47
47
|
end
|
48
|
+
|
49
|
+
# Merges paths for use in generation
|
50
|
+
def merge(other_path)
|
51
|
+
new_parts = parts + other_path.parts
|
52
|
+
Path.new(route, new_parts)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def parts=(parts)
|
57
|
+
@parts = parts
|
58
|
+
@dynamic = @parts.any?{|p| p.is_a?(Variable)}
|
59
|
+
end
|
60
|
+
|
48
61
|
end
|
49
62
|
end
|
50
63
|
end
|
data/lib/usher/util/generate.rb
CHANGED
@@ -29,9 +29,7 @@ class Usher
|
|
29
29
|
|
30
30
|
class URL
|
31
31
|
|
32
|
-
|
33
|
-
@usher = usher
|
34
|
-
end
|
32
|
+
attr_accessor :usher
|
35
33
|
|
36
34
|
def generate_full(routing_lookup, request, params = nil)
|
37
35
|
path = path_for_routing_lookup(routing_lookup, params)
|
@@ -49,20 +47,20 @@ class Usher
|
|
49
47
|
result << (path.route.generate_with && path.route.generate_with.host) ? path.route.generate_with.host : request.host
|
50
48
|
port = path.route.generate_with && path.route.generate_with.port || request.port
|
51
49
|
if result[4] == ?s
|
52
|
-
result << ':' << port.to_s
|
50
|
+
result << ':' << port.to_s unless port == 443
|
53
51
|
else
|
54
|
-
result << ':' << port.to_s
|
52
|
+
result << ':' << port.to_s unless port == 80
|
55
53
|
end
|
56
54
|
result
|
57
55
|
end
|
58
|
-
|
59
|
-
def path_for_routing_lookup(routing_lookup, params)
|
56
|
+
|
57
|
+
def path_for_routing_lookup(routing_lookup, params = {})
|
60
58
|
path = case routing_lookup
|
61
59
|
when Symbol
|
62
60
|
route = @usher.named_routes[routing_lookup]
|
63
|
-
|
61
|
+
route.find_matching_path(params || {})
|
64
62
|
when Route
|
65
|
-
|
63
|
+
routing_lookup.find_matching_path(params || {})
|
66
64
|
when nil
|
67
65
|
params.is_a?(Hash) ? @usher.path_for_options(params) : raise
|
68
66
|
when Route::Path
|
@@ -94,23 +92,21 @@ class Usher
|
|
94
92
|
when Route::Variable::Glob
|
95
93
|
value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new)
|
96
94
|
value.each_with_index do |current_value, index|
|
97
|
-
current_value = current_value.to_s unless current_value.is_a?(String)
|
98
95
|
part.valid!(current_value)
|
99
|
-
result << current_value
|
96
|
+
result << current_value.to_s
|
100
97
|
result << '/' if index != value.size - 1
|
101
98
|
end
|
102
99
|
when Route::Variable
|
103
100
|
value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new)
|
104
|
-
value = value.to_s unless value.is_a?(String)
|
105
101
|
part.valid!(value)
|
106
|
-
result << value
|
102
|
+
result << value.to_s
|
107
103
|
else
|
108
104
|
result << part
|
109
105
|
end
|
110
106
|
end
|
111
107
|
result = Rack::Utils.uri_escape(result)
|
112
108
|
|
113
|
-
|
109
|
+
unless params.nil? || params.empty?
|
114
110
|
has_query = result[??]
|
115
111
|
params.each do |k,v|
|
116
112
|
case v
|
@@ -125,7 +121,9 @@ class Usher
|
|
125
121
|
end
|
126
122
|
result
|
127
123
|
end
|
124
|
+
|
128
125
|
end
|
126
|
+
|
129
127
|
end
|
130
128
|
end
|
131
129
|
end
|