Rails : PaperTrail gem : Custom events to track fields changed

In my project, I was asked to keep track of changes in records. I found PaperTrails gem, and its found to be pretty useful.

But it lacks a straightforward feature to know the fields changed in that particular version. Then I thought to write custom event name so that I could grep the field_name I wish to check.

# == Schema Information
#
# Table name: tasks
#
#  id                :integer          not null, primary key
#  worker_id         :integer
#  customer_id       :integer
#  kind              :integer          default(0)
#  status            :integer          default(0)
#  note              :text
#  start_on          :datetime
#  complete_on       :datetime
#  started_on        :datetime
#  completed_on      :datetime
#  created_at        :datetime         not null
#  updated_at        :datetime         not null
#  driver_acceptance :integer          default(0)
#  refuel_id         :integer
#  shift_id          :integer
#

class Task < ActiveRecord::Base
  include Timeable
  has_paper_trail
  
  # Update event-name on update
  before_save :paper_trail_events, on: :update

  enum status: { scheduled: 0, started: 1, skipped: 2, completed: 3, paused: 4, rescheduled: 5 }
  enum kind: { inter_location: 0, refuel: 1, break: 2 }
  enum driver_acceptance: { pending: 0, accepted: 1, rejected: 2, sent: 3 }

  private

    def paper_trail_events
      changed_fields         = self.changes.keys - ['created_at', 'updated_at']
      self.paper_trail_event = "Updated #{changed_fields.join(',')}"
    end
end

In Rails Console

> Task.find(1768).versions.map &:event
=> ["Updated start_on,complete_on,status", "Updated worker_id"]

 

Advertisements

Rails | Avoiding race conditions while updating particular field in DB

It’s okay to make mistakes when you are up to building simple applications. But for mission critical applications like Banking/Payment Processors, you gotta be very careful and keep listening to seniors and experts in critical matters.

Lets say you are working on a feature to update the total amount deposited in a particular account. Simple code will do like

# just after amount is debited from payee's account
def credit_receiver_account(debited_amount)
  update_attribute :total_amount, total + debited_amount
end

What’s wrong with the code above?

Continue reading