diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb index fa075d1cdf9fd..30347bc6b7a70 100644 --- a/activerecord/lib/active_record/attributes.rb +++ b/activerecord/lib/active_record/attributes.rb @@ -266,6 +266,12 @@ def load_schema! # :nodoc: end end + def decorate_attribute(name, type_name, default: NO_DEFAULT_PROVIDED, **options) + attribute(name, default: default) do |subtype| + Type.lookup(type_name, **options, subtype: subtype, adapter: Type.adapter_name_from(self)) + end + end + private NO_DEFAULT_PROVIDED = Object.new # :nodoc: private_constant :NO_DEFAULT_PROVIDED diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb index a750e572ecea9..35a17ddada352 100644 --- a/activerecord/test/cases/serialized_attribute_test.rb +++ b/activerecord/test/cases/serialized_attribute_test.rb @@ -424,6 +424,24 @@ def changed_in_place?(old, new) end end + def test_decorate_attribute_on_serialized_attribute + old_registry = ActiveRecord::Type.registry + ActiveRecord::Type.registry = ActiveRecord::Type.registry.dup + ActiveRecord::Type.register :encrypted, EncryptedType + + klass = Class.new(ActiveRecord::Base) do + self.table_name = Topic.table_name + store :content + decorate_attribute :content, :encrypted + end + + topic = klass.create!(content: { trial: true }) + + assert_equal({ "trial" => true }, topic.content) + ensure + ActiveRecord::Type.registry = old_registry + end + def test_decorated_type_with_type_for_attribute old_registry = ActiveRecord::Type.registry ActiveRecord::Type.registry = ActiveRecord::Type.registry.dup