Skip to content

Commit

Permalink
Merge pull request #12403 from thedarkone/attr-method-missing-fix
Browse files Browse the repository at this point in the history
Fix AR#method_missing re-dispatching into overwritten attribute methods

Conflicts:
	activerecord/lib/active_record/attribute_methods.rb
  • Loading branch information
rafaelfranca committed Dec 4, 2013
2 parents d261c5c + e9bf87f commit c294055
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 3 deletions.
19 changes: 18 additions & 1 deletion activerecord/lib/active_record/attribute_methods.rb
Expand Up @@ -128,6 +128,16 @@ def method_defined_within?(name, klass, sup = klass.superclass) # :nodoc:
end
end

def find_generated_attribute_method(method_name) # :nodoc:
klass = self
until klass == Base
gen_methods = klass.generated_attribute_methods
return gen_methods.instance_method(method_name) if method_defined_within?(method_name, gen_methods, Object)
klass = klass.superclass
end
nil
end

# Returns +true+ if +attribute+ is an attribute method and table exists,
# +false+ otherwise.
#
Expand Down Expand Up @@ -163,7 +173,14 @@ def attribute_names
def method_missing(method, *args, &block) # :nodoc:
self.class.define_attribute_methods
if respond_to_without_attributes?(method)
send(method, *args, &block)
# make sure to invoke the correct attribute method, as we might have gotten here via a `super`
# call in a overwritten attribute method
if attribute_method = self.class.find_generated_attribute_method(method)
# this is probably horribly slow, but should only happen at most once for a given AR class
attribute_method.bind(self).call(*args, &block)
else
send(method, *args, &block)
end
else
super
end
Expand Down
35 changes: 33 additions & 2 deletions activerecord/test/cases/attribute_methods_test.rb
Expand Up @@ -767,8 +767,7 @@ def test_read_attribute_with_nil_should_not_asplode
# that by defining a 'foo' method in the generated methods module for B.
# (That module will be inserted between the two, e.g. [B, <GeneratedAttributes>, A].)
def test_inherited_custom_accessors
klass = Class.new(ActiveRecord::Base) do
self.table_name = "topics"
klass = new_topic_like_ar_class do
self.abstract_class = true
def title; "omg"; end
def title=(val); self.author_name = val; end
Expand All @@ -783,8 +782,40 @@ def title=(val); self.author_name = val; end
assert_equal "lol", topic.author_name
end

def test_on_the_fly_super_invokable_generated_attribute_methods_via_method_missing
klass = new_topic_like_ar_class do
def title
super + '!'
end
end

real_topic = topics(:first)
assert_equal real_topic.title + '!', klass.find(real_topic.id).title
end

def test_on_the_fly_super_invokable_generated_predicate_attribute_methods_via_method_missing
klass = new_topic_like_ar_class do
def title?
!super
end
end

real_topic = topics(:first)
assert_equal !real_topic.title?, klass.find(real_topic.id).title?
end

private

def new_topic_like_ar_class(&block)
klass = Class.new(ActiveRecord::Base) do
self.table_name = 'topics'
class_eval(&block)
end

assert_empty klass.generated_attribute_methods.instance_methods(false)
klass
end

def cached_columns
Topic.columns.map(&:name) - Topic.serialized_attributes.keys
end
Expand Down

0 comments on commit c294055

Please sign in to comment.