# File lib/active_record/dynamic_matchers.rb, line 3 def respond_to?(method_id, include_private = false) if match = DynamicFinderMatch.match(method_id) return true if all_attributes_exists?(match.attribute_names) elsif match = DynamicScopeMatch.match(method_id) return true if all_attributes_exists?(match.attribute_names) end super end
# File lib/active_record/dynamic_matchers.rb, line 77 def aggregate_mapping(reflection) mapping = reflection.options[:mapping] || [reflection.name, reflection.name] mapping.first.is_a?(Array) ? mapping : [mapping] end
# File lib/active_record/dynamic_matchers.rb, line 72 def all_attributes_exists?(attribute_names) (expand_attribute_names_for_aggregates(attribute_names) - column_methods_hash.keys).empty? end
Similar in purpose to expand_hash_conditions_for_aggregates.
# File lib/active_record/dynamic_matchers.rb, line 60 def expand_attribute_names_for_aggregates(attribute_names) attribute_names.map { |attribute_name| unless (aggregation = reflect_on_aggregation(attribute_name.to_sym)).nil? aggregate_mapping(aggregation).map do |field_attr, _| field_attr.to_sym end else attribute_name.to_sym end }.flatten end
Enables dynamic finders like User.find_by_user_name(user_name)
and <tt>User.scoped_by_user_name(user_name). Refer to Dynamic
attribute-based finders section at the top of this file for more detailed
information.
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).
Each dynamic finder using scoped_by_* 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 lib/active_record/dynamic_matchers.rb, line 24 def method_missing(method_id, *arguments, &block) if match = (DynamicFinderMatch.match(method_id) || DynamicScopeMatch.match(method_id)) attribute_names = match.attribute_names super unless all_attributes_exists?(attribute_names) if !(match.is_a?(DynamicFinderMatch) && match.instantiator? && arguments.first.is_a?(Hash)) && arguments.size < attribute_names.size method_trace = "#{__FILE__}:#{__LINE__}:in `#{method_id}'" backtrace = [method_trace] + caller raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{attribute_names.size})", backtrace end if match.respond_to?(:scope?) && match.scope? self.class_eval " def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args) attributes = Hash[[:#{attribute_names.join(',:')}].zip(args)] # attributes = Hash[[:user_name, :password].zip(args)] # scoped(:conditions => attributes) # scoped(:conditions => attributes) end # end ", __FILE__, __LINE__ + 1 send(method_id, *arguments) elsif match.finder? options = if arguments.length > attribute_names.size arguments.extract_options! else {} end relation = options.any? ? scoped(options) : scoped relation.send :find_by_attributes, match, attribute_names, *arguments, &block elsif match.instantiator? scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block end else super end end