joshbuddy-usher 0.5.1 → 0.5.2
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 +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
|