Rails: update dataset without updating magic timestamp columns
In Rails, you can have several „magic columns” identified by their names. These columns include updated_at
and created_at
. The latter one is set to the current time when a dataset is created and the first one is set to the current time when a dataset gets updated.
For a work project I had to update some values (a lock field for example) w/o updating these magic columns. After doing some research on the interwebs there are several solutions:
Disabling magic columns before calling save
This was the first and easiest one. You can simply set ActiveRecord::Base.record_timestamps = false
and then call save
. Wrapped up in a helper method this would be like this:
def wo_timestamp(&block)
old = ActiveRecord::Base.record_timestamps
ActiveRecord::Base.record_timestamps = false
begin
yield
ensure
ActiveRecord::Base.record_timestamps = old
end
end
This is fine because it is easy, but it's not threadsafe. In my case I needed a threadsafe solution because it was a threaded server application.
Using update_all
or update_column
Nice solution, threadsafe and no magic. Both fire an UPDATE
statement w/o setting the timestamp values. Nice, but needs more complicated and less readable code. With update_column
you also need to call it once for every column.
Use meta programming
Rails uses a callback method record_timestamps
to decide if it should update the timestamp columns. This gives us the ability to do this on just one object:
def save_wo_timestamp
class <<self
def record_timestamps; false; end
end
begin self.save ensure class <<self remove_method :record_timestamps end end end
I overwrite the method on this specific object, save it to database and remove the overwritten method after that. Nice, clean and easy. Yay :-)