method_missing(method_id, *arguments)
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).
This also enables you to initialize a record if it is not found, such as
find_or_initialize_by_amount(amount) or
find_or_create_by_user_and_password(user, password).
Each dynamic finder 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)
if match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(method_id.to_s)
finder = determine_finder(match)
attribute_names = extract_attribute_names_from_match(match)
super unless all_attributes_exists?(attribute_names)
self.class_eval %{
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)
set_readonly_option!(options)
if options[:conditions]
with_scope(:find => finder_options) do
ActiveSupport::Deprecation.silence { send(:
end
else
ActiveSupport::Deprecation.silence { send(:
end
end
}, __FILE__, __LINE__
send(method_id, *arguments)
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)
self.class_eval %{
def self.#{method_id}(*args)
if args[0].is_a?(Hash)
attributes = args[0].with_indifferent_access
find_attributes = attributes.slice(*[:#{attribute_names.join(',:')}])
else
find_attributes = attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args)
end
options = { :conditions => find_attributes }
set_readonly_option!(options)
record = find_initial(options)
if record.nil?
record = self.new { |r| r.send(:attributes=, attributes, false) }
record
else
record
end
end
}, __FILE__, __LINE__
send(method_id, *arguments)
else
super
end
end