diff options
author | Jon Leighton <j@jonathanleighton.com> | 2012-05-04 14:30:57 +0100 |
---|---|---|
committer | Jon Leighton <j@jonathanleighton.com> | 2012-05-04 14:31:19 +0100 |
commit | 510cf0ad93f07e9285178c8b7ba7d4d68c139fec (patch) | |
tree | 151a0fe3bc9c050c943c17c6bc0f82b717a30dcc | |
parent | ffad3600ea01cda457b447fafd4d38907f4d0bfb (diff) | |
download | rails-510cf0ad93f07e9285178c8b7ba7d4d68c139fec.tar.gz rails-510cf0ad93f07e9285178c8b7ba7d4d68c139fec.tar.bz2 rails-510cf0ad93f07e9285178c8b7ba7d4d68c139fec.zip |
extract deprecated dynamic methods
-rw-r--r-- | activerecord/lib/active_record/dynamic_matchers.rb | 178 | ||||
-rw-r--r-- | activerecord/test/cases/named_scope_test.rb | 4 |
2 files changed, 40 insertions, 142 deletions
diff --git a/activerecord/lib/active_record/dynamic_matchers.rb b/activerecord/lib/active_record/dynamic_matchers.rb index 8726442d0b..e278e62ce7 100644 --- a/activerecord/lib/active_record/dynamic_matchers.rb +++ b/activerecord/lib/active_record/dynamic_matchers.rb @@ -1,5 +1,10 @@ module ActiveRecord - module DynamicMatchers + module DynamicMatchers #:nodoc: + # This code in this file seems to have a lot of indirection, but the indirection + # is there to provide extension points for the active_record_deprecated_finders + # gem. When we stop supporting active_record_deprecated_finders (from Rails 5), + # then we can remove the indirection. + def respond_to?(name, include_private = false) match = Method.match(self, name) match && match.valid? || super @@ -7,15 +12,6 @@ module ActiveRecord private - # Enables dynamic finders like <tt>User.find_by_user_name(user_name)</tt> 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 <tt>find_all_by_amount(amount, options)</tt>. - # - # Each dynamic finder using <tt>scoped_by_*</tt> is also defined in the class after it - # is first invoked, so that future attempts to use it do not run through method_missing. def method_missing(name, *arguments, &block) match = Method.match(self, name) @@ -28,28 +24,27 @@ module ActiveRecord end class Method - def self.match(model, name) - klass = klasses.find { |k| name =~ k.pattern } - klass.new(model, name) if klass - end + @matchers = [] - def self.klasses - [ - FindBy, FindAllBy, FindLastBy, FindByBang, ScopedBy, - FindOrInitializeBy, FindOrCreateBy, FindOrCreateByBang - ] - end + class << self + attr_reader :matchers - def self.pattern - /^#{prefix}_([_a-zA-Z]\w*)#{suffix}$/ - end + def match(model, name) + klass = matchers.find { |k| name =~ k.pattern } + klass.new(model, name) if klass + end - def self.prefix - raise NotImplementedError - end + def pattern + /^#{prefix}_([_a-zA-Z]\w*)#{suffix}$/ + end - def self.suffix - '' + def prefix + raise NotImplementedError + end + + def suffix + '' + end end attr_reader :model, :name, :attribute_names @@ -77,20 +72,20 @@ module ActiveRecord end end - class Finder < Method + module Finder + # Extended in active_record_deprecated_finders def body - <<-CODE - result = #{result} - result && block_given? ? yield(result) : result - CODE + result end + # Extended in active_record_deprecated_finders def result - "scoped.apply_finder_options(options).#{finder}(#{attributes_hash})" + "#{finder}(#{attributes_hash})" end + # Extended in active_record_deprecated_finders def signature - attribute_names.join(', ') + ", options = {}" + attribute_names.join(', ') end def attributes_hash @@ -102,7 +97,10 @@ module ActiveRecord end end - class FindBy < Finder + class FindBy < Method + Method.matchers << self + include Finder + def self.prefix "find_by" end @@ -112,7 +110,10 @@ module ActiveRecord end end - class FindByBang < Finder + class FindByBang < Method + Method.matchers << self + include Finder + def self.prefix "find_by" end @@ -125,108 +126,5 @@ module ActiveRecord "find_by!" end end - - class FindAllBy < Finder - def self.prefix - "find_all_by" - end - - def finder - "where" - end - - def result - "#{super}.to_a" - end - end - - class FindLastBy < Finder - def self.prefix - "find_last_by" - end - - def finder - "where" - end - - def result - "#{super}.last" - end - end - - class ScopedBy < Finder - def self.prefix - "scoped_by" - end - - def body - "where(#{attributes_hash})" - end - end - - class Instantiator < Method - # This is nasty, but it doesn't matter because it will be deprecated. - def self.dispatch(klass, attribute_names, instantiator, args, block) - if args.length == 1 && args.first.is_a?(Hash) - attributes = args.first.stringify_keys - conditions = attributes.slice(*attribute_names) - rest = [attributes.except(*attribute_names)] - else - raise ArgumentError, "too few arguments" unless args.length >= attribute_names.length - - conditions = Hash[attribute_names.map.with_index { |n, i| [n, args[i]] }] - rest = args.drop(attribute_names.length) - end - - klass.where(conditions).first || - klass.create_with(conditions).send(instantiator, *rest, &block) - end - - def signature - "*args, &block" - end - - def body - "#{self.class}.dispatch(self, #{attribute_names.inspect}, #{instantiator.inspect}, args, block)" - end - - def instantiator - raise NotImplementedError - end - end - - class FindOrInitializeBy < Instantiator - def self.prefix - "find_or_initialize_by" - end - - def instantiator - "new" - end - end - - class FindOrCreateBy < Instantiator - def self.prefix - "find_or_create_by" - end - - def instantiator - "create" - end - end - - class FindOrCreateByBang < Instantiator - def self.prefix - "find_or_create_by" - end - - def self.suffix - "!" - end - - def instantiator - "create!" - end - end end end diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index 393e578219..2e87c5be2f 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -442,11 +442,11 @@ end class DynamicScopeMatchTest < ActiveRecord::TestCase def test_scoped_by_no_match - assert_nil ActiveRecord::DynamicMatchers::ScopedBy.match(nil, "not_scoped_at_all") + assert_nil ActiveRecord::DynamicMatchers::Method.match(nil, "not_scoped_at_all") end def test_scoped_by - match = ActiveRecord::DynamicMatchers::ScopedBy.match(nil, "scoped_by_age_and_sex_and_location") + match = ActiveRecord::DynamicMatchers::Method.match(nil, "scoped_by_age_and_sex_and_location") assert_not_nil match assert_equal %w(age sex location), match.attribute_names end |