Merge pull request #4732 from pwim/refactor-dynamic-match

Refactor dynamic match
This commit is contained in:
Jon Leighton 2012-01-30 23:37:09 -08:00
commit 49e69237c4
3 changed files with 62 additions and 30 deletions

@ -6,33 +6,23 @@ module ActiveRecord
#
class DynamicFinderMatch
def self.match(method)
finder = :first
bang = false
instantiator = nil
case method.to_s
when /^find_(all_|last_)?by_([_a-zA-Z]\w*)$/
finder = :last if $1 == 'last_'
finder = :all if $1 == 'all_'
names = $2
when /^find_by_([_a-zA-Z]\w*)\!$/
bang = true
names = $1
when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
instantiator = $1 == 'initialize' ? :new : :create
names = $2
else
return nil
method = method.to_s
klass = [FindBy, FindByBang, FindOrInitializeCreateBy].find do |klass|
klass.matches?(method)
end
new(finder, instantiator, bang, names.split('_and_'))
klass.new(method) if klass
end
def initialize(finder, instantiator, bang, attribute_names)
@finder = finder
@instantiator = instantiator
@bang = bang
@attribute_names = attribute_names
def self.matches?(method)
method =~ self::METHOD_PATTERN
end
def initialize(method)
@finder = :first
@instantiator = nil
match_data = method.match(self.class::METHOD_PATTERN)
@attribute_names = match_data[-1].split("_and_")
initialize_from_match_data(match_data)
end
attr_reader :finder, :attribute_names, :instantiator
@ -41,16 +31,54 @@ def finder?
@finder && !@instantiator
end
def instantiator?
@finder == :first && @instantiator
end
def creator?
@finder == :first && @instantiator == :create
end
def instantiator?
@instantiator
end
def bang?
@bang
false
end
def valid_arguments?(arguments)
arguments.size >= @attribute_names.size
end
private
def initialize_from_match_data(match_data)
end
end
class FindBy < DynamicFinderMatch
METHOD_PATTERN = /^find_(all_|last_)?by_([_a-zA-Z]\w*)$/
def initialize_from_match_data(match_data)
@finder = :last if match_data[1] == 'last_'
@finder = :all if match_data[1] == 'all_'
end
end
class FindByBang < DynamicFinderMatch
METHOD_PATTERN = /^find_by_([_a-zA-Z]\w*)\!$/
def bang?
true
end
end
class FindOrInitializeCreateBy < DynamicFinderMatch
METHOD_PATTERN = /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
def initialize_from_match_data(match_data)
@instantiator = match_data[1] == 'initialize' ? :new : :create
end
def valid_arguments?(arguments)
arguments.size == 1 && arguments.first.is_a?(Hash) || super
end
end
end

@ -25,7 +25,7 @@ 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
unless match.valid_arguments?(arguments)
method_trace = "#{__FILE__}:#{__LINE__}:in `#{method_id}'"
backtrace = [method_trace] + caller
raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{attribute_names.size})", backtrace

@ -19,5 +19,9 @@ def initialize(scope, attribute_names)
attr_reader :scope, :attribute_names
alias :scope? :scope
def valid_arguments?(arguments)
arguments.size >= @attribute_names.size
end
end
end