Module: SugarCRM::FinderMethods::ClassMethods

Defined in:
lib/sugarcrm/finders/finder_methods.rb

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_id, *arguments, &block) ⇒ Object (private)

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.



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/sugarcrm/finders/finder_methods.rb', line 160

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?
      self.class_eval <<-EOS, __FILE__, __LINE__ + 1
        def self.#{method_id}(*args)
          options = args.extract_options!
          attributes = construct_attributes_from_arguments(
            [:#{attribute_names.join(',:')}],
            args
          )
          finder_options = { :conditions => attributes }
          validate_find_options(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
      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 }

          record = find(:first, options)

          if record.nil?
            record = self.new(unprotected_attributes_for_create)
            #{'record.save' if instantiator == :create}
            record
          else
            record
          end
        end
      EOS
      send(method_id, *arguments, &block)
    end
  else
    super
  end
end