读Ruby for Rails的思考之method_missing

Ruby里方法的查找顺序是本类=>本类include的模块=>父类=>父类include的模块...
而当最终还是没有找到该方法时,会触发内建的method_missing方法的执行
默认的method_missing会触发异常,而我们可以通过override method_missing方法来提供一些特性
比如Rails的ActiveRecord,User.find_by_name()这个方法本来没有在User类里定义,但是可以通过override method_missing来匹配数据库fields
然后使用class_eval和send动态创建和调用查询方法

我们来看看C:\ruby\lib\ruby\gems\1.8\gems\activerecord-1.15.3\lib\active_record\base.rb中的一段代码就清除了:
 # Enables dynamic finders like find_by_user_name(user_name) and find_by_user_name_and_password(user_name, password) that are turned into
 # find(:first, :conditions => ["user_name = ?", user_name]) and  find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])
 # respectively. Also works for find(:all), but using find_all_by_amount(50) that are turned into find(:all, :conditions => ["amount = ?", 50]).
 #
 # It's even possible to use all the additional parameters to find. For example, the full interface for find_all_by_amount
 # is actually find_all_by_amount(amount, options).
   def method_missing(method_id, *arguments)
     if match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(method_id.to_s)
       finder, deprecated_finder = determine_finder(match), determine_deprecated_finder(match)

       attribute_names = extract_attribute_names_from_match(match)
       super unless all_attributes_exists?(attribute_names)

       attributes = construct_attributes_from_arguments(attribute_names, arguments)

       case extra_options = arguments[attribute_names.size]
         when nil
           options = { :conditions => attributes }
           set_readonly_option!(options)
           ActiveSupport::Deprecation.silence { send(finder, options) }

         when Hash
           finder_options = extra_options.merge(:conditions => attributes)
           validate_find_options(finder_options)
           set_readonly_option!(finder_options)

           if extra_options[:conditions]
             with_scope(:find => { :conditions => extra_options[:conditions] }) do
               ActiveSupport::Deprecation.silence { send(finder, finder_options) }
             end
           else
             ActiveSupport::Deprecation.silence { send(finder, finder_options) }
           end

         else
           ActiveSupport::Deprecation.silence do
             send(deprecated_finder, sanitize_sql(attributes), *arguments[attribute_names.length..-1])
           end
       end
     elsif match = /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/.match(method_id.to_s)
       instantiator = determine_instantiator(match)
       attribute_names = extract_attribute_names_from_match(match)
       super unless all_attributes_exists?(attribute_names)

       attributes = construct_attributes_from_arguments(attribute_names, arguments)
       options = { :conditions => attributes }
       set_readonly_option!(options)

       find_initial(options) || send(instantiator, attributes)
     else
       super
     end
   end

你可能感兴趣的:(sql,xml,Ruby,Rails,ActiveRecord)