From d1e0f11b49c9c7598efffbc44a94539b6e982e5c Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 18 Jul 2016 22:11:35 +0900 Subject: Delegate to `scope` rather than `merge!` for collection proxy `merge! association.scope(nullify: false)` is expensive but most methods do not need the merge. --- .../associations/collection_association.rb | 6 ++-- .../active_record/associations/collection_proxy.rb | 32 ++++++++++++++-------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 46923f690a..f747491a12 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -313,9 +313,9 @@ module ActiveRecord record end - def scope(opts = {}) - scope = super() - scope.none! if opts.fetch(:nullify, true) && null_scope? + def scope + scope = super + scope.none! if null_scope? scope end diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 9f91f2b536..63f746688c 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -28,12 +28,9 @@ module ActiveRecord # is computed directly through SQL and does not trigger by itself the # instantiation of the actual post records. class CollectionProxy < Relation - delegate :exists?, :update_all, :arel, to: :scope - def initialize(klass, association) #:nodoc: @association = association super klass, klass.arel_table, klass.predicate_builder - merge! association.scope(nullify: false) end def target @@ -956,14 +953,6 @@ module ActiveRecord @association end - # We don't want this object to be put on the scoping stack, because - # that could create an infinite loop where we call an @association - # method, which gets the current scope, which is this object, which - # delegates to @association, and so on. - def scoping - @association.scope.scoping { yield } - end - # Returns a Relation object for the records in this association def scope @association.scope @@ -1126,6 +1115,19 @@ module ActiveRecord self end + def respond_to?(name, include_private = false) + super || scope.respond_to?(name, include_private) + end + + delegate_methods = [ + QueryMethods, + SpawnMethods, + ].flat_map { |klass| + klass.public_instance_methods(false) + } - self.public_instance_methods(false) + [:scoping] + + delegate(*delegate_methods, to: :scope) + private def find_nth_with_limit(index, limit) # :doc: @@ -1149,6 +1151,14 @@ module ActiveRecord def exec_queries load_target end + + def method_missing(method, *args, &block) + if scope.respond_to?(method) + scope.public_send(method, *args, &block) + else + super + end + end end end end -- cgit v1.2.3