Rails enum issue when converting instance to different class using #becomes
In Rails you can use #becomes method to convert object/model to different class/model keeping the attributes. This is extremly handy if you are creating new version oof the model but you still want to keep old model around (legacy) so they both can work on same table
V2::Book.create!(status: "archived") # status that not exist in Book (version 1) b2 = V2::Book.last b1 = Book.last b2.id == b1.id # true converted = b1.becomes(V2::Book) converted.class # V2::Book 👍 converted.persisted # true 👍 converted.id == b2.id # true 👍 converted.status # nil 😬 - This is wrong, it should be "archived"
Reason is
b2.status # "archived" b1.status # nil
Book.new(status: "archived") # raises ArgumentError: 'archived' is not a valid status
Solution?
delegator
class Book enum :status, { draft: "draft", published: "published" } def to_v2 V2::Campaign::BecomesV2Delegator.new(becomes(V2::Book)) end end class V2::Book class BecomesV2Delegator < SimpleDelegator def status read_attribute_before_type_cast("status") end end # status is no longer enum end b1 = Book.last b1.to_v2.status # "archived"
Other "Kind of" workarounds:
1
converted2 = V2::Book.new(b1.attributes_before_type_cast) converted = b1.becomes(V2::Book) converted.class # V2::Book 👍 converted.persisted # false 🫤 - I totaly breaks links converted.id == b2.id # true 👍
2
conv3 = b1.becomes(V2::Book).tap do |b| b.status = b1.read_attribute_before_type_cast('status') # 'status; end # raises ArgumentError: 'archived' is not a valid status # for some reason
3
V2::Book.find(b1.id)
or
b1.becomes(V2::Book).reload
both of which do DB call