mahemoff
mahemoff

Reputation: 46379

Rails timestamps for specific columns

I'm looking for a way to add a *_updated_at columns for several columns in a record.

It would work similarly to normal Rails timestamps, ie based on convention. If there's a DB column called author_updated_at, the class would automagically update it whenever author attribute changes.

Not too hard to write, but thought I'd ask here in case anyone has done it before or there's a gem around. I'd also be interested to know if there are any performance issues with this approach, though I think it should be negligible if using before_save. There's no extra reads or writes needed.

Upvotes: 1

Views: 692

Answers (2)

Rafael Oliveira
Rafael Oliveira

Reputation: 2923

I solved with a different version of @max solution.

previous_changes wasn't working for me. I'm using Rails 4.2.

module AttributeTimestampable
  extend ActiveSupport::Concern

  included do
    after_save do
      self.changed.each do |attr|
        timestamp = "#{attr}_updated_at"
        update_column(timestamp, updated_at) if self.respond_to? timestamp
      end
    end
  end
end

Upvotes: 1

max
max

Reputation: 101811

You might want to try paper_trail - it tracks changes to a model but in a slightly different fashion - instead of adding extra columns for each attribute to track it uses a table to store versions of the model. This gives you a full blown versioning system without doing much work.

You can easily revert to previous versions and track who created a revision etc.

But if all you need is a light-weight solution to store a timestamp for the changes to a limited set of attributes you can use model callbacks to set the *_updated_at columns.

module AttributeTimestamps

  extend ActiveSupport::Concern

  included do
    after_validation(on: :update) do
      # Use ActiveModel::Dirty to get the changed attributes
      self.previous_changes.each_with_index do |attr|
        setter = "#{attr}_updated_at="
        self.call(setter, self.updated_at) if self.respond_to? setter
      end
    end
  end
end

class MyModel < ActiveRecord::Base
  include AttributeTimestamps  
end

Upvotes: 4

Related Questions