Skip to content

Commit f8c10e7

Browse files
Merge pull request rails#49096 from abaldwin88/document_class_method_scope_behavior
Document clean chain behavior for ActiveRecord scope
2 parents e4d47ea + cebac59 commit f8c10e7

File tree

1 file changed

+55
-0
lines changed

1 file changed

+55
-0
lines changed

guides/source/active_record_querying.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1935,6 +1935,61 @@ SELECT books.* FROM books WHERE books.out_of_print = true
19351935

19361936
[`unscoped`]: https://api.rubyonrails.org/classes/ActiveRecord/Scoping/Default/ClassMethods.html#method-i-unscoped
19371937

1938+
### New Chains Inside Scope Block
1939+
1940+
Unlike class methods, [`scope`][] can easily start a new clean chain against the
1941+
model it is defined on.
1942+
1943+
```ruby
1944+
class Topic < ApplicationRecord
1945+
scope :toplevel, -> { where(parent_id: nil) }
1946+
scope :children, -> { where.not(parent_id: nil) }
1947+
scope :has_children, -> {
1948+
where(id: Topic.children.select(:parent_id))
1949+
}
1950+
end
1951+
1952+
Topic.toplevel.has_children
1953+
```
1954+
1955+
Inside `has_children` the `Topic` chain generates a subquery like this:
1956+
1957+
```sql
1958+
SELECT "topics"."parent_id" FROM "topics" WHERE "topics"."parent_id" IS NOT NULL
1959+
```
1960+
1961+
Class methods have different behavior which can be surprising if you expect them
1962+
to work exactly like scopes.
1963+
1964+
```ruby
1965+
class Topic < ApplicationRecord
1966+
def self.toplevel
1967+
where(parent_id: nil)
1968+
end
1969+
1970+
def self.children
1971+
where.not(parent_id: nil)
1972+
end
1973+
1974+
def self.has_children
1975+
where(id: Topic.children.select(:parent_id))
1976+
end
1977+
end
1978+
1979+
Topic.toplevel.has_children
1980+
```
1981+
1982+
`Topic` inside `has_children` will implicitly include `toplevel` from the outer
1983+
chain resulting in a subquery of:
1984+
1985+
```sql
1986+
SELECT "topics"."parent_id" FROM "topics" WHERE "topics"."parent_id" IS NULL AND "topics"."parent_id" IS NOT NULL
1987+
```
1988+
1989+
NOTE: In class methods, `self` refers back to the model. In scope, `self` acts as
1990+
a chained relation. For the example above, `self` and `Topic` are interchangeable
1991+
within the class method definition.
1992+
19381993
Dynamic Finders
19391994
---------------
19401995

0 commit comments

Comments
 (0)