Method_missing is a common technique for Ruby metaprogramming (metaprogramming). The basic idea is to invoke a method that does not exist by implementing a callback. A typical example is the dynamic lookup of a ActiveRecord (the Dynamics Finder). For example: We have email attributes so we can call User.find_by_email (' joe@example.com '), although, ActiveRecord::Base does not have a method called Find_by_email.
Respond_to? is not known as method_missing, often used when it is necessary to confirm that a feedback object needs to be confirmed so that no feedback object will result in a subsequent call error.
Here is an example of how to apply the two:
Example
We have classes legislator class, and now we want to add a find_by_first_name (' John ') dynamic call to it. Realize the function of find (: first_name => ' John ').
Copy Code code as follows:
Class legislator
#假设这是一个真实的实现
def find (conditions = {})
End
#在本身定义毕竟这是他的方法
def self.method_missing (Method_sym, *arguments, &block)
# the ' argument ' is ' a Symbol, so your need to_s it if you want to pattern match
If method_sym.to_s =~/^find_by_ (. *) $/
Find ($1.to_sym => arguments.first)
Else
Super
End
End
End
So this time call
Copy Code code as follows:
Legislator.respond_to? (: Find_by_first_name)
will prompt for an error, then continue
Copy Code code as follows:
Class legislator
# omitted
# It's important to know Object defines respond_to to take two the method to check, and parameters:the to include PRI Vate methods
# http://www.ruby-doc.org/core/classes/Object.html#M000333
def self.respond_to? (Method_sym, include_private = False)
If method_sym.to_s =~/^find_by_ (. *) $/
True
Else
Super
End
End
End
As described in the code annotation respond_to? requires two parameters, if you do not provide will produce argumenterror.
Correlation Reflection DRY
If we notice that there are duplicate code here. We can refer to ActiveRecord implementations encapsulated in ActiveRecord::D ynamicfindermatch to avoid duplication in method_missing and respond_to.
Copy Code code as follows:
Class Legislatordynamicfindermatch
Attr_accessor:attribute
def initialize (method_sym)
If method_sym.to_s =~/^find_by_ (. *) $/
@attribute = $1.to_sym
End
End
Def match?
@attribute!= Nil
End
End
Class legislator
def self.method_missing (Method_sym, *arguments, &block)
Match = Legislatordynamicfindermatch.new (method_sym)
If Match.match?
Find (Match.attribute => arguments.first)
Else
Super
End
End
def self.respond_to? (Method_sym, include_private = False)
If Legislatordynamicfindermatch.new (method_sym). Match?
True
Else
Super
End
End
End
Cache method_missing
Method_missing that can be repeated multiple times may consider caching.
Another thing we can learn from ActiveRecord is that when we define method_missing, we send the Now-defined method. As follows:
Copy Code code as follows:
Class legislator
def self.method_missing (Method_sym, *arguments, &block)
Match = Legislatordynamicfindermatch.new (method_sym)
If Match.match?
Define_dynamic_finder (Method_sym, Match.attribute)
Send (Method_sym, Arguments.first)
Else
Super
End
End
Protected
def self.define_dynamic_finder (finder, attribute)
Class_eval <<-ruby
Def Self.#{finder} (#{attribute}) # def self.find_by_first_name (first_name)
Find (: #{attribute} => #{attribute}) # Find (: first_name => first_name)
End # End
Ruby
End
End
Test
The test section is as follows:
Copy Code code as follows:
Describe Legislatordynamicfindermatch do
Describe ' find_by_first_name ' do
Before do
@match = legislatordynamicfindermatch.new (: find_by_first_name)
End
It ' should have attribute:first_name ' do
@match. Attribute.should =: first_name
End
It ' should is a match ' do
@match. Should Be_a_match
End
End
Describe ' ZOMG ' do
Before do
@match = Legislatordynamicfindermatch (: ZOMG)
End
It ' should have nil attribute ' do
@match. Attribute.should Be_nil
End
It ' should not being a match ' do
@match. Should_not Be_a_match
End
End
End
Here is the RSpec example:
Copy Code code as follows:
Describe legislator, ' dynamic Find_by_first_name ' do
It ' should call find (: first_name => first_name) ' Do
Legislator.should_receive (: Find). With (: first_name => ' John ')
Legislator.find_by_first_name (' John ')
End
End