No release in over a year
A gem to track changes on ActiveRecord models
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

 Project Readme

immosquare Active Record Change Tracker

This extension allows you to automatically track changes to your ActiveRecord models. It records changes to specified attributes whenever a record is saved.

Installation

gem "immosquare-active-record-change-tracker"

Generate the migration:

rails generate immosquare_active_record_change_tracker:install

# create_table(:active_record_change_trackers) do |t|
#   t.references(:recordable, :polymorphic => true, :foreign_key => false, :index => false, :null => false)
#   t.references(:modifier, :polymorphic => true, :foreign_key => false, :index => false, :null => true)
#   t.string(:event, :null => true, :limit => 10)
#   t.text(:data, :null => true, :limit => 4_294_967_295)
#   t.datetime(:created_at, :null => false)
# end

# add_index(:active_record_change_trackers, [:recordable_type, :recordable_id])
# add_index(:active_record_change_trackers, [:modifier_type, :modifier_id])

Then run the migration :

rails db:migrate

Usage

To enable history tracking for a model, add track_active_record_changes to your model

class YourModel < ApplicationRecord
  track_active_record_changes

  # rest of your model code...
end

By default, changes to all attributes (except created_at and updated_at) will be tracked. You can specify options to include or exclude specific attributes:

Exclude certain attributes :

class YourModel < ApplicationRecord
  track_active_record_changes(except: [:attribute1, :attribute2])

  # rest of your model code...
end

This will track changes to all attributes except :attribute1 and :attribute2.

Include only certain attributes :

class YourModel < ApplicationRecord
  track_active_record_changes(only: [:attribute3, :attribute4])

  # rest of your model code...
end

This will track changes only to :attribute3 and :attribute4.

Specify a modifier using a block :

The modifier can be specified by providing a block to track_active_record_changes, which allows you to capture dynamic context at the time changes are saved. (https://zainab-alshaikhli01.medium.com/activesupport-currentattributes-e3d43270207c)

class YourModel < ApplicationRecord
  track_active_record_changes(except: [:attribute1]) do
    ## Your Logic to get the modifier
    Current.admin.present? ? Current.admin : Current.user
  end
  # rest of your model code...
end

Security Considerations

The gem stores the before/after values of every changed attribute in the data JSON column of active_record_change_trackers. Two things to keep in mind:

1. Filter sensitive attributes

By default, every attribute except created_at and updated_at is tracked. If you enable tracking on a model that holds sensitive data, those values will be persisted in plaintext (or as their stored representation) inside the history table.

Always exclude credentials, tokens and regulated PII explicitly:

class User < ApplicationRecord
  track_active_record_changes(except: [
    :password_digest,
    :encrypted_password,
    :reset_password_token,
    :confirmation_token,
    :unlock_token,
    :remember_token,
    :api_token
  ])
end

The safer pattern is to use only: with an explicit allow-list of the attributes you actually want to audit, rather than relying on except: to remember every sensitive field.

2. Protect access to history_records

The gem does not enforce any authorization on the history_records association. Never expose it through an API, a serializer or an admin view without an access control layer — doing so leaks the full diff history of the record, including any field you forgot to exclude in step 1.

Considerations for Deletion

The gem is compatible with the paranoia gem (https://github.com/rubysherpas/paranoia) :

  • If your model has acts_as_paranoid, then the deletion of a record will be recorded in the active_record_change_trackers table with the event destroy, and the records of create and update will be retained.
  • A really_destroy! command will completely delete the record from the active_record_change_trackers table.
  • Without this gem, the deletion of a record will not be recorded in the active_record_change_trackers table, and the records of create and update will be deleted.

Order matters with paranoia

acts_as_paranoid must be declared before track_active_record_changes. The tracker reads paranoid? at macro-call time to decide between dependent: :destroy (hard cleanup) and after_real_destroy (paranoia-aware cleanup). If you call track_active_record_changes first, the tracker will treat the model as non-paranoid and hard-delete the history on every soft-delete.

class YourModel < ApplicationRecord
  acts_as_paranoid                  # first
  track_active_record_changes       # then
end

Accessing Change History

Each model that includes track_active_record_changes automatically has access to its change history through the history_records association. The history records are ordered by created_at in descending order, meaning the most recent changes are listed first.

Example:

class YourModel < ApplicationRecord
  track_active_record_changes

  # rest of your model code...
end

Access change history :

your_model_instance.history_records

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/immosquare/immosquare-active-record-change-tacker. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open-source under the terms of the MIT License.