mardi 4 août 2015

How to save nil into serialized attribute in Rails 4.2

I am upgrading an app to Rails 4.2 and am running into an issue where nil values in a field that is serialized as an Array are getting interpreted as an empty array. Is there a way to get Rails 4.2 to differentiate between nil and an empty array for a serialized-as-Array attribute?

Top level problem demonstration:

#[old_app]
 > Rails.version
 => "3.0.3"
 > a = AsrProperty.new; a.save; a.keeps
 => nil

#[new_app]
 > Rails.version
 => "4.2.3"
 > a = AsrProperty.new; a.save; a.keeps
 => []

But it is important for my code to distinguish between nil and [], so this is a problem.

The model:

class AsrProperty < ActiveRecord::Base
  serialize :keeps, Array
  #[...]
end

I think the issue lies with Rails deciding to take a shortcut for attribute that are serialized as a specific type (e.g. Array) by storing the empty instance of that type as nil in the database. This can be seen by looking at the SQL statement executed in each app:

[old_app]: INSERT INTO asr_properties (lock_version, keeps) VALUES (0, NULL)

Note that the above log line has been edited for clarity; there are other serialized attributes that were being written due to old Rails' behavior.

[new_app]: INSERT INTO asr_properties (lock_version) VALUES (0)

There is a workaround: by removing the "Array" declaration on the serialization, Rails is forced to save [] and {} differently:

class AsrProperty < ActiveRecord::Base
  serialize :keeps #NOT ARRAY
  #[...]
end

Allows:

 > a = AsrProperty.new; a.save; a.keeps
 => []

I'll use this workaround for now, but: (1) I feel like declaring a type might allow more efficiency, and also prevents bugs by explicitly prohibiting the wrong data type being stored (2) I'd really like to figure out the "right" way to do it, if Rails does allow it.

So: can Rails 4.2 be told to store [] as its own thing in a serialized-as-Array attribute?



via Chebli Mohamed

Aucun commentaire:

Enregistrer un commentaire