usher 0.7.4 → 0.7.5
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/benchmarks/recognition_bm.rb +1 -1
- data/lib/usher/delimiters.rb +1 -1
- data/lib/usher/grapher.rb +35 -38
- data/lib/usher/interface.rb +7 -5
- data/lib/usher/interface/rack.rb +1 -1
- data/lib/usher/interface/rack/builder.rb +22 -5
- data/lib/usher/interface/rack/middleware.rb +7 -3
- data/lib/usher/interface/rack/route.rb +11 -5
- data/lib/usher/node.rb +29 -15
- data/lib/usher/node/response.rb +8 -3
- data/lib/usher/node/root.rb +1 -1
- data/spec/private/path_spec.rb +0 -6
- data/spec/private/rack/route_spec.rb +16 -0
- data/spec/private/sinatra/recognize_spec.rb +0 -20
- data/usher.gemspec +1 -1
- metadata +2 -5
- data/lib/usher/interface/email.rb +0 -27
- data/lib/usher/interface/merb.rb +0 -61
- data/spec/private/email/recognize_spec.rb +0 -37
data/README.markdown
CHANGED
data/lib/usher/delimiters.rb
CHANGED
data/lib/usher/grapher.rb
CHANGED
@@ -2,23 +2,26 @@ class Usher
|
|
2
2
|
# Find nearest matching routes based on parameter keys.
|
3
3
|
class Grapher
|
4
4
|
|
5
|
-
attr_reader :
|
6
|
-
|
5
|
+
attr_reader :router, :orders, :key_count, :cache, :significant_keys
|
6
|
+
|
7
|
+
# @param router An Usher instance you wish to create a grapher for.
|
7
8
|
def initialize(router)
|
8
9
|
@router = router
|
9
10
|
reset!
|
10
11
|
end
|
11
12
|
|
12
13
|
# Add route for matching
|
14
|
+
# @param route [Route] Add route for matching against
|
13
15
|
def add_route(route)
|
14
|
-
|
15
|
-
|
16
|
+
@cache.clear
|
17
|
+
process_route(route)
|
16
18
|
end
|
17
19
|
|
18
20
|
# Finds a matching path based on params hash
|
21
|
+
# @param params [Hash<Symbol, String>] A hash of parameters you wish to use in matching.
|
22
|
+
# @return [nil, Route] Returns the matching {Usher::Route::Path} or nil if no path matches.
|
19
23
|
def find_matching_path(params)
|
20
24
|
unless params.empty?
|
21
|
-
process_routes
|
22
25
|
set = params.keys & significant_keys
|
23
26
|
if cached = cache[set]
|
24
27
|
return cached
|
@@ -40,55 +43,49 @@ class Usher
|
|
40
43
|
end
|
41
44
|
|
42
45
|
private
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
dynamic_parts_with_defaults = path.dynamic_parts.select{|part| part.default_value }.map{|dp| dp.name}
|
54
|
-
dynamic_parts_without_defaults = path.dynamic_parts.select{|part| !part.default_value }.map{|dp| dp.name}
|
46
|
+
# Processes route.
|
47
|
+
# @param route [Route] Processes the route for use in the grapher.
|
48
|
+
def process_route(route)
|
49
|
+
route.paths.each do |path|
|
50
|
+
if path.dynamic?
|
51
|
+
path.dynamic_keys.each do |k|
|
52
|
+
orders[path.dynamic_keys.size][k] << path
|
53
|
+
key_count[k] += 1
|
54
|
+
end
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
dynamic_parts_with_defaults.each_with_index do |dp, index|
|
59
|
-
current_set << dp unless (index & i) == 0
|
60
|
-
end
|
56
|
+
dynamic_parts_with_defaults = path.dynamic_parts.select{|part| part.default_value }.map{|dp| dp.name}
|
57
|
+
dynamic_parts_without_defaults = path.dynamic_parts.select{|part| !part.default_value }.map{|dp| dp.name}
|
61
58
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
59
|
+
(1...(2 ** (dynamic_parts_with_defaults.size))).each do |i|
|
60
|
+
current_set = dynamic_parts_without_defaults.dup
|
61
|
+
dynamic_parts_with_defaults.each_with_index do |dp, index|
|
62
|
+
current_set << dp unless (index & i) == 0
|
66
63
|
end
|
67
64
|
|
68
|
-
|
69
|
-
|
70
|
-
if router.consider_destination_keys?
|
71
|
-
path.route.destination_keys.each do |k|
|
72
|
-
orders[path.route.destination_keys.size][k] << path
|
65
|
+
current_set.each do |k|
|
66
|
+
orders[current_set.size][k] << path
|
73
67
|
key_count[k] += 1
|
74
68
|
end
|
75
69
|
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
if router.consider_destination_keys?
|
74
|
+
path.route.destination_keys.each do |k|
|
75
|
+
orders[path.route.destination_keys.size][k] << path
|
76
|
+
key_count[k] += 1
|
77
|
+
end
|
76
78
|
end
|
77
79
|
end
|
78
|
-
@
|
80
|
+
@significant_keys = key_count.keys.uniq
|
79
81
|
end
|
80
82
|
|
81
|
-
|
82
|
-
@significant_keys ||= key_count.keys.uniq
|
83
|
-
end
|
84
|
-
|
83
|
+
# Resets the router to its initial state.
|
85
84
|
def reset!
|
86
85
|
@significant_keys = nil
|
87
86
|
@orders = Hash.new{|h,k| h[k] = Hash.new{|h2, k2| h2[k2] = []}}
|
88
87
|
@key_count = Hash.new(0)
|
89
88
|
@cache = {}
|
90
|
-
@routes = []
|
91
|
-
@processed = false
|
92
89
|
end
|
93
90
|
|
94
91
|
end
|
data/lib/usher/interface.rb
CHANGED
@@ -2,22 +2,24 @@ class Usher
|
|
2
2
|
# Various interfaces for Usher.
|
3
3
|
module Interface
|
4
4
|
|
5
|
-
autoload(:Email, File.join(File.dirname(__FILE__), 'interface', 'email'))
|
6
|
-
autoload(:Merb, File.join(File.dirname(__FILE__), 'interface', 'merb'))
|
7
5
|
autoload(:Rails20, File.join(File.dirname(__FILE__), 'interface', 'rails20'))
|
8
6
|
autoload(:Rails22, File.join(File.dirname(__FILE__), 'interface', 'rails22'))
|
9
7
|
autoload(:Rails23, File.join(File.dirname(__FILE__), 'interface', 'rails23'))
|
10
8
|
autoload(:Rack, File.join(File.dirname(__FILE__), 'interface', 'rack'))
|
11
9
|
autoload(:Rails3, File.join(File.dirname(__FILE__), 'interface', 'rails3'))
|
12
|
-
autoload(:Text, File.join(File.dirname(__FILE__), 'interface', 'text'))
|
13
10
|
autoload(:Sinatra, File.join(File.dirname(__FILE__), 'interface', 'sinatra'))
|
14
|
-
|
11
|
+
|
12
|
+
# Returns the appropriate interface class for a given name.
|
13
|
+
# @param name [Symbol, String] The interface you wish to load. This can be `:rails20`, `:rails22`, `:rails23`, `:rack`, `:rails3` or `:sinatra`
|
15
14
|
def self.class_for(name)
|
16
15
|
Usher::Interface.const_get(name.to_s.split(/_/).map{|e| e.capitalize}.join) or
|
17
16
|
raise ArgumentError, "Interface #{name.inspect} doesn't exist."
|
18
17
|
end
|
19
18
|
|
20
|
-
#
|
19
|
+
# Returns the appropriate interface class for a given name.
|
20
|
+
# @param name [Symbol, String] The interface you wish to load. This can be `:rails20`, `:rails22`, `:rails23`, `:rack`, `:rails3` or `:sinatra`
|
21
|
+
# @param args [Object] Any additional parameters the interface wishes to recieve
|
22
|
+
# @return [Object] An intatiated interface
|
21
23
|
def self.for(name, *args, &block)
|
22
24
|
class_for(name).new(*args, &block)
|
23
25
|
end
|
data/lib/usher/interface/rack.rb
CHANGED
@@ -120,7 +120,7 @@ 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?
|
123
|
+
if 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
|
@@ -1,35 +1,52 @@
|
|
1
1
|
class Usher
|
2
2
|
module Interface
|
3
3
|
class Rack
|
4
|
-
# Replacement for
|
4
|
+
# Replacement for {Rack::Builder} which using Usher to map requests instead of a simple Hash.
|
5
5
|
# As well, add convenience methods for the request methods.
|
6
|
-
#
|
7
6
|
class Builder < ::Rack::Builder
|
8
7
|
def initialize(&block)
|
9
8
|
@usher = Usher::Interface::Rack.new
|
10
9
|
super
|
11
10
|
end
|
12
|
-
|
11
|
+
|
12
|
+
# Maps a path to a block.
|
13
|
+
# @param path [String] Path to map to.
|
14
|
+
# @param options [Hash] Options for added path.
|
15
|
+
# @see Usher#add_route
|
13
16
|
def map(path, options = nil, &block)
|
14
17
|
@usher.add(path, options).to(&block)
|
15
18
|
@ins << @usher unless @ins.last == @usher
|
16
19
|
end
|
17
20
|
|
18
|
-
#
|
19
|
-
#
|
21
|
+
# Maps a path with request methods `HEAD` and `GET` to a block.
|
22
|
+
# @param path [String] Path to map to.
|
23
|
+
# @param options [Hash] Options for added path.
|
24
|
+
# @see Usher#add_route
|
20
25
|
def get(path, options = nil, &block)
|
21
26
|
self.map(path, options.merge!(:conditions => {:request_method => "HEAD"}), &block)
|
22
27
|
self.map(path, options.merge!(:conditions => {:request_method => "GET"}), &block)
|
23
28
|
end
|
24
29
|
|
30
|
+
# Maps a path with request methods `POST` to a block.
|
31
|
+
# @param path [String] Path to map to.
|
32
|
+
# @param options [Hash] Options for added path.
|
33
|
+
# @see Usher#add_route
|
25
34
|
def post(path, options = nil, &block)
|
26
35
|
self.map(path, options.merge!(:conditions => {:request_method => "POST"}), &block)
|
27
36
|
end
|
28
37
|
|
38
|
+
# Maps a path with request methods `PUT` to a block.
|
39
|
+
# @param path [String] Path to map to.
|
40
|
+
# @param options [Hash] Options for added path.
|
41
|
+
# @see Usher#add_route
|
29
42
|
def put(path, options = nil, &block)
|
30
43
|
self.map(path, options.merge!(:conditions => {:request_method => "PUT"}), &block)
|
31
44
|
end
|
32
45
|
|
46
|
+
# Maps a path with request methods `DELETE` to a block.
|
47
|
+
# @param path [String] Path to map to.
|
48
|
+
# @param options [Hash] Options for added path.
|
49
|
+
# @see Usher#add_route
|
33
50
|
def delete(path, options = nil, &block)
|
34
51
|
self.map(path, options.merge!(:conditions => {:request_method => "DELETE"}), &block)
|
35
52
|
end
|
@@ -2,15 +2,19 @@ class Usher
|
|
2
2
|
module Interface
|
3
3
|
class Rack
|
4
4
|
# Middleware for using Usher's rack interface to recognize the request, then, pass on to the next application.
|
5
|
-
# Values are stored in
|
6
|
-
#
|
5
|
+
# Values are stored in `env` normally. The details of that storage is in the Rack interface itself.
|
6
|
+
# @see Usher::Interface::Rack
|
7
7
|
class Middleware
|
8
|
-
|
8
|
+
|
9
|
+
# @param app [#call] Application to call next
|
10
|
+
# @param router [Usher::Interface::Rack] The router call first before calling the next application
|
9
11
|
def initialize(app, router)
|
10
12
|
@app = app
|
11
13
|
@router = router
|
12
14
|
end
|
13
15
|
|
16
|
+
# @param env [Hash] The environment hash
|
17
|
+
# @return [#each] The application's return
|
14
18
|
def call(env)
|
15
19
|
@router.call(env)
|
16
20
|
@app.call(env)
|
@@ -1,21 +1,27 @@
|
|
1
1
|
class Usher
|
2
2
|
module Interface
|
3
3
|
class Rack
|
4
|
+
# Route specific for Rack with redirection support built in.
|
4
5
|
class Route < Usher::Route
|
5
6
|
|
6
|
-
|
7
|
-
|
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"
|
11
11
|
end
|
12
|
-
|
12
|
+
to { |env|
|
13
|
+
params = env[Usher::Interface::Rack::ENV_KEY_PARAMS]
|
13
14
|
response = ::Rack::Response.new
|
14
|
-
response.redirect(path, status)
|
15
|
+
response.redirect(eval(%|"#{path}"|), status)
|
15
16
|
response.finish
|
16
|
-
|
17
|
+
}
|
17
18
|
self
|
18
19
|
end
|
20
|
+
|
21
|
+
def serves_static_from(root)
|
22
|
+
match_partially!
|
23
|
+
@destination = ::Rack::File.new(root)
|
24
|
+
end
|
19
25
|
end
|
20
26
|
end
|
21
27
|
end
|
data/lib/usher/node.rb
CHANGED
@@ -3,6 +3,19 @@ require File.join('usher', 'node', 'root_ignoring_trailing_delimiters')
|
|
3
3
|
require File.join('usher', 'node', 'response')
|
4
4
|
|
5
5
|
class Usher
|
6
|
+
|
7
|
+
# The node class used to walk the tree looking for a matching route. The node has three different things that it looks for.
|
8
|
+
# ## Normal
|
9
|
+
# The normal hash is used to normally find matching parts. As well, the reserved key, `nil` is used to denote a variable match.
|
10
|
+
# ## Greedy
|
11
|
+
# The greedy hash is used when you want to match on the entire path. This match can trancend delimiters (unlike the normal match)
|
12
|
+
# and match as much of the path as needed.
|
13
|
+
# ## Request
|
14
|
+
# The request hash is used to find request method restrictions after the entire path has been consumed.
|
15
|
+
#
|
16
|
+
# Once the node finishes looking for matches, it looks for a `terminates` on the node that is usable. If it finds one, it wraps it into a {Node::Response}
|
17
|
+
# and returns that. All actual matching though should normally be done off of {Node::Root#lookup}
|
18
|
+
# @see Root
|
6
19
|
class Node
|
7
20
|
|
8
21
|
attr_reader :normal, :greedy, :request
|
@@ -12,6 +25,21 @@ class Usher
|
|
12
25
|
@parent, @value = parent, value
|
13
26
|
end
|
14
27
|
|
28
|
+
def inspect
|
29
|
+
out = ''
|
30
|
+
out << " " * depth
|
31
|
+
out << "#{terminates? ? '* ' : ''}#{depth}: #{value.inspect}\n"
|
32
|
+
[:normal, :greedy, :request].each do |node_type|
|
33
|
+
send(node_type).each do |k,v|
|
34
|
+
out << (" " * (depth + 1)) << "#{node_type.to_s[0].chr} #{k.inspect} ==> \n" << v.inspect
|
35
|
+
end if send(node_type)
|
36
|
+
end
|
37
|
+
out
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
|
15
43
|
def depth
|
16
44
|
@depth ||= parent.is_a?(Node) ? parent.depth + 1 : 0
|
17
45
|
end
|
@@ -40,20 +68,6 @@ class Usher
|
|
40
68
|
@route_set ||= root.route_set
|
41
69
|
end
|
42
70
|
|
43
|
-
def inspect
|
44
|
-
out = ''
|
45
|
-
out << " " * depth
|
46
|
-
out << "#{terminates? ? '* ' : ''}#{depth}: #{value.inspect}\n"
|
47
|
-
[:normal, :greedy, :request].each do |node_type|
|
48
|
-
send(node_type).each do |k,v|
|
49
|
-
out << (" " * (depth + 1)) << "#{node_type.to_s[0].chr} #{k.inspect} ==> \n" << v.inspect
|
50
|
-
end if send(node_type)
|
51
|
-
end
|
52
|
-
out
|
53
|
-
end
|
54
|
-
|
55
|
-
protected
|
56
|
-
|
57
71
|
def find(request_object, original_path, path, params = [])
|
58
72
|
# terminates or is partial
|
59
73
|
|
@@ -72,7 +86,7 @@ class Usher
|
|
72
86
|
case child_node.value
|
73
87
|
when String
|
74
88
|
when Route::Variable::Single
|
75
|
-
variable = child_node.value
|
89
|
+
variable = child_node.value # get the variable
|
76
90
|
variable.valid!(part) # do a validity check
|
77
91
|
until path.empty? || (variable.look_ahead === path.first) # variables have a look ahead notion,
|
78
92
|
next_path_part = path.shift # and until they are satified,
|
data/lib/usher/node/response.rb
CHANGED
@@ -1,21 +1,26 @@
|
|
1
1
|
class Usher
|
2
2
|
class Node
|
3
|
-
|
4
|
-
|
5
|
-
attr_accessor :only_trailing_delimiters
|
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)
|
6
5
|
|
6
|
+
# The params from recognition
|
7
|
+
# @return [Array<Symbol, String>] The parameters detected from recognition returned as an array of arrays.
|
7
8
|
def params
|
8
9
|
@params ||= path.convert_params_array(params_as_array)
|
9
10
|
end
|
10
11
|
|
12
|
+
# @return [Boolean] The state of partial matching
|
11
13
|
def partial_match?
|
12
14
|
!remaining_path.nil?
|
13
15
|
end
|
14
16
|
|
17
|
+
# The params from recognition
|
18
|
+
# @return [Hash<Symbol, String>] The parameters detected from recognition returned as a hash.
|
15
19
|
def params_as_hash
|
16
20
|
@params_as_hash ||= params_as_array.inject({}){|hash, val| hash[path.dynamic_keys[hash.size]] = val; hash}
|
17
21
|
end
|
18
22
|
|
23
|
+
# @return [Object] The destination assigned to the matching enclosed path.
|
19
24
|
def destination
|
20
25
|
path && path.route.destination
|
21
26
|
end
|
data/lib/usher/node/root.rb
CHANGED
data/spec/private/path_spec.rb
CHANGED
@@ -45,12 +45,6 @@ describe "Usher route adding" do
|
|
45
45
|
route_set.named_routes.size == 0
|
46
46
|
end
|
47
47
|
|
48
|
-
it "should calculate depths for nodes" do
|
49
|
-
route_set.add_named_route(:route, '/bad/route/three/four')
|
50
|
-
route_set.root.depth.should == 0
|
51
|
-
route_set.root.normal['/'].depth.should == 1
|
52
|
-
end
|
53
|
-
|
54
48
|
describe "merging paths" do
|
55
49
|
before do
|
56
50
|
@r1 = route_set.add_route("/foo/bar")
|
@@ -21,6 +21,22 @@ describe "Rack interface extensions for Usher::Route" do
|
|
21
21
|
status, headers, body = @route_set.call(@env)
|
22
22
|
headers["Location"].should eql("/")
|
23
23
|
end
|
24
|
+
|
25
|
+
it "should redirect '/:id.html' to '/:id'" do
|
26
|
+
@route_set.get("/:id.html").redirect('/#{params[:id]}')
|
27
|
+
@env = Rack::MockRequest.env_for("/123.html")
|
28
|
+
status, headers, body = @route_set.call(@env)
|
29
|
+
headers["Location"].should eql("/123")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "static file serving" do
|
34
|
+
it "should serve from a static directory" do
|
35
|
+
@route_set.get("/static").serves_static_from(File.dirname(__FILE__))
|
36
|
+
@env = Rack::MockRequest.env_for("/static/#{File.basename(__FILE__)}")
|
37
|
+
status, headers, body = @route_set.call(@env)
|
38
|
+
body.path.should == File.join(File.dirname(__FILE__), File.basename(__FILE__))
|
39
|
+
end
|
24
40
|
end
|
25
41
|
|
26
42
|
describe "chaining" do
|
@@ -117,24 +117,4 @@ describe "Usher (for Sinatra) route recognition" do
|
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
120
|
-
describe "recognize paths" do
|
121
|
-
|
122
|
-
it "should recognize basic routes" do
|
123
|
-
pending "undefined method `request_method' for nil:NilClass"
|
124
|
-
@app.get("/foo/:bar") { "foo" }
|
125
|
-
@app.router.recognize_path("/foo/:bar").should == nil
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
describe "priority" do
|
130
|
-
|
131
|
-
it "should set correctly priorities" do
|
132
|
-
pending "match the wrong route"
|
133
|
-
@app.get("/:id/:right", :priority => 10) { "right" }
|
134
|
-
@app.get("/:id/:wrong") { "wrong" }
|
135
|
-
response = @app.call_with_mock_request('/foo/bar')
|
136
|
-
response.status.should == 200
|
137
|
-
response.body.should == "right"
|
138
|
-
end
|
139
|
-
end
|
140
120
|
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.7.
|
8
|
+
s.version = "0.7.5"
|
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
|
- 7
|
8
|
-
-
|
9
|
-
version: 0.7.
|
8
|
+
- 5
|
9
|
+
version: 0.7.5
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Daniel Neighman
|
@@ -108,8 +108,6 @@ files:
|
|
108
108
|
- lib/usher/exceptions.rb
|
109
109
|
- lib/usher/grapher.rb
|
110
110
|
- lib/usher/interface.rb
|
111
|
-
- lib/usher/interface/email.rb
|
112
|
-
- lib/usher/interface/merb.rb
|
113
111
|
- lib/usher/interface/rack.rb
|
114
112
|
- lib/usher/interface/rack/builder.rb
|
115
113
|
- lib/usher/interface/rack/middleware.rb
|
@@ -140,7 +138,6 @@ files:
|
|
140
138
|
- lib/usher/util/rails.rb
|
141
139
|
- spec/private/delimiters_spec.rb
|
142
140
|
- spec/private/destination_spec.rb
|
143
|
-
- spec/private/email/recognize_spec.rb
|
144
141
|
- spec/private/generate_spec.rb
|
145
142
|
- spec/private/generate_with_spec.rb
|
146
143
|
- spec/private/grapher_spec.rb
|
@@ -1,27 +0,0 @@
|
|
1
|
-
class Usher
|
2
|
-
module Interface
|
3
|
-
class Email
|
4
|
-
|
5
|
-
def initialize(&blk)
|
6
|
-
@routes = Usher.new(:delimiters => ['@', '-', '.'], :valid_regex => '[\+a-zA-Z0-9]+')
|
7
|
-
instance_eval(&blk) if blk
|
8
|
-
end
|
9
|
-
|
10
|
-
def for(path, &block)
|
11
|
-
@routes.add_route(path).to(block)
|
12
|
-
end
|
13
|
-
|
14
|
-
def reset!
|
15
|
-
@routes.reset!
|
16
|
-
end
|
17
|
-
|
18
|
-
def act(email)
|
19
|
-
response = @routes.recognize(email, email)
|
20
|
-
if response.path
|
21
|
-
response.path.route.destination.call(response.params_as_hash)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
data/lib/usher/interface/merb.rb
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
require 'merb-core'
|
2
|
-
require 'merb-core/dispatch/router/behavior'
|
3
|
-
|
4
|
-
class Usher
|
5
|
-
module Interface
|
6
|
-
class Merb
|
7
|
-
|
8
|
-
# merb does everything with class methods.
|
9
|
-
|
10
|
-
@root_behavior = ::Merb::Router::Behavior.new.defaults(:action => "index")
|
11
|
-
|
12
|
-
class << self
|
13
|
-
attr_accessor :root_behavior
|
14
|
-
|
15
|
-
UsherRoutes = Usher.new
|
16
|
-
|
17
|
-
def prepare(first = [], last = [], &block)
|
18
|
-
@routes = []
|
19
|
-
root_behavior._with_proxy(&block)
|
20
|
-
@routes = first + @routes + last
|
21
|
-
compile
|
22
|
-
self
|
23
|
-
end
|
24
|
-
|
25
|
-
def compile
|
26
|
-
routes.each do |r|
|
27
|
-
r.segments
|
28
|
-
end
|
29
|
-
|
30
|
-
#puts r.inspect; UsherRoutes.add_route(r) }
|
31
|
-
#routes.each {|r| }
|
32
|
-
end
|
33
|
-
|
34
|
-
def named_routes
|
35
|
-
UsherRoutes.named_routes
|
36
|
-
end
|
37
|
-
|
38
|
-
def routes
|
39
|
-
UsherRoutes.routes
|
40
|
-
end
|
41
|
-
|
42
|
-
def route_for(request)
|
43
|
-
p request
|
44
|
-
p UsherRoutes.tree
|
45
|
-
UsherRoutes.recognize(request)
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
49
|
-
|
50
|
-
#class BootLoader < ::Merb::BootLoader
|
51
|
-
#end
|
52
|
-
|
53
|
-
def load_into_merb!
|
54
|
-
::Merb.send(:remove_const, "Router")
|
55
|
-
::Merb.const_set("Router", Usher::Interface::MerbInterface)
|
56
|
-
#::Merb::BootLoader.const_set("Router", Usher::Interface::Merb::BootLoader)
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
|
2
|
-
require "usher"
|
3
|
-
|
4
|
-
def build_email_mock(email)
|
5
|
-
request = mock "Request"
|
6
|
-
request.should_receive(:email).any_number_of_times.and_return(email)
|
7
|
-
request
|
8
|
-
end
|
9
|
-
|
10
|
-
describe "Usher (for email) route recognition" do
|
11
|
-
|
12
|
-
before(:each) do
|
13
|
-
@route_set = Usher::Interface.for(:email)
|
14
|
-
end
|
15
|
-
|
16
|
-
it "should recognize a simple request" do
|
17
|
-
receiver = mock('receiver')
|
18
|
-
receiver.should_receive(:action).with({}).exactly(1)
|
19
|
-
@route_set.for('joshbuddy@gmail.com') { |params| receiver.action(params) }
|
20
|
-
@route_set.act('joshbuddy@gmail.com')
|
21
|
-
end
|
22
|
-
|
23
|
-
it "should recognize a wildcard domain" do
|
24
|
-
receiver = mock('receiver')
|
25
|
-
receiver.should_receive(:action).with({:domain => 'gmail.com'}).exactly(1)
|
26
|
-
@route_set.for('joshbuddy@{!domain,.*}') { |params| receiver.action(params) }
|
27
|
-
@route_set.act('joshbuddy@gmail.com')
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should recognize a complex email" do
|
31
|
-
receiver = mock('receiver')
|
32
|
-
receiver.should_receive(:action).with({:subject => 'sub+ect', :id => '123', :sid => '456', :tok => 'sdqwe123ae', :domain => 'mydomain.org'}).exactly(1)
|
33
|
-
@route_set.for(':subject.{!id,\d+}-{!sid,\d+}-{!tok,\w+}@{!domain,.*}') { |params| receiver.action(params) }
|
34
|
-
@route_set.act('sub+ect.123-456-sdqwe123ae@mydomain.org')
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|