usher 0.7.5 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/usher/exceptions.rb +2 -0
- data/lib/usher/interface/rack.rb +8 -4
- data/lib/usher/interface/sinatra.rb +33 -5
- data/lib/usher/node/failed_response.rb +34 -0
- data/lib/usher/node/response.rb +6 -0
- data/lib/usher/node/root_ignoring_trailing_delimiters.rb +1 -1
- data/lib/usher/node.rb +10 -3
- data/lib/usher/route/variable.rb +1 -1
- data/lib/usher/splitter.rb +2 -2
- data/lib/usher/util/generate.rb +1 -1
- data/lib/usher/util/parser.rb +6 -14
- data/lib/usher.rb +11 -3
- data/spec/private/rack/dispatch_spec.rb +9 -0
- data/spec/private/recognize_spec.rb +11 -2
- data/spec/private/sinatra/recognize_spec.rb +12 -0
- data/spec/private/splitter_spec.rb +6 -6
- data/usher.gemspec +1 -1
- metadata +5 -4
data/lib/usher/exceptions.rb
CHANGED
@@ -7,4 +7,6 @@ class Usher
|
|
7
7
|
class MissingParameterException < RuntimeError; end
|
8
8
|
# Raised when a route is added with identical variable names and allow_identical_variable_names? is false
|
9
9
|
class MultipleParameterException < RuntimeError; end
|
10
|
+
# Raised when a route is added with two regex validators
|
11
|
+
class DoubleRegexpException < RuntimeError; end
|
10
12
|
end
|
data/lib/usher/interface/rack.rb
CHANGED
@@ -33,7 +33,7 @@ class Usher
|
|
33
33
|
if redirect_on_trailing_delimiters
|
34
34
|
options[:ignore_trailing_delimiters] = true
|
35
35
|
end
|
36
|
-
usher_options = {:request_methods => request_methods, :generator => generator, :allow_identical_variable_names => allow_identical_variable_names}
|
36
|
+
usher_options = {:request_methods => request_methods, :generator => generator, :allow_identical_variable_names => allow_identical_variable_names, :detailed_failure => true}
|
37
37
|
usher_options.merge!(options)
|
38
38
|
@router = Usher.new(usher_options)
|
39
39
|
@router.route_class = Rack::Route
|
@@ -120,12 +120,12 @@ class Usher
|
|
120
120
|
env[router_key] = self
|
121
121
|
request = ::Rack::Request.new(env)
|
122
122
|
response = @router.recognize(request, request.path_info)
|
123
|
-
if redirect_on_trailing_delimiters and response.only_trailing_delimiters and (request.get? || request.head?)
|
123
|
+
if response.succeeded? && redirect_on_trailing_delimiters and response.only_trailing_delimiters and (request.get? || request.head?)
|
124
124
|
response = ::Rack::Response.new
|
125
125
|
response.redirect(request.path_info[0, request.path_info.size - 1], 302)
|
126
126
|
response.finish
|
127
127
|
else
|
128
|
-
after_match(request, response) if response
|
128
|
+
after_match(request, response) if response.succeeded?
|
129
129
|
determine_respondant(response).call(env)
|
130
130
|
end
|
131
131
|
end
|
@@ -161,11 +161,15 @@ class Usher
|
|
161
161
|
#
|
162
162
|
# @api private
|
163
163
|
def determine_respondant(response)
|
164
|
-
usable_response = use_destinations? && response && response.destination
|
164
|
+
usable_response = response.succeeded? && use_destinations? && response && response.destination
|
165
165
|
if usable_response && response.destination.respond_to?(:call)
|
166
166
|
response.destination
|
167
167
|
elsif usable_response && response.destination.respond_to?(:args) && response.destination.args.first.respond_to?(:call)
|
168
168
|
response.args.first
|
169
|
+
elsif !response.succeeded? && response.request_method?
|
170
|
+
rack_response = ::Rack::Response.new("Method not allowed", 405)
|
171
|
+
rack_response['Allow'] = response.acceptable_responses_only_strings.join(", ")
|
172
|
+
proc { |env| rack_response.finish }
|
169
173
|
else
|
170
174
|
_app
|
171
175
|
end
|
@@ -23,10 +23,18 @@ class Usher
|
|
23
23
|
private
|
24
24
|
def route!(base=self.class, pass_block=nil)
|
25
25
|
if base.router and match = base.router.recognize(@request, @request.path_info)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
if match.succeeded?
|
27
|
+
@block_params = match.params.map { |p| p.last }
|
28
|
+
(@params ||= {}).merge!(match.params_as_hash)
|
29
|
+
pass_block = catch(:pass) do
|
30
|
+
route_eval(&match.destination)
|
31
|
+
end
|
32
|
+
elsif match.request_method?
|
33
|
+
route_eval {
|
34
|
+
response['Allow'] = match.acceptable_responses_only_strings.join(", ")
|
35
|
+
status 405
|
36
|
+
}
|
37
|
+
return
|
30
38
|
end
|
31
39
|
end
|
32
40
|
|
@@ -76,7 +84,8 @@ class Usher
|
|
76
84
|
:ignore_trailing_delimiters => true,
|
77
85
|
:generator => Usher::Util::Generators::URL.new,
|
78
86
|
:delimiters => ['/', '.', '-'],
|
79
|
-
:valid_regex => '[0-9A-Za-z\$_\+!\*\',]+'
|
87
|
+
:valid_regex => '[0-9A-Za-z\$_\+!\*\',]+',
|
88
|
+
:detailed_failure => true)
|
80
89
|
block_given? ? yield(@router) : @router
|
81
90
|
end
|
82
91
|
|
@@ -114,6 +123,25 @@ class Usher
|
|
114
123
|
</html>
|
115
124
|
HTML
|
116
125
|
end
|
126
|
+
error 405 do
|
127
|
+
content_type 'text/html'
|
128
|
+
|
129
|
+
(<<-HTML).gsub(/^ {17}/, '')
|
130
|
+
<!DOCTYPE html>
|
131
|
+
<html>
|
132
|
+
<head>
|
133
|
+
<style type="text/css">
|
134
|
+
body { text-align:center;font-family:helvetica,arial;font-size:22px;
|
135
|
+
color:#888;margin:20px}
|
136
|
+
#c {margin:0 auto;width:500px;text-align:left}
|
137
|
+
</style>
|
138
|
+
</head>
|
139
|
+
<body>
|
140
|
+
<h2>Sinatra sorta knows this ditty, but the request method is not allowed.</h2>
|
141
|
+
</body>
|
142
|
+
</html>
|
143
|
+
HTML
|
144
|
+
end
|
117
145
|
end
|
118
146
|
|
119
147
|
@_configured = true
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Usher
|
2
|
+
class Node
|
3
|
+
# The response from {Usher::Node::Root#lookup}. Adds some convenience methods for common parameter manipulation.
|
4
|
+
class FailedResponse < Struct.new(:last_matching_node, :fail_type, :fail_sub_type)
|
5
|
+
# The success of the response
|
6
|
+
# @return [Boolean] Always returns false
|
7
|
+
def succeeded?
|
8
|
+
false
|
9
|
+
end
|
10
|
+
|
11
|
+
def request_method?
|
12
|
+
fail_type == :request_method
|
13
|
+
end
|
14
|
+
|
15
|
+
def normal_or_greedy?
|
16
|
+
fail_type == :normal_or_greedy
|
17
|
+
end
|
18
|
+
|
19
|
+
def acceptable_responses
|
20
|
+
case fail_type
|
21
|
+
when :request_method
|
22
|
+
last_matching_node.request.keys
|
23
|
+
when :normal_or_greedy
|
24
|
+
(last_matching_node.greedy || []) + (last_matching_node.normal || [])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def acceptable_responses_only_strings
|
29
|
+
acceptable_responses.select{|r| r.is_a?(String)}
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/usher/node/response.rb
CHANGED
@@ -3,6 +3,12 @@ class Usher
|
|
3
3
|
# The response from {Usher::Node::Root#lookup}. Adds some convenience methods for common parameter manipulation.
|
4
4
|
class Response < Struct.new(:path, :params_as_array, :remaining_path, :matched_path, :only_trailing_delimiters)
|
5
5
|
|
6
|
+
# The success of the response
|
7
|
+
# @return [Boolean] Always returns true
|
8
|
+
def succeeded?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
6
12
|
# The params from recognition
|
7
13
|
# @return [Array<Symbol, String>] The parameters detected from recognition returned as an array of arrays.
|
8
14
|
def params
|
@@ -13,7 +13,7 @@ class Usher
|
|
13
13
|
if path.size > 1
|
14
14
|
new_path = path.gsub(@stripper, '')
|
15
15
|
response = lookup_without_stripping(request_object, new_path)
|
16
|
-
response.only_trailing_delimiters = (new_path.size != path.size) if response
|
16
|
+
response.only_trailing_delimiters = (new_path.size != path.size) if response && response.succeeded?
|
17
17
|
response
|
18
18
|
else
|
19
19
|
lookup_without_stripping(request_object, path)
|
data/lib/usher/node.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require File.join('usher', 'node', 'root')
|
2
2
|
require File.join('usher', 'node', 'root_ignoring_trailing_delimiters')
|
3
3
|
require File.join('usher', 'node', 'response')
|
4
|
+
require File.join('usher', 'node', 'failed_response')
|
4
5
|
|
5
6
|
class Usher
|
6
7
|
|
@@ -120,19 +121,25 @@ class Usher
|
|
120
121
|
route_candidates << ret
|
121
122
|
end
|
122
123
|
route_candidates.sort!{|r1, r2| r1.path.route.priority <=> r2.path.route.priority}
|
123
|
-
route_candidates.last
|
124
|
+
request_method_respond(route_candidates.last, request_method_type)
|
124
125
|
else
|
125
126
|
if specific_node = request[request_object.send(request_method_type)] and ret = specific_node.find(request_object, original_path, path.dup, params && params.dup)
|
126
127
|
ret
|
127
128
|
elsif general_node = request[nil] and ret = general_node.find(request_object, original_path, path.dup, params && params.dup)
|
128
|
-
ret
|
129
|
+
request_method_respond(ret, request_method_type)
|
130
|
+
else
|
131
|
+
request_method_respond(nil, request_method_type)
|
129
132
|
end
|
130
133
|
end
|
131
134
|
else
|
132
|
-
nil
|
135
|
+
route_set.detailed_failure? ? FailedResponse.new(self, :normal_or_greedy, nil) : nil
|
133
136
|
end
|
134
137
|
end
|
135
138
|
|
139
|
+
def request_method_respond(ret, request_method_respond)
|
140
|
+
ret || (route_set.detailed_failure? ? FailedResponse.new(self, :request_method, request_method_respond) : nil)
|
141
|
+
end
|
142
|
+
|
136
143
|
def activate_normal!
|
137
144
|
@normal ||= {}
|
138
145
|
end
|
data/lib/usher/route/variable.rb
CHANGED
@@ -12,7 +12,7 @@ class Usher
|
|
12
12
|
include Validator
|
13
13
|
def valid!(val)
|
14
14
|
begin
|
15
|
-
@validator.call(val)
|
15
|
+
@validator.call(val) or raise(ValidationException.new("#{val} does not conform to #{@validator}"))
|
16
16
|
rescue Exception => e
|
17
17
|
raise ValidationException.new("#{val} does not conform to #{@validator}, root cause #{e.inspect}")
|
18
18
|
end
|
data/lib/usher/splitter.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Usher
|
2
2
|
class Splitter
|
3
3
|
|
4
|
-
def self.
|
4
|
+
def self.new(delimiters_array)
|
5
5
|
delimiters = Delimiters.new(delimiters_array)
|
6
6
|
delimiters.any?{|d| d.size > 1} ?
|
7
7
|
MultiCharacterSplitterInstance.new(delimiters) :
|
@@ -13,7 +13,7 @@ class Usher
|
|
13
13
|
def initialize(delimiters)
|
14
14
|
@url_split_regex = Regexp.new("[^#{delimiters.regexp_char_class}]+|[#{delimiters.regexp_char_class}]")
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def split(path)
|
18
18
|
path.scan(@url_split_regex)
|
19
19
|
end
|
data/lib/usher/util/generate.rb
CHANGED
@@ -178,7 +178,7 @@ class Usher
|
|
178
178
|
when Array
|
179
179
|
v.each do |v_part|
|
180
180
|
extra_params_result << '&' unless extra_params_result.empty?
|
181
|
-
extra_params_result << Rack::Utils.escape(
|
181
|
+
extra_params_result << Rack::Utils.escape(k.to_s) << '%5B%5D=' << Rack::Utils.escape(v_part.to_s)
|
182
182
|
end
|
183
183
|
else
|
184
184
|
extra_params_result << '&' unless extra_params_result.empty?
|
data/lib/usher/util/parser.rb
CHANGED
@@ -6,16 +6,9 @@ class Usher
|
|
6
6
|
|
7
7
|
attr_reader :router
|
8
8
|
|
9
|
-
def
|
10
|
-
new(
|
11
|
-
router,
|
12
|
-
Regexp.new('((:|\*)?' + valid_regex + '|' + router.delimiters_regex + '|\(|\)|\||\{)')
|
13
|
-
)
|
14
|
-
end
|
15
|
-
|
16
|
-
def initialize(router, split_regex)
|
9
|
+
def initialize(router, valid_regex)
|
17
10
|
@router = router
|
18
|
-
@split_regex =
|
11
|
+
@split_regex = Regexp.new('((:|\*)?' + valid_regex + '|' + router.delimiters_regex + '|\(|\)|\||\{)')
|
19
12
|
@delimiters_regex = Regexp.new(router.delimiters_regex)
|
20
13
|
end
|
21
14
|
|
@@ -102,12 +95,10 @@ class Usher
|
|
102
95
|
end
|
103
96
|
|
104
97
|
case part[0]
|
105
|
-
when
|
106
|
-
|
107
|
-
current_group << Usher::Route::Variable::Glob.new(part[1, part.size - 1], nil, requirements && requirements[var_name])
|
108
|
-
when ?:
|
98
|
+
when ?*, ?:
|
99
|
+
variable_class = part[0] == ?* ? Usher::Route::Variable::Glob : Usher::Route::Variable::Single
|
109
100
|
var_name = part[1, part.size - 1].to_sym
|
110
|
-
current_group <<
|
101
|
+
current_group << variable_class.new(part[1, part.size - 1], requirements && requirements[var_name].is_a?(Regexp) ? requirements[var_name] : nil, requirements && requirements[var_name])
|
111
102
|
when ?{
|
112
103
|
pattern = ''
|
113
104
|
count = 1
|
@@ -131,6 +122,7 @@ class Usher
|
|
131
122
|
when ?: then Usher::Route::Variable::Single
|
132
123
|
end
|
133
124
|
variable_name = variable[0, variable.size - 1].to_sym
|
125
|
+
raise DoubleRegexpException.new("#{variable_name} has two regex validators, #{pattern} and #{requirements[variable_name]}") if requirements && requirements[variable_name] && requirements[variable_name].is_a?(Regexp)
|
134
126
|
current_group << variable_class.new(variable_name, Regexp.new(pattern), requirements && requirements[variable_name])
|
135
127
|
elsif simple
|
136
128
|
static = Usher::Route::Static::Greedy.new(pattern)
|
data/lib/usher.rb
CHANGED
@@ -56,7 +56,7 @@ class Usher
|
|
56
56
|
@routes = []
|
57
57
|
@grapher = Grapher.new(self)
|
58
58
|
@priority_lookups = false
|
59
|
-
@parser = Util::Parser.
|
59
|
+
@parser = Util::Parser.new(self, valid_regex)
|
60
60
|
end
|
61
61
|
|
62
62
|
# Creates a route set, with options
|
@@ -67,6 +67,7 @@ class Usher
|
|
67
67
|
# @option options [nil or Generator] :generator (nil) Take a look at `Usher::Util::Generators for examples.`.
|
68
68
|
# @option options [Boolean] :ignore_trailing_delimiters (false) Ignore trailing delimiters in recognizing paths.
|
69
69
|
# @option options [Boolean] :consider_destination_keys (false) When generating, and using hash destinations, you can have Usher use the destination hash to match incoming params.
|
70
|
+
# @option options [Boolean] :detailed_failure (false) When a route fails to match, return a {Node::FailedResponse} instead of a `nil`
|
70
71
|
# Example, you create a route with a destination of :controller => 'test', :action => 'action'. If you made a call to generator with :controller => 'test',
|
71
72
|
# :action => 'action', it would pick that route to use for generation.
|
72
73
|
# @option options [Boolean] :allow_identical_variable_names (true) When adding routes, allow identical variable names to be used.
|
@@ -79,6 +80,8 @@ class Usher
|
|
79
80
|
self.ignore_trailing_delimiters = options && options.key?(:ignore_trailing_delimiters) ? options.delete(:ignore_trailing_delimiters) : false
|
80
81
|
self.consider_destination_keys = options && options.key?(:consider_destination_keys) ? options.delete(:consider_destination_keys) : false
|
81
82
|
self.allow_identical_variable_names = options && options.key?(:allow_identical_variable_names) ? options.delete(:allow_identical_variable_names) : true
|
83
|
+
self.detailed_failure = options && options.key?(:detailed_failure) ? options.delete(:detailed_failure) : false
|
84
|
+
|
82
85
|
unless options.nil? || options.empty?
|
83
86
|
raise "unrecognized options -- #{options.keys.join(', ')}"
|
84
87
|
end
|
@@ -90,6 +93,11 @@ class Usher
|
|
90
93
|
@allow_identical_variable_names
|
91
94
|
end
|
92
95
|
|
96
|
+
# @return [Boolean] State of detailed_failure feature.
|
97
|
+
def detailed_failure?
|
98
|
+
@detailed_failure
|
99
|
+
end
|
100
|
+
|
93
101
|
# @return [Boolean] State of ignore_trailing_delimiters feature.
|
94
102
|
def ignore_trailing_delimiters?
|
95
103
|
@ignore_trailing_delimiters
|
@@ -320,7 +328,7 @@ class Usher
|
|
320
328
|
|
321
329
|
private
|
322
330
|
|
323
|
-
attr_accessor :request_methods, :ignore_trailing_delimiters, :consider_destination_keys, :allow_identical_variable_names
|
331
|
+
attr_accessor :request_methods, :ignore_trailing_delimiters, :consider_destination_keys, :allow_identical_variable_names, :detailed_failure
|
324
332
|
attr_reader :valid_regex
|
325
333
|
attr_writer :parser
|
326
334
|
|
@@ -340,7 +348,7 @@ class Usher
|
|
340
348
|
|
341
349
|
def valid_regex=(valid_regex)
|
342
350
|
@valid_regex = valid_regex
|
343
|
-
@splitter = Splitter.
|
351
|
+
@splitter = Splitter.new(self.delimiters)
|
344
352
|
@valid_regex
|
345
353
|
end
|
346
354
|
|
@@ -66,6 +66,15 @@ describe "Usher (for rack) route dispatching" do
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
+
it "should returns HTTP 405 if the method mis-matches" do
|
70
|
+
route_set.reset!
|
71
|
+
route_set.add('/sample', :conditions => {:request_method => 'POST'}).to(@app)
|
72
|
+
route_set.add('/sample', :conditions => {:request_method => 'PUT'}).to(@app)
|
73
|
+
response = route_set.call_with_mock_request('/sample', 'GET')
|
74
|
+
response.status.should eql(405)
|
75
|
+
response['Allow'].should == 'POST, PUT'
|
76
|
+
end
|
77
|
+
|
69
78
|
it "should returns HTTP 404 if route doesn't exist" do
|
70
79
|
response = route_set.call_with_mock_request("/not-existing-url")
|
71
80
|
response.status.should eql(404)
|
@@ -288,9 +288,18 @@ describe "Usher route recognition" do
|
|
288
288
|
result.params.should == [[:name, "homer"],[:surname, "simpson"]]
|
289
289
|
end
|
290
290
|
|
291
|
-
it "should
|
291
|
+
it "should use a regexp requirement as part of recognition" do
|
292
292
|
@route_set.add_route('/products/show/:id', :id => /\d+/, :conditions => {:method => 'get'})
|
293
|
-
|
293
|
+
@route_set.recognize(build_request({:method => 'get', :path => '/products/show/qweasd', :domain => 'admin.host.com'})).should be_nil
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should use a inline regexp and proc requirement as part of recognition" do
|
297
|
+
@route_set.add_route('/products/show/{:id,^\d+$}', :id => proc{|v| v == '123'}, :conditions => {:method => 'get'})
|
298
|
+
proc { @route_set.recognize(build_request({:method => 'get', :path => '/products/show/234', :domain => 'admin.host.com'}))}.should raise_error(Usher::ValidationException)
|
299
|
+
end
|
300
|
+
|
301
|
+
it "should not allow the use of an inline regexp and regexp requirement as part of recognition" do
|
302
|
+
proc { @route_set.add_route('/products/show/{:id,^\d+$}', :id => /\d+/, :conditions => {:method => 'get'}) }.should raise_error(Usher::DoubleRegexpException)
|
294
303
|
end
|
295
304
|
|
296
305
|
it "should recognize multiple optional parts" do
|
@@ -117,4 +117,16 @@ describe "Usher (for Sinatra) route recognition" do
|
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
120
|
+
describe "method not allowed" do
|
121
|
+
|
122
|
+
it "should correctly generate a not found page without images and return a 405" do
|
123
|
+
@app.post('/bar') { 'found' }
|
124
|
+
@app.put('/bar') { 'found' }
|
125
|
+
response = @app.call_with_mock_request('/bar')
|
126
|
+
response.status.should == 405
|
127
|
+
response.headers['Allow'].should == 'POST, PUT'
|
128
|
+
response.body.should_not match(/__sinatra__/)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
120
132
|
end
|
@@ -4,37 +4,37 @@ require "usher"
|
|
4
4
|
describe Usher::Splitter, "#split" do
|
5
5
|
describe "when there are single-character delimiters" do
|
6
6
|
it "should split correctly" do
|
7
|
-
Usher::Splitter.
|
7
|
+
Usher::Splitter.new(['.', '/']).split('/one/two.three/').should == ['/', 'one', '/', 'two', '.', 'three', '/']
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
11
|
describe "when there are multi-character delimiters" do
|
12
12
|
it "should split correctly" do
|
13
|
-
Usher::Splitter.
|
13
|
+
Usher::Splitter.new(['/', '%28', '%29']).split('/one%28two%29three/').should == ['/', 'one', '%28', 'two', '%29', 'three', '/']
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
17
|
describe "when there is no delimiter in the end" do
|
18
18
|
it "should split correctly" do
|
19
|
-
Usher::Splitter.
|
19
|
+
Usher::Splitter.new(['.', '/']).split('/one/two.three').should == ['/', 'one', '/', 'two', '.', 'three']
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
describe "when there is no delimiter in the beginning" do
|
24
24
|
it "should split correctly" do
|
25
|
-
Usher::Splitter.
|
25
|
+
Usher::Splitter.new(['.', '/']).split('one/two.three/').should == ['one', '/', 'two', '.', 'three', '/']
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
29
|
describe "when delimiters are consecutive" do
|
30
30
|
it "should split correctly" do
|
31
|
-
Usher::Splitter.
|
31
|
+
Usher::Splitter.new(['/', '!']).split('/cheese/!parmesan').should == ['/', 'cheese', '/', '!', 'parmesan']
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
35
|
describe "when delimiters contain escaped characters" do
|
36
36
|
it "should split correctly" do
|
37
|
-
Usher::Splitter.
|
37
|
+
Usher::Splitter.new(['/', '\(', '\)']).split('/cheese(parmesan)').should == ['/', 'cheese', '(', 'parmesan', ')']
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
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
|
+
s.version = "0.8.0"
|
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
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 8
|
8
|
+
- 0
|
9
|
+
version: 0.8.0
|
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-05 00:00:00 -04:00
|
23
23
|
default_executable:
|
24
24
|
dependencies:
|
25
25
|
- !ruby/object:Gem::Dependency
|
@@ -121,6 +121,7 @@ files:
|
|
121
121
|
- lib/usher/interface/sinatra.rb
|
122
122
|
- lib/usher/interface/text.rb
|
123
123
|
- lib/usher/node.rb
|
124
|
+
- lib/usher/node/failed_response.rb
|
124
125
|
- lib/usher/node/response.rb
|
125
126
|
- lib/usher/node/root.rb
|
126
127
|
- lib/usher/node/root_ignoring_trailing_delimiters.rb
|