mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Bring back private class methods accessibility in named scope
The receiver in a scope was changed from `klass` to `relation` itself
for all scopes (named scope, default_scope, and association scope)
behaves consistently.
In addition. Before 5.2, if both an AR model class and a Relation
instance have same named methods (e.g. `arel_attribute`,
`predicate_builder`, etc), named scope doesn't respect relation instance
information.
For example:
```ruby
class Post < ActiveRecord::Base
has_many :comments1, class_name: "RecentComment1"
has_many :comments2, class_name: "RecentComment2"
end
class RecentComment1 < ActiveRecord::Base
self.table_name = "comments"
default_scope { where(arel_attribute(:created_at).gteq(2.weeks.ago)) }
end
class RecentComment2 < ActiveRecord::Base
self.table_name = "comments"
default_scope { recent_updated }
scope :recent_updated, -> { where(arel_attribute(:updated_at).gteq(2.weeks.ago)) }
end
```
If eager loading `Post.eager_load(:comments1, :comments2).to_a`,
`:comments1` (default_scope) respects aliased table name, but
`:comments2` (using named scope) may not work correctly since named
scope doesn't respect relation instance information. See also 801ccab
.
But this is a breaking change between releases without deprecation.
I decided to bring back private class methods accessibility in named
scope.
Fixes #31740.
Fixes #32331.
This commit is contained in:
parent
1518457a67
commit
b9be64cc3e
4 changed files with 19 additions and 2 deletions
|
@ -313,6 +313,13 @@ module ActiveRecord
|
||||||
klass.current_scope = previous
|
klass.current_scope = previous
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def _exec_scope(*args, &block) # :nodoc:
|
||||||
|
@delegate_to_klass = true
|
||||||
|
instance_exec(*args, &block) || self
|
||||||
|
ensure
|
||||||
|
@delegate_to_klass = false
|
||||||
|
end
|
||||||
|
|
||||||
# Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
|
# Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
|
||||||
# statement and sends it straight to the database. It does not instantiate the involved models and it does not
|
# statement and sends it straight to the database. It does not instantiate the involved models and it does not
|
||||||
# trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
|
# trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
|
||||||
|
|
|
@ -82,6 +82,9 @@ module ActiveRecord
|
||||||
if @klass.respond_to?(method)
|
if @klass.respond_to?(method)
|
||||||
self.class.delegate_to_scoped_klass(method)
|
self.class.delegate_to_scoped_klass(method)
|
||||||
scoping { @klass.public_send(method, *args, &block) }
|
scoping { @klass.public_send(method, *args, &block) }
|
||||||
|
elsif defined?(@delegate_to_klass) &&
|
||||||
|
@delegate_to_klass && @klass.respond_to?(method, true)
|
||||||
|
@klass.send(method, *args, &block)
|
||||||
elsif arel.respond_to?(method)
|
elsif arel.respond_to?(method)
|
||||||
ActiveSupport::Deprecation.warn \
|
ActiveSupport::Deprecation.warn \
|
||||||
"Delegating #{method} to arel is deprecated and will be removed in Rails 6.0."
|
"Delegating #{method} to arel is deprecated and will be removed in Rails 6.0."
|
||||||
|
|
|
@ -183,7 +183,7 @@ module ActiveRecord
|
||||||
if body.respond_to?(:to_proc)
|
if body.respond_to?(:to_proc)
|
||||||
singleton_class.send(:define_method, name) do |*args|
|
singleton_class.send(:define_method, name) do |*args|
|
||||||
scope = all
|
scope = all
|
||||||
scope = scope.instance_exec(*args, &body) || scope
|
scope = scope._exec_scope(*args, &body)
|
||||||
scope = scope.extending(extension) if extension
|
scope = scope.extending(extension) if extension
|
||||||
scope
|
scope
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,9 +12,16 @@ class Topic < ActiveRecord::Base
|
||||||
|
|
||||||
scope :scope_with_lambda, lambda { all }
|
scope :scope_with_lambda, lambda { all }
|
||||||
|
|
||||||
scope :by_lifo, -> { where(author_name: "lifo") }
|
scope :by_lifo, -> { where(author_name: author_name) }
|
||||||
scope :replied, -> { where "replies_count > 0" }
|
scope :replied, -> { where "replies_count > 0" }
|
||||||
|
|
||||||
|
class << self
|
||||||
|
private
|
||||||
|
def author_name
|
||||||
|
"lifo"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
scope "approved_as_string", -> { where(approved: true) }
|
scope "approved_as_string", -> { where(approved: true) }
|
||||||
scope :anonymous_extension, -> {} do
|
scope :anonymous_extension, -> {} do
|
||||||
def one
|
def one
|
||||||
|
|
Loading…
Reference in a new issue