usher 0.8.0 → 0.8.1
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.markdown +1 -1
- data/Rakefile +6 -8
- data/lib/usher.rb +5 -0
- data/lib/usher/interface/rack.rb +11 -11
- data/lib/usher/interface/rack/route.rb +8 -3
- data/lib/usher/interface/rails23.rb +19 -7
- data/lib/usher/interface/rails3.rb +1 -1
- data/lib/usher/node.rb +52 -19
- data/lib/usher/node/response.rb +1 -1
- data/lib/usher/node/root.rb +20 -10
- data/lib/usher/route.rb +12 -1
- data/lib/usher/route/path.rb +25 -17
- data/lib/usher/util/generate.rb +1 -1
- data/lib/usher/util/parser.rb +4 -2
- data/lib/usher/util/rails.rb +9 -7
- data/spec/private/meta_spec.rb +23 -0
- data/spec/private/path_spec.rb +1 -1
- data/spec/private/rack/route_spec.rb +7 -0
- data/spec/private/recognize_spec.rb +13 -4
- data/spec/private/sinatra/recognize_spec.rb +10 -1
- data/spec/spec_helper.rb +2 -0
- data/usher.gemspec +1 -1
- metadata +4 -3
data/README.markdown
CHANGED
@@ -143,7 +143,7 @@ In your config/initializers/usher.rb (create if it doesn't exist) add:
|
|
143
143
|
|
144
144
|
## Sinatra
|
145
145
|
|
146
|
-
In Sinatra, you get the extra method,
|
146
|
+
In Sinatra, you get the extra method, `generate`, which lets you generate a url. Name your routes with `:name` when you define them.
|
147
147
|
|
148
148
|
require 'rubygems'
|
149
149
|
require 'usher'
|
data/Rakefile
CHANGED
@@ -9,7 +9,7 @@ YARD::Rake::YardocTask.new do |t|
|
|
9
9
|
t.options = ['--markup=markdown'] # optional
|
10
10
|
end
|
11
11
|
|
12
|
-
task :spec => ['spec:private', 'spec:rails2_2:
|
12
|
+
task :spec => ['spec:private', 'spec:rails2_2:spec', 'spec:rails2_3:spec']
|
13
13
|
namespace(:spec) do
|
14
14
|
Spec::Rake::SpecTask.new(:private) do |t|
|
15
15
|
t.spec_opts ||= []
|
@@ -24,7 +24,7 @@ namespace(:spec) do
|
|
24
24
|
sh('unzip -qq spec/rails2_2/vendor.zip -dspec/rails2_2')
|
25
25
|
end
|
26
26
|
|
27
|
-
Spec::Rake::SpecTask.new(:
|
27
|
+
Spec::Rake::SpecTask.new(:only_spec) do |t|
|
28
28
|
t.spec_opts ||= []
|
29
29
|
t.spec_opts << "-rubygems"
|
30
30
|
t.spec_opts << "--options" << "spec/spec.opts"
|
@@ -34,9 +34,8 @@ namespace(:spec) do
|
|
34
34
|
task :cleanup do
|
35
35
|
sh('rm -rf spec/rails2_2/vendor')
|
36
36
|
end
|
37
|
-
|
38
|
-
task :spec => :unzip
|
39
|
-
task :cleanup => :spec
|
37
|
+
|
38
|
+
task :spec => [:unzip, :only_spec, :cleanup]
|
40
39
|
end
|
41
40
|
|
42
41
|
namespace(:rails2_3) do
|
@@ -45,7 +44,7 @@ namespace(:spec) do
|
|
45
44
|
sh('unzip -qq spec/rails2_3/vendor.zip -dspec/rails2_3')
|
46
45
|
end
|
47
46
|
|
48
|
-
Spec::Rake::SpecTask.new(:
|
47
|
+
Spec::Rake::SpecTask.new(:only_spec) do |t|
|
49
48
|
t.spec_opts ||= []
|
50
49
|
t.spec_opts << "-rubygems"
|
51
50
|
t.spec_opts << "--options" << "spec/spec.opts"
|
@@ -55,8 +54,7 @@ namespace(:spec) do
|
|
55
54
|
sh('rm -rf spec/rails2_3/vendor')
|
56
55
|
end
|
57
56
|
|
58
|
-
task :spec => :unzip
|
59
|
-
task :cleanup => :spec
|
57
|
+
task :spec => [:unzip, :only_spec, :cleanup]
|
60
58
|
end
|
61
59
|
|
62
60
|
|
data/lib/usher.rb
CHANGED
@@ -252,6 +252,11 @@ class Usher
|
|
252
252
|
route
|
253
253
|
end
|
254
254
|
|
255
|
+
def add_meta(meta, path, options = nil)
|
256
|
+
route = get_route(path, options)
|
257
|
+
root.add_meta(route, meta)
|
258
|
+
end
|
259
|
+
|
255
260
|
# Recognizes a `request`
|
256
261
|
# @param request [#path] The request object. Must minimally respond to #path if no path argument is supplied here.
|
257
262
|
# @param path [String] The path to be recognized.
|
data/lib/usher/interface/rack.rb
CHANGED
@@ -23,7 +23,7 @@ class Usher
|
|
23
23
|
# * <tt>generator</tt> - Route generator to use. (Default <tt>Usher::Util::Generators::URL.new</tt>)
|
24
24
|
# * <tt>allow_identical_variable_names</tt> - Option to prevent routes with identical variable names to be added. eg, /:variable/:variable would raise an exception if this option is not enabled. (Default <tt>false</tt>)
|
25
25
|
def initialize(options = {}, &blk)
|
26
|
-
@_app = options[:default_app] || proc{|env| ::Rack::Response.new("
|
26
|
+
@_app = options[:default_app] || proc{|env| ::Rack::Response.new("Not Found", 404).finish }
|
27
27
|
@use_destinations = options.key?(:use_destinations) ? options.delete(:use_destinations) : true
|
28
28
|
@router_key = options.delete(:router_key) || ENV_KEY_DEFAULT_ROUTER
|
29
29
|
request_methods = options.delete(:request_methods) || [:request_method, :host, :port, :scheme]
|
@@ -56,14 +56,14 @@ class Usher
|
|
56
56
|
new_one
|
57
57
|
end
|
58
58
|
|
59
|
-
# Adds a route to the route set with a
|
59
|
+
# Adds a route to the route set with a `path` and optional `options`.
|
60
60
|
# See <tt>Usher#add_route</tt> for more details about the format of the route and options accepted here.
|
61
61
|
def add(path, options = nil)
|
62
62
|
@router.add_route(path, options)
|
63
63
|
end
|
64
64
|
alias_method :path, :add
|
65
65
|
|
66
|
-
# Sets the default application when route matching is unsuccessful. Accepts either an application
|
66
|
+
# Sets the default application when route matching is unsuccessful. Accepts either an application `app` or a block to call.
|
67
67
|
#
|
68
68
|
# default { |env| ... }
|
69
69
|
# default DefaultApp
|
@@ -79,27 +79,27 @@ class Usher
|
|
79
79
|
# it returns route, and because you may want to work with the route,
|
80
80
|
# for example give it a name, we returns the route with GET request
|
81
81
|
|
82
|
-
# Convenience method for adding a route that only matches request method
|
82
|
+
# Convenience method for adding a route that only matches request method `GET`.
|
83
83
|
def only_get(path, options = {})
|
84
84
|
add(path, options.merge!(:conditions => {:request_method => ["GET"]}))
|
85
85
|
end
|
86
86
|
|
87
|
-
# Convenience method for adding a route that only matches request methods
|
87
|
+
# Convenience method for adding a route that only matches request methods `GET` and `HEAD`.
|
88
88
|
def get(path, options = {})
|
89
89
|
add(path, options.merge!(:conditions => {:request_method => ["HEAD", "GET"]}))
|
90
90
|
end
|
91
91
|
|
92
|
-
# Convenience method for adding a route that only matches request method
|
92
|
+
# Convenience method for adding a route that only matches request method `POST`.
|
93
93
|
def post(path, options = {})
|
94
94
|
add(path, options.merge!(:conditions => {:request_method => "POST"}))
|
95
95
|
end
|
96
96
|
|
97
|
-
# Convenience method for adding a route that only matches request method
|
97
|
+
# Convenience method for adding a route that only matches request method `PUT`.
|
98
98
|
def put(path, options = {})
|
99
99
|
add(path, options.merge!(:conditions => {:request_method => "PUT"}))
|
100
100
|
end
|
101
101
|
|
102
|
-
# Convenience method for adding a route that only matches request method
|
102
|
+
# Convenience method for adding a route that only matches request method `DELETE`.
|
103
103
|
def delete(path, options = {})
|
104
104
|
add(path, options.merge!(:conditions => {:request_method => "DELETE"}))
|
105
105
|
end
|
@@ -167,7 +167,7 @@ class Usher
|
|
167
167
|
elsif usable_response && response.destination.respond_to?(:args) && response.destination.args.first.respond_to?(:call)
|
168
168
|
response.args.first
|
169
169
|
elsif !response.succeeded? && response.request_method?
|
170
|
-
rack_response = ::Rack::Response.new("Method
|
170
|
+
rack_response = ::Rack::Response.new("Method Not Allowed", 405)
|
171
171
|
rack_response['Allow'] = response.acceptable_responses_only_strings.join(", ")
|
172
172
|
proc { |env| rack_response.finish }
|
173
173
|
else
|
@@ -177,8 +177,8 @@ class Usher
|
|
177
177
|
|
178
178
|
# Consume the path from path_info to script_name
|
179
179
|
def consume_path!(request, response)
|
180
|
-
request.env["SCRIPT_NAME"] = (request.env["SCRIPT_NAME"] + response.matched_path)
|
181
|
-
request.env["PATH_INFO"] = response.remaining_path
|
180
|
+
request.env["SCRIPT_NAME"] = (request.env["SCRIPT_NAME"] + response.matched_path)
|
181
|
+
request.env["PATH_INFO"] = response.remaining_path || ""
|
182
182
|
end
|
183
183
|
|
184
184
|
def default_app
|
@@ -4,7 +4,7 @@ class Usher
|
|
4
4
|
# Route specific for Rack with redirection support built in.
|
5
5
|
class Route < Usher::Route
|
6
6
|
|
7
|
-
# Redirect route to some other path.
|
7
|
+
# Redirect route to some other path.
|
8
8
|
def redirect(path, status = 302)
|
9
9
|
unless (300..399).include?(status)
|
10
10
|
raise ArgumentError, "Status has to be an integer between 300 and 399"
|
@@ -18,9 +18,14 @@ class Usher
|
|
18
18
|
self
|
19
19
|
end
|
20
20
|
|
21
|
+
# Serves either files from a directory, or a single static file.
|
21
22
|
def serves_static_from(root)
|
22
|
-
|
23
|
-
|
23
|
+
if File.directory?(root)
|
24
|
+
match_partially!
|
25
|
+
@destination = ::Rack::File.new(root)
|
26
|
+
else
|
27
|
+
@destination = proc{|env| env['PATH_INFO'] = File.basename(root); ::Rack::File.new(File.dirname(root)).call(env)}
|
28
|
+
end
|
24
29
|
end
|
25
30
|
end
|
26
31
|
end
|
@@ -93,11 +93,11 @@ class Usher
|
|
93
93
|
Array(destinations).each do |d| d.module_eval { include Helpers }
|
94
94
|
@router.named_routes.keys.each do |name|
|
95
95
|
@module.module_eval <<-end_eval # We use module_eval to avoid leaks
|
96
|
-
def #{name}_url(
|
97
|
-
|
96
|
+
def #{name}_url(*args)
|
97
|
+
UsherRailsRouter.generate(args, {}, :generate, :#{name})
|
98
98
|
end
|
99
|
-
def #{name}_path(
|
100
|
-
|
99
|
+
def #{name}_path(*args)
|
100
|
+
UsherRailsRouter.generate(args, {}, :generate, :#{name})
|
101
101
|
end
|
102
102
|
end_eval
|
103
103
|
end
|
@@ -107,12 +107,24 @@ class Usher
|
|
107
107
|
{ }
|
108
108
|
end
|
109
109
|
"
|
110
|
+
unless @module.const_defined?(:UsherRailsRouter)
|
111
|
+
@module.const_set(:UsherRailsRouter, self)
|
112
|
+
end
|
113
|
+
|
110
114
|
@router.named_routes.helpers.__send__(:extend, @module)
|
111
115
|
end
|
112
116
|
end
|
113
117
|
|
114
|
-
def generate(
|
115
|
-
|
118
|
+
def generate(args, recall = {}, method = :generate, route_name = nil)
|
119
|
+
if args.is_a?(Hash)
|
120
|
+
options = args
|
121
|
+
args = nil
|
122
|
+
else
|
123
|
+
args = Array(args)
|
124
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
125
|
+
end
|
126
|
+
|
127
|
+
route = if route_name
|
116
128
|
@router.named_routes[route_name]
|
117
129
|
else
|
118
130
|
merged_options = options
|
@@ -125,7 +137,7 @@ class Usher
|
|
125
137
|
case method
|
126
138
|
when :generate
|
127
139
|
merged_options ||= recall.merge(options)
|
128
|
-
url = generate_url(route, merged_options)
|
140
|
+
url = generate_url(route, args ? args << merged_options : merged_options)
|
129
141
|
url.slice!(-1) if url[-1] == ?/
|
130
142
|
url
|
131
143
|
else
|
@@ -35,7 +35,7 @@ class Usher
|
|
35
35
|
|
36
36
|
# Generates a url from Rack env and identifiers or significant keys.
|
37
37
|
#
|
38
|
-
# To generate a url by named route, pass the name in as a
|
38
|
+
# To generate a url by named route, pass the name in as a `Symbol`.
|
39
39
|
# url(env, :dashboard) # => "/dashboard"
|
40
40
|
#
|
41
41
|
# Additional parameters can be passed in as a hash
|
data/lib/usher/node.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
require File.join('usher', 'node', 'root')
|
2
4
|
require File.join('usher', 'node', 'root_ignoring_trailing_delimiters')
|
3
5
|
require File.join('usher', 'node', 'response')
|
@@ -19,8 +21,10 @@ class Usher
|
|
19
21
|
# @see Root
|
20
22
|
class Node
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
+
Terminates = Struct.new(:choices, :default)
|
25
|
+
|
26
|
+
attr_reader :normal, :greedy, :request, :terminates, :unique_terminating_routes, :meta
|
27
|
+
attr_accessor :default_terminates, :request_method_type, :parent, :value, :request_methods
|
24
28
|
|
25
29
|
def initialize(parent, value)
|
26
30
|
@parent, @value = parent, value
|
@@ -29,7 +33,7 @@ class Usher
|
|
29
33
|
def inspect
|
30
34
|
out = ''
|
31
35
|
out << " " * depth
|
32
|
-
out << "#{terminates
|
36
|
+
out << "#{terminates ? '* ' : ''}#{depth}: #{value.inspect}\n"
|
33
37
|
[:normal, :greedy, :request].each do |node_type|
|
34
38
|
send(node_type).each do |k,v|
|
35
39
|
out << (" " * (depth + 1)) << "#{node_type.to_s[0].chr} #{k.inspect} ==> \n" << v.inspect
|
@@ -38,15 +42,44 @@ class Usher
|
|
38
42
|
out
|
39
43
|
end
|
40
44
|
|
41
|
-
|
45
|
+
def add_meta(obj)
|
46
|
+
create_meta << obj
|
47
|
+
end
|
42
48
|
|
49
|
+
def add_terminate(path)
|
50
|
+
if path.route.when_proc
|
51
|
+
create_terminate.choices << path
|
52
|
+
else
|
53
|
+
create_terminate.default = path
|
54
|
+
end
|
55
|
+
unique_terminating_routes << path.route
|
56
|
+
end
|
43
57
|
|
58
|
+
def remove_terminate(path)
|
59
|
+
if terminates
|
60
|
+
terminates.choices.delete(path)
|
61
|
+
terminates.default = nil if terminates.default == path
|
62
|
+
end
|
63
|
+
unique_terminating_routes.delete_if{|r| r == path.route}
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
def create_terminate
|
69
|
+
@unique_terminating_routes ||= Set.new
|
70
|
+
@terminates ||= Terminates.new([], nil)
|
71
|
+
end
|
72
|
+
|
73
|
+
def create_meta
|
74
|
+
@meta ||= []
|
75
|
+
end
|
76
|
+
|
44
77
|
def depth
|
45
78
|
@depth ||= parent.is_a?(Node) ? parent.depth + 1 : 0
|
46
79
|
end
|
47
80
|
|
48
|
-
def
|
49
|
-
@terminates && @terminates.route.recognizable?
|
81
|
+
def pick_terminate(request_object)
|
82
|
+
@terminates.choices.find{|(p, t)| p.call(request_object) && t && t.route.recognizable?} || (@terminates.default && @terminates.default.route.recognizable? ? @terminates.default : nil) if @terminates
|
50
83
|
end
|
51
84
|
|
52
85
|
def ancestors
|
@@ -69,19 +102,19 @@ class Usher
|
|
69
102
|
@route_set ||= root.route_set
|
70
103
|
end
|
71
104
|
|
72
|
-
def find(request_object, original_path, path, params = [])
|
105
|
+
def find(request_object, original_path, path, params = [], gathered_meta = nil)
|
106
|
+
(gathered_meta ||= []).concat(meta) if meta
|
73
107
|
# terminates or is partial
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
Response.new(
|
78
|
-
Response.new(terminates, params, nil, original_path)
|
108
|
+
if terminating_path = pick_terminate(request_object) and (path.empty? || terminating_path.route.partial_match?)
|
109
|
+
response = terminating_path.route.partial_match? ?
|
110
|
+
Response.new(terminating_path, params, path.join, original_path[0, original_path.size - path.join.size], false, gathered_meta) :
|
111
|
+
Response.new(terminating_path, params, nil, original_path, false, gathered_meta)
|
79
112
|
# terminates or is partial
|
80
113
|
elsif !path.empty? and greedy and match_with_result_output = greedy.match_with_result(whole_path = path.join)
|
81
114
|
child_node, matched_part = match_with_result_output
|
82
115
|
whole_path.slice!(0, matched_part.size)
|
83
116
|
params << matched_part if child_node.value.is_a?(Route::Variable)
|
84
|
-
child_node.find(request_object, original_path, whole_path.empty? ? whole_path : route_set.splitter.split(whole_path), params)
|
117
|
+
child_node.find(request_object, original_path, whole_path.empty? ? whole_path : route_set.splitter.split(whole_path), params, gathered_meta)
|
85
118
|
elsif !path.empty? and normal and child_node = normal[path.first] || normal[nil]
|
86
119
|
part = path.shift
|
87
120
|
case child_node.value
|
@@ -93,7 +126,7 @@ class Usher
|
|
93
126
|
next_path_part = path.shift # and until they are satified,
|
94
127
|
part << next_path_part
|
95
128
|
end if variable.look_ahead
|
96
|
-
params << part
|
129
|
+
params << part # because its a variable, we need to add it to the params array
|
97
130
|
when Route::Variable::Glob
|
98
131
|
params << []
|
99
132
|
loop do
|
@@ -110,22 +143,22 @@ class Usher
|
|
110
143
|
end
|
111
144
|
end
|
112
145
|
end
|
113
|
-
child_node.find(request_object, original_path, path, params)
|
146
|
+
child_node.find(request_object, original_path, path, params, gathered_meta)
|
114
147
|
elsif request_method_type
|
115
148
|
if route_set.priority_lookups?
|
116
149
|
route_candidates = []
|
117
|
-
if specific_node = request[request_object.send(request_method_type)] and ret = specific_node.find(request_object, original_path, path.dup, params &&
|
150
|
+
if specific_node = request[request_object.send(request_method_type)] and ret = specific_node.find(request_object, original_path, path.dup, params.dup, gathered_meta && gathered_meta.dup)
|
118
151
|
route_candidates << ret
|
119
152
|
end
|
120
|
-
if general_node = request[nil] and ret = general_node.find(request_object, original_path, path.dup, params &&
|
153
|
+
if general_node = request[nil] and ret = general_node.find(request_object, original_path, path.dup, params.dup, gathered_meta && gathered_meta.dup)
|
121
154
|
route_candidates << ret
|
122
155
|
end
|
123
156
|
route_candidates.sort!{|r1, r2| r1.path.route.priority <=> r2.path.route.priority}
|
124
157
|
request_method_respond(route_candidates.last, request_method_type)
|
125
158
|
else
|
126
|
-
if specific_node = request[request_object.send(request_method_type)] and ret = specific_node.find(request_object, original_path, path.dup, params &&
|
159
|
+
if specific_node = request[request_object.send(request_method_type)] and ret = specific_node.find(request_object, original_path, path.dup, params.dup, gathered_meta && gathered_meta.dup)
|
127
160
|
ret
|
128
|
-
elsif general_node = request[nil] and ret = general_node.find(request_object, original_path, path.dup, params &&
|
161
|
+
elsif general_node = request[nil] and ret = general_node.find(request_object, original_path, path.dup, params.dup, gathered_meta && gathered_meta.dup)
|
129
162
|
request_method_respond(ret, request_method_type)
|
130
163
|
else
|
131
164
|
request_method_respond(nil, request_method_type)
|
data/lib/usher/node/response.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Usher
|
2
2
|
class Node
|
3
3
|
# The response from {Usher::Node::Root#lookup}. Adds some convenience methods for common parameter manipulation.
|
4
|
-
class Response < Struct.new(:path, :params_as_array, :remaining_path, :matched_path, :only_trailing_delimiters)
|
4
|
+
class Response < Struct.new(:path, :params_as_array, :remaining_path, :matched_path, :only_trailing_delimiters, :meta)
|
5
5
|
|
6
6
|
# The success of the response
|
7
7
|
# @return [Boolean] Always returns true
|
data/lib/usher/node/root.rb
CHANGED
@@ -12,15 +12,19 @@ class Usher
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def add(route)
|
15
|
-
route
|
15
|
+
get_nodes_for_route(route) {|p, n| n.add_terminate(p)}
|
16
16
|
end
|
17
17
|
|
18
18
|
def delete(route)
|
19
|
-
route
|
19
|
+
get_nodes_for_route(route) {|p, n| n.remove_terminate(p)}
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_meta(route, obj)
|
23
|
+
get_nodes_for_route(route) {|p, n| n.add_meta(obj)}
|
20
24
|
end
|
21
25
|
|
22
26
|
def unique_routes(node = self, routes = [])
|
23
|
-
routes
|
27
|
+
routes.concat(node.unique_terminating_routes.to_a) if node.unique_terminating_routes
|
24
28
|
[:normal, :greedy, :request].each { |type| node.send(type).values.each { |v| unique_routes(v, routes) } if node.send(type) }
|
25
29
|
routes.uniq!
|
26
30
|
routes
|
@@ -33,16 +37,22 @@ class Usher
|
|
33
37
|
private
|
34
38
|
|
35
39
|
def set_path_with_destination(path, destination = path)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
get_nodes(path) {|n| n.add_terminate(destination)}
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_nodes_for_route(route)
|
44
|
+
route.paths.each do |path|
|
45
|
+
nodes = [path.parts ? path.parts.inject(self){ |node, key| process_path_part(node, key)} : self]
|
46
|
+
nodes = process_request_parts(nodes, request_methods_for_path(path)) if request_methods
|
47
|
+
nodes.each do |node|
|
48
|
+
while node.request_method_type
|
49
|
+
node = (node.request[nil] ||= Node.new(node, Route::RequestMethod.new(node.request_method_type, nil)))
|
50
|
+
end
|
51
|
+
yield path, node
|
41
52
|
end
|
42
|
-
node.terminates = destination
|
43
53
|
end
|
44
54
|
end
|
45
|
-
|
55
|
+
|
46
56
|
def request_method_index(type)
|
47
57
|
request_methods.index(type)
|
48
58
|
end
|
data/lib/usher/route.rb
CHANGED
@@ -6,7 +6,7 @@ require File.join('usher', 'route', 'request_method')
|
|
6
6
|
|
7
7
|
class Usher
|
8
8
|
class Route
|
9
|
-
attr_reader :original_path, :paths, :requirements, :conditions, :named, :generate_with, :default_values, :match_partially, :destination, :priority
|
9
|
+
attr_reader :original_path, :paths, :requirements, :conditions, :named, :generate_with, :default_values, :match_partially, :destination, :priority, :when_proc
|
10
10
|
attr_accessor :parent_route, :router, :recognizable
|
11
11
|
|
12
12
|
class GenerateWith < Struct.new(:scheme, :port, :host)
|
@@ -15,6 +15,12 @@ class Usher
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
def ==(other_route)
|
19
|
+
if other_route.is_a?(Route)
|
20
|
+
original_path == other_route.original_path && requirements == other_route.requirements && conditions == other_route.conditions && match_partially == other_route.match_partially && recognizable == other_route.recognizable && parent_route == other_route.parent_route && generate_with == other_route.generate_with
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
18
24
|
def initialize(original_path, parsed_paths, router, conditions, requirements, default_values, generate_with, match_partially, priority)
|
19
25
|
@original_path, @router, @requirements, @conditions, @default_values, @match_partially, @priority = original_path, router, requirements, conditions, default_values, match_partially, priority
|
20
26
|
@recognizable = true
|
@@ -22,6 +28,11 @@ class Usher
|
|
22
28
|
@generate_with = GenerateWith.new(generate_with[:scheme], generate_with[:port], generate_with[:host]) if generate_with
|
23
29
|
end
|
24
30
|
|
31
|
+
def when(&block)
|
32
|
+
@when_proc = block
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
25
36
|
def destination_keys
|
26
37
|
@destination_keys ||= case
|
27
38
|
when Hash
|
data/lib/usher/route/path.rb
CHANGED
@@ -11,6 +11,10 @@ class Usher
|
|
11
11
|
build_generator
|
12
12
|
end
|
13
13
|
|
14
|
+
def ==(other_path)
|
15
|
+
other_path.is_a?(Path) ? route == other_path.route && parts == other_path.parts : nil
|
16
|
+
end
|
17
|
+
|
14
18
|
def convert_params_array(ary)
|
15
19
|
ary.empty? ? ary : dynamic_keys.zip(ary)
|
16
20
|
end
|
@@ -79,35 +83,39 @@ class Usher
|
|
79
83
|
@dynamic = @parts && @parts.any?{|p| p.is_a?(Variable)}
|
80
84
|
end
|
81
85
|
|
82
|
-
def
|
83
|
-
|
84
|
-
interpolating_path = ''
|
85
|
-
|
86
|
+
def interpolating_path
|
87
|
+
unless @interpolating_path
|
88
|
+
@interpolating_path = ''
|
86
89
|
parts.each_with_index do |part, index|
|
87
90
|
case part
|
88
91
|
when String
|
89
|
-
interpolating_path << part
|
92
|
+
@interpolating_path << part
|
90
93
|
when Static::Greedy
|
91
|
-
interpolating_path << part.generate_with
|
94
|
+
@interpolating_path << part.generate_with
|
92
95
|
when Variable::Glob
|
93
|
-
interpolating_path << '#{('
|
94
|
-
interpolating_path << "Array(arg#{index})"
|
96
|
+
@interpolating_path << '#{('
|
97
|
+
@interpolating_path << "Array(arg#{index})"
|
95
98
|
if part.default_value
|
96
|
-
interpolating_path << ' || '
|
97
|
-
interpolating_path << part.default_value.inspect
|
99
|
+
@interpolating_path << ' || '
|
100
|
+
@interpolating_path << part.default_value.inspect
|
98
101
|
end
|
99
|
-
interpolating_path << ').join(route.router.delimiters.first)}'
|
102
|
+
@interpolating_path << ').join(route.router.delimiters.first)}'
|
100
103
|
when Variable
|
101
|
-
interpolating_path << '#{'
|
102
|
-
interpolating_path << "arg#{index}"
|
104
|
+
@interpolating_path << '#{'
|
105
|
+
@interpolating_path << "arg#{index}"
|
103
106
|
if part.default_value
|
104
|
-
interpolating_path << ' || '
|
105
|
-
interpolating_path << part.default_value.inspect
|
107
|
+
@interpolating_path << ' || '
|
108
|
+
@interpolating_path << part.default_value.inspect
|
106
109
|
end
|
107
|
-
interpolating_path << '}'
|
110
|
+
@interpolating_path << '}'
|
108
111
|
end
|
109
112
|
end
|
110
|
-
|
113
|
+
end
|
114
|
+
@interpolating_path
|
115
|
+
end
|
116
|
+
|
117
|
+
def build_generator
|
118
|
+
if parts
|
111
119
|
generating_method = "def generate"
|
112
120
|
if dynamic?
|
113
121
|
generating_method << "("
|
data/lib/usher/util/generate.rb
CHANGED
@@ -75,7 +75,7 @@ class Usher
|
|
75
75
|
result << generate_path(path, params)
|
76
76
|
end
|
77
77
|
|
78
|
-
# Generates a completed URL based on a
|
78
|
+
# Generates a completed URL based on a `route` or set of optional `params`
|
79
79
|
#
|
80
80
|
# set = Usher.new
|
81
81
|
# route = set.add_named_route(:test_route, '/:controller/:action')
|
data/lib/usher/util/parser.rb
CHANGED
@@ -43,14 +43,16 @@ class Usher
|
|
43
43
|
part.default_value = default_values[part.name] if part.is_a?(Usher::Route::Variable) && default_values && default_values[part.name]
|
44
44
|
case part
|
45
45
|
when Usher::Route::Variable::Glob
|
46
|
-
|
46
|
+
possible_look_ahead = path[index + 1, path.size].find{|p| !p.is_a?(Usher::Route::Variable) && !router.delimiters.unescaped.include?(p)} || nil
|
47
|
+
if part.look_ahead && !part.look_ahead_priority && possible_look_ahead != part.look_ahead
|
47
48
|
part.look_ahead = nil
|
48
49
|
part.look_ahead_priority = true
|
49
50
|
else
|
50
51
|
part.look_ahead = path[index + 1, path.size].find{|p| !p.is_a?(Usher::Route::Variable) && !router.delimiters.unescaped.include?(p)} || nil
|
51
52
|
end
|
52
53
|
when Usher::Route::Variable
|
53
|
-
|
54
|
+
possible_look_ahead = router.delimiters.first_in(path[index + 1, path.size]) || router.delimiters.unescaped.first
|
55
|
+
if part.look_ahead && !part.look_ahead_priority && possible_look_ahead != part.look_ahead
|
54
56
|
part.look_ahead = nil
|
55
57
|
part.look_ahead_priority = true
|
56
58
|
else
|
data/lib/usher/util/rails.rb
CHANGED
@@ -10,16 +10,18 @@ class Usher
|
|
10
10
|
ActionController::Routing.module_eval "remove_const(:Routes); Routes = Usher::Interface.for(:rails23)"
|
11
11
|
|
12
12
|
when '2.2'
|
13
|
-
|
14
|
-
include ActionController::Resources
|
15
|
-
end
|
13
|
+
Usher::Interface::Rails22::Mapper.module_eval("include ActionController::Resources")
|
16
14
|
ActionController::Routing.module_eval "remove_const(:Routes); Routes = Usher::Interface.for(:rails22)"
|
17
15
|
|
18
16
|
when '2.0'
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
Usher::Interface::Rails20::Mapper.module_eval("include ActionController::Resources")
|
18
|
+
ActionController::Routing.module_eval <<-CODE
|
19
|
+
remove_const(:Routes);
|
20
|
+
interface = Usher::Interface.for(:rails20);
|
21
|
+
interface.configuration_file = File.join(RAILS_ROOT, 'config', 'routes.rb')
|
22
|
+
Routes = interface;
|
23
|
+
CODE
|
24
|
+
when '3.0'
|
23
25
|
ActionController::Routing.module_eval <<-CODE
|
24
26
|
remove_const(:Routes);
|
25
27
|
interface = Usher::Interface.for(:rails20);
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
|
2
|
+
require "usher"
|
3
|
+
|
4
|
+
describe "Usher metadata" do
|
5
|
+
|
6
|
+
it "should add meta data to a path" do
|
7
|
+
usher = Usher.new
|
8
|
+
usher.add_route('/test')
|
9
|
+
usher.add_route('/test/test2')
|
10
|
+
usher.add_route('/test/test3')
|
11
|
+
usher.add_route('/test/test2/:variable')
|
12
|
+
usher.add_meta(:test, '/test')
|
13
|
+
usher.add_meta(:test2, '/test/test2')
|
14
|
+
usher.add_meta(:test3, '/test/test2/:something')
|
15
|
+
|
16
|
+
usher.recognize_path('/test').meta.should == [:test]
|
17
|
+
usher.recognize_path('/test/test3').meta.should == [:test]
|
18
|
+
usher.recognize_path('/test/test2').meta.should == [:test, :test2]
|
19
|
+
usher.recognize_path('/test/test2/variable1').meta.should == [:test, :test2, :test3]
|
20
|
+
usher.recognize_path('/test/test2/variable2').meta.should == [:test, :test2, :test3]
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
data/spec/private/path_spec.rb
CHANGED
@@ -36,7 +36,7 @@ describe "Usher route adding" do
|
|
36
36
|
route_set.add_named_route(:route, '/bad/route', :controller => 'sample').should == route_set.named_routes[:route]
|
37
37
|
end
|
38
38
|
|
39
|
-
it "should allow named routes to be
|
39
|
+
it "should allow named routes to be deleted" do
|
40
40
|
route_set.add_named_route(:route, '/bad/route', :controller => 'sample').should == route_set.named_routes[:route]
|
41
41
|
route_set.route_count.should == 1
|
42
42
|
route_set.named_routes.size == 1
|
@@ -37,6 +37,13 @@ describe "Rack interface extensions for Usher::Route" do
|
|
37
37
|
status, headers, body = @route_set.call(@env)
|
38
38
|
body.path.should == File.join(File.dirname(__FILE__), File.basename(__FILE__))
|
39
39
|
end
|
40
|
+
|
41
|
+
it "should serve a specific file" do
|
42
|
+
@route_set.get("/static-file").serves_static_from(__FILE__)
|
43
|
+
@env = Rack::MockRequest.env_for("/static-file")
|
44
|
+
status, headers, body = @route_set.call(@env)
|
45
|
+
body.path.should == __FILE__
|
46
|
+
end
|
40
47
|
end
|
41
48
|
|
42
49
|
describe "chaining" do
|
@@ -84,6 +84,15 @@ describe "Usher route recognition" do
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
+
describe 'when proc' do
|
88
|
+
|
89
|
+
it "pick the correct route" do
|
90
|
+
not_target_route = @route_set.add_route('/sample').when{|r| r.protocol == 'https'}
|
91
|
+
target_route = @route_set.add_route('/sample').when{|r| r.protocol == 'http'}
|
92
|
+
@route_set.recognize(build_request({:method => 'get', :path => '/sample', :protocol => 'http'})).path.route.should == target_route
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
87
96
|
it "should recognize path with a trailing slash" do
|
88
97
|
@route_set = Usher.new(:request_methods => [:protocol, :domain, :port, :query_string, :remote_ip, :user_agent, :referer, :method, :subdomains], :ignore_trailing_delimiters => true)
|
89
98
|
|
@@ -399,14 +408,14 @@ describe "Usher route recognition" do
|
|
399
408
|
|
400
409
|
it "should not add routes added to the dup to the original" do
|
401
410
|
@r2.add_route("/bar", :bar => "bar")
|
402
|
-
@r2.recognize(
|
403
|
-
@route_set.recognize(
|
411
|
+
@r2.recognize( build_request(:path => "/bar")).path.route.destination.should == {:bar => "bar"}
|
412
|
+
@route_set.recognize(build_request(:path => "/bar")).should == nil
|
404
413
|
end
|
405
414
|
|
406
415
|
it "should not delete routes added to the dup to the original" do
|
407
416
|
@r2.delete_route("/foo")
|
408
|
-
@route_set.recognize(
|
409
|
-
@r2.recognize(
|
417
|
+
@route_set.recognize(build_request(:path => "/foo")).path.route.destination.should == {:foo => "foo"}
|
418
|
+
@r2.recognize( build_request(:path => "/foo")).should == nil
|
410
419
|
end
|
411
420
|
|
412
421
|
|
@@ -106,6 +106,16 @@ describe "Usher (for Sinatra) route recognition" do
|
|
106
106
|
response.status.should == 200
|
107
107
|
response.body.should == "/hi-18"
|
108
108
|
end
|
109
|
+
|
110
|
+
it "should map route with complex params" do
|
111
|
+
@app.get('/hi/:foo/:bar/:baz(.:format)') { "/#{params[:foo]}/#{params[:bar]}/#{params[:baz]}/#{params[:format]}" }
|
112
|
+
response = @app.call_with_mock_request('/hi/foo/bar/baz')
|
113
|
+
response.status.should == 200
|
114
|
+
response.body.should == "/foo/bar/baz/"
|
115
|
+
response = @app.call_with_mock_request('/hi/foo/bar-bax/baz')
|
116
|
+
response.status.should == 200
|
117
|
+
response.body.should == "/foo/bar-bax/baz/"
|
118
|
+
end
|
109
119
|
end
|
110
120
|
|
111
121
|
describe "not found" do
|
@@ -128,5 +138,4 @@ describe "Usher (for Sinatra) route recognition" do
|
|
128
138
|
response.body.should_not match(/__sinatra__/)
|
129
139
|
end
|
130
140
|
end
|
131
|
-
|
132
141
|
end
|
data/spec/spec_helper.rb
CHANGED
data/usher.gemspec
CHANGED
@@ -5,7 +5,7 @@ require "base64"
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "usher"
|
8
|
-
s.version = "0.8.
|
8
|
+
s.version = "0.8.1"
|
9
9
|
s.authors = ["Daniel Neighman", "Daniel Vartanov", "Jakub Šťastný", "Joshua Hull", "Davide D'Agostino"].sort
|
10
10
|
s.homepage = "http://github.com/joshbuddy/usher"
|
11
11
|
s.summary = "Pure ruby general purpose router with interfaces for rails, rack, email or choose your own adventure"
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 8
|
8
|
-
-
|
9
|
-
version: 0.8.
|
8
|
+
- 1
|
9
|
+
version: 0.8.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Daniel Neighman
|
@@ -19,7 +19,7 @@ authors:
|
|
19
19
|
autorequire:
|
20
20
|
bindir: bin
|
21
21
|
cert_chain:
|
22
|
-
date: 2010-05-
|
22
|
+
date: 2010-05-20 00:00:00 -04:00
|
23
23
|
default_executable:
|
24
24
|
dependencies:
|
25
25
|
- !ruby/object:Gem::Dependency
|
@@ -142,6 +142,7 @@ files:
|
|
142
142
|
- spec/private/generate_spec.rb
|
143
143
|
- spec/private/generate_with_spec.rb
|
144
144
|
- spec/private/grapher_spec.rb
|
145
|
+
- spec/private/meta_spec.rb
|
145
146
|
- spec/private/parser_spec.rb
|
146
147
|
- spec/private/path_spec.rb
|
147
148
|
- spec/private/rack/dispatch_spec.rb
|