method_missing
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.
# File activerecord/lib/active_record/base.rb, line 1869
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 "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
# 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 "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 Related methods
- Instance methods
- ==
- []
- []=
- attribute_for_inspect
- attribute_names
- attribute_present?
- attributes
- attributes=
- attributes_before_type_cast
- becomes
- cache_key
- clone
- column_for_attribute
- connection
- decrement
- decrement!
- delete
- destroy
- destroyed?
- eql?
- freeze
- frozen?
- has_attribute?
- hash
- id
- id=
- id_before_type_cast
- increment
- increment!
- inspect
- new_record?
- quoted_id
- readonly!
- readonly?
- reload
- save
- save!
- to_param
- toggle
- toggle!
- update_attribute
- update_attributes
- update_attributes!
- Class methods
- ===
- abstract_class?
- accessible_attributes
- all
- allow_concurrency
- allow_concurrency=
- attr_accessible
- attr_protected
- attr_readonly
- base_class
- benchmark
- class_name
- column_methods_hash
- column_names
- columns
- columns_hash
- connected?
- connection
- connection_pool
- content_columns
- count_by_sql
- create
- decrement_counter
- delete
- delete_all
- descends_from_active_record?
- destroy
- destroy_all
- establish_connection
- exists?
- find
- find_by_sql
- finder_needs_type_condition?
- first
- full_table_name_prefix
- get_primary_key
- human_attribute_name
- human_name
- increment_counter
- inheritance_column
- inherited
- inspect
- last
- merge_conditions
- mysql_connection
- new
- postgresql_connection
- primary_key
- protected_attributes
- quote_value
- readonly_attributes
- remove_connection
- reset_column_information
- reset_column_information_and_inheritable_attributes_for_all_subclasses
- reset_counters
- reset_primary_key
- reset_sequence_name
- reset_subclasses
- reset_table_name
- respond_to?
- retrieve_connection
- sanitize
- self_and_descendants_from_active_record
- sequence_name
- serialize
- serialized_attributes
- set_inheritance_column
- set_primary_key
- set_sequence_name
- set_table_name
- silence
- sqlite3_connection
- sqlite_connection
- sti_name
- table_exists?
- table_name
- update
- update_all
- update_counters
- verification_timeout
- verification_timeout=
- Protected methods
-
aggregate_mapping -
class_name_of_active_record_descendant -
class_of_active_record_descendant -
compute_type -
current_scoped_methods -
default_scope -
encode_quoted_value -
expand_hash_conditions_for_aggregates -
expand_range_bind_variables -
quote_bound_value -
raise_if_bind_arity_mismatch -
replace_bind_variables -
replace_named_bind_variables -
sanitize_sql_array -
sanitize_sql_for_assignment -
sanitize_sql_for_conditions -
sanitize_sql_hash_for_assignment -
sanitize_sql_hash_for_conditions -
scope -
scoped? -
scoped_methods -
set_readonly_option! -
subclasses -
validate_find_options -
with_exclusive_scope -
with_scope - Private methods
-
add_conditions! -
add_group! -
add_joins! -
add_limit! -
add_lock! -
add_order! -
all_attributes_exists? -
array_of_strings? -
attribute_condition -
construct_attributes_from_arguments -
construct_finder_sql -
default_select -
define_attr_method -
expand_attribute_names_for_aggregates -
expand_id_conditions -
find_every -
find_from_ids -
find_initial -
find_last -
find_one -
find_some -
instantiate -
merge_includes -
merge_joins -
method_missing -
parse_sqlite_config! -
quoted_table_name -
reverse_sql_order -
safe_to_array -
type_condition -
type_name_with_module -
undecorated_table_name -
assign_attributes -
assign_multiparameter_attributes -
attributes_from_column_definition -
attributes_protected_by_default -
attributes_with_quotes -
clone_attribute_value -
clone_attributes -
comma_pair_list -
convert_number_column_value -
create -
create_or_update -
ensure_proper_type -
execute_callstack_for_multiparameter_attributes -
extract_callstack_for_multiparameter_attributes -
find_parameter_position -
instantiate_time_object -
interpolate_sql -
log_protected_attribute_removal -
object_from_yaml -
quote_columns -
quote_value -
quoted_column_names -
quoted_comma_pair_list -
remove_attributes_protected_from_mass_assignment -
remove_readonly_attributes -
type_cast_attribute_value -
update