Rails provides some good tools like automatically updating
updated_at columns. Developers do not need to worry about these columns.
Rails updates these columns automatically which is great.
However I have a unique business need where I need to update a column but I do not want
updated_at to be changed. Or we can see the problem this way. I want to change the
updated_at to a particular value.
Look at the sql that is generated. Rails discarded the
updated_at value that I had supplied and replaced the value by the current time. Rails works fine if you supply
created_at value. It is the
updated_at value that is discarded.
Rails provides a feature called ActiveRecord::Base.record_timestamps . Using this feature I can tell rails to not to auto time stamp records.
Let’s try that.
It worked. I have successfully set
updated_at to year 1909. However there is a problem.
For a brief duration
User.record_timestamps was set to false. That is a class level variable. It means that for that brief duration if any other User record is updated then that record will not have correct
updated_at value. That is not right. I want just one record ( User.first) to not to change
updated_at without changing the behavior for the whole application.
In order to isolate the behavior to only the record we are interested in, I can do this.
In order to restrict the changes to a model, I am opening up the metaclass of u ( user object) and in that object I am adding a method called
record_timestamps . The idea is to insert a method called
record_timestamps in the metaclass which will return true and in this way the changes are restricted to a single object rather than making change at the class level.
At this point the meta class of the user object has the method
record_timestamps and this returns false. Now I update the record with
updated_at set to 100 years ago. And I succeed.
Now I need to put the object behavior back to normal. I open up the metaclass and call super on the method so that the method call will go up the chain. And that’s what happens when I try to test
updated_at. This time the
updated_at value that I set is ignored and rails changes the
This strategy of opening up an instance object works but it is messy. I would like to have a method that is much easier to use and this is what I came up with. Stick this piece of code in an initializer.
This is how you can use it.
Good usage of remove_method
In the above solution I used super when I want to bring back the default auto time stamping behavior. In stead of super I can also use remove_method. More about the what remove_method does is here .
Using the above technique, I can fully control
updated_at values without rails messing up anything.