aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/scoping
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/scoping')
-rw-r--r--activerecord/lib/active_record/scoping/default.rb26
-rw-r--r--activerecord/lib/active_record/scoping/named.rb48
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