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 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