Skip to content

Commit a662191

Browse files
committed
alias_attribute: handle user defined source methods
`alias_attribute` used to define a "jump method", e.g. `alias_attribute :foo, :bar` was pretty much a macro to generate ```ruby def foo bar end ``` This is convienient because it's easy, it doesn't impose an order of declaration or anything like that. But it's also much less efficient than a true `alias_method`. It also used to cause cache size explosion which we fixed in rails#52118, but making it behave like Ruby's `alias_method`, by doing a real alias. But this breaks some expectations (literally from the documentation): ```ruby attr_accessor :name attribute_method_suffix '_short?' define_attribute_methods :name alias_attribute :nickname, :name ``` Here we're not supposed to alias a generated method, but a user defined one. So this assumption can only hold for Active Record, not Active Model.
1 parent 17ba9d2 commit a662191

File tree

2 files changed

+48
-3
lines changed

2 files changed

+48
-3
lines changed

activemodel/lib/active_model/attribute_methods.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,12 @@ def eagerly_generate_alias_attribute_methods(new_name, old_name) # :nodoc:
215215
end
216216

217217
def generate_alias_attribute_methods(code_generator, new_name, old_name)
218-
define_attribute_method(old_name, _owner: code_generator, as: new_name)
218+
ActiveSupport::CodeGenerator.batch(code_generator, __FILE__, __LINE__) do |owner|
219+
attribute_method_patterns.each do |pattern|
220+
alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
221+
end
222+
attribute_method_patterns_cache.clear
223+
end
219224
end
220225

221226
def alias_attribute_method_definition(code_generator, pattern, new_name, old_name) # :nodoc:
@@ -228,7 +233,7 @@ def alias_attribute_method_definition(code_generator, pattern, new_name, old_nam
228233
call_args = []
229234
call_args << parameters if parameters
230235

231-
define_call(code_generator, method_name, target_name, mangled_name, parameters, call_args, namespace: :alias_attribute)
236+
define_call(code_generator, method_name, target_name, mangled_name, parameters, call_args, namespace: :alias_attribute, as: method_name)
232237
end
233238

234239
# Is +new_name+ an alias?
@@ -441,7 +446,7 @@ def build_mangled_name(name)
441446
mangled_name = name
442447

443448
unless NAME_COMPILABLE_REGEXP.match?(name)
444-
mangled_name = "__temp__#{name.unpack1("h*")}"
449+
mangled_name = :"__temp__#{name.unpack1("h*")}"
445450
end
446451

447452
mangled_name

activemodel/test/cases/attribute_methods_test.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,4 +371,44 @@ def attribute?(name)
371371
assert_equal :model_1, NameClash::Model1.new.x_changed?
372372
assert_equal :model_2, NameClash::Model2.new.x_changed?
373373
end
374+
375+
test "alias attribute respects user defined method" do
376+
model = Class.new do
377+
include ActiveModel::AttributeMethods
378+
379+
attr_accessor :name
380+
define_attribute_methods :name
381+
382+
alias_attribute :nickname, :name
383+
384+
def initialize(name)
385+
@name = name
386+
end
387+
end
388+
389+
instance = model.new("George")
390+
assert_equal "George", instance.name
391+
assert_equal "George", instance.nickname
392+
end
393+
394+
test "alias attribute respects user defined method in parent classes" do
395+
model = Class.new do
396+
include ActiveModel::AttributeMethods
397+
398+
attr_accessor :name
399+
define_attribute_methods :name
400+
401+
def initialize(name)
402+
@name = name
403+
end
404+
end
405+
406+
subclass = Class.new(model) do
407+
alias_attribute :nickname, :name
408+
end
409+
410+
instance = subclass.new("George")
411+
assert_equal "George", instance.name
412+
assert_equal "George", instance.nickname
413+
end
374414
end

0 commit comments

Comments
 (0)