diff --git a/CHANGELOG.md b/CHANGELOG.md index 4360c4c..b588ebb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### bug fix +* Fixed `Enumerize::ActiveModelAttributesSupport::Type#deserialize` to properly handle arrays for `multiple: true` attributes. Previously, deserializing an array would return `nil` instead of the enumerated values. This bug only affected ActiveModel::Attributes usage (not ActiveRecord) and was exposed when used with gems like store_model v2.0.0+ that call `deserialize` during load. + ### enchancements * Support only Ruby 3.1+ and Rails 7.0+. (by [@nashby](https://github.com/nashby)) diff --git a/lib/enumerize/activemodel.rb b/lib/enumerize/activemodel.rb index ae00735..b57a043 100644 --- a/lib/enumerize/activemodel.rb +++ b/lib/enumerize/activemodel.rb @@ -40,7 +40,15 @@ def serialize(value) end def deserialize(value) - @attr.find_value(value) + return nil if value.nil? + + # Use find_values for arrays on multiple: true attributes + # Otherwise use find_value for single values + if value.is_a?(Array) && @attr.arguments[:multiple] + @attr.find_values(*value) + else + @attr.find_value(value) + end end end end diff --git a/test/activemodel_test.rb b/test/activemodel_test.rb index af98af5..b2c2e52 100644 --- a/test/activemodel_test.rb +++ b/test/activemodel_test.rb @@ -107,6 +107,82 @@ class InterestsRequiredActiveModelUser < ActiveModelUser expect(user.errors[:interests]).must_be_empty end + + describe 'Type#deserialize' do + it 'deserializes single value' do + type = model.attribute_types['sex'] + result = type.deserialize('male') + expect(result).must_be_instance_of Enumerize::Value + expect(result.to_s).must_equal 'male' + end + + it 'returns nil for nil single value' do + type = model.attribute_types['sex'] + result = type.deserialize(nil) + expect(result).must_be_nil + end + + it 'returns nil for invalid single value' do + type = model.attribute_types['sex'] + result = type.deserialize('invalid') + expect(result).must_be_nil + end + + it 'treats array as invalid for non-multiple attribute' do + type = model.attribute_types['sex'] + result = type.deserialize(['male', 'female']) + expect(result).must_be_nil + end + + it 'deserializes array of valid values for multiple attribute' do + type = model.attribute_types['interests'] + result = type.deserialize(['music', 'sports']) + expect(result).must_be_instance_of Array + expect(result.map(&:to_s)).must_equal ['music', 'sports'] + end + + it 'deserializes empty array for multiple attribute' do + type = model.attribute_types['interests'] + result = type.deserialize([]) + expect(result).must_equal [] + end + + it 'filters out invalid values from array' do + type = model.attribute_types['interests'] + result = type.deserialize(['music', 'invalid', 'sports']) + expect(result.map(&:to_s)).must_equal ['music', 'sports'] + end + + it 'returns nil for nil array value' do + type = model.attribute_types['interests'] + result = type.deserialize(nil) + expect(result).must_be_nil + end + + it 'preserves values through serialize/deserialize cycle for single value' do + type = model.attribute_types['sex'] + user = model.new(sex: 'female') + + serialized = type.serialize(user.sex) + expect(serialized).must_equal 'female' + + deserialized = type.deserialize(serialized) + expect(deserialized.to_s).must_equal 'female' + end + + it 'preserves values through serialize/deserialize cycle for multiple values' do + type = model.attribute_types['interests'] + user = model.new(interests: ['music', 'programming']) + + # Serialize the entire set + serialized = user.interests.map { |v| type.serialize(v) } + expect(serialized).must_equal ['music', 'programming'] + + # Deserialize back + deserialized = type.deserialize(serialized) + expect(deserialized.map(&:to_s)).must_equal ['music', 'programming'] + end + end end else