Rails 5.1 Change Tracking in Callbacks

February 12, 2018 – Howard Wilson – 2-minute read

This article was written before Drivy was acquired by Getaround, and became Getaround EU. Some references to Drivy may therefore remain in the post

After recently upgrading to Rails 5.1, we noticed that certain model changes were no longer getting logged properly by PaperTrail. After a bit of digging, this turned out to be due to a subtle difference in the way that Rails now tracks changes.

It’s a little contrived, but let’s say we have a model that becomes active once info is present, like this:

class Car
  after_save do
    self.state = "active" if info_was.blank? && info.present? && state != "active"
    puts changes # Let's see how changes are tracked
    save!
  end
end

In Rails 5, we get something like:

> car.update!(info: "foo")
{
  "info" => [nil, "foo"],
  "active" => [false, true],
  "updated_at" => [Fri, 19 Jan 2018 15:48:12 CET +01:00, Fri, 19 Jan 2018 15:48:15 CET +01:00]
}

However, after upgrading to Rails 5.1, we’ll get:

> car.update!(info: "foo")
{
  "info" => [nil, "foo"],
  "updated_at" => [Fri, 19 Jan 2018 15:48:12 CET +01:00, Fri, 19 Jan 2018 15:48:15 CET +01:00]
}

The state transition is now missing from the tracked changes.

NOTE: As of Rails 5.1, we’ll now see also see the following warning, but it doesn’t give us any indication of this small change in behavior.

DEPRECATION WARNING: The behavior of changes inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after save returned (e.g. the opposite of what it returns now). To maintain the current behavior, use saved_changes instead.

Admittedly this is a small and isolated change, and this illustration is something of an anti-pattern, but this might just help clarify the new behavior for others seeing similar issues!

Did you enjoy this post? Join Getaround's engineering team!
View openings