PaperTrail Documentation

repository·master·Indexed 27 days ago

https://github.com/paper-trail-gem/paper_trail

A Ruby on Rails gem for tracking changes to ActiveRecord models. PaperTrail enables developers to audit model lifecycles, view previous versions of records, revert changes, and restore destroyed records. It provides a comprehensive API for navigating version history, filtering monitored attributes via :ignore, :only, and :skip, and managing version limits. The library supports tracking specific lifecycle events, manual version saving, and diffing versions using object_changes.

Tokens
12.2K
Snippets
38
Records
74
Agent score
92%

What's inside PaperTrail

  1. Delete old versions

    master
    Because PaperTrail versions are self-contained snapshots, you can safely delete old version records without affecting the integrity of newer versions. You can delete versions using standard ActiveRecord deletion methods on the PaperTrail::Version model.
  2. Configure PaperTrail with Spork, Zeus, or Spring

    master

    If using test runners like Spork, Zeus, or Spring, you must manually require the PaperTrail framework helpers within your prefork block or test helper to ensure they are loaded correctly.

    # spec/rails_helper.rb
    require 'spork'
    
    Spork.prefork do
      ENV["RAILS_ENV"] ||= 'test'
      require 'spec_helper'
      require File.expand_path("../../config/environment", __FILE__)
      require 'rspec/rails'
      require 'paper_trail/frameworks/rspec'
      require 'paper_trail/frameworks/cucumber'
    end
  3. Migrate existing YAML version data to JSON/JSONB

    master

    If you are switching from the default YAML serializer to PostgreSQL JSON/JSONB, you must migrate existing data.

    Option 1: Direct Migration (Slow but safe) Loop through records and update them using YAML.load into a temporary column, then rename the columns.

    add_column :versions, :new_object, :jsonb
    
    PaperTrail::Version.where.not(object: nil).find_each do |version|
      version.update_column(:new_object, YAML.load(version.object))
    end
    
    remove_column :versions, :object
    rename_column :versions, :new_object, :object

    Option 2: Background Migration (Faster for large datasets)

    1. Rename the existing object column to old_object.
    2. Add a new object column with type jsonb.
    3. Use a background script to convert records from old_object (YAML) to object (JSON).
    4. Remove old_object once complete.
    # Background script example
    PaperTrail::Version.where.not(old_object: nil).find_each do |version|
      version.update_columns old_object: nil, object: YAML.load(version.old_object)
    end
  4. Migrate from YAML to JSON serializer in PaperTrail

    master

    To avoid the security complexities and configuration requirements of YAML.safe_load, it is recommended to switch from the YAML serializer to JSON.

    Users with PostgreSQL should consider using json(b) columns. Users with other databases can use JSON stored in a text column. This change makes you unaffected by the YAML.safe_load security updates applied in PaperTrail 13 and 14.

  5. Configure Minitest for PaperTrail testing

    master

    To speed up tests, you can disable PaperTrail globally for the entire Ruby process in your test environment configuration. To enable versioning for specific tests, implement a with_versioning helper method in your test_helper.rb that toggles PaperTrail.enabled and PaperTrail.request.enabled.

    # in config/environments/test.rb
    config.after_initialize do
      PaperTrail.enabled = false
    end
    
    # in test/test_helper.rb
    def with_versioning
      was_enabled = PaperTrail.enabled?
      was_enabled_for_request = PaperTrail.request.enabled?
      PaperTrail.enabled = true
      PaperTrail.request.enabled = true
      begin
        yield
      ensure
        PaperTrail.enabled = was_enabled
        PaperTrail.request.enabled = was_enabled_for_request
      end
    end
    
    # usage
    test 'something that needs versioning' do
      with_versioning do
        # your test
      end
    end
  6. Install PaperTrail via generator

    master

    Run the paper_trail:install generator to create a migration file for the versions table. The generator does not run the migration automatically; you must run it manually.

    You can optionally provide a custom version class name as an argument.

    bin/rails generate paper_trail:install [VERSION_CLASS_NAME] [options]
  7. Configure Cucumber for PaperTrail testing

    master

    To use PaperTrail with Cucumber, require paper_trail/frameworks/cucumber in your features/support/env.rb. By default, PaperTrail will be disabled for all scenarios. To enable it for a specific scenario, wrap the code in a with_versioning block within a step definition.

    # features/support/env.rb
    ENV["RAILS_ENV"] ||= 'cucumber'
    require File.expand_path(File.dirname(__FILE__) + '/../../config/environment')
    # ...
    require 'paper_trail/frameworks/cucumber'
    
    # usage in step definition
    Given /I want versioning on my model/ do
      with_versioning do
        # PaperTrail will be turned on for all code inside of this block
      end
    end
  8. Diff versions using object_changes

    master

    To track specific attribute changes, add an object_changes column to your versions table. PaperTrail will then store the diff in each version.

    As of version 10.0.0, changes are stored for create, update, and destroy events. The changeset method on a version record reads this column and returns a hash of the changes (e.g., {'attribute' => [old_value, new_value]}).

    Note: PaperTrail stores full object snapshots, not diffs, to ensure each version is self-contained and independent.

  9. Basic Usage of PaperTrail

    master

    Once has_paper_trail is added to a model, you can access its history via the versions method. PaperTrail stores the state of the model before a change occurred, allowing you to retrieve original values.

    Accessing Versions

    widget = Widget.find 42
    widget.versions # Returns an array of PaperTrail::Version objects

    Inspecting a Version

    v = widget.versions.last
    v.event          # 'update', 'create', or 'destroy'
    v.created_at      # Timestamp of the change
    v.whodunnit      # ID of the user (requires set_paper_trail_whodunnit)
    v.reify           # Returns the model as it was BEFORE the change
    widget = Widget.find 153
    widget.name                                 # 'Doobly'
    
    # Add has_paper_trail to Widget model.
    
    widget.versions                             # []
    widget.update name: 'Wotsit'
    widget.versions.last.reify.name             # 'Doobly'
    widget.versions.last.event                  # 'update'
  10. Configure RSpec for PaperTrail testing

    master

    Load the PaperTrail RSpec helper in spec/rails_helper.rb to automatically disable PaperTrail for all tests by default. You can enable it for specific tests using a with_versioning block or by passing the versioning: true option to a describe or it block. The helper also resets whodunnit to nil and PaperTrail.request.controller_info to {} to prevent data spillover between tests.

    # spec/rails_helper.rb
    ENV["RAILS_ENV"] ||= 'test'
    require 'spec_helper'
    require File.expand_path("../../config/environment", __FILE__)
    require 'rspec/rails'
    # ...
    require 'paper_trail/frameworks/rspec'
    
    # usage examples
    describe 'RSpec test group' do
      with_versioning do
        it 'within a `with_versioning` block it will be turned on' do
          expect(PaperTrail).to be_enabled
        end
      end
    
      it 'can be turned on at the `it` or `describe` level', versioning: true do
        expect(PaperTrail).to be_enabled
      end
    end