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 CHANGED
@@ -30,7 +30,7 @@ Any probably more!
30
30
 
31
31
  From the rdoc:
32
32
 
33
- Creates a route from +path+ and +options+
33
+ Creates a route from `path` and `options`
34
34
 
35
35
  ### `path`
36
36
  A path consists a mix of dynamic and static parts delimited by `/`
@@ -8,7 +8,7 @@ u.add_route('/simple/again')
8
8
  u.add_route('/simple/again/and/again')
9
9
  u.add_route('/dynamic/:variable')
10
10
  u.add_route('/rails/:controller/:action/:id')
11
- u.add_route('/greedy/{!:greed,.*}')
11
+ u.add_route('/greedy/{!greed,.*}')
12
12
 
13
13
  TIMES = 50_000
14
14
 
@@ -5,7 +5,7 @@ class Usher
5
5
  attr_reader :unescaped
6
6
 
7
7
  # Creates a list of delimiters
8
- # @param arr [Array<String>] delimters to use
8
+ # @param ary [Array<String>] delimters to use
9
9
  def initialize(ary)
10
10
  super ary
11
11
  @unescaped = self.map do |delimiter|
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 :routes, :router, :orders, :key_count, :cache
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
- reset! if @processed
15
- routes << route
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
- def process_routes
44
- return if @processed
45
- routes.each do |route|
46
- route.paths.each do |path|
47
- if path.dynamic?
48
- path.dynamic_keys.each do |k|
49
- orders[path.dynamic_keys.size][k] << path
50
- key_count[k] += 1
51
- end
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
- (1...(2 ** (dynamic_parts_with_defaults.size))).each do |i|
57
- current_set = dynamic_parts_without_defaults.dup
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
- current_set.each do |k|
63
- orders[current_set.size][k] << path
64
- key_count[k] += 1
65
- end
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
- end
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
- @processed = true
80
+ @significant_keys = key_count.keys.uniq
79
81
  end
80
82
 
81
- def significant_keys
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
@@ -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
- # Usher::Interface.for(:rack, &block)
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
@@ -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 <tt>Rack::Builder</tt> which using Usher to map requests instead of a simple Hash.
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
- # it returns route, and because you may want to work with the route,
19
- # for example give it a name, we returns the route with GET request
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 <tt>env</tt> normally.
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
- attr_accessor :redirect_on_trailing_slash
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
- @destination = lambda do |env|
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
- end
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 # get the variable
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,
@@ -1,21 +1,26 @@
1
1
  class Usher
2
2
  class Node
3
- class Response < Struct.new(:path, :params_as_array, :remaining_path, :matched_path)
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
@@ -1,7 +1,7 @@
1
1
  class Usher
2
2
  class Node
3
3
  class Root < Node
4
-
4
+
5
5
  def initialize(route_set, request_methods)
6
6
  super(route_set, nil)
7
7
  self.request_methods = request_methods
@@ -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.4"
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
- - 4
9
- version: 0.7.4
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
@@ -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