From bd03b760675dff7eef45cc696fd3aed6fcf104d9 Mon Sep 17 00:00:00 2001 From: Shinji Nakamatsu <19329+snaka@users.noreply.github.com> Date: Wed, 6 Aug 2025 22:52:49 +0900 Subject: [PATCH 1/2] Add comprehensive tests for ActiveRecord Type#cast and bulk operations - Test Type#cast returns Enumerize::Value for symbols, integers, and strings - Test Type#cast handles invalid values and preserves existing Value objects - Test insert_all/upsert_all work with mixed value types and handle invalid values --- test/activerecord_test.rb | 102 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/test/activerecord_test.rb b/test/activerecord_test.rb index e345de9..5fdc213 100644 --- a/test/activerecord_test.rb +++ b/test/activerecord_test.rb @@ -754,4 +754,106 @@ class AdminUser < User expect(User.exists?(status: :active)).must_equal true expect(User.exists?(interests: [:music, :sports])).must_equal true end + + it 'Type#cast returns Enumerize::Value for valid symbols' do + type = User.type_for_attribute('status') + result = type.cast(:active) + + expect(result).must_be_instance_of Enumerize::Value + expect(result.to_s).must_equal 'active' + expect(result.value).must_equal 1 + end + + it 'Type#cast returns Enumerize::Value for integers' do + type = User.type_for_attribute('status') + result = type.cast(1) + + expect(result).must_be_instance_of Enumerize::Value + expect(result.to_s).must_equal 'active' + expect(result.value).must_equal 1 + end + + it 'Type#cast returns Enumerize::Value for valid string keys' do + type = User.type_for_attribute('status') + result = type.cast('active') + + expect(result).must_be_instance_of Enumerize::Value + expect(result.to_s).must_equal 'active' + expect(result.value).must_equal 1 + end + + it 'Type#cast returns nil for invalid values' do + type = User.type_for_attribute('status') + result = type.cast(:invalid) + + expect(result).must_be_nil + end + + it 'Type#cast preserves existing Enumerize::Value objects' do + type = User.type_for_attribute('status') + original_value = User.enumerized_attributes[:status].find_value(:active) + result = type.cast(original_value) + + expect(result).must_equal original_value + end + + it 'insert_all with mixed value types works correctly' do + User.delete_all + + User.insert_all([ + { sex: :male, status: :active }, + { sex: 'female', status: 2 }, + { sex: :male, status: 'blocked' } + ]) + + users = User.all.to_a + expect(users.size).must_equal 3 + + expect(users[0].sex).must_equal 'male' + expect(users[0].status).must_equal 'active' + + expect(users[1].sex).must_equal 'female' + expect(users[1].status).must_equal 'blocked' + + expect(users[2].sex).must_equal 'male' + expect(users[2].status).must_equal 'blocked' + end + + it 'insert_all handles invalid enum values gracefully' do + User.delete_all + + User.insert_all([ + { sex: :invalid_sex, status: 999 } # Use invalid integer for status + ]) + + user = User.first + expect(user.sex).must_be_nil + expect(user.status).must_be_nil + end + + it 'upsert_all with different value formats works correctly' do + User.delete_all + + User.upsert_all([ + { id: 1, sex: :male, status: 1 }, + { id: 2, sex: 'female', status: :blocked } + ]) + + User.upsert_all([ + { id: 1, sex: 'male', status: :active }, + { id: 3, sex: :female, status: 2 } + ]) + + users = User.order(:id).to_a + expect(users.size).must_equal 3 + + expect(users[0].sex).must_equal 'male' + expect(users[0].status).must_equal 'active' + + expect(users[1].sex).must_equal 'female' + expect(users[1].status).must_equal 'blocked' + + expect(users[2].sex).must_equal 'female' + expect(users[2].status).must_equal 'blocked' + end end From 8cc471602764bf9b0d13d9336ae31c543afc35f4 Mon Sep 17 00:00:00 2001 From: Shinji Nakamatsu <19329+snaka@users.noreply.github.com> Date: Thu, 21 Aug 2025 21:45:29 +0900 Subject: [PATCH 2/2] Fix Type#cast to handle symbol values correctly in Rails 8.0 bulk operations Rails 8.0 changed how Type#cast is used during insert_all/upsert_all operations, causing symbol enum values (e.g., :active) to fail. The previous implementation always delegated to @subtype.cast first, which could incorrectly transform symbol values before looking them up. This fix: - First attempts to find the enumerize value directly with the input value - Only delegates to @subtype.cast if the direct lookup fails - Ensures both symbol values (:active) and their database representations (1) work correctly This maintains backward compatibility while fixing bulk operations in Rails 8.0. --- lib/enumerize/activerecord.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/enumerize/activerecord.rb b/lib/enumerize/activerecord.rb index 8e508da..068b280 100644 --- a/lib/enumerize/activerecord.rb +++ b/lib/enumerize/activerecord.rb @@ -123,12 +123,14 @@ def serialize(value) def cast(value) return value if @subtype.is_a?(Type) + return value if value.is_a?(::Enumerize::Value) - if value.is_a?(::Enumerize::Value) - value - else - @attr.find_value(@subtype.cast(value)) - end + # First try to find the enumerize value directly + enumerize_value = @attr.find_value(value) + return enumerize_value if enumerize_value + + # If not found, delegate to subtype then try to find value + @attr.find_value(@subtype.cast(value)) end def as_json(options = nil)