Dan Benjamin Dan Benjamin - 4 months ago 26
Ruby Question

Audit functionality for rails models

I'm trying to implement an audit functionality for some of my rails models and store it on an external "event" database (we chose BigQuery).

Each event should be very basic:
before_json, after_json, diff, action, object_id

So, I started building this concern that I am planning on adding to my models:

module Auditable
extend ActiveSupport::Concern

included do

before_destroy {audit(:destroy)}

after_validation on: :update do
audit(:update)
end

after_validation on: :create do
audit(:create)
end

def audit(action)
EventSender.send(before_json, self.to_json, diff, action, self.id)
end
end
end


The only thing I dont know how to implement is getting the before state of the object so I can populate the relevant fields and the diff between the two states.

Any ideas on how I can do it?

Answer

I solved it the following way:

module Auditable
  require 'active_record/diff'

  extend ActiveSupport::Concern

  included do
    include ActiveRecord::Diff

    before_destroy {audit(:destroy, before: before_state)}

    after_validation on: :update do
      audit(:update, before: before_state, after: self)
    end

    after_create do
      audit(:create, after: self)
    end

    def audit(action, before: {}, after: {})
      diff = case action
             when :update
               before.diff(after)
             when :create
               after
             when :destroy
               before
             end

      BigQueryClient.new.insert(
        self.class.to_s.downcase,
        {
          before: before.to_json,
          after: after.to_json,
          diff: diff.to_json,
          action: action,
          "#{self.class.to_s.downcase.to_sym}_id": self.id
        },
        dataset_name: "audit"
      )
    end

    private

    def before_state
      self.class.find(self.id)
    end
  end
end

Notice that I'm using an external gem called "activerecord-diff" to calculate the diff between the before and after.