class method self.method_missing

Ruby on Rails 2.3.18

Since v2.2.3 Last seen in v3.1.12 Private

Available in: v2.2.3 v2.3.18 v3.0.20 v3.1.12

Signature

self.method_missing(method_id, *arguments, &block)

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) by using find_all_by_amount(50) that is 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).

Also enables dynamic scopes like scoped_by_user_name(user_name) and scoped_by_user_name_and_password(user_name, password) that are turned into scoped(:conditions => [“user_name = ?”, user_name]) and scoped(:conditions => [“user_name = ? AND password = ?”, user_name, password]) respectively.

Each dynamic finder, scope or initializer/creator is also defined in the class after it is first invoked, so that future attempts to use it do not run through method_missing.

Parameters

method_id req
arguments rest
block block
Source
# File activerecord/lib/active_record/base.rb, line 1873
        def method_missing(method_id, *arguments, &block)
          if match = DynamicFinderMatch.match(method_id)
            attribute_names = match.attribute_names
            super unless all_attributes_exists?(attribute_names)
            if match.finder?
              finder = match.finder
              bang = match.bang?
              # def self.find_by_login_and_activated(*args)
              #   options = args.extract_options!
              #   attributes = construct_attributes_from_arguments(
              #     [:login,:activated],
              #     args
              #   )
              #   finder_options = { :conditions => attributes }
              #   validate_find_options(options)
              #   set_readonly_option!(options)
              #
              #   if options[:conditions]
              #     with_scope(:find => finder_options) do
              #       find(:first, options)
              #     end
              #   else
              #     find(:first, options.merge(finder_options))
              #   end
              # end
              self.class_eval <<-EOS, __FILE__, __LINE__ + 1
                def self.#{method_id}(*args)
                  options = if args.length > #{attribute_names.size}
                              args.extract_options!
                            else
                              {}
                            end
                  attributes = construct_attributes_from_arguments(
                    [:#{attribute_names.join(',:')}],
                    args
                  )
                  finder_options = { :conditions => attributes }
                  validate_find_options(options)
                  set_readonly_option!(options)

                  #{'result = ' if bang}if options[:conditions]
                    with_scope(:find => finder_options) do
                      find(:#{finder}, options)
                    end
                  else
                    find(:#{finder}, options.merge(finder_options))
                  end
                  #{'result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attributes.to_a.collect {|pair| "#{pair.first} = #{pair.second}"}.join(\', \')}")' if bang}
                end
              EOS
              send(method_id, *arguments)
            elsif match.instantiator?
              instantiator = match.instantiator
              # def self.find_or_create_by_user_id(*args)
              #   guard_protected_attributes = false
              #
              #   if args[0].is_a?(Hash)
              #     guard_protected_attributes = true
              #     attributes = args[0].with_indifferent_access
              #     find_attributes = attributes.slice(*[:user_id])
              #   else
              #     find_attributes = attributes = construct_attributes_from_arguments([:user_id], args)
              #   end
              #
              #   options = { :conditions => find_attributes }
              #   set_readonly_option!(options)
              #
              #   record = find(:first, options)
              #
              #   if record.nil?
              #     record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) }
              #     yield(record) if block_given?
              #     record.save
              #     record
              #   else
              #     record
              #   end
              # end
              self.class_eval <<-EOS, __FILE__, __LINE__ + 1
                def self.#{method_id}(*args)
                  attributes = [:#{attribute_names.join(',:')}]
                  protected_attributes_for_create, unprotected_attributes_for_create = {}, {}
                  args.each_with_index do |arg, i|
                    if arg.is_a?(Hash)
                      protected_attributes_for_create = args[i].with_indifferent_access
                    else
                      unprotected_attributes_for_create[attributes[i]] = args[i]
                    end
                  end

                  find_attributes = (protected_attributes_for_create.merge(unprotected_attributes_for_create)).slice(*attributes)

                  options = { :conditions => find_attributes }
                  set_readonly_option!(options)

                  record = find(:first, options)

                  if record.nil?
                    record = self.new do |r|
                      r.send(:attributes=, protected_attributes_for_create, true) unless protected_attributes_for_create.empty?
                      r.send(:attributes=, unprotected_attributes_for_create, false) unless unprotected_attributes_for_create.empty?
                    end
                    #{'yield(record) if block_given?'}
                    #{'record.save' if instantiator == :create}
                    record
                  else
                    record
                  end
                end
              EOS
              send(method_id, *arguments, &block)
            end
          elsif match = DynamicScopeMatch.match(method_id)
            attribute_names = match.attribute_names
            super unless all_attributes_exists?(attribute_names)
            if match.scope?
              self.class_eval <<-EOS, __FILE__, __LINE__ + 1
                def self.#{method_id}(*args)                        # def self.scoped_by_user_name_and_password(*args)
                  options = args.extract_options!                   #   options = args.extract_options!
                  attributes = construct_attributes_from_arguments( #   attributes = construct_attributes_from_arguments(
                    [:#{attribute_names.join(',:')}], args          #     [:user_name, :password], args
                  )                                                 #   )
                                                                    # 
                  scoped(:conditions => attributes)                 #   scoped(:conditions => attributes)
                end                                                 # end
              EOS
              send(method_id, *arguments)
            end
          else
            super
          end
        end

Defined in activerecord/lib/active_record/base.rb line 1873 · View on GitHub · Improve this page · Find usages on GitHub

Defined in ActiveRecord::Base

Type at least 2 characters to search.

↑↓ navigate · open · esc close