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