Skip to content
CAI
Software that uses CAICheck a score

paper-trail-gem/paper_trail

69.6

Adequate · 26 September 2026

3.1k

lines of production code

Ruby

primary language

4

measurements over time

CAI band scale
CAI trend line
CAI lens gauges

What this system is

This system is the PaperTrail gem, a Ruby library that provides versioning and audit logging capabilities for ActiveRecord models. It tracks create, update, and destroy events, storing historical data in a configurable versions table with support for detailed change diffs and object reification. The library offers robust integration with modern Rails versions, including features for handling encrypted attributes, PostgreSQL arrays, and Single Table Inheritance, while allowing users to customize serialization formats and query version history.

How it got here

2009–2013 — Modular architecture and modernization

13 changes.

This period focused on refactoring PaperTrail into a modular architecture with a new Config singleton and request-scoped API, while dropping support for older Ruby and Rails versions. The project overhauled its infrastructure by migrating from Test::Unit to RSpec, replacing legacy generators and initialization hooks, and introducing a safer JSON serializer alongside enhanced query capabilities.

2014–2017 — Rails 7 compatibility and test infrastructure

25 changes.

This period focused on modernizing PaperTrail's integration with Rails 7, including refactoring callbacks, supporting PostgreSQL arrays, and handling encrypted attributes. A comprehensive test suite was built using a dummy application to verify these changes, alongside significant documentation updates for security and migration guidance.

2018–2023 — Events system and generator enhancements

8 changes.

The project refactored internal version tracking into a modular Events system with stricter validation and improved memory efficiency. It expanded the generator suite to support customizable version tables, UUID primary keys, and MySQL-specific schema configurations. Additionally, test coverage was broadened to include new shared examples for encryption and query filtering, alongside expanded Rails version support.

Features

Add JSON serializer and refactor YAML serializer for safer loading

The gem now includes a built-in JSON serializer (PaperTrail::Serializers::JSON) as an alternative to the default YAML serializer, allowing users to store version data as JSON for potentially better performance or compatibility. The default YAML serializer has been refactored into a module and updated to use YAML.safe\_load by default on supported Rails versions to prevent unsafe deserialization vulnerabilities, while maintaining backward compatibility with older Rails releases. Both serializers now support querying object attributes via the where\_object\_condition method.

_lib/paper\trail/serializers · high confidence

Add RSpec helpers for temporary versioning control

Introduces new RSpec helper methods (\with\_versioning\) at both instance and class levels, allowing tests to temporarily enable PaperTrail versioning within specific code blocks or test contexts while ensuring the original state is restored afterward.

_lib/paper\trail/frameworks/rspec · high confidence

Customizable version table generation via new migration generator

A new migration generator has been added to allow users to create custom version tables with a specific class name. By passing a custom version class name (defaulting to 'Version'), the generator creates a migration that sets up the corresponding table using standard Rails naming conventions. This enables users to define their own version models and table structures rather than relying on a single hardcoded default.

_lib/generators/paper\trail · high confidence

New generator to update item\_subtype for STI entries

A new generator, \paper\_trail:update\_item\_subtype\, is now available to help users migrate existing version records when using Single Table Inheritance (STI). This tool generates a migration that updates the \item\_subtype\ column for versions that previously referenced a base class, allowing them to correctly reference the specific subclass. The generator supports custom hints to map specific version IDs to their corresponding subtype attributes, ensuring accurate migration of historical data.

_lib/generators/paper\_trail/update\_item\subtype · high confidence

Support for serializing PostgreSQL array columns

PaperTrail now includes a dedicated serializer for PostgreSQL array columns, ensuring that array data is correctly serialized and deserialized when tracking changes. This new component handles legacy data formats from Rails versions prior to 5.0.2 by detecting string-based arrays and converting them using ActiveRecord's internal deserialization logic, while passing through modern array structures directly.

_lib/paper\_trail/type\serializers · high confidence

Removals

Removal of legacy Rails initialization file

The legacy \rails/init.rb\ file has been removed. This file previously executed \require 'paper\_trail'\ to load the PaperTrail gem during Rails initialization. Its removal indicates that the application no longer relies on this specific initialization hook for loading the gem, likely due to modern Rails autoloading conventions or bundler configuration.

rails · high confidence

Removed obsolete PaperTrail generator

The legacy PaperTrail generator and its associated migration template have been removed. Users can no longer use the generator to scaffold the versions table migration; the migration file itself is also deleted, meaning the table structure is no longer provided via this code path.

generators · high confidence

Behavioural changes

Documentation updates for security, triage, and legacy warnings

