Skip to content

Commit 7d525d3

Browse files
Merge pull request rails#49910 from jonathanhefner/active_model-type_for_attribute
Port `type_for_attribute` to Active Model
2 parents d0f40f7 + 83f543b commit 7d525d3

File tree

7 files changed

+66
-22
lines changed

7 files changed

+66
-22
lines changed

activemodel/CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,17 @@
1+
* Port the `type_for_attribute` method to Active Model. Classes that include
2+
`ActiveModel::Attributes` will now provide this method. This method behaves
3+
the same for Active Model as it does for Active Record.
4+
5+
```ruby
6+
class MyModel
7+
include ActiveModel::Attributes
8+
9+
attribute :my_attribute, :integer
10+
end
11+
12+
MyModel.type_for_attribute(:my_attribute) # => #<ActiveModel::Type::Integer ...>
13+
```
14+
15+
*Jonathan Hefner*
116

217
Please check [7-1-stable](https://github.com/rails/rails/blob/7-1-stable/activemodel/CHANGELOG.md) for previous changes.

activemodel/lib/active_model/attribute_registration.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,21 @@ def attribute_types # :nodoc:
4040
end
4141
end
4242

43+
# Returns the type of the specified attribute after applying any
44+
# modifiers. This method is the only valid source of information for
45+
# anything related to the types of a model's attributes. The return value
46+
# of this method will implement the interface described by
47+
# ActiveModel::Type::Value (though the object itself may not subclass it).
48+
def type_for_attribute(attribute_name, &block)
49+
attribute_name = resolve_attribute_name(attribute_name)
50+
51+
if block
52+
attribute_types.fetch(attribute_name, &block)
53+
else
54+
attribute_types[attribute_name]
55+
end
56+
end
57+
4358
private
4459
PendingType = Struct.new(:name, :type) do # :nodoc:
4560
def apply_to(attribute_set)

activemodel/test/cases/attribute_registration_test.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,17 @@ def initialize(name, cast_type)
7373
assert_equal Type::Value.new, klass.attribute_types["bar"]
7474
end
7575

76+
test ".type_for_attribute returns the registered attribute type" do
77+
klass = class_with { attribute :foo, TYPE_1 }
78+
assert_same TYPE_1, klass.type_for_attribute("foo")
79+
assert_same TYPE_1, klass.type_for_attribute(:foo)
80+
end
81+
82+
test ".type_for_attribute returns the default type when an unregistered attribute is specified" do
83+
klass = class_with { attribute :foo, TYPE_1 }
84+
assert_equal Type::Value.new, klass.type_for_attribute("bar")
85+
end
86+
7687
test "new attributes can be registered at any time" do
7788
klass = class_with { attribute :foo, TYPE_1 }
7889
assert_includes klass._default_attributes, "foo"

activemodel/test/cases/attributes_test.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,5 +175,13 @@ def attribute=(_, _)
175175
ModelForAttributesTest.attribute :foo, :unknown
176176
end
177177
end
178+
179+
test ".type_for_attribute supports attribute aliases" do
180+
with_alias = Class.new(ModelForAttributesTest) do
181+
alias_attribute :integer_field, :x
182+
end
183+
184+
assert_equal with_alias.type_for_attribute(:integer_field), with_alias.type_for_attribute(:x)
185+
end
178186
end
179187
end

activerecord/lib/active_record/attributes.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,15 @@ def _default_attributes # :nodoc:
249249
end
250250
end
251251

252+
##
253+
# :method: type_for_attribute
254+
# :call-seq: type_for_attribute(attribute_name, &block)
255+
#
256+
# See ActiveModel::AttributeRegistration::ClassMethods#type_for_attribute.
257+
#
258+
# This method will access the database and load the model's schema if
259+
# necessary.
260+
252261
protected
253262
def reload_schema_from_cache(*)
254263
reset_default_attributes!

activerecord/lib/active_record/model_schema.rb

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -439,28 +439,6 @@ def yaml_encoder # :nodoc:
439439
@yaml_encoder ||= ActiveModel::AttributeSet::YAMLEncoder.new(attribute_types)
440440
end
441441

442-
# Returns the type of the attribute with the given name, after applying
443-
# all modifiers. This method is the only valid source of information for
444-
# anything related to the types of a model's attributes. This method will
445-
# access the database and load the model's schema if it is required.
446-
#
447-
# The return value of this method will implement the interface described
448-
# by ActiveModel::Type::Value (though the object itself may not subclass
449-
# it).
450-
#
451-
# +attr_name+ The name of the attribute to retrieve the type for. Must be
452-
# a string or a symbol.
453-
def type_for_attribute(attr_name, &block)
454-
attr_name = attr_name.to_s
455-
attr_name = attribute_aliases[attr_name] || attr_name
456-
457-
if block
458-
attribute_types.fetch(attr_name, &block)
459-
else
460-
attribute_types[attr_name]
461-
end
462-
end
463-
464442
# Returns the column object for the named attribute.
465443
# Returns an +ActiveRecord::ConnectionAdapters::NullColumn+ if the
466444
# named attribute does not exist.

activerecord/test/cases/attributes_test.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ class CustomPropertiesTest < ActiveRecord::TestCase
5151
assert_equal 3, data.overloaded_float
5252
end
5353

54+
test ".type_for_attribute supports attribute aliases" do
55+
with_alias = Class.new(OverloadedType) do
56+
alias_attribute :overloaded_float, :x
57+
end
58+
59+
assert_equal with_alias.type_for_attribute(:overloaded_float), with_alias.type_for_attribute(:x)
60+
end
61+
5462
test "overloaded properties with limit" do
5563
assert_equal 50, OverloadedType.type_for_attribute("overloaded_string_with_limit").limit
5664
assert_equal 255, UnoverloadedType.type_for_attribute("overloaded_string_with_limit").limit

0 commit comments

Comments
 (0)