Merge pull request #32355 from kamipo/delegate_to_klass_in_a_scope

Bring back private class methods accessibility in named scope
This commit is contained in:
Rafael França 2018-04-06 13:16:01 -04:00 committed by GitHub
commit 7fac2b496a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 30 additions and 1 deletions

View File

@ -30,6 +30,7 @@ module ActiveRecord
@offsets = {} @offsets = {}
@loaded = false @loaded = false
@predicate_builder = predicate_builder @predicate_builder = predicate_builder
@delegate_to_klass = false
end end
def initialize_copy(other) def initialize_copy(other)
@ -313,6 +314,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
@ -446,6 +454,7 @@ module ActiveRecord
end end
def reset def reset
@delegate_to_klass = false
@to_sql = @arel = @loaded = @should_eager_load = nil @to_sql = @arel = @loaded = @should_eager_load = nil
@records = [].freeze @records = [].freeze
@offsets = {} @offsets = {}

View File

@ -82,6 +82,11 @@ 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 @delegate_to_klass && @klass.respond_to?(method, true)
ActiveSupport::Deprecation.warn \
"Delegating missing #{method} method to #{@klass}. " \
"Accessibility of private/protected class methods in :scope is deprecated and will be removed in Rails 6.0."
@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."

View File

@ -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

View File

@ -303,6 +303,13 @@ class NamedScopingTest < ActiveRecord::TestCase
assert_equal "lifo", topic.author_name assert_equal "lifo", topic.author_name
end end
def test_deprecated_delegating_private_method
assert_deprecated do
scope = Topic.all.by_private_lifo
assert_not scope.instance_variable_get(:@delegate_to_klass)
end
end
def test_reserved_scope_names def test_reserved_scope_names
klass = Class.new(ActiveRecord::Base) do klass = Class.new(ActiveRecord::Base) do
self.table_name = "topics" self.table_name = "topics"

View File

@ -12,9 +12,17 @@ class Topic < ActiveRecord::Base
scope :scope_with_lambda, lambda { all } scope :scope_with_lambda, lambda { all }
scope :by_private_lifo, -> { where(author_name: private_lifo) }
scope :by_lifo, -> { where(author_name: "lifo") } scope :by_lifo, -> { where(author_name: "lifo") }
scope :replied, -> { where "replies_count > 0" } scope :replied, -> { where "replies_count > 0" }
class << self
private
def private_lifo
"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