Skip to content

Commit

Permalink
Merge pull request #34122 from kamipo/generate_relation_methods
Browse files Browse the repository at this point in the history
Generate delegation methods to named scope in the definition time
  • Loading branch information
kamipo committed Oct 10, 2018
2 parents 6c69a96 + 136b738 commit a52c698
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 5 deletions.
30 changes: 30 additions & 0 deletions activerecord/lib/active_record/relation/delegation.rb
Expand Up @@ -17,6 +17,7 @@ def initialize_relation_delegate_cache
delegate = Class.new(klass) {
include ClassSpecificRelation
}
include_relation_methods(delegate)
mangled_name = klass.name.gsub("::", "_")
const_set mangled_name, delegate
private_constant mangled_name
Expand All @@ -29,6 +30,35 @@ def inherited(child_class)
child_class.initialize_relation_delegate_cache
super
end

protected
def include_relation_methods(delegate)
superclass.include_relation_methods(delegate) unless base_class?
delegate.include generated_relation_methods
end

private
def generated_relation_methods
@generated_relation_methods ||= Module.new.tap do |mod|
mod_name = "GeneratedRelationMethods"
const_set mod_name, mod
private_constant mod_name
end
end

def generate_relation_method(method)
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
generated_relation_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{method}(*args, &block)
scoping { klass.#{method}(*args, &block) }
end
RUBY
else
generated_relation_methods.send(:define_method, method) do |*args, &block|
scoping { klass.public_send(method, *args, &block) }
end
end
end
end

extend ActiveSupport::Concern
Expand Down
2 changes: 2 additions & 0 deletions activerecord/lib/active_record/scoping/named.rb
Expand Up @@ -191,6 +191,8 @@ def scope(name, body, &block)
scope
end
end

generate_relation_method(name)
end

private
Expand Down
Expand Up @@ -2433,6 +2433,7 @@ def test_replace_returns_target
def test_collection_association_with_private_kernel_method
firm = companies(:first_firm)
assert_equal [accounts(:signals37)], firm.accounts.open
assert_equal [accounts(:signals37)], firm.accounts.available
end

def test_association_with_or_doesnt_set_inverse_instance_key
Expand Down
10 changes: 10 additions & 0 deletions activerecord/test/cases/relations_test.rb
Expand Up @@ -1812,6 +1812,16 @@ def test_relation_join_method
assert_equal "Thank you for the welcome,Thank you again for the welcome", Post.first.comments.join(",")
end

def test_relation_with_private_kernel_method
accounts = Account.all
assert_equal [accounts(:signals37)], accounts.open
assert_equal [accounts(:signals37)], accounts.available

sub_accounts = SubAccount.all
assert_equal [accounts(:signals37)], sub_accounts.open
assert_equal [accounts(:signals37)], sub_accounts.available
end

test "#skip_query_cache!" do
Post.cache do
assert_queries(1) do
Expand Down
13 changes: 10 additions & 3 deletions activerecord/test/models/account.rb
Expand Up @@ -11,9 +11,8 @@ def self.destroyed_account_ids
end

# Test private kernel method through collection proxy using has_many.
def self.open
where("firm_name = ?", "37signals")
end
scope :open, -> { where("firm_name = ?", "37signals") }
scope :available, -> { open }

before_destroy do |account|
if account.firm
Expand All @@ -32,3 +31,11 @@ def private_method
"Sir, yes sir!"
end
end

class SubAccount < Account
def self.instantiate_instance_of(klass, attributes, column_types = {}, &block)
klass = superclass
super
end
private_class_method :instantiate_instance_of
end
8 changes: 6 additions & 2 deletions activerecord/test/models/post.rb
Expand Up @@ -297,8 +297,6 @@ class SubConditionalStiPost < ConditionalStiPost
class FakeKlass
extend ActiveRecord::Delegation::DelegateCache

inherited self

class << self
def connection
Post.connection
Expand Down Expand Up @@ -335,5 +333,11 @@ def arel_table
def predicate_builder
Post.predicate_builder
end

def base_class?
true
end
end

inherited self
end

0 comments on commit a52c698

Please sign in to comment.