method_missing(method_id, *arguments, &block)
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.
Show source
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 "def self.\#{method_id}(*args)\noptions = args.extract_options!\nattributes = construct_attributes_from_arguments(\n[:\#{attribute_names.join(',:')}],\nargs\n)\nfinder_options = { :conditions => attributes }\nvalidate_find_options(options)\nset_readonly_option!(options)\n\n\#{'result = ' if bang}if options[:conditions]\nwith_scope(:find => finder_options) do\nfind(:\#{finder}, options)\nend\nelse\nfind(:\#{finder}, options.merge(finder_options))\nend\n\#{'result || raise(RecordNotFound, \"Couldn\\'t find \#{name} with \#{attributes.to_a.collect {|pair| \"\#{pair.first} = \#{pair.second}\"}.join(\\', \\')}\")' if bang}\nend\n", __FILE__, __LINE__ + 1
send(method_id, *arguments)
elsif match.instantiator?
instantiator = match.instantiator
self.class_eval "def self.\#{method_id}(*args)\nattributes = [:\#{attribute_names.join(',:')}]\nprotected_attributes_for_create, unprotected_attributes_for_create = {}, {}\nargs.each_with_index do |arg, i|\nif arg.is_a?(Hash)\nprotected_attributes_for_create = args[i].with_indifferent_access\nelse\nunprotected_attributes_for_create[attributes[i]] = args[i]\nend\nend\n\nfind_attributes = (protected_attributes_for_create.merge(unprotected_attributes_for_create)).slice(*attributes)\n\noptions = { :conditions => find_attributes }\nset_readonly_option!(options)\n\nrecord = find(:first, options)\n\nif record.nil?\nrecord = self.new do |r|\nr.send(:attributes=, protected_attributes_for_create, true) unless protected_attributes_for_create.empty?\nr.send(:attributes=, unprotected_attributes_for_create, false) unless unprotected_attributes_for_create.empty?\nend\n\#{'yield(record) if block_given?'}\n\#{'record.save' if instantiator == :create}\nrecord\nelse\nrecord\nend\nend\n", __FILE__, __LINE__ + 1
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 "def self.\#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)\noptions = args.extract_options! # options = args.extract_options!\nattributes = construct_attributes_from_arguments( # attributes = construct_attributes_from_arguments(\n[:\#{attribute_names.join(',:')}], args # [:user_name, :password], args\n) # )\n#\nscoped(:conditions => attributes) # scoped(:conditions => attributes)\nend # end\n", __FILE__, __LINE__ + 1
send(method_id, *arguments)
end
else
super
end
end