Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add attributes_with_aliases and attribute_names_with_aliases #48619

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 22 additions & 0 deletions activemodel/lib/active_model/attributes.rb
Expand Up @@ -75,6 +75,16 @@ def attribute_names
attribute_types.keys
end

def attribute_names_with_aliases(replace: false)
attrs = attribute_names.map(&:clone)
attribute_aliases.each do |k, v|
next unless (i = attrs.find_index(v))

attrs[replace ? i : attrs.length] = k
end
attrs
end

private
def define_method_attribute=(name, owner:)
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
Expand Down Expand Up @@ -134,6 +144,18 @@ def attribute_names
@attributes.keys
end

def attributes_with_aliases(replace: false)
attrs = attributes.clone
attribute_aliases.each do |k, v|
attrs[k] = replace ? attrs.delete(v) : send(v)
end
attrs
end

def attribute_names_with_aliases(replace: false)
attributes_with_aliases(replace: replace).keys
end

def freeze # :nodoc:
@attributes = @attributes.clone.freeze unless frozen?
super
Expand Down
35 changes: 35 additions & 0 deletions activemodel/test/cases/attributes_test.rb
Expand Up @@ -41,6 +41,10 @@ def attribute=(_, _)
end
end

class ModelWithAliasAttribute < ModelWithGeneratedAttributeMethods
alias_attribute :alias_foo, :foo
end

test "models that proxy attributes do not conflict with models with generated methods" do
ModelWithGeneratedAttributeMethods.new

Expand Down Expand Up @@ -175,5 +179,36 @@ def attribute=(_, _)
ModelForAttributesTest.attribute :foo, :unknown
end
end

test "reading attributes with aliases" do
model = ModelWithAliasAttribute.new
model.foo = "foo"
expected_attributes = { "foo" => "foo", "alias_foo" => "foo" }
assert_equal expected_attributes, model.attributes_with_aliases
end

test "reading attributes replaced by aliases" do
model = ModelWithAliasAttribute.new
model.foo = "foo"
expected_attributes = { "alias_foo" => "foo" }
assert_equal expected_attributes, model.attributes_with_aliases(replace: true)
end

test "reading attribute names with aliases" do
names = [
"foo",
"alias_foo"
]
assert_equal names, ModelWithAliasAttribute.attribute_names_with_aliases
assert_equal names, ModelWithAliasAttribute.new.attribute_names_with_aliases
end

test "reading attribute names replaced by aliases" do
names = [
"alias_foo"
]
assert_equal names, ModelWithAliasAttribute.attribute_names_with_aliases(replace: true)
assert_equal names, ModelWithAliasAttribute.new.attribute_names_with_aliases(replace: true)
end
end
end
22 changes: 22 additions & 0 deletions activerecord/lib/active_record/attribute_methods.rb
Expand Up @@ -163,6 +163,16 @@ def attribute_names
end.freeze
end

def attribute_names_with_aliases(replace: false)
attrs = attribute_names.map(&:clone)
attribute_aliases.each do |k, v|
next unless (i = attrs.find_index(v))

attrs[replace ? i : attrs.length] = k
end
attrs
end

# Returns true if the given attribute exists, otherwise false.
#
# class Person < ActiveRecord::Base
Expand Down Expand Up @@ -270,6 +280,18 @@ def attributes
@attributes.to_hash
end

def attributes_with_aliases(replace: false)
attrs = attributes.clone
attribute_aliases.each do |k, v|
attrs[k] = replace ? attrs.delete(v) : send(v)
end
attrs
end

def attribute_names_with_aliases(replace: false)
attributes_with_aliases(replace: replace).keys
end

# Returns an <tt>#inspect</tt>-like string for the value of the
# attribute +attr_name+. String attributes are truncated up to 50
# characters. Other attributes return the value of <tt>#inspect</tt>
Expand Down
26 changes: 26 additions & 0 deletions activerecord/test/cases/attribute_methods_test.rb
Expand Up @@ -1130,6 +1130,32 @@ def some_method_that_is_not_on_super
assert_equal "abcd", model.read_attribute_before_type_cast("new_bank_balance")
end

test "reading attributes with aliases" do
topic = Topic.new(title: "Hello")
expected_attributes = topic.attributes.merge("heading" => "Hello")
assert_equal expected_attributes, topic.attributes_with_aliases
end

test "reading attributes replaced by aliases" do
topic = Topic.new(title: "Hello")
expected_attributes = topic.attributes.except("title").merge("heading" => "Hello")
assert_equal expected_attributes, topic.attributes_with_aliases(replace: true)
end

test "reading attribute names with aliases" do
model = @target.new
expected_names = @target.column_names + @target.attribute_aliases.keys
assert_equal expected_names, @target.attribute_names_with_aliases
assert_equal expected_names, model.attribute_names_with_aliases
end

test "reading attribute names replaced by aliases" do
model = @target.new
expected_names = @target.column_names - @target.attribute_aliases.values + @target.attribute_aliases.keys
assert_equal expected_names, @target.attribute_names_with_aliases(replace: true)
assert_equal expected_names, model.attribute_names_with_aliases(replace: true)
end

private
def new_topic_like_ar_class(&block)
klass = Class.new(ActiveRecord::Base) do
Expand Down