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) 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