Added new documentation files to guide users: \pt\_13\_yaml\_safe\_load.md\ explains the migration to \YAML.safe\_load\ to address [CVE redacted] and provides configuration steps for continuing to use the YAML serializer; \triage.md\ provides standard responses for handling usage questions and feature proposals; \warning\_about\_not\_setting\_whodunnit.md\ clarifies the deprecation of the automatic \before\_action\ for \whodunnit\ tracking in version 5 and instructions for upgrading to version 6. Additionally, \bug\_report\_template.rb\ was added as a placeholder redirecting users to the new GitHub issue template location.

doc · high confidence

Install generator now supports UUID primary keys and MySQL-specific schema options

The PaperTrail install generator has been updated to allow users to specify UUIDs for the \item\_id\ column and the version table's primary key by passing the \--uuid\ flag, which is useful for applications that already use UUIDs. Additionally, the generator now automatically configures the versions table for MySQL databases by setting the \utf8mb4\ character set and collation, and limiting the \item\_type\ column length to 191 characters to comply with MySQL's index key length constraints, ensuring smoother integration for MySQL users.

_lib/generators/paper\trail/install · high confidence

Introduce explicit ActiveRecord Version model class

The ActiveRecord framework integration now provides a dedicated \PaperTrail::Version\ model class that inherits from \ActiveRecord::Base\ and includes the \PaperTrail::VersionConcern\. This change establishes a concrete, extensible base class for version records, allowing users to subclass or extend the model more easily, while also clarifying in documentation that the related \VersionAssociation\ model is provided by a separate gem.

_lib/paper\_trail/frameworks/active\record · high confidence

Major refactoring of core module and removal of deprecated features

The \lib/paper\_trail.rb\ entry point has been significantly restructured to improve modularity and compatibility. The custom timestamp field configuration (\timestamp\_field=\) has been removed, enforcing that the versions table timestamp column must be named \created\_at\. The module now relies on a dedicated \PaperTrail::Config\ singleton for global settings (enabled state, serializer) and introduces a \PaperTrail.request\ API for managing request-scoped variables like \whodunnit\, replacing the previous class-level instance variables. Additionally, the file now explicitly requires \active\_support/all\ and conditionally loads framework-specific code via a Railtie or direct ActiveRecord requirement, ensuring safer lazy-loading for non-Rails applications.

lib · high confidence

PaperTrail v17.0.0 introduces a modular architecture and new versioning capabilities

This release refactors the gem's internal structure into distinct modules (Cleaner, Config, Reifier, RecordTrail, etc.) to improve maintainability and separation of concerns. It adds new query methods to the Version model, including \where\_attribute\_changes\, \where\_object\_changes\_from\, and \where\_object\_changes\_to\, allowing users to filter versions based on specific attribute modifications. The \clean\_versions!\ method is now part of a dedicated Cleaner module and accepts options to keep a specific number of versions per item per date. Additionally, the gem now supports Rails 8.1 and drops support for Rails 6.1, 7.0, and Ruby 3.0, 3.1.

_lib/paper\trail · high confidence

Project infrastructure and configuration overhaul

The repository has been restructured with new configuration files: a \.gitignore\ to exclude build artifacts and test databases, an \.rspec\ file for default test runner settings, and a comprehensive \.rubocop.yml\ with plugins (rubocop-packaging, rubocop-performance, rubocop-rails, rubocop-rake, rubocop-rspec) and a \.rubocop\_todo.yml\ for existing offenses. The \Appraisals\ file now defines test matrices for Rails 7.1, 7.2, 8.0, and 8.1. The \Rakefile\ has been rewritten to use RSpec and RuboCop tasks, replacing the old test/rdoc tasks, and includes database preparation tasks. Legacy plugin hooks (\init.rb\, \install.rb\, \uninstall.rb\) have been removed, and the license file was renamed from \MIT-LICENSE\ to \LICENSE\. The \README.md\ has been significantly expanded with a table of contents, compatibility tables, and detailed usage instructions.

(repo-wide) · high confidence

Rails integration refactored to use modern callbacks and explicit request context

The Rails framework integration has been restructured to replace deprecated \before\_filter\ callbacks with \before\_action\, ensuring compatibility with modern Rails versions. The controller extension now explicitly manages the \PaperTrail.request\ context for \whodunnit\ and \controller\_info\, and the railtie initializer is configured to run before app initializers to guarantee correct boot order. Additionally, deprecation warnings are now properly routed through the Rails deprecator for Rails 7.1+.

_lib/paper\trail/frameworks/rails · high confidence

Refactor version query logic into dedicated query objects

