rails/rails — pull request #13792 by jonashuckestein (2014-01-22 13:25:50+00:00)
Title: Opt-out of always writing of serialized columns (`serialize :some_attr, dirty: :never`)

This is a first cut at a simple patch that allows developers to protect certain serialized columns from being written to the database on every update. The default behavior is not affected and this patch is 100% backwards compatible. Quick example:

class MyModel < ActiveRecord::Base

# some_field gets written on every write
serialize :field1
# same as above
serialize :field2, dirty: :always
serialize :field3, Object, dirty: :always

# field4 only gets written, if
# field4_will_change was called
serialize :field4, dirty: :never
end

The current second argument, `class_name`, still works and is optional as well.

This patch fixes #8328 in a very lightweight way. If this gets any interest, I'd love to discuss

1) Allowing values `:clone` and `:hash` to the dirty option. This would store a hash or copy of the object after initialization and compare it when saving. This is rather expensive so it has to be strictly opt-in.

2) Consolidating the existing `serialized_attributes` and the new `serialized_attribute_options` class attributes. IMO it would be nice to have something like `MyModel.serialized_attribute_options == {my_field: {dirty: :always, coder: XXX}}`. This could be implemented by first deprecating `serialized_attributes` in case somebody is directly using it. It may even make sense to simplify the `serialize` method more, by making the current `class_name` argument a part of the options hash

I've never attempted to contribute to rails, so forgive my ignorance wrt process. I'm also more than happy to change the naming, formatting and structure of the code as needed. I'll also write some specs (I already wrote some but wasn't able to run them... :().

Brief rationale: Consider a system with two separate processes `A` and `B`. `A` reads your model object, does something for a second and then call save on it. Whatever your serialized column's value was when `A` read it is now written to the database. In the meantime, `B` might have modified that column. Those changes are now lost.