Method: MessageRouter::Router.match
- Defined in:
- lib/message_router/router.rb
.match(*args, &block) ⇒ Object
The 1st argument to a matcher can be:
-
true, false, or nil
-
String or Regexp, which match against env. Strings match against the 1st word.
-
Array - Elements can be Strings or Regexps. They are matched against ‘body’. Matches if any element is matches.
-
Hash - Keys are expected to be a subset of the env’s keys. The values are String, Regexp, or Array to be match again the corresponding value in the env Hash. True if there is a match for all keys.
-
Symbol - Calls a helper method of the same name. If the helper can take an argument, the env will be passed to it. The return value of the helper method determines if the matcher matches.
-
Anything that responds to #call - It is passed the env as the only arugment. The return value determines if the matcher matches.
Because Routers are trigged by the method #call, one could use a Router as the 1st argument to a matcher. However, it would actually run that Router’s code, which is not intuitive, and therefore not recommonded. If the 1st argument to a matcher resolves to a true value, then the 2nd argument is sent ‘#call(env)`. If that also returns a true value, then the matcher has “matched” and the router stops. However, if the 2nd argument returns false, then the router will continue running. This allows us to mount sub-routers and continue trying other rules if those subrouters fail to match something. The 2nd argument to #match can also be specified with a block. If the 1st argument is skipped, then it is assumed to be true. This is useful for passing a message to a sub-router, which will return nil if it doesn’t match. For example:
match MyOtherRouter.new
is a short-hand for:
match true, MyOtherRouter.new
It is important to keep in mind that blocks, procs, and lambdas, whether they are the 1st or 2nd argument, will be run in the scope of the router, just like the methods referenced by Symbols. That means that they have access to all the helper methods. However, it also means they have the ability to edit/add instance variables on the router; NEVER DO THIS. If you want to use an instance variable inside a helper, block, proc, or lambda, you MUST use the env hash instance. Examples:
# BAD
match :my_helper do
@cached_user ||= User.find_by_id(@user_id)
end
def find_user
@id ||= User.get_id_from_guid(env['guid'])
end
# GOOD
match :my_helper do
env['cached_user'] ||= User.find_by_id(env['user_id'])
end
def find_user
env['id'] ||= User.get_id_from_guid(env['guid'])
end
If you do not follow this requirement, then when subsequent keywords are routed, they will see the instance variables from the previous message. In the case of the above example, every subsequent message will have
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/message_router/router.rb', line 146 def match *args, &block args << block if block case args.size when 0 raise ArgumentError, "You must provide either a block or an argument which responds to call." when 1 if args[0].respond_to?(:env) condition = true action = args[0] elsif args[0].respond_to?(:call) condition = true action = args[0] elsif args[0].kind_of?(Hash) && args[0].values.size == 1 && args[0].values[0].respond_to?(:call) # Syntactical suger to make: # match :cool? => OnlyForCoolPeopleRouter # work just like: # match :cool?, OnlyForCoolPeopleRouter condition = args[0].keys[0] action = args[0].values[0] else raise ArgumentError, "You must provide either a block or a 2nd argument which responds to call." end when 2 condition, action = args raise ArgumentError, "The 2nd argument must respond to call." unless action.respond_to?(:call) else raise ArgumentError, "Too many arguments. Note: you may not provide a block when a 2nd argument has been provided." end # Save the arguments for later. rules << [condition, action] end |