Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions lib/enumerize/activerecord.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,19 @@ def initialize(attr, subtype)
end

def serialize(value)
v = @attr.find_value(value)
return value unless v
# First try to find the enumerize value directly
enumerize_value = @attr.find_value(value)

# If not found and we have a subtype, delegate to it for transformation
# (e.g., normalization) and try again
if !enumerize_value && @subtype
casted_value = @subtype.cast(value)
enumerize_value = @attr.find_value(casted_value)
end

v.value
return value unless enumerize_value

enumerize_value.value
end

def cast(value)
Expand All @@ -127,10 +136,15 @@ def cast(value)

# 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))
# If not found and we have a subtype, delegate to it for transformation
# (e.g., normalization) and try again
if !enumerize_value && @subtype
casted_value = @subtype.cast(value)
enumerize_value = @attr.find_value(casted_value)
end

enumerize_value
end

def as_json(options = nil)
Expand Down
28 changes: 22 additions & 6 deletions lib/enumerize/attribute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ def respond_to_missing?(method, include_private=false)
@value_hash.include?(method.to_s) || super
end

# Defines getter and setter methods for the enumerated attribute.
#
# The setter method behavior:
# - Valid enum values: Stores the underlying value
# - Empty strings: Converts to nil (historically consistent behavior, now explicitly maintained)
# - Invalid values: Passes through to allow Rails processing (e.g., normalizes, type casting)
#
# Note: Empty string to nil conversion was implicit in previous versions but is now
# explicitly implemented to ensure consistent behavior across all ORMs.
def define_methods!(mod)
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{name}
Expand All @@ -105,20 +114,27 @@ def #{name}
end

def #{name}=(new_value)
allowed_value_or_nil = self.class.enumerized_attributes[:#{name}].find_value(new_value)
allowed_value_or_nil = allowed_value_or_nil.value unless allowed_value_or_nil.nil?
allowed_value = self.class.enumerized_attributes[:#{name}].find_value(new_value)

value_to_assign = if allowed_value
allowed_value.value
elsif new_value == ''
nil
else
new_value
end

if defined?(super)
super allowed_value_or_nil
super value_to_assign
elsif respond_to?(:write_attribute, true)
write_attribute '#{name}', allowed_value_or_nil
write_attribute '#{name}', value_to_assign
else
@#{name} = allowed_value_or_nil
@#{name} = value_to_assign
end

_enumerized_values_for_validation['#{name}'] = new_value.nil? ? nil : new_value.to_s

allowed_value_or_nil
value_to_assign
end

def #{name}_text
Expand Down
33 changes: 27 additions & 6 deletions test/activerecord_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
t.string :foo
t.boolean :newsletter_subscribed, default: true
t.json :store_accessor_store_with_no_defaults
t.string :locale
end

create_table :documents do |t|
Expand Down Expand Up @@ -114,6 +115,12 @@ class User < ActiveRecord::Base
enumerize :language, :in => [:en, :jp]
enumerize :country_code, :in => [:us, :ca]

# normalizes is available in Rails 7.1+
if ActiveRecord.gem_version >= Gem::Version.new("7.1")
normalizes :locale, with: ->(value) { value.downcase.strip.presence }
end
enumerize :locale, :in => %i[de en pl]

serialize :interests, type: Array
enumerize :interests, :in => [:music, :sports, :dancing, :programming], :multiple => true

Expand Down Expand Up @@ -808,13 +815,13 @@ class AdminUser < User

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
Expand Down Expand Up @@ -846,14 +853,28 @@ class AdminUser < User

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

# normalizes is available in Rails 7.1+
if ActiveRecord.gem_version >= Gem::Version.new("7.1")
it 'supports AR#normalizes class methods' do
User.delete_all
User.create!(locale: 'de')
expect(User.exists?(locale: ' DE ')).must_equal true
end

it 'supports AR#normalizes instance methods' do
user = User.new(locale: ' DE ')
expect(user.locale).must_equal 'de'
end
end
end