Skip to content

Commit 9ccbab1

Browse files
authored
Merge pull request rails#52145 from Shopify/alias-attribute-override-inherited-methods
Fix `alias_attribute` to ignore methods defined in parent classes
2 parents 98636b3 + 403743e commit 9ccbab1

File tree

3 files changed

+43
-17
lines changed

3 files changed

+43
-17
lines changed

activemodel/lib/active_model/attribute_methods.rb

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -312,26 +312,33 @@ def define_attribute_method(attr_name, _owner: generated_attribute_methods, as:
312312
end
313313
end
314314

315-
def define_attribute_method_pattern(pattern, attr_name, owner:, as:) # :nodoc:
315+
def define_attribute_method_pattern(pattern, attr_name, owner:, as:, override: false) # :nodoc:
316316
canonical_method_name = pattern.method_name(attr_name)
317317
public_method_name = pattern.method_name(as)
318318

319-
unless instance_method_already_implemented?(public_method_name)
320-
generate_method = "define_method_#{pattern.proxy_target}"
319+
# If defining a regular attribute method, we don't override methods that are explictly
320+
# defined in parrent classes.
321+
if instance_method_already_implemented?(public_method_name)
322+
# However, for `alias_attribute`, we always define the method.
323+
# We check for override second because `instance_method_already_implemented?`
324+
# also check for dangerous methods.
325+
return unless override
326+
end
321327

322-
if respond_to?(generate_method, true)
323-
send(generate_method, attr_name.to_s, owner: owner, as: as)
324-
else
325-
define_proxy_call(
326-
owner,
327-
canonical_method_name,
328-
pattern.proxy_target,
329-
pattern.parameters,
330-
attr_name.to_s,
331-
namespace: :active_model_proxy,
332-
as: public_method_name
333-
)
334-
end
328+
generate_method = "define_method_#{pattern.proxy_target}"
329+
330+
if respond_to?(generate_method, true)
331+
send(generate_method, attr_name.to_s, owner: owner, as: as)
332+
else
333+
define_proxy_call(
334+
owner,
335+
canonical_method_name,
336+
pattern.proxy_target,
337+
pattern.parameters,
338+
attr_name.to_s,
339+
namespace: :active_model_proxy,
340+
as: public_method_name
341+
)
335342
end
336343
end
337344

activerecord/lib/active_record/attribute_methods.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def alias_attribute_method_definition(code_generator, pattern, new_name, old_nam
9191
raise ArgumentError, "#{self.name} model aliases `#{old_name}`, but `#{old_name}` is not an attribute. " \
9292
"Use `alias_method :#{new_name}, :#{old_name}` or define the method manually."
9393
else
94-
define_attribute_method_pattern(pattern, old_name, owner: code_generator, as: new_name)
94+
define_attribute_method_pattern(pattern, old_name, owner: code_generator, as: new_name, override: true)
9595
end
9696
end
9797

activerecord/test/cases/attribute_methods_test.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,6 +1217,25 @@ def some_method_that_is_not_on_super
12171217
alias_attribute :subject, :title
12181218
end
12191219

1220+
test "#alias_attribute override methods defined in parent models" do
1221+
parent_model = Class.new(ActiveRecord::Base) do
1222+
self.abstract_class = true
1223+
1224+
def subject
1225+
"Abstract Subject"
1226+
end
1227+
end
1228+
1229+
subclass = Class.new(parent_model) do
1230+
self.table_name = "topics"
1231+
alias_attribute :subject, :title
1232+
end
1233+
1234+
obj = subclass.new
1235+
obj.title = "hey"
1236+
assert_equal("hey", obj.subject)
1237+
end
1238+
12201239
test "aliases to the same attribute name do not conflict with each other" do
12211240
first_model_object = ToBeLoadedFirst.new(author_name: "author 1")
12221241
assert_equal("author 1", first_model_object.subject)

0 commit comments

Comments
 (0)