The version querying logic in \lib/paper\_trail/queries\ has been restructured into specific query classes (\WhereAttributeChanges\, \WhereObject\, \WhereObjectChanges\, \WhereObjectChangesFrom\, and \WhereObjectChangesTo\). This change extracts the implementation details for filtering versions by attribute changes, object state, and object change states into separate, testable units. The refactoring ensures that queries against JSON/JSONB columns use appropriate SQL operators (e.g., \@\>\ for JSONB, \ILIKE\ for JSON) while maintaining compatibility with the existing \object\_changes\_adapter\ configuration, allowing users to continue filtering versions using the same public API methods but with cleaner, more maintainable internal logic.

_lib/paper\trail/queries · high confidence

Refactored attribute serialization to support PostgreSQL arrays and Rails 7 encryption

The attribute serialization logic has been restructured into dedicated modules (factory, cast, object, and changes serializers) to improve maintainability and fix specific compatibility issues. This change adds support for serializing PostgreSQL array types, which were previously unsuited for JSON text columns, and ensures that encrypted attributes are handled correctly with ActiveRecord's built-in encryption (introduced in Rails 7) by preventing the versioning of unencrypted plaintext values. Additionally, it fixes deserialization of enums written by PaperTrail 4 and resolves a Rails 7 time attribute delegation error.

_lib/paper\_trail/attribute\serializers · high confidence

Refactored version tracking into a modular Events system with improved change detection

The internal version tracking logic has been reorganized into a new \PaperTrail::Events\ module, splitting behavior into \Base\, \Create\, \Update\, and \Destroy\ classes. This refactoring introduces stricter validation by forbidding specific metadata keys (such as \id\, \created\_at\, and \updated\_at\) and adds support for the optional \item\_subtype\ column in version records. The \Update\ event now explicitly handles touch events by preventing the recording of \object\_changes\ when Rails dirty-tracking is unavailable, and ensures that timestamp-only updates are treated as notable changes only if non-timestamp attributes were also changed or ignored. These changes improve memory efficiency and clarify the conditions under which version records are created and what data they contain.

_lib/paper\trail/events · high confidence

Removal of obsolete Jeweler-based Rake tasks

The dedicated Rake task file for gem specification management (paper\_trail\_tasks.rake) has been removed. This file previously relied on the Jeweler library to define gem metadata such as name, summary, and authors. Its deletion indicates that gem specification management is no longer handled via this specific Rake-based approach, likely consolidating build configuration elsewhere.

tasks · high confidence

Restructured framework integrations and added RSpec matchers

The framework integration code has been reorganized into dedicated files for ActiveRecord, Rails, RSpec, and Cucumber. For users, this introduces new RSpec matchers (\have\_a\_version\_with\ and \have\_a\_version\_with\_changes\) to verify version history, and ensures consistent test environment setup via hooks in both RSpec and Cucumber helpers. The ActiveRecord integration now explicitly manages loading order and includes the PaperTrail model module.

_lib/paper\trail/frameworks · high confidence

Updated install templates for version table schema and initializer defaults

The generator templates for the \versions\ table and the \paper\_trail.rb\ initializer have been updated. The new \create\_versions.rb\ template now includes a commented-out \bigint\ option for the \whodunnit\ column to suggest performance improvements for numeric IDs, and adds specific comments regarding MySQL fractional second precision for the \created\_at\ column. A new \add\_object\_changes\_to\_versions.rb\ template has been added to support storing change diffs in an optional \object\_changes\ text column. Additionally, the generated initializer now explicitly sets \has\_paper\_trail\_defaults\ to track \touch\ events alongside create, update, and destroy, ensuring consistent default behavior for new installations.

_lib/generators/paper\trail/install/templates · high confidence

Test coverage

Add dummy Rails application configuration for testing; Added RSpec test suite for PaperTrail core and test infrastructure; Added RSpec test suite for PaperTrail core components; Added RSpec tests for PaperTrail serializers; Added Rakefile and config.ru for the dummy application; Added controller tests for PaperTrail request state and versioning metadata; Added dummy application controllers for testing; Added request specs for article versioning behavior; Added shared test suites for ActiveRecord encryption and query filtering; Added test environment configuration files for the dummy application; Added test fixtures for PaperTrail event-specific models; Added test fixtures for STI family hierarchy; Added test fixtures for custom PaperTrail version models; Added test fixtures for dummy app initializers; Added test support utilities for serialization, migrations, and performance; Added tests for Kitchen::Banana model; Added tests for On model versioning behaviors; Added tests for PostgreSQL array serialization; Added tests for the PaperTrail install generator; Added tests for the new Events module; Expanded dummy app models for comprehensive test coverage; Expanded model test coverage for versioning behaviors; Removed legacy Test::Unit test suite and configuration; Updated test database schema for dummy application.

Dependencies

Expanded Rails version support for testing

The gemfiles directory now includes dedicated Appraisal configurations for Rails 7.1, 7.2, 8.0, and 8.1, enabling the library to be tested against these specific versions. The Rails 8.0 and 8.1 configurations additionally pin the sqlite3 gem to version 2.1 or higher, while all configurations maintain compatibility with json versions below 3.

gemfiles · high confidence

Update development dependencies and drop support for older Ruby versions

The gemspec has been updated to require Ruby 3.2.0 or higher, dropping support for older EOL versions. Development dependencies have been refreshed to modern versions, including rspec-rails 7.1.1, rubocop 1.91.0 (with associated plugins like rubocop-rails 2.30.3 and rubocop-rspec 3.5.0), and appraisal 2.5. Additionally, several gems extracted from the Ruby standard library (benchmark, bigdecimal, drb, logger, mutex\_m) are now explicitly listed as development dependencies to ensure test suite compatibility.

(dependencies) · high confidence

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

How this codebase got here

This is the PUBLIC form of this artifact. Findings are listed in full, but the details of SECURITY findings — which rule fired, in which file, on which line, and how to fix it — are deliberately withheld, and any secret-scanner results are excluded entirely. Where detail is absent here it was REMOVED FOR PUBLICATION; it is not missing from the analysis. The complete artifact is available from the repository owner.

Score

  • CAI 50 → 70 (+20.0)
  • Rubric changed (rubric-2026.08.15 → rubric-2026.09.15) — scores are not directly comparable.

Lenses

  • Code Health 100 → 97 (-2.6)
  • Architecture 96 → 99 (+3.8)
  • Maturity 61 → 56 (-4.5)
  • Readiness 27 → 67 (+40.0)
  • Security 63 → 89 (+25.7)
  • Domain Modelling 100 (new)

Resolved (11)

  • Coverage not measured — test suite did not build
  • Dimension evaluation failed
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • LLM evaluation failed
  • Medium: security finding (details withheld)
  • No exposed public API
  • No tests found
  • Test reliability not included

New (21)

  • Ambiguous method name paper_trail on both class and instance levels. On the class level, it likely returns the configuration object or metadata, while on the instance level, it likely returns the RecordTrail or history object. This name is too generic and does not convey the different return types or intents (configuration vs. runtime history context).
  • Documentation: no installation or build instructions (README.md)
  • Documentation: no licence statement (README.md)
  • Documentation: no usage examples (README.md)
  • Duplicated block (10 lines × 2) (lib/paper_trail/queries/versions/where_object_changes_from.rb)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • Inconsistent naming for boolean state checks and setters across different scopes. While Ruby conventions often use ? for checks, the library mixes enabled? (PaperTrail, Request) with enabled (Config getter) and enabled= (setters). More critically, Request exposes both global enabled? and model-specific enabled_for_model?, which can be confusing regarding precedence and scope without clear documentation. Additionally, Config uses enabled for the getter while PaperTrail uses enabled?, violating the convention that getters returning booleans should end in ?.
  • No dependency advisory monitoring
  • Orphaned files with no living knowledge
  • Redundant abstraction layer. The VersionConcern exposes where_object which likely delegates to WhereObject.execute(). While this is a common pattern, the existence of the explicit WhereObject class in the public API surface suggests that the query builder objects are exposed but not consistently named or documented as first-class citizens. If where_object is the primary entry point, the internal WhereObject class should ideally be private or not part of the 'exposed' surface if it's not meant to be instantiated directly by users.
  • TodoComment (lib/paper_trail/attribute_serializers/object_attribute.rb)
  • TodoComment (lib/paper_trail/attribute_serializers/object_changes_attribute.rb)
  • TodoComment (lib/paper_trail/queries/versions/where_object_changes.rb)
  • TodoComment (lib/paper_trail/record_trail.rb)
  • TodoComment (lib/paper_trail/version_concern.rb)
  • TodoComment (spec/dummy_app/app/models/elephant.rb)
  • …and 1 more

Changes since last survey

  • 2 commits — 1 feature/other, 1 fixes

By area

  • (root) — 2 commits

Notable commits

  • fix: Fix ci and rubocop (#1557)
  • change: #1459 - create paper_trail.rb initializer that disables versioning on touch events by default. (#1520)

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

Survey your own repository

paper-trail-gem/paper_trail was measured the same way every project in this corpus was: the same rubric, at a pinned commit, with the result published in full. Point a surveyor at a repository you know and see whether you agree with it.

About this page

  • The score is its most recent published measurement, taken on 26 September 2026 at a pinned commit. It is not a live figure and does not change until the project is measured again.
  • Measured at commit 86ce1b83078ef8594cad8f62877ac37f3cb011cc — the exact code this score is about.
  • Scored under rubric-2026.09.15 — the same rubric and the same method as every other entry in this index.
  • Measured by watchdog.canine.dev using codehealth-analyzer preprod-d0929f7ac71f.