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