diff options
Diffstat (limited to 'activerecord/lib/active_record/scoping')
-rw-r--r-- | activerecord/lib/active_record/scoping/default.rb | 26 | ||||
-rw-r--r-- | activerecord/lib/active_record/scoping/named.rb | 48 |
2 files changed, 53 insertions, 21 deletions
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb index 2daa48859a..6caf9b3251 100644 --- a/activerecord/lib/active_record/scoping/default.rb +++ b/activerecord/lib/active_record/scoping/default.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module Scoping module Default @@ -5,11 +7,8 @@ module ActiveRecord included do # Stores the default scope for the class. - class_attribute :default_scopes, instance_writer: false, instance_predicate: false - class_attribute :default_scope_override, instance_writer: false, instance_predicate: false - - self.default_scopes = [] - self.default_scope_override = nil + class_attribute :default_scopes, instance_writer: false, instance_predicate: false, default: [] + class_attribute :default_scope_override, instance_writer: false, instance_predicate: false, default: nil end module ClassMethods @@ -32,7 +31,14 @@ module ActiveRecord # Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10" # } def unscoped - block_given? ? relation.scoping { yield } : relation + block_given? ? _scoping(relation) { yield } : relation + end + + def _scoping(relation) # :nodoc: + previous, self.current_scope = current_scope(true), relation + yield + ensure + self.current_scope = previous end # Are there attributes associated with this scope? @@ -110,13 +116,17 @@ module ActiveRecord if default_scope_override # The user has defined their own default scope method, so call that - evaluate_default_scope { default_scope } + evaluate_default_scope do + if scope = default_scope + (base_rel ||= relation).merge!(scope) + end + end elsif default_scopes.any? base_rel ||= relation evaluate_default_scope do default_scopes.inject(base_rel) do |default_scope, scope| scope = scope.respond_to?(:to_proc) ? scope : scope.method(:call) - default_scope.merge(base_rel.instance_exec(&scope)) + default_scope.merge!(base_rel.instance_exec(&scope)) end end end diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb index 27cdf8cb7e..573d97b819 100644 --- a/activerecord/lib/active_record/scoping/named.rb +++ b/activerecord/lib/active_record/scoping/named.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/array" require "active_support/core_ext/hash/except" require "active_support/core_ext/kernel/singleton_class" @@ -22,20 +24,36 @@ module ActiveRecord # You can define a scope that applies to all finders using # {default_scope}[rdoc-ref:Scoping::Default::ClassMethods#default_scope]. def all - if current_scope - current_scope.clone + scope = current_scope + + if scope + if self == scope.klass + scope.clone + else + relation.merge!(scope) + end else default_scoped end end - def default_scoped # :nodoc: - scope = build_default_scope + def scope_for_association(scope = relation) # :nodoc: + if current_scope&.empty_scope? + scope + else + default_scoped(scope) + end + end - if scope - relation.spawn.merge!(scope) + def default_scoped(scope = relation) # :nodoc: + build_default_scope(scope) || scope + end + + def default_extensions # :nodoc: + if scope = current_scope || build_default_scope + scope.extensions else - relation + [] end end @@ -151,22 +169,26 @@ module ActiveRecord "a class method with the same name." end + if method_defined_within?(name, Relation) + raise ArgumentError, "You tried to define a scope named \"#{name}\" " \ + "on the model \"#{self.name}\", but ActiveRecord::Relation already defined " \ + "an instance method with the same name." + end + valid_scope_name?(name) extension = Module.new(&block) if block if body.respond_to?(:to_proc) singleton_class.send(:define_method, name) do |*args| - scope = all.scoping { instance_exec(*args, &body) } + scope = all._exec_scope(*args, &body) scope = scope.extending(extension) if extension - - scope || all + scope end else singleton_class.send(:define_method, name) do |*args| - scope = all.scoping { body.call(*args) } + scope = body.call(*args) || all scope = scope.extending(extension) if extension - - scope || all + scope end end end |