diff options
author | Ryuta Kamizono <kamipo@gmail.com> | 2018-03-29 06:52:04 +0900 |
---|---|---|
committer | Ryuta Kamizono <kamipo@gmail.com> | 2019-02-06 00:37:08 +0900 |
commit | 2935d07569337aeaa06d03a56180d235a7fbbebf (patch) | |
tree | 6546991716de14af2b40aaa60cb1ecf6bcbd9853 /activerecord/test | |
parent | 44232b485485634d681c60868c619323f882e59f (diff) | |
download | rails-2935d07569337aeaa06d03a56180d235a7fbbebf.tar.gz rails-2935d07569337aeaa06d03a56180d235a7fbbebf.tar.bz2 rails-2935d07569337aeaa06d03a56180d235a7fbbebf.zip |
Chaining named scope is no longer leaking to class level querying methods
Active Record uses `scoping` to delegate to named scopes from relations
for propagating the chaining source scope. It was needed to restore the
source scope in named scopes, but it was caused undesired behavior that
pollute all class level querying methods.
Example:
```ruby
class Topic < ActiveRecord::Base
scope :toplevel, -> { where(parent_id: nil) }
scope :children, -> { where.not(parent_id: nil) }
scope :has_children, -> { where(id: Topic.children.select(:parent_id)) }
end
# Works as expected.
Topic.toplevel.where(id: Topic.children.select(:parent_id))
# Doesn't work due to leaking `toplevel` to `Topic.children`.
Topic.toplevel.has_children
```
Since #29301, the receiver in named scopes has changed from the model
class to the chaining source scope, so the polluting class level
querying methods is no longer required for that purpose.
Fixes #14003.
Diffstat (limited to 'activerecord/test')
-rw-r--r-- | activerecord/test/cases/scoping/named_scoping_test.rb | 10 | ||||
-rw-r--r-- | activerecord/test/models/topic.rb | 7 |
2 files changed, 17 insertions, 0 deletions
diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb index 418a2ae04e..27f9df295f 100644 --- a/activerecord/test/cases/scoping/named_scoping_test.rb +++ b/activerecord/test/cases/scoping/named_scoping_test.rb @@ -447,6 +447,16 @@ class NamedScopingTest < ActiveRecord::TestCase assert_equal [posts(:sti_comments)], Post.with_special_comments.with_post(4).to_a.uniq end + def test_chaining_doesnt_leak_conditions_to_another_scopes + expected = Topic.where(approved: false).where(id: Topic.children.select(:parent_id)) + assert_equal expected.to_a, Topic.rejected.has_children.to_a + end + + def test_nested_scoping + expected = Reply.approved + assert_equal expected.to_a, Topic.rejected.nested_scoping(expected) + end + def test_scopes_batch_finders assert_equal 4, Topic.approved.count diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb index a6a47687a2..fdb461ed7f 100644 --- a/activerecord/test/models/topic.rb +++ b/activerecord/test/models/topic.rb @@ -10,6 +10,9 @@ class Topic < ActiveRecord::Base scope :approved, -> { where(approved: true) } scope :rejected, -> { where(approved: false) } + scope :children, -> { where.not(parent_id: nil) } + scope :has_children, -> { where(id: Topic.children.select(:parent_id)) } + scope :scope_with_lambda, lambda { all } scope :by_lifo, -> { where(author_name: "lifo") } @@ -88,6 +91,10 @@ class Topic < ActiveRecord::Base write_attribute(:approved, val) end + def self.nested_scoping(scope) + scope.base + end + private def default_written_on |