From aa1608bed4c87d69278d6c9851c904066d8e8252 Mon Sep 17 00:00:00 2001 From: Sean Doyle Date: Sun, 7 Dec 2025 13:44:34 -0500 Subject: [PATCH] Integrate with Active Record First, register the `ActiveModel::Type::Collection` with `ActiveRecord::Type`. Next, add test coverage for writing to and reading from a `TEXT` and `JSON` column. --- .../lib/active_model/type/collection.rb | 4 ++ activerecord/lib/active_record/type.rb | 2 + .../test/cases/collection_attribute_test.rb | 54 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 activerecord/test/cases/collection_attribute_test.rb diff --git a/activemodel/lib/active_model/type/collection.rb b/activemodel/lib/active_model/type/collection.rb index 1d24beb41c578..037998ff8a101 100644 --- a/activemodel/lib/active_model/type/collection.rb +++ b/activemodel/lib/active_model/type/collection.rb @@ -48,6 +48,8 @@ def serialize(value) end def deserialize(value) + return nil if value.nil? + serializer.decode(value).map { |el| @type_object.deserialize(el) } end @@ -66,6 +68,8 @@ def changed_in_place?(raw_old_value, new_value) end def valid_value?(value) + return true if value.nil? + value.is_a?(Array) && value.all? { |el| @type_object.valid_value?(el) } end diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb index dadc02095e6b5..290208375ce98 100644 --- a/activerecord/lib/active_record/type.rb +++ b/activerecord/lib/active_record/type.rb @@ -59,6 +59,7 @@ def current_adapter_name BigInteger = ActiveModel::Type::BigInteger Binary = ActiveModel::Type::Binary Boolean = ActiveModel::Type::Boolean + Collection = ActiveModel::Type::Collection Decimal = ActiveModel::Type::Decimal Float = ActiveModel::Type::Float Integer = ActiveModel::Type::Integer @@ -69,6 +70,7 @@ def current_adapter_name register(:big_integer, Type::BigInteger, override: false) register(:binary, Type::Binary, override: false) register(:boolean, Type::Boolean, override: false) + register(:collection, Type::Collection, override: false) register(:date, Type::Date, override: false) register(:datetime, Type::DateTime, override: false) register(:decimal, Type::Decimal, override: false) diff --git a/activerecord/test/cases/collection_attribute_test.rb b/activerecord/test/cases/collection_attribute_test.rb new file mode 100644 index 0000000000000..f0514800ce001 --- /dev/null +++ b/activerecord/test/cases/collection_attribute_test.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require "cases/helper" + +class CollectionAttributeTest < ActiveRecord::TestCase + self.use_transactional_tests = false + + class CollectionDataTypeOnText < ActiveRecord::Base + attribute :integers, :collection, element_type: :integer + end + + class CollectionDataTypeOnJson < ActiveRecord::Base + attribute :integers, :collection, element_type: :integer + end + + setup do + @connection = ActiveRecord::Base.lease_connection + @connection.create_table(CollectionDataTypeOnText.table_name, force: true) { |t| t.text :integers } + @connection.create_table(CollectionDataTypeOnJson.table_name, force: true) { |t| t.json :integers } + end + + teardown do + @connection.drop_table CollectionDataTypeOnText.table_name, if_exists: true + @connection.drop_table CollectionDataTypeOnJson.table_name, if_exists: true + CollectionDataTypeOnText.reset_column_information + CollectionDataTypeOnJson.reset_column_information + end + + test "writes :collection attribute instance to text column" do + integers = ["1", "2", "3"] + record = CollectionDataTypeOnText.create!(integers: integers) + + assert_equal integers.map(&:to_i), record.integers + end + + test "writes :collection attribute instance to json column" do + integers = ["1", "2", "3"] + record = CollectionDataTypeOnJson.create!(integers: integers) + + assert_equal integers.map(&:to_i), record.integers + end + + test "reads nil :collection attribute instance from text column" do + record = CollectionDataTypeOnText.create!(integers: nil) + + assert_empty record.integers + end + + test "reads nil :collection attribute instance from json column" do + record = CollectionDataTypeOnJson.create!(integers: nil) + + assert_empty record.integers + end +end