This is my model
class Setting < ApplicationRecord
serialize :additional_settings, JSON
store(:additional_settings,
accessors: %i[duration_type remind_before],
coder: JSON)
enum duration_type: %i[days hours]
end
additional_settings is a JSON column
> Setting.duration_types
> {"days": 0 ,"hours": 1}
this works fine
But
> a = Setting.first
> #<Setting id: 32, name: "start_date_setting", additional_settings: {"remind_before"=>1, "duration_type"=>1}>
> a.days?
> false
> a.hours?
> false
this doesn't work as expected
and
> a.days!
> (0.5ms) BEGIN
SQL (0.8ms) UPDATE `settings` SET `updated_at` = '2020-05-23 06:09:21', `additional_settings` = '\"{\\\"remind_before\\\":1,\\\"duration_type\\\":\\\"days\\\"}\"' WHERE `settings`.`id` = 32
(2.0ms) COMMIT
this should actually update duration_type as 0 but its updated as "days"
does this work only for integer fields?
There is a way to make enum work with string values. You can do it like this:
But it won't help to make the code work in this case. The problem here is that ActiveRecord expects
enumto be defined on the attribute and stored in the database as a column. It's not compatible with stores.Here is an implementation of
#days?: click. As you can see, Rails is checkingself[attr]whereattris the name of the enum (duration_typein our case). Andself[attr]is equal toself.attributes[attr]. For the modelSettingattributes contains onlyadditional_settings, so no value found, soself.attributes[:duration_type]givesnil.There is a question why
a.days!work without exception in this case then, right? Well, it's tricky. Here is an implementation of this method: click. It's basically a call toupdate!(attr => value)whereattrisduration_typeand value is enum's value. Under the hoodupdate!callsassign_attributeslike this:s.assign_attributes(duration_type: "days"), - which is equal tos.duration_type = "days". And because attr accessor is defined forduration_type(you specified it instorecall) it writes value toadditional_settingsand saves it.Here is a test to check how it works: