paper-trail-gem/paper_trail
69.6
Adequate · 26 September 2026
3.1k
lines of production code
Ruby
primary language
4
measurements over time
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.