diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 58110d0fa..c8aabcf7a 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -54,11 +54,11 @@ For most development, testing with sqlite only is easiest and sufficient. CI will run the rest. ``` -DB=sqlite bundle exec appraisal rails-6.1 rake -DB=sqlite bundle exec appraisal rails-7.0 rake -DB=mysql bundle exec appraisal rails-7.0 rake +DB=sqlite bundle exec appraisal rails-7.1 rake +DB=sqlite bundle exec appraisal rails-8.0 rake +DB=mysql bundle exec appraisal rails-7.1 rake createuser --superuser postgres -DB=postgres bundle exec appraisal rails-7.0 rake +DB=postgres bundle exec appraisal rails-7.1 rake ``` ## The dummy_app diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4e194e498..1ee05bc6b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: uses: ruby/setup-ruby@v1 with: # See "Lowest supported ruby version" in CONTRIBUTING.md - ruby-version: '3.0' + ruby-version: '3.2' - name: Bundle run: | gem install bundler @@ -59,15 +59,11 @@ jobs: # have set this up with each database as a separate job, but then we'd be # duplicating the matrix configuration three times. matrix: - gemfile: [ 'rails_6.1', 'rails_7.0', 'rails_7.1', 'rails_7.2', 'rails_8.0' ] + gemfile: [ 'rails_7.1', 'rails_7.2', 'rails_8.0', 'rails_8.1' ] # To keep matrix size down, only test highest and lowest rubies. # See "Lowest supported ruby version" in CONTRIBUTING.md - ruby: [ '3.1', '3.3' ] - exclude: - # Rails 8 requires ruby 3.2+. - - gemfile: 'rails_8.0' - ruby: '3.1' + ruby: [ '3.2', '3.4' ] steps: - name: Checkout source uses: actions/checkout@v4 diff --git a/.rubocop.yml b/.rubocop.yml index 604accf64..9e1c542c7 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,4 +1,4 @@ -require: +plugins: - rubocop-packaging - rubocop-performance - rubocop-rails @@ -23,7 +23,10 @@ AllCops: NewCops: enable # See "Lowest supported ruby version" in CONTRIBUTING.md - TargetRubyVersion: 3.0 + TargetRubyVersion: 3.2 + +Gemspec/DevelopmentDependencies: + Enabled: false Layout/ArgumentAlignment: EnforcedStyle: with_fixed_indentation @@ -65,7 +68,7 @@ Layout/SpaceAroundOperators: # from these is of questionable value. Metrics/AbcSize: Exclude: - - 'spec/dummy_app/db/migrate/*' + - "spec/dummy_app/db/migrate/*" # Not a useful metric compared to, e.g. `AbcSize`. Metrics/BlockLength: @@ -96,9 +99,13 @@ Naming/FileName: Naming/HeredocDelimiterNaming: Enabled: false -Naming/PredicateName: +Naming/PredicatePrefix: AllowedMethods: has_paper_trail +# It is currently broken as of rubocop 1.77.0. +Naming/PredicateMethod: + Enabled: false + # Too subtle to lint. # Two-letter param names are OK. Consider `send_email(to:, cc:)`. # Even one-letter names are OK. Consider `draw_point(x, y)`. @@ -115,6 +122,12 @@ Performance/CollectionLiteralInLoop: Exclude: - spec/**/* +Rails/ApplicationRecord: + Enabled: false + +Rails/Delegate: + Enabled: false + # This cop only applies to app dev, not gem dev. Rails/RakeEnvironment: Enabled: false @@ -123,6 +136,9 @@ Rails/RakeEnvironment: Rails/SkipsModelValidations: Enabled: false +RSpec/BeEq: + Enabled: false + # This cop does not seem to work in rubocop-rspec 1.28.0 RSpec/DescribeClass: Enabled: false @@ -133,6 +149,9 @@ RSpec/DescribeClass: RSpec/ExampleLength: Enabled: false +RSpec/IndexedLet: + Enabled: false + # In an ideal world, each example has a single expectation. In the real world, # sometimes setup is a pain and you don't want to duplicate setup in multiple # examples, or make the specs more confusing with many nested `context`s, and @@ -140,6 +159,12 @@ RSpec/ExampleLength: RSpec/MultipleExpectations: Enabled: false +# It may be possible for us to use safe_load, but we'd have to pass the +# safelists, like `whitelist_classes` into our serializer, and the serializer +# interface is a public API, so that would be a breaking change. +Security/YAMLLoad: + Enabled: false + # Please use semantic style, e.g. `do` when there's a side-effect, else `{}`. # The semantic style is too nuanced to lint, so the cop is disabled. Style/BlockDelimiters: @@ -150,6 +175,9 @@ Style/BlockDelimiters: Style/DoubleNegation: Enabled: false +Style/FetchEnvVar: + Enabled: false + # Avoid annotated tokens except in desperately complicated format strings. # In 99% of format strings they actually make it less readable. Style/FormatStringToken: @@ -182,8 +210,8 @@ Style/IfUnlessModifier: # - https://github.com/bbatsov/ruby-style-guide/issues/556 Style/ModuleFunction: Exclude: - - 'lib/paper_trail/serializers/json.rb' - - 'lib/paper_trail/serializers/yaml.rb' + - "lib/paper_trail/serializers/json.rb" + - "lib/paper_trail/serializers/yaml.rb" # Too subtle to lint. Use `format` for multiple variables. For single variables, # use either interpolation or concatenation, whichever is easier to read. diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 4c60969d9..89686cb9c 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,23 +1,13 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2022-11-26 07:45:38 UTC using RuboCop version 1.22.3. +# on 2025-03-31 23:04:51 UTC using RuboCop version 1.75.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# It may be possible for us to use safe_load, but we'd have to pass the -# safelists, like `whitelist_classes` into our serializer, and the serializer -# interface is a public API, so that would be a breaking change. -# Offense count: 13 -# Cop supports --auto-correct. -Security/YAMLLoad: +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +Rails/ActiveSupportOnLoad: Exclude: - - 'lib/paper_trail/serializers/yaml.rb' - - 'spec/models/book_spec.rb' - - 'spec/models/gadget_spec.rb' - - 'spec/models/no_object_spec.rb' - - 'spec/models/person_spec.rb' - - 'spec/models/version_spec.rb' - - 'spec/paper_trail/events/destroy_spec.rb' - - 'spec/paper_trail/serializer_spec.rb' + - "lib/paper_trail/frameworks/active_record.rb" diff --git a/Appraisals b/Appraisals index 115cbb567..15c738c53 100644 --- a/Appraisals +++ b/Appraisals @@ -9,28 +9,20 @@ # > the version from the appraisal takes precedence. # > https://github.com/thoughtbot/appraisal -appraise "rails-6.1" do - gem "rails", "~> 6.1.0" - gem "rails-controller-testing", "~> 1.0.5" -end - -appraise "rails-7.0" do - gem "rails", "~> 7.0.3.1" - gem "rails-controller-testing", "~> 1.0.5" -end - appraise "rails-7.1" do gem "rails", "~> 7.1.0" - gem "rails-controller-testing", "~> 1.0.5" end appraise "rails-7.2" do gem "rails", "~> 7.2.0" - gem "rails-controller-testing", "~> 1.0.5" end appraise "rails-8.0" do - gem "rails", "~> 8.0.0.rc1" - gem "rails-controller-testing", "~> 1.0.5" + gem "rails", "~> 8.0.0" + gem "sqlite3", ">= 2.1" +end + +appraise "rails-8.1" do + gem "rails", "~> 8.1.0" gem "sqlite3", ">= 2.1" end diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f5b0ab85..5ad095633 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,45 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/). ### Breaking Changes +- None + +### Added + +- None + +### Fixed + +- None + +## 17.0.0 (2025-10-24) + +### Breaking Changes + +- None + +### Added + +- `rails generate paper_trail:install` now accepts an argument for custom versions table, e.g. + `rails generate paper_trail:install CommentVersion` created `comment_versions` table +- `rails generate paper_trail:update_item_subtype` now supports custom version classes via + `--version-class-name` option, e.g. `--version-class-name=CommentVersion` + +### Fixed + +- None + +### Dependencies + +- Drop support for Rails 6.1, which [reached EoL on 2024-10-01][2] +- Drop support for Rails 7.0, which [reached EoL on 2025-04-01][2] +- Add support for Rails 8.1 +- Drop support for Ruby 3.0, which [reached EoL on 2024-04-23][3] +- Drop support for Ruby 3.1, which [reached EoL on 2024-03-31][3] + +## 16.0.0 (2024-11-08) + +### Breaking Changes + - [#1478](https://github.com/paper-trail-gem/paper_trail/issues/1478) Do not allow multiple `has_paper_trail` definitions for models. Previously, when `has_paper_trail` was called on a parent and a child from STI, then possibly multiple `version` records diff --git a/README.md b/README.md index 970d1e66d..993a30eaa 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ This is the _user guide_. See also, the Choose version: [Unreleased](https://github.com/paper-trail-gem/paper_trail/blob/master/README.md), +[17.0](https://github.com/paper-trail-gem/paper_trail/blob/v17.0.0/README.md), +[16.0](https://github.com/paper-trail-gem/paper_trail/blob/v16.0.0/README.md), [15.2](https://github.com/paper-trail-gem/paper_trail/blob/v15.2.0/README.md), [14.0](https://github.com/paper-trail-gem/paper_trail/blob/v14.0.0/README.md), [13.0](https://github.com/paper-trail-gem/paper_trail/blob/v13.0.0/README.md), @@ -68,6 +70,7 @@ Choose version: - [6.b. Custom Serializer](#6b-custom-serializer) - [6.c. Custom Object Changes](#6c-custom-object-changes) - [6.d. Excluding the Object Column](#6d-excluding-the-object-column) + - [6.e. Error handling](#6e-error-handling) - [7. Testing](#7-testing) - [7.a. Minitest](#7a-minitest) - [7.b. RSpec](#7b-rspec) @@ -92,10 +95,11 @@ Choose version: | paper_trail | ruby | activerecord | |-------------|----------|---------------| -| unreleased | >= 3.1.0 | >= 6.1, <= 8.0 | -| 15.2 | >= 3.1.0 | >= 6.1, <= 7.2 | -| 15.1 | >= 3.1.0 | >= 6.1, <= 7.1 | -| 15 | >= 3.0.0 | >= 6.1, < 7.2 | +| unreleased | >= 3.2.0 | >= 7.1, <= 8.1 | +| 16 | >= 3.0.0 | >= 6.1, <= 8.0 | +| 15.2 | >= 3.0.0 | >= 6.1, <= 7.2 | +| 15.1 | >= 3.0.0 | >= 6.1, <= 7.1 | +| 15 | >= 3.0.0 | >= 6.1, <= 7.1 | | 14 | >= 2.7.0 | >= 6.0, < 7.1 | | 13 | >= 2.6.0 | >= 5.2, < 7.1 | | 12 | >= 2.6.0 | >= 5.2, < 7.1 | @@ -1151,6 +1155,8 @@ Be advised that redefining an association is an undocumented feature of Rails. PaperTrail has one generator, `paper_trail:install`. It writes, but does not run, a migration file. The migration creates the `versions` table. +You can provide a custom version table name e.g., for having multiple version tables. You will still need setup a custom Version class and configure it to use the custom table. See [6.a. Custom Version Classes](#6a-custom-version-classes). + #### Reference The most up-to-date documentation for this generator can be found by running @@ -1159,19 +1165,25 @@ convenience. ``` Usage: - rails generate paper_trail:install [options] + bin/rails generate paper_trail:install [VERSION_CLASS_NAME] [options] Options: - [--with-changes], [--no-with-changes] # Store changeset (diff) with each version - [--uuid] # To use paper_trail with projects using uuid for id + [--skip-namespace] # Skip namespace (affects only isolated engines) + # Default: false + [--skip-collision-check] # Skip collision check + # Default: false + [--with-changes], [--no-with-changes], [--skip-with-changes] # Store changeset (diff) with each version + # Default: false + [--uuid], [--no-uuid], [--skip-uuid] # Use uuid instead of bigint for item_id type (use only if tables use UUIDs) + # Default: false Runtime options: - -f, [--force] # Overwrite files that already exist - -p, [--pretend], [--no-pretend] # Run but do not make any changes - -q, [--quiet], [--no-quiet] # Suppress status output - -s, [--skip], [--no-skip] # Skip files that already exist + -f, [--force] # Overwrite files that already exist + -p, [--pretend], [--no-pretend], [--skip-pretend] # Run but do not make any changes + -q, [--quiet], [--no-quiet], [--skip-quiet] # Suppress status output + -s, [--skip], [--no-skip], [--skip-skip] # Skip files that already exist -Generates (but does not run) a migration to add a versions table. +Generates (but does not run) a migration to add a versions table. Can be customized by providing a Version class name. See section 5.c. Generators in README.md for more information. ``` ### 5.d. Protected Attributes @@ -1448,6 +1460,18 @@ The `object` column ends up storing a lot of duplicate data if you have models t and that are updated many times. You can save ~50% of storage space by removing the column from the versions table. It's important to note that this will disable `reify` and `where_object`. +### 6.e. Error handling + +You can change the behavior of error handling when an exception is raised while creating a version, by setting the `version_error_behavior` option: + +```ruby +# config/initializers/paper_trail.rb +PaperTrail.config.version_error_behavior = :legacy # (Default) Raise on create, log on update/delete. +PaperTrail.config.version_error_behavior = :log # Only log error. +PaperTrail.config.version_error_behavior = :exception # Raise exception. +PaperTrail.config.version_error_behavior = :silent # No-op. +``` + ## 7. Testing You may want to turn PaperTrail off to speed up your tests. See [Turning @@ -1755,8 +1779,8 @@ Released under the MIT licence. [1]: http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html [2]: https://github.com/paper-trail-gem/paper_trail/issues/163 [3]: http://railscasts.com/episodes/255-undo-with-paper-trail -[4]: https://api.travis-ci.org/paper-trail-gem/paper_trail.svg?branch=master -[5]: https://travis-ci.org/paper-trail-gem/paper_trail +[4]: https://github.com/paper-trail-gem/paper_trail/actions/workflows/test.yml/badge.svg +[5]: https://github.com/paper-trail-gem/paper_trail/actions/workflows/test.yml [6]: https://github.com/westonganger/paper_trail-association_tracking [9]: https://github.com/paper-trail-gem/paper_trail/tree/3.0-stable [10]: https://github.com/paper-trail-gem/paper_trail/tree/2.7-stable @@ -1799,6 +1823,6 @@ Released under the MIT licence. [52]: http://guides.rubyonrails.org/active_record_callbacks.html [53]: https://badge.fury.io/rb/paper_trail.svg [54]: https://rubygems.org/gems/paper_trail -[55]: https://api.dependabot.com/badges/compatibility_score?dependency-name=paper_trail&package-manager=bundler&version-scheme=semver +[55]: https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=paper_trail&package-manager=bundler&previous-version=15.2.0&new-version=16.0.0 [56]: https://dependabot.com/compatibility-score.html?dependency-name=paper_trail&package-manager=bundler&version-scheme=semver [57]: https://bundler.io/v2.3/man/bundle-install.1.html diff --git a/Rakefile b/Rakefile index 0c7ed0c0e..346af57d2 100644 --- a/Rakefile +++ b/Rakefile @@ -13,7 +13,7 @@ task :install_database_yml do # It's tempting to use `git clean` here, but this rake task will be run by # people working on changes that haven't been committed yet, so we have to # be more selective with what we delete. - ::FileUtils.rm("spec/dummy_app/db/database.yml", force: true) + FileUtils.rm("spec/dummy_app/db/database.yml", force: true) FileUtils.cp( "spec/dummy_app/config/database.#{ENV['DB']}.yml", @@ -32,7 +32,7 @@ task :clean do # TODO: only works locally. doesn't respect database.yml system "dropdb --if-exists paper_trail_test > /dev/null 2>&1" when nil, "sqlite" - ::FileUtils.rm(::Dir.glob("spec/dummy_app/db/*.sqlite3")) + FileUtils.rm(Dir.glob("spec/dummy_app/db/*.sqlite3")) else raise "Don't know how to clean specified RDBMS: #{ENV['DB']}" end diff --git a/gemfiles/rails_7.0.gemfile b/gemfiles/rails_7.0.gemfile deleted file mode 100644 index 58bdd3cbf..000000000 --- a/gemfiles/rails_7.0.gemfile +++ /dev/null @@ -1,8 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "rails", "~> 7.0.3.1" -gem "rails-controller-testing", "~> 1.0.5" - -gemspec path: "../" diff --git a/gemfiles/rails_7.1.gemfile b/gemfiles/rails_7.1.gemfile index 94b10cdc0..35a0ba34a 100644 --- a/gemfiles/rails_7.1.gemfile +++ b/gemfiles/rails_7.1.gemfile @@ -3,6 +3,5 @@ source "https://rubygems.org" gem "rails", "~> 7.1.0" -gem "rails-controller-testing", "~> 1.0.5" gemspec path: "../" diff --git a/gemfiles/rails_7.2.gemfile b/gemfiles/rails_7.2.gemfile index a806930de..92cc2b2d2 100644 --- a/gemfiles/rails_7.2.gemfile +++ b/gemfiles/rails_7.2.gemfile @@ -3,6 +3,5 @@ source "https://rubygems.org" gem "rails", "~> 7.2.0" -gem "rails-controller-testing", "~> 1.0.5" gemspec path: "../" diff --git a/gemfiles/rails_8.0.gemfile b/gemfiles/rails_8.0.gemfile index aee6dfbf2..8d7dc0699 100644 --- a/gemfiles/rails_8.0.gemfile +++ b/gemfiles/rails_8.0.gemfile @@ -2,8 +2,7 @@ source "https://rubygems.org" -gem "rails", "~> 8.0.0.rc1" -gem "rails-controller-testing", "~> 1.0.5" +gem "rails", "~> 8.0.0" gem "sqlite3", ">= 2.1" gemspec path: "../" diff --git a/gemfiles/rails_6.1.gemfile b/gemfiles/rails_8.1.gemfile similarity index 57% rename from gemfiles/rails_6.1.gemfile rename to gemfiles/rails_8.1.gemfile index 9d88e7a49..9c9320eea 100644 --- a/gemfiles/rails_6.1.gemfile +++ b/gemfiles/rails_8.1.gemfile @@ -2,7 +2,7 @@ source "https://rubygems.org" -gem "rails", "~> 6.1.0" -gem "rails-controller-testing", "~> 1.0.5" +gem "rails", "~> 8.1.0" +gem "sqlite3", ">= 2.1" gemspec path: "../" diff --git a/lib/generators/paper_trail/install/USAGE b/lib/generators/paper_trail/install/USAGE index 4af3d7b2d..9f32d3033 100644 --- a/lib/generators/paper_trail/install/USAGE +++ b/lib/generators/paper_trail/install/USAGE @@ -1,3 +1,31 @@ Description: Generates (but does not run) a migration to add a versions table. Also generates an initializer - file for configuring PaperTrail. See section 5.c. Generators in README.md for more information. + file for configuring PaperTrail. Can be customized by providing a Version class name. + See section 5.c. Generators in README.md for more information. + +Examples: + rails generate paper_trail:install + + This will create: + db/migrate/[TIMESTAMP]_create_versions.rb + config/initializers/paper_trail.rb + + rails generate paper_trail:install --with-changes + + This will create: + db/migrate/[TIMESTAMP]_create_versions.rb + db/migrate/[TIMESTAMP]_add_object_changes_to_versions.rb + config/initializers/paper_trail.rb + + rails generate paper_trail:install CommentVersion + + This will create: + db/migrate/[TIMESTAMP]_create_comment_versions.rb + config/initializers/paper_trail.rb + + rails generate paper_trail:install ProductVersion --with-changes --uuid + + This will create: + db/migrate/[TIMESTAMP]_create_product_versions.rb + db/migrate/[TIMESTAMP]_add_object_changes_to_product_versions.rb + config/initializers/paper_trail.rb diff --git a/lib/generators/paper_trail/install/install_generator.rb b/lib/generators/paper_trail/install/install_generator.rb index 65e9d5655..6811fb929 100644 --- a/lib/generators/paper_trail/install/install_generator.rb +++ b/lib/generators/paper_trail/install/install_generator.rb @@ -27,19 +27,21 @@ class InstallGenerator < MigrationGenerator desc: "Use uuid instead of bigint for item_id type (use only if tables use UUIDs)" ) - desc "Generates (but does not run) a migration to add a versions table." \ - " See section 5.c. Generators in README.md for more information." + desc "Generates (but does not run) a migration to add a versions table. " \ + "Can be customized by providing a Version class name. " \ + "See section 5.c. Generators in README.md for more information." def create_migration_file + # Use the table_name to create the proper migration filename add_paper_trail_migration( - "create_versions", + "create_#{table_name}", item_type_options: item_type_options, versions_table_options: versions_table_options, item_id_type_options: item_id_type_options, version_table_primary_key_type: version_table_primary_key_type ) if options.with_changes? - add_paper_trail_migration("add_object_changes_to_versions") + add_paper_trail_migration("add_object_changes_to_#{table_name}") end end diff --git a/lib/generators/paper_trail/install/templates/add_object_changes_to_versions.rb.erb b/lib/generators/paper_trail/install/templates/add_object_changes_to_versions.rb.erb index e0e8c106d..c4480e213 100644 --- a/lib/generators/paper_trail/install/templates/add_object_changes_to_versions.rb.erb +++ b/lib/generators/paper_trail/install/templates/add_object_changes_to_versions.rb.erb @@ -1,12 +1,12 @@ # This migration adds the optional `object_changes` column, in which PaperTrail # will store the `changes` diff for each update event. See the readme for # details. -class AddObjectChangesToVersions < ActiveRecord::Migration<%= migration_version %> +class AddObjectChangesTo<%= version_class_name.pluralize %> < ActiveRecord::Migration<%= migration_version %> # The largest text column available in all supported RDBMS. # See `create_versions.rb` for details. TEXT_BYTES = 1_073_741_823 def change - add_column :versions, :object_changes, :text, limit: TEXT_BYTES + add_column :<%= table_name %>, :object_changes, :text, limit: TEXT_BYTES end end diff --git a/lib/generators/paper_trail/install/templates/create_versions.rb.erb b/lib/generators/paper_trail/install/templates/create_versions.rb.erb index d0e4f87aa..5856dd352 100644 --- a/lib/generators/paper_trail/install/templates/create_versions.rb.erb +++ b/lib/generators/paper_trail/install/templates/create_versions.rb.erb @@ -1,6 +1,6 @@ -# This migration creates the `versions` table, the only schema PT requires. +# This migration creates the `<%= table_name %>` table for the <%= version_class_name %> class. # All other migrations PT provides are optional. -class CreateVersions < ActiveRecord::Migration<%= migration_version %> +class Create<%= version_class_name.pluralize %> < ActiveRecord::Migration<%= migration_version %> # The largest text column available in all supported RDBMS is # 1024^3 - 1 bytes, roughly one gibibyte. We specify a size @@ -9,7 +9,7 @@ class CreateVersions < ActiveRecord::Migration<%= migration_version %> TEXT_BYTES = 1_073_741_823 def change - create_table :versions<%= versions_table_options %><%= version_table_primary_key_type %> do |t| + create_table :<%= table_name %><%= versions_table_options %><%= version_table_primary_key_type %> do |t| # Consider using bigint type for performance if you are going to store only numeric ids. # t.bigint :whodunnit t.string :whodunnit @@ -36,6 +36,6 @@ class CreateVersions < ActiveRecord::Migration<%= migration_version %> t.string :event, null: false t.text :object, limit: TEXT_BYTES end - add_index :versions, %i[item_type item_id] + add_index :<%= table_name %>, %i[item_type item_id] end end diff --git a/lib/generators/paper_trail/migration_generator.rb b/lib/generators/paper_trail/migration_generator.rb index e5588ad4c..edb9e3b48 100644 --- a/lib/generators/paper_trail/migration_generator.rb +++ b/lib/generators/paper_trail/migration_generator.rb @@ -8,6 +8,10 @@ module PaperTrail class MigrationGenerator < ::Rails::Generators::Base include ::Rails::Generators::Migration + # Define arguments for the generator + argument :version_class_name, type: :string, default: "Version", + desc: "The name of the Version class (e.g., CommentVersion)" + def self.next_migration_number(dirname) ::ActiveRecord::Generators::Base.next_migration_number(dirname) end @@ -19,10 +23,17 @@ def add_paper_trail_migration(template, extra_options = {}) if self.class.migration_exists?(migration_dir, template) ::Kernel.warn "Migration already exists: #{template}" else + # Map the dynamic template name to the actual template file + template_file = map_template_name(template) + migration_template( - "#{template}.rb.erb", + "#{template_file}.rb.erb", "db/migrate/#{template}.rb", - { migration_version: migration_version }.merge(extra_options) + { + migration_version: migration_version, + table_name: table_name, + version_class_name: version_class_name + }.merge(extra_options) ) end end @@ -34,5 +45,21 @@ def migration_version ActiveRecord::VERSION::MINOR ) end + + # Convert Version class name to table name using Rails conventions + def table_name + version_class_name.underscore.pluralize + end + + # Map the dynamic template name to the actual template file + def map_template_name(template) + if template.start_with?("create_") + "create_versions" + elsif template.start_with?("add_object_changes_to_") + "add_object_changes_to_versions" + else + template + end + end end end diff --git a/lib/generators/paper_trail/update_item_subtype/templates/update_versions_for_item_subtype.rb.erb b/lib/generators/paper_trail/update_item_subtype/templates/update_versions_for_item_subtype.rb.erb index 1e45d22b0..721692c20 100644 --- a/lib/generators/paper_trail/update_item_subtype/templates/update_versions_for_item_subtype.rb.erb +++ b/lib/generators/paper_trail/update_item_subtype/templates/update_versions_for_item_subtype.rb.erb @@ -1,6 +1,6 @@ # This migration updates existing `versions` that have `item_type` that refers to # the base_class, and changes them to refer to the subclass instead. -class UpdateVersionsForItemSubtype < ActiveRecord::Migration<%= migration_version %> +class Update<%= version_class_name.pluralize %>ForItemSubtype < ActiveRecord::Migration<%= migration_version %> include ActionView::Helpers::TextHelper def up <%= @@ -18,7 +18,8 @@ class UpdateVersionsForItemSubtype < ActiveRecord::Migration<%= migration_versio # # Versions of item_type "Plant" with IDs between 42 and 1337 will be updated based on `genus` # hints = {"Animal"=>{1..4=>"species"}, "Plant"=>{42..1337=>"genus"}} hint_descriptions = "" - hints = args.inject(Hash.new{|h, k| h[k] = {}}) do |s, v| + # Use @hints over args to not break the test itself since args could now include --version_class_name=CommentVersion + hints = (@hints || []).inject(Hash.new{|h, k| h[k] = {}}) do |s, v| klass, column, range = parse_custom_entry(v) hint_descriptions << " # Versions of item_type \"#{klass}\" with IDs between #{ range.first} and #{range.last} will be updated based on \`#{column}\`\n" @@ -32,7 +33,7 @@ class UpdateVersionsForItemSubtype < ActiveRecord::Migration<%= migration_versio %> # Find all ActiveRecord models mentioned in existing versions changes = Hash.new { |h, k| h[k] = [] } - model_names = PaperTrail::Version.select(:item_type).distinct + model_names = <%= fully_qualified_version_class_name %>.select(:item_type).distinct model_names.map(&:item_type).each do |model_name| hint = hints[model_name] if defined?(hints) begin @@ -40,7 +41,7 @@ class UpdateVersionsForItemSubtype < ActiveRecord::Migration<%= migration_versio # Actually implements an inheritance_column? (Usually "type") has_inheritance_column = klass.columns.map(&:name).include?(klass.inheritance_column) # Find domain of types stored in PaperTrail versions - PaperTrail::Version.where(item_type: model_name, item_subtype: nil).select(:id, :object, :object_changes).each do |obj| + <%= fully_qualified_version_class_name %>.where(item_type: model_name, item_subtype: nil).select(:id, :object, :object_changes).each do |obj| if (object_detail = PaperTrail.serializer.load(obj.object || obj.object_changes)) is_found = false subtype_name = nil @@ -72,11 +73,11 @@ class UpdateVersionsForItemSubtype < ActiveRecord::Migration<%= migration_versio v.sort.each do |id| block_of_ids << id if (id_count += 1) % 100 == 0 - num_updated += PaperTrail::Version.where(id: block_of_ids).update_all(item_subtype: k) + num_updated += <%= fully_qualified_version_class_name %>.where(id: block_of_ids).update_all(item_subtype: k) block_of_ids = [] end end - num_updated += PaperTrail::Version.where(id: block_of_ids).update_all(item_subtype: k) + num_updated += <%= fully_qualified_version_class_name %>.where(id: block_of_ids).update_all(item_subtype: k) if num_updated > 0 say "Associated #{pluralize(num_updated, 'record')} to #{k}", subitem: true end diff --git a/lib/generators/paper_trail/update_item_subtype/update_item_subtype_generator.rb b/lib/generators/paper_trail/update_item_subtype/update_item_subtype_generator.rb index 9278dacd5..19f9f36d5 100644 --- a/lib/generators/paper_trail/update_item_subtype/update_item_subtype_generator.rb +++ b/lib/generators/paper_trail/update_item_subtype/update_item_subtype_generator.rb @@ -7,13 +7,34 @@ module PaperTrail class UpdateItemSubtypeGenerator < MigrationGenerator source_root File.expand_path("templates", __dir__) + # Remove the inherited version_class_name argument as we use an option instead + remove_argument :version_class_name + + argument :hints, type: :array, default: [], banner: "hint1 hint2" + + class_option :version_class_name, + type: :string, + default: "Version", + aliases: ["-v"], + desc: "The name of the Version class (e.g., CommentVersion)" + desc( - "Generates (but does not run) a migration to update item_subtype for "\ + "Generates (but does not run) a migration to update item_subtype for " \ "STI entries in an existing versions table." ) def create_migration_file - add_paper_trail_migration("update_versions_for_item_subtype", sti_type_options: options) + add_paper_trail_migration("update_#{table_name}_for_item_subtype", sti_type_options: options) + end + + # Return the version class name from options + def version_class_name + options[:version_class_name] + end + + # Return the fully qualified class name for use in ERB templates + def fully_qualified_version_class_name + version_class_name == "Version" ? "PaperTrail::Version" : version_class_name end end end diff --git a/lib/paper_trail.rb b/lib/paper_trail.rb index 88ba98aaf..09c68bbbe 100644 --- a/lib/paper_trail.rb +++ b/lib/paper_trail.rb @@ -115,10 +115,6 @@ def version VERSION::STRING end - def active_record_gte_7_0? - @active_record_gte_7_0 ||= ::ActiveRecord.gem_version >= ::Gem::Version.new("7.0.0") - end - def deprecator @deprecator ||= ActiveSupport::Deprecation.new("16.0", "PaperTrail") end diff --git a/lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb b/lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb index 1371b3b55..5672e79ef 100644 --- a/lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb +++ b/lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb @@ -32,7 +32,7 @@ def deserialize(attr, val) if defined_enums[attr] && val.is_a?(::String) # Because PT 4 used to save the string version of enums to `object_changes` val - elsif PaperTrail.active_record_gte_7_0? && val.is_a?(ActiveRecord::Type::Time::Value) + elsif val.is_a?(ActiveRecord::Type::Time::Value) # Because Rails 7 time attribute throws a delegation error when you deserialize # it with the factory. # See ActiveRecord::Type::Time::Value crashes when loaded from YAML on rails 7.0 diff --git a/lib/paper_trail/attribute_serializers/object_attribute.rb b/lib/paper_trail/attribute_serializers/object_attribute.rb index 2ef869def..50a8a79a5 100644 --- a/lib/paper_trail/attribute_serializers/object_attribute.rb +++ b/lib/paper_trail/attribute_serializers/object_attribute.rb @@ -10,10 +10,7 @@ def initialize(model_class) @model_class = model_class # ActiveRecord since 7.0 has a built-in encryption mechanism - @encrypted_attributes = - if PaperTrail.active_record_gte_7_0? - @model_class.encrypted_attributes&.map(&:to_s) - end + @encrypted_attributes = @model_class.encrypted_attributes&.map(&:to_s) end def serialize(attributes) diff --git a/lib/paper_trail/attribute_serializers/object_changes_attribute.rb b/lib/paper_trail/attribute_serializers/object_changes_attribute.rb index 658050891..8299ca161 100644 --- a/lib/paper_trail/attribute_serializers/object_changes_attribute.rb +++ b/lib/paper_trail/attribute_serializers/object_changes_attribute.rb @@ -10,10 +10,7 @@ def initialize(item_class) @item_class = item_class # ActiveRecord since 7.0 has a built-in encryption mechanism - @encrypted_attributes = - if PaperTrail.active_record_gte_7_0? - @item_class.encrypted_attributes&.map(&:to_s) - end + @encrypted_attributes = @item_class.encrypted_attributes&.map(&:to_s) end def serialize(changes) diff --git a/lib/paper_trail/compatibility.rb b/lib/paper_trail/compatibility.rb index cfb6f2cd5..3a3bf35ad 100644 --- a/lib/paper_trail/compatibility.rb +++ b/lib/paper_trail/compatibility.rb @@ -17,8 +17,8 @@ module PaperTrail # newer rails versions. Most PT users should avoid incompatible rails # versions. module Compatibility - ACTIVERECORD_GTE = ">= 6.1" # enforced in gemspec - ACTIVERECORD_LT = "< 8.1" # not enforced in gemspec + ACTIVERECORD_GTE = ">= 7.1" # enforced in gemspec + ACTIVERECORD_LT = "< 8.2" # not enforced in gemspec E_INCOMPATIBLE_AR = <<-EOS PaperTrail %s is not compatible with ActiveRecord %s. We allow PT diff --git a/lib/paper_trail/events/base.rb b/lib/paper_trail/events/base.rb index f84a8b441..0112fba58 100644 --- a/lib/paper_trail/events/base.rb +++ b/lib/paper_trail/events/base.rb @@ -157,7 +157,7 @@ def evaluate_only # @api private def ignored_attr_has_changed? ignored = calculated_ignored_array + @record.paper_trail_options[:skip] - ignored.any? && (changed_in_latest_version & ignored).any? + ignored.any? && changed_in_latest_version.intersect?(ignored) end # Rails 5.1 changed the API of `ActiveRecord::Dirty`. See diff --git a/lib/paper_trail/frameworks/active_record.rb b/lib/paper_trail/frameworks/active_record.rb index 43eb3fb22..c8f40b35e 100644 --- a/lib/paper_trail/frameworks/active_record.rb +++ b/lib/paper_trail/frameworks/active_record.rb @@ -3,7 +3,7 @@ # Either ActiveRecord has already been loaded by the Lazy Load Hook in our # Railtie, or else we load it now. require "active_record" -::PaperTrail::Compatibility.check_activerecord(::ActiveRecord.gem_version) +PaperTrail::Compatibility.check_activerecord(ActiveRecord.gem_version) # Now we can load the parts of PT that depend on AR. require "paper_trail/has_paper_trail" diff --git a/lib/paper_trail/frameworks/cucumber.rb b/lib/paper_trail/frameworks/cucumber.rb index df32f4c86..2f0b5b6f0 100644 --- a/lib/paper_trail/frameworks/cucumber.rb +++ b/lib/paper_trail/frameworks/cucumber.rb @@ -5,7 +5,7 @@ PaperTrail.enabled = false PaperTrail.request.enabled = true PaperTrail.request.whodunnit = nil - PaperTrail.request.controller_info = {} if defined?(::Rails) + PaperTrail.request.controller_info = {} if defined?(Rails) end module PaperTrail diff --git a/lib/paper_trail/frameworks/rspec.rb b/lib/paper_trail/frameworks/rspec.rb index bc8654e04..95cdb59f7 100644 --- a/lib/paper_trail/frameworks/rspec.rb +++ b/lib/paper_trail/frameworks/rspec.rb @@ -5,24 +5,24 @@ require "paper_trail/frameworks/rspec/helpers" RSpec.configure do |config| - config.include ::PaperTrail::RSpec::Helpers::InstanceMethods - config.extend ::PaperTrail::RSpec::Helpers::ClassMethods + config.include PaperTrail::RSpec::Helpers::InstanceMethods + config.extend PaperTrail::RSpec::Helpers::ClassMethods config.before(:each) do - ::PaperTrail.enabled = false - ::PaperTrail.request.enabled = true - ::PaperTrail.request.whodunnit = nil - ::PaperTrail.request.controller_info = {} if defined?(::Rails) && defined?(::RSpec::Rails) + PaperTrail.enabled = false + PaperTrail.request.enabled = true + PaperTrail.request.whodunnit = nil + PaperTrail.request.controller_info = {} if defined?(Rails) && defined?(RSpec::Rails) end config.before(:each, versioning: true) do - ::PaperTrail.enabled = true + PaperTrail.enabled = true end end RSpec::Matchers.define :be_versioned do # check to see if the model has `has_paper_trail` declared on it - match { |actual| actual.is_a?(::PaperTrail::Model::InstanceMethods) } + match { |actual| actual.is_a?(PaperTrail::Model::InstanceMethods) } end RSpec::Matchers.define :have_a_version_with do |attributes| diff --git a/lib/paper_trail/model_config.rb b/lib/paper_trail/model_config.rb index 652c87297..5e65be0e6 100644 --- a/lib/paper_trail/model_config.rb +++ b/lib/paper_trail/model_config.rb @@ -4,7 +4,7 @@ module PaperTrail # Configures an ActiveRecord model, mostly at application boot time, but also # sometimes mid-request, with methods like enable/disable. class ModelConfig - E_CANNOT_RECORD_AFTER_DESTROY = <<-STR.strip_heredoc.freeze + E_CANNOT_RECORD_AFTER_DESTROY = <<~STR paper_trail.on_destroy(:after) is incompatible with ActiveRecord's belongs_to_required_by_default. Use on_destroy(:before) or disable belongs_to_required_by_default. @@ -49,7 +49,7 @@ def on_create def on_destroy(recording_order = "before") assert_valid_recording_order_for_on_destroy(recording_order) @model_class.send( - "#{recording_order}_destroy", + :"#{recording_order}_destroy", lambda do |r| return unless r.paper_trail.save_version? r.paper_trail.record_destroy(recording_order) @@ -236,7 +236,7 @@ def setup_associations(options) def setup_callbacks_from_options(options_on = []) options_on.each do |event| - public_send("on_#{event}") + public_send(:"on_#{event}") end end diff --git a/lib/paper_trail/queries/versions/where_object.rb b/lib/paper_trail/queries/versions/where_object.rb index 9fa6319e4..20b49f2f0 100644 --- a/lib/paper_trail/queries/versions/where_object.rb +++ b/lib/paper_trail/queries/versions/where_object.rb @@ -39,7 +39,7 @@ def json values = [] @attributes.each do |field, value| predicates.push "object->>? = ?" - values.concat([field, value.to_s]) + values.push(field, value.to_s) end sql = predicates.join(" and ") @version_model_class.where(sql, *values) diff --git a/lib/paper_trail/queries/versions/where_object_changes.rb b/lib/paper_trail/queries/versions/where_object_changes.rb index 5ece2cbbf..9a4a8a2bd 100644 --- a/lib/paper_trail/queries/versions/where_object_changes.rb +++ b/lib/paper_trail/queries/versions/where_object_changes.rb @@ -53,7 +53,7 @@ def json predicates.push( "((object_changes->>? ILIKE ?) OR (object_changes->>? ILIKE ?))" ) - values.concat([field, "[#{value.to_json},%", field, "[%,#{value.to_json}]%"]) + values.push(field, "[#{value.to_json},%", field, "[%,#{value.to_json}]%") end sql = predicates.join(" and ") @version_model_class.where(sql, *values) diff --git a/lib/paper_trail/queries/versions/where_object_changes_from.rb b/lib/paper_trail/queries/versions/where_object_changes_from.rb index 13aaf09f4..d7a8dd01f 100644 --- a/lib/paper_trail/queries/versions/where_object_changes_from.rb +++ b/lib/paper_trail/queries/versions/where_object_changes_from.rb @@ -46,7 +46,7 @@ def json predicates.push( "(object_changes->>? ILIKE ?)" ) - values.concat([field, "[#{value.to_json},%"]) + values.push(field, "[#{value.to_json},%") end sql = predicates.join(" and ") @version_model_class.where(sql, *values) diff --git a/lib/paper_trail/queries/versions/where_object_changes_to.rb b/lib/paper_trail/queries/versions/where_object_changes_to.rb index 2c9a09b16..bfa8aa243 100644 --- a/lib/paper_trail/queries/versions/where_object_changes_to.rb +++ b/lib/paper_trail/queries/versions/where_object_changes_to.rb @@ -46,7 +46,7 @@ def json predicates.push( "(object_changes->>? ILIKE ?)" ) - values.concat([field, "[%#{value.to_json}]"]) + values.push(field, "[%#{value.to_json}]") end sql = predicates.join(" and ") @version_model_class.where(sql, *values) diff --git a/lib/paper_trail/record_trail.rb b/lib/paper_trail/record_trail.rb index 0cf90146a..77e0aa23d 100644 --- a/lib/paper_trail/record_trail.rb +++ b/lib/paper_trail/record_trail.rb @@ -22,7 +22,7 @@ def clear_rolled_back_versions # Invoked via`after_update` callback for when a previous version is # reified and then saved. def clear_version_instance - @record.send("#{@record.class.version_association_name}=", nil) + @record.send(:"#{@record.class.version_association_name}=", nil) end # Returns true if this instance is the current, live one; @@ -128,7 +128,7 @@ def record_update(force:, in_after_callback:, is_touch:) def reset_timestamp_attrs_for_update_if_needed return if live? @record.send(:timestamp_attributes_for_update_in_model).each do |column| - @record.send("restore_#{column}!") + @record.send(:"restore_#{column}!") end end @@ -202,7 +202,7 @@ def versions_between(start_time, end_time) # @api private def assign_and_reset_version_association(version) - @record.send("#{@record.class.version_association_name}=", version) + @record.send(:"#{@record.class.version_association_name}=", version) @record.send(@record.class.versions_association_name).reset end @@ -240,6 +240,10 @@ def build_version_on_update(force:, in_after_callback:, is_touch:) # instead of `merge`. data.merge!(data_for_update) + # Skip version if object_changes_adapter produced no meaningful diff. + object_changes = data[:object_changes] + return if object_changes.blank? || object_changes == "{}" || object_changes == "[]" + # Using `version_class.new` reduces memory usage compared to # `versions_assoc.build`. It's a trade-off though. We have to clear # the association cache (see `versions.reset`) and that could cause an diff --git a/lib/paper_trail/reifier.rb b/lib/paper_trail/reifier.rb index d78f87209..b2dfc141f 100644 --- a/lib/paper_trail/reifier.rb +++ b/lib/paper_trail/reifier.rb @@ -14,7 +14,7 @@ def reify(version, options) attrs = version.object_deserialized model = init_model(attrs, options, version) reify_attributes(model, version, attrs) - model.send "#{model.class.version_association_name}=", version + model.send :"#{model.class.version_association_name}=", version model end @@ -93,8 +93,8 @@ def init_unversioned_attrs(attrs, model) def reify_attribute(k, v, model, version) if model.has_attribute?(k) model[k.to_sym] = v - elsif model.respond_to?("#{k}=") - model.send("#{k}=", v) + elsif model.respond_to?(:"#{k}=") + model.send(:"#{k}=", v) elsif version.logger version.logger.warn( "Attribute #{k} does not exist on #{version.item_type} (Version id: #{version.id})." diff --git a/lib/paper_trail/serializers/yaml.rb b/lib/paper_trail/serializers/yaml.rb index 9e6bc036f..d53fb0e50 100644 --- a/lib/paper_trail/serializers/yaml.rb +++ b/lib/paper_trail/serializers/yaml.rb @@ -27,7 +27,7 @@ def load(string) # recent [memory optimizations](https://github.com/paper-trail-gem/paper_trail/pull/1189), # when coming from `recordable_object_changes`, it will be a `HashWithIndifferentAccess`. def dump(object) - object = object.to_hash if object.is_a?(HashWithIndifferentAccess) + object = object.to_hash if object.is_a?(ActiveSupport::HashWithIndifferentAccess) ::YAML.dump object end diff --git a/lib/paper_trail/version_concern.rb b/lib/paper_trail/version_concern.rb index c49bfe0c0..197f1cc58 100644 --- a/lib/paper_trail/version_concern.rb +++ b/lib/paper_trail/version_concern.rb @@ -320,7 +320,7 @@ def load_changeset end # First, deserialize the `object_changes` column. - changes = HashWithIndifferentAccess.new(object_changes_deserialized) + changes = ActiveSupport::HashWithIndifferentAccess.new(object_changes_deserialized) # The next step is, perhaps unfortunately, called "de-serialization", # and appears to be responsible for custom attribute serializers. For an diff --git a/lib/paper_trail/version_number.rb b/lib/paper_trail/version_number.rb index 0b3ab84b3..2a979e9a5 100644 --- a/lib/paper_trail/version_number.rb +++ b/lib/paper_trail/version_number.rb @@ -7,8 +7,8 @@ module PaperTrail # because of this confusion, but it's not worth the breaking change. # People are encouraged to use `PaperTrail.gem_version` instead. module VERSION - MAJOR = 15 - MINOR = 2 + MAJOR = 17 + MINOR = 0 TINY = 0 # Set PRE to nil unless it's a pre-release (beta, rc, etc.) diff --git a/paper_trail.gemspec b/paper_trail.gemspec index 08a8e412c..808c8f3f7 100644 --- a/paper_trail.gemspec +++ b/paper_trail.gemspec @@ -20,7 +20,8 @@ has been destroyed. s.license = "MIT" s.metadata = { - "changelog_uri" => "https://github.com/paper-trail-gem/paper_trail/blob/master/CHANGELOG.md" + "changelog_uri" => "https://github.com/paper-trail-gem/paper_trail/blob/master/CHANGELOG.md", + "rubygems_mfa_required" => "true" } # > Files included in this gem. .. Only add files you can require to this @@ -47,15 +48,23 @@ has been destroyed. # about 3 years, per https://www.ruby-lang.org/en/downloads/branches/ # # See "Lowest supported ruby version" in CONTRIBUTING.md - s.required_ruby_version = ">= 3.0.0" + s.required_ruby_version = ">= 3.2.0" # We no longer specify a maximum activerecord version. # See discussion in paper_trail/compatibility.rb - s.add_dependency "activerecord", ::PaperTrail::Compatibility::ACTIVERECORD_GTE + s.add_dependency "activerecord", PaperTrail::Compatibility::ACTIVERECORD_GTE # PT supports request_store versions for 3 years. s.add_dependency "request_store", "~> 1.4" + # The following gems have been extracted from the Ruby stdlib to gems, and we + # must manually include them here in order for specs to run. + s.add_development_dependency "benchmark", "~> 0.4.0" + s.add_development_dependency "bigdecimal", "~> 3.1" + s.add_development_dependency "drb", "~> 2.2" + s.add_development_dependency "logger", "~> 1.6" + s.add_development_dependency "mutex_m", "~> 0.3.0" + s.add_development_dependency "appraisal", "~> 2.5" s.add_development_dependency "byebug", "~> 11.1" s.add_development_dependency "ffaker", "~> 2.20" @@ -65,16 +74,17 @@ has been destroyed. # For `spec/dummy_app`. Technically, we only need `actionpack` (as of 2020). # However, that might change in the future, and the advantages of specifying a # subset (e.g. actionpack only) are unclear. - s.add_development_dependency "rails", ::PaperTrail::Compatibility::ACTIVERECORD_GTE + s.add_development_dependency "rails", PaperTrail::Compatibility::ACTIVERECORD_GTE + s.add_development_dependency "rails-controller-testing", "~> 1.0.5" s.add_development_dependency "rake", "~> 13.0" - s.add_development_dependency "rspec-rails", "~> 6.0.3" - s.add_development_dependency "rubocop", "~> 1.22.2" - s.add_development_dependency "rubocop-packaging", "~> 0.5.1" - s.add_development_dependency "rubocop-performance", "~> 1.11.5" - s.add_development_dependency "rubocop-rails", "~> 2.12.4" - s.add_development_dependency "rubocop-rake", "~> 0.6.0" - s.add_development_dependency "rubocop-rspec", "~> 2.5.0" + s.add_development_dependency "rspec-rails", "~> 7.1.1" + s.add_development_dependency "rubocop", "~> 1.75" + s.add_development_dependency "rubocop-packaging", "~> 0.6.0" + s.add_development_dependency "rubocop-performance", "~> 1.24.0" + s.add_development_dependency "rubocop-rails", "~> 2.30.3" + s.add_development_dependency "rubocop-rake", "~> 0.7.1" + s.add_development_dependency "rubocop-rspec", "~> 3.5.0" s.add_development_dependency "simplecov", "~> 0.21.2" # ## Database Adapters diff --git a/spec/controllers/articles_controller_spec.rb b/spec/controllers/articles_controller_spec.rb index 15d11c35b..7b6639451 100644 --- a/spec/controllers/articles_controller_spec.rb +++ b/spec/controllers/articles_controller_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe ArticlesController, type: :controller do +RSpec.describe ArticlesController do describe "PaperTrail.request.enabled?" do context "when PaperTrail.enabled? == true" do before { PaperTrail.enabled = true } diff --git a/spec/controllers/widgets_controller_spec.rb b/spec/controllers/widgets_controller_spec.rb index ae24ff422..44fb3e183 100644 --- a/spec/controllers/widgets_controller_spec.rb +++ b/spec/controllers/widgets_controller_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe WidgetsController, type: :controller, versioning: true do +RSpec.describe WidgetsController, :versioning do before { request.env["REMOTE_ADDR"] = "127.0.0.1" } after { RequestStore.store[:paper_trail] = nil } @@ -23,9 +23,9 @@ post :create, params: { widget: { name: "Flugel" } } expect(PaperTrail.request.enabled?).to eq(true) expect(PaperTrail.request.whodunnit).to(eq(153)) - expect(PaperTrail.request.controller_info.present?).to(eq(true)) - expect(PaperTrail.request.controller_info.key?(:ip)).to(eq(true)) - expect(PaperTrail.request.controller_info.key?(:user_agent)).to(eq(true)) + expect(PaperTrail.request.controller_info.present?).to(be(true)) + expect(PaperTrail.request.controller_info.key?(:ip)).to(be(true)) + expect(PaperTrail.request.controller_info.key?(:user_agent)).to(be(true)) end end diff --git a/spec/dummy_app/app/controllers/application_controller.rb b/spec/dummy_app/app/controllers/application_controller.rb index bb999f02f..374974f2e 100644 --- a/spec/dummy_app/app/controllers/application_controller.rb +++ b/spec/dummy_app/app/controllers/application_controller.rb @@ -27,6 +27,6 @@ def info_for_paper_trail private def modify_current_user - @current_user = OpenStruct.new(id: 153) + @current_user = User.new(id: 153) end end diff --git a/spec/dummy_app/app/models/fruit.rb b/spec/dummy_app/app/models/fruit.rb index 4cac19bdd..5fa40c97e 100644 --- a/spec/dummy_app/app/models/fruit.rb +++ b/spec/dummy_app/app/models/fruit.rb @@ -6,7 +6,5 @@ class Fruit < ApplicationRecord has_paper_trail versions: { class_name: "JsonVersion" } end - if PaperTrail.active_record_gte_7_0? - encrypts :supplier - end + encrypts :supplier end diff --git a/spec/dummy_app/app/models/person.rb b/spec/dummy_app/app/models/person.rb index 61d93497b..6ce8585c3 100644 --- a/spec/dummy_app/app/models/person.rb +++ b/spec/dummy_app/app/models/person.rb @@ -24,7 +24,7 @@ def time_zone=(value) super else zone = ::Time.find_zone(value) # nil if can't find time zone - super zone + super(zone) end end diff --git a/spec/dummy_app/app/models/user.rb b/spec/dummy_app/app/models/user.rb new file mode 100644 index 000000000..32949b9e6 --- /dev/null +++ b/spec/dummy_app/app/models/user.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class User < ApplicationRecord +end diff --git a/spec/dummy_app/app/models/vegetable.rb b/spec/dummy_app/app/models/vegetable.rb index 5b4ad52d7..2e70f23f4 100644 --- a/spec/dummy_app/app/models/vegetable.rb +++ b/spec/dummy_app/app/models/vegetable.rb @@ -6,7 +6,5 @@ class Vegetable < ApplicationRecord class_name: ENV["DB"] == "postgres" ? "JsonbVersion" : "PaperTrail::Version" }, on: %i[create update] - if PaperTrail.active_record_gte_7_0? - encrypts :supplier - end + encrypts :supplier end diff --git a/spec/dummy_app/app/versions/abstract_version.rb b/spec/dummy_app/app/versions/abstract_version.rb index 36e40d7a2..33d8a4b89 100644 --- a/spec/dummy_app/app/versions/abstract_version.rb +++ b/spec/dummy_app/app/versions/abstract_version.rb @@ -2,5 +2,6 @@ class AbstractVersion < ApplicationRecord include PaperTrail::VersionConcern + self.abstract_class = true end diff --git a/spec/dummy_app/app/versions/no_object_version.rb b/spec/dummy_app/app/versions/no_object_version.rb index 5b5beb90b..799cfa195 100644 --- a/spec/dummy_app/app/versions/no_object_version.rb +++ b/spec/dummy_app/app/versions/no_object_version.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true # Demonstrates a table that omits the `object` column. -class NoObjectVersion < ::PaperTrail::Version +class NoObjectVersion < PaperTrail::Version self.table_name = "no_object_versions" end diff --git a/spec/dummy_app/config.ru b/spec/dummy_app/config.ru index 2a0e152c1..5dcaacfcc 100644 --- a/spec/dummy_app/config.ru +++ b/spec/dummy_app/config.ru @@ -2,5 +2,5 @@ # This file is used by Rack-based servers to start the application. -require ::File.expand_path("config/environment", __dir__) +require File.expand_path("config/environment", __dir__) run Dummy::Application diff --git a/spec/dummy_app/config/initializers/backtrace_silencers.rb b/spec/dummy_app/config/initializers/backtrace_silencers.rb index fbd1add62..c9897c769 100644 --- a/spec/dummy_app/config/initializers/backtrace_silencers.rb +++ b/spec/dummy_app/config/initializers/backtrace_silencers.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + # Be sure to restart your server when you modify this file. # You can add backtrace silencers for libraries that you're using but don't wish diff --git a/spec/dummy_app/config/initializers/inflections.rb b/spec/dummy_app/config/initializers/inflections.rb index 8138cabcd..73732d874 100644 --- a/spec/dummy_app/config/initializers/inflections.rb +++ b/spec/dummy_app/config/initializers/inflections.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Add new inflection rules using the following format diff --git a/spec/dummy_app/config/initializers/mime_types.rb b/spec/dummy_app/config/initializers/mime_types.rb index f75864f9c..df5ec138c 100644 --- a/spec/dummy_app/config/initializers/mime_types.rb +++ b/spec/dummy_app/config/initializers/mime_types.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Add new mime types for use in respond_to blocks: diff --git a/spec/dummy_app/db/migrate/20110208155312_set_up_test_tables.rb b/spec/dummy_app/db/migrate/20110208155312_set_up_test_tables.rb index 801d0fa79..6a7ca23a8 100644 --- a/spec/dummy_app/db/migrate/20110208155312_set_up_test_tables.rb +++ b/spec/dummy_app/db/migrate/20110208155312_set_up_test_tables.rb @@ -6,7 +6,7 @@ # Starting with AR 5.1, we must specify which version of AR we are using. # I tried using `const_get` but I got a `NameError`, then I learned about # `::ActiveRecord::Migration::Current`. -class SetUpTestTables < ::ActiveRecord::Migration::Current +class SetUpTestTables < ActiveRecord::Migration::Current MYSQL_ADAPTERS = [ "ActiveRecord::ConnectionAdapters::MysqlAdapter", "ActiveRecord::ConnectionAdapters::Mysql2Adapter" @@ -392,6 +392,8 @@ def up t.string :name t.text :supplier end + + create_table :users, force: true end def down diff --git a/spec/generators/paper_trail/install_generator_spec.rb b/spec/generators/paper_trail/install_generator_spec.rb index c5b8fa93a..9588bb539 100644 --- a/spec/generators/paper_trail/install_generator_spec.rb +++ b/spec/generators/paper_trail/install_generator_spec.rb @@ -6,6 +6,7 @@ RSpec.describe PaperTrail::InstallGenerator, type: :generator do include GeneratorSpec::TestCase + destination File.expand_path("tmp", __dir__) after do @@ -139,4 +140,82 @@ ) end end + + describe "with custom version class name" do + before do + prepare_destination + run_generator %w[CommentVersion] + end + + it "generates a migration with the correct filename and content for the custom table" do + expected_parent_class = lambda { + old_school = "ActiveRecord::Migration" + ar_version = ActiveRecord::VERSION + format("%s[%d.%d]", old_school, ar_version::MAJOR, ar_version::MINOR) + }.call + + expect(destination_root).to( + have_structure { + directory("db") { + directory("migrate") { + migration("create_comment_versions") { + contains("class CreateCommentVersions < " + expected_parent_class) + contains "def change" + contains "create_table :comment_versions" + contains "add_index :comment_versions, %i[item_type item_id]" + } + } + } + } + ) + end + end + + describe "with custom version class name and `--with-changes`" do + before do + prepare_destination + run_generator %w[ProductVersion --with-changes] + end + + it "generates migrations with correct filenames and content for the custom table" do + expect(destination_root).to( + have_structure { + directory("db") { + directory("migrate") { + migration("create_product_versions") { + contains "class CreateProductVersions" + contains "create_table :product_versions" + } + migration("add_object_changes_to_product_versions") { + contains "class AddObjectChangesToProductVersions" + contains "add_column :product_versions, :object_changes, :text" + } + } + } + } + ) + end + end + + describe "with namespaced custom version class" do + before do + prepare_destination + run_generator %w[CommentVersion] + end + + it "handles namespaced class names correctly with proper filenames" do + expect(destination_root).to( + have_structure { + directory("db") { + directory("migrate") { + migration("create_comment_versions") { + contains "class CreateCommentVersions" + contains "create_table :comment_versions" + } + } + } + } + ) + end + end end diff --git a/spec/models/animal_spec.rb b/spec/models/animal_spec.rb index 20b748519..5bf9f4460 100644 --- a/spec/models/animal_spec.rb +++ b/spec/models/animal_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe Animal, type: :model, versioning: true do +RSpec.describe Animal, :versioning do it "baseline test setup" do expect(described_class.new).to be_versioned expect(described_class.inheritance_column).to eq("species") diff --git a/spec/models/article_spec.rb b/spec/models/article_spec.rb index d8ec2b146..12bebd33d 100644 --- a/spec/models/article_spec.rb +++ b/spec/models/article_spec.rb @@ -2,11 +2,11 @@ require "spec_helper" -RSpec.describe Article, type: :model, versioning: true do +RSpec.describe Article, :versioning do describe ".create" do it "also creates a version record" do expect { described_class.create }.to( - change { PaperTrail::Version.count }.by(+1) + change(PaperTrail::Version, :count).by(+1) ) end end diff --git a/spec/models/book_spec.rb b/spec/models/book_spec.rb index d51f15fd5..859b0807c 100644 --- a/spec/models/book_spec.rb +++ b/spec/models/book_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe Book, versioning: true do +RSpec.describe Book, :versioning do context "with :has_many :through" do it "store version on source <<" do book = described_class.create(title: "War and Peace") @@ -10,7 +10,7 @@ Person.create(name: "Solzhenitsyn") count = PaperTrail::Version.count (book.authors << dostoyevsky) - expect((PaperTrail::Version.count - count)).to(eq(1)) + expect(PaperTrail::Version.count - count).to(eq(1)) expect(book.authorships.first.versions.first).to(eq(PaperTrail::Version.last)) end @@ -20,10 +20,10 @@ Person.create(name: "Solzhenitsyn") count = PaperTrail::Version.count book.authors.create(name: "Tolstoy") - expect((PaperTrail::Version.count - count)).to(eq(2)) + expect(PaperTrail::Version.count - count).to(eq(2)) expect( [PaperTrail::Version.order(:id).to_a[-2].item, PaperTrail::Version.last.item] - ).to match_array([Person.last, Authorship.last]) + ).to contain_exactly(Person.last, Authorship.last) end it "store version on join destroy" do @@ -33,7 +33,7 @@ (book.authors << dostoyevsky) count = PaperTrail::Version.count book.authorships.reload.last.destroy - expect((PaperTrail::Version.count - count)).to(eq(1)) + expect(PaperTrail::Version.count - count).to(eq(1)) expect(PaperTrail::Version.last.reify.book).to(eq(book)) expect(PaperTrail::Version.last.reify.author).to(eq(dostoyevsky)) end @@ -45,7 +45,7 @@ book.authors << dostoyevsky count = PaperTrail::Version.count book.authorships.reload.destroy_all - expect((PaperTrail::Version.count - count)).to(eq(1)) + expect(PaperTrail::Version.count - count).to(eq(1)) expect(PaperTrail::Version.last.reify.book).to(eq(book)) expect(PaperTrail::Version.last.reify.author).to(eq(dostoyevsky)) end diff --git a/spec/models/boolit_spec.rb b/spec/models/boolit_spec.rb index c022bce7d..d3f72b5cf 100644 --- a/spec/models/boolit_spec.rb +++ b/spec/models/boolit_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" require "support/custom_json_serializer" -RSpec.describe Boolit, type: :model, versioning: true do +RSpec.describe Boolit, :versioning do let(:boolit) { described_class.create! } before { boolit.update!(name: FFaker::Name.name) } diff --git a/spec/models/callback_modifier_spec.rb b/spec/models/callback_modifier_spec.rb index e3a47bfc1..081fa74bb 100644 --- a/spec/models/callback_modifier_spec.rb +++ b/spec/models/callback_modifier_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe CallbackModifier, type: :model, versioning: true do +RSpec.describe CallbackModifier, :versioning do describe "paper_trail_on_destroy" do it "adds :destroy to paper_trail_options[:on]" do modifier = NoArgDestroyModifier.create!(some_content: FFaker::Lorem.sentence) diff --git a/spec/models/car_spec.rb b/spec/models/car_spec.rb index 37f98c88c..f0c93b0a4 100644 --- a/spec/models/car_spec.rb +++ b/spec/models/car_spec.rb @@ -2,10 +2,10 @@ require "spec_helper" -RSpec.describe Car, type: :model do +RSpec.describe Car do it { is_expected.to be_versioned } - describe "changeset", versioning: true do + describe "changeset", :versioning do it "has the expected keys (see issue 738)" do car = described_class.create!(name: "Alice") car.update(name: "Bob") @@ -13,7 +13,7 @@ end end - describe "attributes and accessors", versioning: true do + describe "attributes and accessors", :versioning do it "reifies attributes that are not AR attributes" do car = described_class.create name: "Pinto", color: "green" car.update color: "yellow" diff --git a/spec/models/cat_spec.rb b/spec/models/cat_spec.rb index 100e87d3c..18f007081 100644 --- a/spec/models/cat_spec.rb +++ b/spec/models/cat_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe Cat, type: :model, versioning: true do +RSpec.describe Cat, :versioning do describe "#descends_from_active_record?" do it "returns false, meaning that Cat is an STI subclass" do expect(described_class.descends_from_active_record?).to eq(false) diff --git a/spec/models/custom_primary_key_record_spec.rb b/spec/models/custom_primary_key_record_spec.rb index 1c1a125dd..2788d74d9 100644 --- a/spec/models/custom_primary_key_record_spec.rb +++ b/spec/models/custom_primary_key_record_spec.rb @@ -2,11 +2,11 @@ require "spec_helper" -RSpec.describe CustomPrimaryKeyRecord, type: :model do +RSpec.describe CustomPrimaryKeyRecord do it { is_expected.to be_versioned } describe "#versions" do - it "returns instances of CustomPrimaryKeyRecordVersion", versioning: true do + it "returns instances of CustomPrimaryKeyRecordVersion", :versioning do custom_primary_key_record = described_class.create! custom_primary_key_record.update!(name: "bob") version = custom_primary_key_record.versions.last diff --git a/spec/models/document_spec.rb b/spec/models/document_spec.rb index 19bbe8027..63d10e904 100644 --- a/spec/models/document_spec.rb +++ b/spec/models/document_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe Document, type: :model, versioning: true do +RSpec.describe Document, :versioning do describe "have_a_version_with matcher" do it "works with custom versions association" do document = described_class.create!(name: "Foo") diff --git a/spec/models/family/celebrity_family_spec.rb b/spec/models/family/celebrity_family_spec.rb index f7c70aa16..0a963358c 100644 --- a/spec/models/family/celebrity_family_spec.rb +++ b/spec/models/family/celebrity_family_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" module Family - RSpec.describe CelebrityFamily, type: :model, versioning: true do + RSpec.describe CelebrityFamily, :versioning do describe "#joins" do it "works on an STI model" do described_class.create! @@ -32,7 +32,7 @@ module Family context "with belongs_to" do it "uses the correct item_subtype" do parent = described_class.new(name: "Jermaine Jackson") - parent.path_to_stardom = "Emulating Motown greats such as the Temptations and "\ + parent.path_to_stardom = "Emulating Motown greats such as the Temptations and " \ "The Supremes" child1 = parent.children.build(name: "Jaimy Jermaine Jackson") parent.children.build(name: "Autumn Joy Jackson") @@ -54,7 +54,7 @@ module Family context "with has_many" do it "uses the correct item_type in queries" do parent = described_class.new(name: "Gomez Addams") - parent.path_to_stardom = "Buy a Victorian house next to a sprawling graveyard, "\ + parent.path_to_stardom = "Buy a Victorian house next to a sprawling graveyard, " \ "and just become super aloof." parent.children.build(name: "Wednesday") parent.save! @@ -73,8 +73,8 @@ module Family context "with has_many through" do it "uses the correct item_type in queries" do parent = described_class.new(name: "Grandad") - parent.path_to_stardom = "Took a suitcase and started running a market trading "\ - "company out of it, while proclaiming, 'This time next "\ + parent.path_to_stardom = "Took a suitcase and started running a market trading " \ + "company out of it, while proclaiming, 'This time next " \ "year, we'll be millionaires!'" parent.grandsons.build(name: "Del Boy") parent.save! @@ -93,8 +93,8 @@ module Family context "with has_one" do it "uses the correct item_type in queries" do parent = described_class.new(name: "Minnie Marx") - parent.path_to_stardom = "Gain a relentless dedication to the stage by having a "\ - "mother who performs as a yodeling harpist, and then "\ + parent.path_to_stardom = "Gain a relentless dedication to the stage by having a " \ + "mother who performs as a yodeling harpist, and then " \ "bring up 5 boys who have a true zest for comedy." parent.build_mentee(name: "Abraham Schönberg") parent.save! diff --git a/spec/models/foo_widget_spec.rb b/spec/models/foo_widget_spec.rb index 8e3bbfcf8..d500be57d 100644 --- a/spec/models/foo_widget_spec.rb +++ b/spec/models/foo_widget_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" require "support/performance_helpers" -RSpec.describe(FooWidget, versioning: true) do +RSpec.describe(FooWidget, :versioning) do context "with a subclass" do let(:foo) { described_class.create } diff --git a/spec/models/fruit_spec.rb b/spec/models/fruit_spec.rb index 10e12bc92..95e20c6db 100644 --- a/spec/models/fruit_spec.rb +++ b/spec/models/fruit_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" if ENV["DB"] == "postgres" && JsonVersion.table_exists? - RSpec.describe Fruit, type: :model, versioning: true do + RSpec.describe Fruit, :versioning do describe "have_a_version_with_changes matcher" do it "works with Fruit because Fruit uses JsonVersion" do # As of PT 9.0.0, with_version_changes only supports json(b) columns, @@ -17,7 +17,7 @@ end end - describe "queries of versions", versioning: true do + describe "queries of versions", :versioning do let!(:fruit) { described_class.create(name: "Apple", mass: 1, color: "green") } before do diff --git a/spec/models/gadget_spec.rb b/spec/models/gadget_spec.rb index 5c8bd06ff..54d9445f2 100644 --- a/spec/models/gadget_spec.rb +++ b/spec/models/gadget_spec.rb @@ -2,12 +2,12 @@ require "spec_helper" -RSpec.describe Gadget, type: :model do +RSpec.describe Gadget do let(:gadget) { described_class.create!(name: "Wrench", brand: "Acme") } it { is_expected.to be_versioned } - describe "updates", versioning: true do + describe "updates", :versioning do it "generates a version for updates" do expect { gadget.update_attribute(:name, "Hammer") }.to(change { gadget.versions.size }.by(1)) end @@ -35,7 +35,7 @@ gadget.update_attribute(:updated_at, Time.current + 1) }.to(change { gadget.versions.size }.by(1)) expect( - if ::YAML.respond_to?(:unsafe_load) + if YAML.respond_to?(:unsafe_load) YAML.unsafe_load(gadget.versions.last.object_changes).keys else YAML.load(gadget.versions.last.object_changes).keys diff --git a/spec/models/gizmo_spec.rb b/spec/models/gizmo_spec.rb index a5fff8aee..01aa34dde 100644 --- a/spec/models/gizmo_spec.rb +++ b/spec/models/gizmo_spec.rb @@ -3,10 +3,10 @@ require "spec_helper" require "support/performance_helpers" -RSpec.describe Gizmo, type: :model, versioning: true do +RSpec.describe Gizmo, :versioning do context "with a persisted record" do it "does not use the gizmo `updated_at` as the version's `created_at`" do - gizmo = described_class.create(name: "Fred", created_at: Time.current - 1.day) + gizmo = described_class.create(name: "Fred", created_at: 1.day.ago) gizmo.name = "Allen" gizmo.save(touch: false) expect(gizmo.versions.last.created_at).not_to(eq(gizmo.updated_at)) diff --git a/spec/models/joined_version_spec.rb b/spec/models/joined_version_spec.rb index 8046f1d4c..fb4fafc1d 100644 --- a/spec/models/joined_version_spec.rb +++ b/spec/models/joined_version_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe JoinedVersion, type: :model, versioning: true do +RSpec.describe JoinedVersion, :versioning do let(:widget) { Widget.create!(name: FFaker::Name.name) } let(:version) { described_class.first } @@ -15,19 +15,19 @@ describe "#subsequent" do it "does not raise error when there is a default_scope that joins" do - described_class.subsequent(version).first + expect { described_class.subsequent(version).first }.not_to raise_error end end describe "#preceding" do it "does not raise error when there is a default scope that joins" do - described_class.preceding(version).first + expect { described_class.preceding(version).first }.not_to raise_error end end describe "#between" do it "does not raise error when there is a default scope that joins" do - described_class.between(Time.current, 1.minute.from_now).first + expect { described_class.between(Time.current, 1.minute.from_now).first }.not_to raise_error end end end @@ -37,7 +37,7 @@ it "does not raise error when there is a default scope that joins" do widget # persist a widget - version.index + expect { version.index }.not_to raise_error end end end diff --git a/spec/models/json_version_spec.rb b/spec/models/json_version_spec.rb index 72400e3fd..af4cdfb27 100644 --- a/spec/models/json_version_spec.rb +++ b/spec/models/json_version_spec.rb @@ -5,7 +5,7 @@ # The `json_versions` table tests postgres' `json` data type. So, that # table is only created when testing against postgres. if JsonVersion.table_exists? - RSpec.describe JsonVersion, type: :model, versioning: true do + RSpec.describe JsonVersion, :versioning do it "includes the VersionConcern module" do expect(described_class).to include(PaperTrail::VersionConcern) end @@ -30,7 +30,7 @@ end end - context "with valid arguments", versioning: true do + context "with valid arguments", :versioning do it "locates versions according to their `object` contents" do fruit = Fruit.create!(name: "apple") expect(fruit.versions.length).to eq(1) @@ -72,7 +72,7 @@ end end - context "with valid arguments", versioning: true do + context "with valid arguments", :versioning do it "finds versions according to their `object_changes` contents" do fruit = Fruit.create!(name: "apple") fruit.update!(name: "banana", color: "red") @@ -116,7 +116,7 @@ "json_versions"."id" ASC SQL ) - expect(where_red_apple).to match_array([fruit.versions[1]]) + expect(where_red_apple).to contain_exactly(fruit.versions[1]) end end end diff --git a/spec/models/kitchen/banana_spec.rb b/spec/models/kitchen/banana_spec.rb index b4159b104..81479acd7 100644 --- a/spec/models/kitchen/banana_spec.rb +++ b/spec/models/kitchen/banana_spec.rb @@ -3,11 +3,11 @@ require "spec_helper" module Kitchen - RSpec.describe Banana, type: :model do + RSpec.describe Banana do it { is_expected.to be_versioned } describe "#versions" do - it "returns instances of Kitchen::BananaVersion", versioning: true do + it "returns instances of Kitchen::BananaVersion", :versioning do banana = described_class.create! expect(banana.versions.first).to be_a(Kitchen::BananaVersion) end diff --git a/spec/models/legacy_widget_spec.rb b/spec/models/legacy_widget_spec.rb index 8addc1425..8a05e0227 100644 --- a/spec/models/legacy_widget_spec.rb +++ b/spec/models/legacy_widget_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe LegacyWidget, type: :model, versioning: true do +RSpec.describe LegacyWidget, :versioning do describe "#custom_version" do it "knows which version it came from" do widget = described_class.create(name: "foo", version: 2) diff --git a/spec/models/management_spec.rb b/spec/models/management_spec.rb index bf3cb9277..7c67344d2 100644 --- a/spec/models/management_spec.rb +++ b/spec/models/management_spec.rb @@ -2,14 +2,14 @@ require "spec_helper" -::RSpec.describe(::Management, type: :model, versioning: true) do +RSpec.describe(Management, :versioning) do it "utilises the base_class for STI classes having no type column" do - expect(Management.inheritance_column).to eq("type") - expect(Management.columns.map(&:name)).not_to include("type") + expect(described_class.inheritance_column).to eq("type") + expect(described_class.columns.map(&:name)).not_to include("type") # Create, update, and destroy a Management and a Customer customer1 = Customer.create(name: "Cust 1") - customer2 = Management.create(name: "Cust 2") + customer2 = described_class.create(name: "Cust 2") customer1.update(name: "Cust 1a") customer2.update(name: "Cust 2a") customer1.destroy diff --git a/spec/models/no_object_spec.rb b/spec/models/no_object_spec.rb index 95f624927..651ec0860 100644 --- a/spec/models/no_object_spec.rb +++ b/spec/models/no_object_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe NoObject, versioning: true do +RSpec.describe NoObject, :versioning do it "still creates version records" do n = described_class.create!(letter: "A") a = n.versions.last.attributes @@ -27,7 +27,7 @@ # New feature: destroy populates object_changes # https://github.com/paper-trail-gem/paper_trail/pull/1123 - h = if ::YAML.respond_to?(:unsafe_load) + h = if YAML.respond_to?(:unsafe_load) YAML.unsafe_load a["object_changes"] else YAML.load a["object_changes"] @@ -46,7 +46,7 @@ v = n.versions.last expect { v.reify }.to( raise_error( - ::PaperTrail::Error, + PaperTrail::Error, "reify requires an object column" ) ) @@ -60,7 +60,7 @@ n.versions.where_object(foo: "bar") }.to( raise_error( - ::PaperTrail::Error, + PaperTrail::Error, "where_object requires an object column" ) ) diff --git a/spec/models/not_on_update_spec.rb b/spec/models/not_on_update_spec.rb index c811c56c6..2ac42066a 100644 --- a/spec/models/not_on_update_spec.rb +++ b/spec/models/not_on_update_spec.rb @@ -2,14 +2,12 @@ require "spec_helper" -RSpec.describe NotOnUpdate, type: :model do - describe "#save_with_version", versioning: true do +RSpec.describe NotOnUpdate do + describe "#save_with_version", :versioning do let!(:record) { described_class.create! } it "creates a version, regardless" do - expect { record.paper_trail.save_with_version }.to change { - PaperTrail::Version.count - }.by(+1) + expect { record.paper_trail.save_with_version }.to change(PaperTrail::Version, :count).by(+1) end it "captures changes when in_after_callback is true" do diff --git a/spec/models/on/create_spec.rb b/spec/models/on/create_spec.rb index fbf1593f5..39271e4c3 100644 --- a/spec/models/on/create_spec.rb +++ b/spec/models/on/create_spec.rb @@ -4,7 +4,7 @@ require_dependency "on/create" module On - RSpec.describe Create, type: :model, versioning: true do + RSpec.describe Create, :versioning do describe "#versions" do it "only have a version for the create event" do record = described_class.create(name: "Alice") diff --git a/spec/models/on/destroy_spec.rb b/spec/models/on/destroy_spec.rb index 044c0aebd..ce09b1822 100644 --- a/spec/models/on/destroy_spec.rb +++ b/spec/models/on/destroy_spec.rb @@ -4,7 +4,7 @@ require_dependency "on/destroy" module On - RSpec.describe Destroy, type: :model, versioning: true do + RSpec.describe Destroy, :versioning do describe "#versions" do it "only creates one version record, for the destroy event" do record = described_class.create(name: "Alice") diff --git a/spec/models/on/empty_array_spec.rb b/spec/models/on/empty_array_spec.rb index e13ac4deb..e583fe191 100644 --- a/spec/models/on/empty_array_spec.rb +++ b/spec/models/on/empty_array_spec.rb @@ -4,7 +4,7 @@ require_dependency "on/empty_array" module On - RSpec.describe EmptyArray, type: :model, versioning: true do + RSpec.describe EmptyArray, :versioning do describe "#create" do it "does not create any version records" do record = described_class.create(name: "Alice") @@ -15,9 +15,9 @@ module On describe ".paper_trail.update_columns" do it "creates a version record" do widget = Widget.create - assert_equal 1, widget.versions.length + expect(widget.versions.length).to eq(1) widget.paper_trail.update_columns(name: "Bugle") - assert_equal 2, widget.versions.length + expect(widget.versions.length).to eq(2) end end diff --git a/spec/models/on/touch_spec.rb b/spec/models/on/touch_spec.rb index 9b8f8b494..48c5f9892 100644 --- a/spec/models/on/touch_spec.rb +++ b/spec/models/on/touch_spec.rb @@ -4,7 +4,7 @@ require_dependency "on/create" module On - RSpec.describe Touch, type: :model, versioning: true do + RSpec.describe Touch, :versioning do describe "#create" do it "does not create a version" do record = described_class.create(name: "Alice") diff --git a/spec/models/on/update_spec.rb b/spec/models/on/update_spec.rb index 82b456634..82a17c7f1 100644 --- a/spec/models/on/update_spec.rb +++ b/spec/models/on/update_spec.rb @@ -4,7 +4,7 @@ require_dependency "on/update" module On - RSpec.describe Update, type: :model, versioning: true do + RSpec.describe Update, :versioning do describe "#versions" do it "only creates one version record, for the update event" do record = described_class.create(name: "Alice") diff --git a/spec/models/order_spec.rb b/spec/models/order_spec.rb index 99665accc..40d113eb2 100644 --- a/spec/models/order_spec.rb +++ b/spec/models/order_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe Order, type: :model, versioning: true do +RSpec.describe Order, :versioning do context "when the record destroyed" do it "creates a version record for association" do customer = Customer.create! diff --git a/spec/models/person_spec.rb b/spec/models/person_spec.rb index bd44f19e7..687f6c6e6 100644 --- a/spec/models/person_spec.rb +++ b/spec/models/person_spec.rb @@ -6,7 +6,7 @@ # # - has a dozen associations of various types # - has a custom serializer, TimeZoneSerializer, for its `time_zone` attribute -RSpec.describe Person, type: :model, versioning: true do +RSpec.describe Person, :versioning do describe "#time_zone" do it "returns an ActiveSupport::TimeZone" do person = described_class.new(time_zone: "Samoa") @@ -19,13 +19,13 @@ person = described_class.new(time_zone: "Samoa") person.save! len = person.versions.last.object_changes.length - expect((len < 105)).to(be_truthy) + expect(len < 105).to(be_truthy) end it "version.object_changes attribute should have stored the value from serializer" do person = described_class.new(time_zone: "Samoa") person.save! - as_stored_in_version = HashWithIndifferentAccess[ + as_stored_in_version = ActiveSupport::HashWithIndifferentAccess[ YAML.load(person.versions.last.object_changes) ] expect(as_stored_in_version[:time_zone]).to(eq([nil, "Samoa"])) @@ -66,7 +66,7 @@ person.assign_attributes(time_zone: "Pacific Time (US & Canada)") person.save! len = person.versions.last.object.length - expect((len < 105)).to(be_truthy) + expect(len < 105).to(be_truthy) end it "object_changes should not store long serialization of TimeZone object" do @@ -84,7 +84,7 @@ attribute_value_before_change = person.time_zone person.assign_attributes(time_zone: "Pacific Time (US & Canada)") person.save! - as_stored_in_version = HashWithIndifferentAccess[ + as_stored_in_version = ActiveSupport::HashWithIndifferentAccess[ YAML.load(person.versions.last.object) ] expect(as_stored_in_version[:time_zone]).to(eq("Samoa")) @@ -97,7 +97,7 @@ person.save! person.assign_attributes(time_zone: "Pacific Time (US & Canada)") person.save! - as_stored_in_version = HashWithIndifferentAccess[ + as_stored_in_version = ActiveSupport::HashWithIndifferentAccess[ YAML.load(person.versions.last.object_changes) ] expect(as_stored_in_version[:time_zone]).to(eq(["Samoa", "Pacific Time (US & Canada)"])) diff --git a/spec/models/pet_spec.rb b/spec/models/pet_spec.rb index 061e86b23..a507120e9 100644 --- a/spec/models/pet_spec.rb +++ b/spec/models/pet_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" require "rails/generators" -RSpec.describe Pet, type: :model, versioning: true do +RSpec.describe Pet, :versioning do it "baseline test setup" do expect(described_class.new).to be_versioned end @@ -65,7 +65,7 @@ # that examines all existing models to identify use of STI, then updates all older # version entries that may refer to the base_class so they refer to the subclass. # (This is the same as running: rails g paper_trail:update_sti; rails db:migrate) - migrator = ::PaperTrailSpecMigrator.new + migrator = PaperTrailSpecMigrator.new expect { migrator.generate_and_migrate("paper_trail:update_item_subtype", []) }.to output(/Associated 1 record to Cat/).to_stdout @@ -98,7 +98,7 @@ # paper_trail:update_sti` would be unable to determine the previous # inheritance_column, so a generated migration *with no hints* would # accomplish nothing. - migrator = ::PaperTrailSpecMigrator.new + migrator = PaperTrailSpecMigrator.new hints = [] expect { migrator.generate_and_migrate("paper_trail:update_item_subtype", hints) @@ -122,7 +122,7 @@ # This is the same as running: # rails g paper_trail:update_sti Animal(species):1..4; rails db:migrate - migrator = ::PaperTrailSpecMigrator.new + migrator = PaperTrailSpecMigrator.new hints = ["Animal(species):#{cat_ids.first}..#{cat_ids.last}"] expect { migrator.generate_and_migrate("paper_trail:update_item_subtype", hints) diff --git a/spec/models/plant_spec.rb b/spec/models/plant_spec.rb index 4656b1f3d..6a36b9b45 100644 --- a/spec/models/plant_spec.rb +++ b/spec/models/plant_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe Plant, type: :model, versioning: true do +RSpec.describe Plant, :versioning do it "baseline test setup" do expect(described_class.new).to be_versioned expect(described_class.inheritance_column).to eq("species") diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index 59bf49f4e..9959084fe 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" # The `Post` model uses a custom version class, `PostVersion` -RSpec.describe Post, type: :model, versioning: true do +RSpec.describe Post, :versioning do it "inserts records into the correct table, post_versions" do post = described_class.create expect(PostVersion.count).to(eq(1)) diff --git a/spec/models/post_with_status_spec.rb b/spec/models/post_with_status_spec.rb index cd11f0511..79406ef6e 100644 --- a/spec/models/post_with_status_spec.rb +++ b/spec/models/post_with_status_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe PostWithStatus, type: :model do +RSpec.describe PostWithStatus do with_versioning do let(:post) { described_class.create!(status: "draft") } @@ -19,7 +19,7 @@ # Simulate behavior PT 4, which used to save the string version of # enums to `object_changes` version.update(object_changes: "---\nid:\n- \n- 1\nstatus:\n- draft\n- published\n") - assert_equal %w[draft published], version.changeset["status"] + expect(version.changeset["status"]).to eq(%w[draft published]) end context "when storing enum object_changes" do diff --git a/spec/models/skipper_spec.rb b/spec/models/skipper_spec.rb index 2e4189ed6..34f8a0c61 100644 --- a/spec/models/skipper_spec.rb +++ b/spec/models/skipper_spec.rb @@ -2,10 +2,10 @@ require "spec_helper" -RSpec.describe Skipper, type: :model, versioning: true do +RSpec.describe Skipper, :versioning do it { is_expected.to be_versioned } - describe "#update!", versioning: true do + describe "#update!", :versioning do context "when updating a skipped attribute" do let(:t1) { Time.zone.local(2015, 7, 15, 20, 34, 0) } let(:t2) { Time.zone.local(2015, 7, 15, 20, 34, 30) } @@ -70,7 +70,7 @@ skipper = described_class.create!(another_timestamp: t1) skipper.update!(another_timestamp: t2, name: "Foobar") skipper = skipper.versions.last.reify - expect(skipper.another_timestamp).to be(nil) + expect(skipper.another_timestamp).to be_nil end end diff --git a/spec/models/song_spec.rb b/spec/models/song_spec.rb index 370db790e..ec3ab0b80 100644 --- a/spec/models/song_spec.rb +++ b/spec/models/song_spec.rb @@ -2,9 +2,9 @@ require "spec_helper" -::RSpec.describe(::Song, type: :model, versioning: true) do +RSpec.describe(Song, :versioning) do describe "#joins" do - it "works" do + it "sets event to 'create' when a new record is created" do described_class.create! result = described_class. joins(:versions). @@ -17,7 +17,7 @@ context "when the default accessor, length=, is overwritten" do it "returns overwritten value on reified instance" do - song = Song.create(length: 4) + song = described_class.create(length: 4) song.update(length: 5) expect(song.length).to(eq(5)) expect(song.versions.last.reify.length).to(eq(4)) @@ -26,7 +26,7 @@ context "when song name is a virtual attribute (no such db column)" do it "returns overwritten virtual attribute on the reified instance" do - song = Song.create(length: 4) + song = described_class.create(length: 4) song.update(length: 5) song.name = "Good Vibrations" song.save diff --git a/spec/models/thing_spec.rb b/spec/models/thing_spec.rb index 3030abd72..67d4d1115 100644 --- a/spec/models/thing_spec.rb +++ b/spec/models/thing_spec.rb @@ -2,8 +2,8 @@ require "spec_helper" -RSpec.describe Thing, type: :model do - describe "#versions", versioning: true do +RSpec.describe Thing do + describe "#versions", :versioning do let(:thing) { described_class.create! } it "applies the scope option" do diff --git a/spec/models/translation_spec.rb b/spec/models/translation_spec.rb index 23bb1450f..846e22c8b 100644 --- a/spec/models/translation_spec.rb +++ b/spec/models/translation_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe Translation, type: :model, versioning: true do +RSpec.describe Translation, :versioning do context "with non-US translations" do it "not change the number of versions" do described_class.create!(headline: "Headline") diff --git a/spec/models/vegetable_spec.rb b/spec/models/vegetable_spec.rb index 7ab422a80..8080d81ed 100644 --- a/spec/models/vegetable_spec.rb +++ b/spec/models/vegetable_spec.rb @@ -4,8 +4,8 @@ require "support/performance_helpers" if ENV["DB"] == "postgres" && JsonbVersion.table_exists? - ::RSpec.describe Vegetable do - describe "queries of versions", versioning: true do + RSpec.describe Vegetable do + describe "queries of versions", :versioning do let!(:vegetable) { described_class.create(name: "Veggie", mass: 1, color: "green") } before do diff --git a/spec/models/vehicle_spec.rb b/spec/models/vehicle_spec.rb index 4bb49813d..6d693d9a8 100644 --- a/spec/models/vehicle_spec.rb +++ b/spec/models/vehicle_spec.rb @@ -2,6 +2,6 @@ require "spec_helper" -RSpec.describe Vehicle, type: :model do +RSpec.describe Vehicle do it { is_expected.not_to be_versioned } end diff --git a/spec/models/version_spec.rb b/spec/models/version_spec.rb index faa111038..5ace19883 100644 --- a/spec/models/version_spec.rb +++ b/spec/models/version_spec.rb @@ -3,10 +3,11 @@ require "spec_helper" require "support/shared_examples/queries" require "support/shared_examples/active_record_encryption" +require "support/custom_object_changes_adapter" module PaperTrail - ::RSpec.describe Version, type: :model do - describe "#object_changes", versioning: true do + ::RSpec.describe Version do + describe "#object_changes", :versioning do let(:widget) { Widget.create!(name: "Dashboard") } let(:value) { widget.versions.last.object_changes } @@ -24,7 +25,7 @@ module PaperTrail end it "creates a version with custom changes" do - adapter = instance_spy("CustomObjectChangesAdapter") + adapter = instance_spy(CustomObjectChangesAdapter) PaperTrail.config.object_changes_adapter = adapter custom_changes_value = [["name", nil, "Dashboard"]] allow(adapter).to( @@ -66,7 +67,7 @@ module PaperTrail end end - context "with previous version", versioning: true do + context "with previous version", :versioning do it "returns name of whodunnit" do name = FFaker::Name.name widget = Widget.create!(name: FFaker::Name.name) @@ -84,7 +85,7 @@ module PaperTrail end end - context "with previous version", versioning: true do + context "with previous version", :versioning do it "returns a PaperTrail::Version" do name = FFaker::Name.name widget = Widget.create!(name: FFaker::Name.name) @@ -110,12 +111,12 @@ module PaperTrail end end - context "with text columns", versioning: true do + context "with text columns", :versioning do include_examples "queries", :text, ::Widget, :an_integer end if ENV["DB"] == "postgres" - context "with json columns", versioning: true do + context "with json columns", :versioning do include_examples( "queries", :json, @@ -126,7 +127,7 @@ module PaperTrail include_examples("active_record_encryption", ::Fruit) end - context "with jsonb columns", versioning: true do + context "with jsonb columns", :versioning do include_examples( "queries", :jsonb, diff --git a/spec/models/widget_spec.rb b/spec/models/widget_spec.rb index 98557af31..56a6a7bc5 100644 --- a/spec/models/widget_spec.rb +++ b/spec/models/widget_spec.rb @@ -2,8 +2,9 @@ require "spec_helper" require "support/performance_helpers" +require "support/custom_object_changes_adapter" -RSpec.describe Widget, type: :model, versioning: true do +RSpec.describe Widget, :versioning do describe "#changeset" do it "has expected values" do widget = described_class.create(name: "Henry") @@ -25,7 +26,7 @@ it "calls the adapter's load_changeset method" do widget = described_class.create(name: "Henry") - adapter = instance_spy("CustomObjectChangesAdapter") + adapter = instance_spy(CustomObjectChangesAdapter) PaperTrail.config.object_changes_adapter = adapter allow(adapter).to( receive(:load_changeset).with(widget.versions.last).and_return(a: "b", c: "d") @@ -50,7 +51,7 @@ context "when the serializer raises a Psych::DisallowedClass error" do it "prints a warning to stderr" do allow(PaperTrail.serializer).to( - receive(:load).and_raise(::Psych::Exception, "kaboom") + receive(:load).and_raise(Psych::Exception, "kaboom") ) widget = described_class.create(name: "Henry") ver = widget.versions.last @@ -67,13 +68,13 @@ end it "be live" do - expect(described_class.new.paper_trail.live?).to(eq(true)) + expect(described_class.new.paper_trail.live?).to(be(true)) end end context "with a persisted record" do it "have one previous version" do - widget = described_class.create(name: "Henry", created_at: (Time.current - 1.day)) + widget = described_class.create(name: "Henry", created_at: 1.day.ago) expect(widget.versions.length).to(eq(1)) end @@ -90,7 +91,7 @@ it "be live" do widget = described_class.create(name: "Henry") - expect(widget.paper_trail.live?).to(eq(true)) + expect(widget.paper_trail.live?).to(be(true)) end it "use the widget `updated_at` as the version's `created_at`" do @@ -207,7 +208,7 @@ widget = described_class.create(name: "Henry") widget.update(name: "Harry") widget = Whatchamajigger.new(name: "f-zero") - widget.save! + expect { widget.save! }.not_to raise_error end end @@ -407,7 +408,7 @@ widget.update(name: "Beeblebrox") PaperTrail.request.enable_model(described_class) widget.update(name: "Ford") - expect(widget.versions.length).to(eq((count + 1))) + expect(widget.versions.length).to(eq(count + 1)) end end end @@ -476,7 +477,7 @@ end it "return nil for version_at before it was created" do - expect(widget.paper_trail.version_at((t0 - 1))).to(be_nil) + expect(widget.paper_trail.version_at(t0 - 1)).to(be_nil) end it "return how it looked when created for version_at its creation" do @@ -484,7 +485,7 @@ end it "return how it looked before its first update" do - expect(widget.paper_trail.version_at((t1 - 1)).name).to(eq("Widget")) + expect(widget.paper_trail.version_at(t1 - 1).name).to(eq("Widget")) end it "return how it looked after its first update" do @@ -492,7 +493,7 @@ end it "return how it looked before its second update" do - expect(widget.paper_trail.version_at((t2 - 1)).name).to(eq("Fidget")) + expect(widget.paper_trail.version_at(t2 - 1).name).to(eq("Fidget")) end it "return how it looked after its second update" do @@ -581,7 +582,7 @@ end it "return the correct index" do - expect(version.index).to(eq((widget.versions.length - 1))) + expect(version.index).to(eq(widget.versions.length - 1)) end end end @@ -657,7 +658,7 @@ it "not have a version created on destroy" do widget = described_class.new widget.destroy - expect(widget.versions.empty?).to(eq(true)) + expect(widget.versions.empty?).to(be(true)) end end @@ -677,8 +678,10 @@ # Json fields for `object` & `object_changes` attributes is most efficient way # to do the things - this way we will save even more RAM, as well as will skip # the whole YAML serialization - allow(PaperTrail::Version).to receive(:object_changes_col_is_json?).and_return(true) - allow(PaperTrail::Version).to receive(:object_col_is_json?).and_return(true) + allow(PaperTrail::Version).to receive_messages( + object_changes_col_is_json?: true, + object_col_is_json?: true + ) # Force the loading of all lazy things like class definitions, # in order to get the pure benchmark @@ -741,7 +744,7 @@ it { is_expected.to be_versioned } end - describe "`have_a_version_with` matcher", versioning: true do + describe "`have_a_version_with` matcher", :versioning do let(:widget) { described_class.create! name: "Bob", an_integer: 1 } before do @@ -758,7 +761,7 @@ end describe "versioning option" do - context "when enabled", versioning: true do + context "when enabled", :versioning do it "enables versioning" do widget = described_class.create! name: "Bob", an_integer: 1 expect(widget.versions.size).to eq(1) @@ -773,7 +776,7 @@ end end - describe "Callbacks", versioning: true do + describe "Callbacks", :versioning do let(:widget) { described_class.create! name: "Bob", an_integer: 1 } describe "before_save" do @@ -785,7 +788,7 @@ end describe "after_create" do - let(:widget) { described_class.create!(name: "Foobar", created_at: Time.current - 1.week) } + let(:widget) { described_class.create!(name: "Foobar", created_at: 1.week.ago) } it "corresponding version uses the widget's `updated_at`" do expect(widget.versions.last.created_at.to_i).to eq(widget.updated_at.to_i) @@ -794,7 +797,7 @@ describe "after_update" do before do - widget.update!(name: "Foobar", updated_at: Time.current + 1.week) + widget.update!(name: "Foobar", updated_at: 1.week.from_now) end it "clears the `versions_association_name` virtual attribute" do @@ -848,7 +851,7 @@ end end - describe "Association", versioning: true do + describe "Association", :versioning do let(:widget) { described_class.create! name: "Bob", an_integer: 1 } describe "sort order" do @@ -860,24 +863,24 @@ end end - describe "#create", versioning: true do + describe "#create", :versioning do let(:widget) { described_class.create! name: "Bob", an_integer: 1 } it "creates a version record" do wordget = described_class.create - assert_equal 1, wordget.versions.length + expect(wordget.versions.length).to eq(1) end end - describe "#destroy", versioning: true do + describe "#destroy", :versioning do let(:widget) { described_class.create! name: "Bob", an_integer: 1 } it "creates a version record" do widget = described_class.create - assert_equal 1, widget.versions.length + expect(widget.versions.length).to eq(1) widget.destroy versions_for_widget = PaperTrail::Version.with_item_keys("Widget", widget.id) - assert_equal 2, versions_for_widget.length + expect(versions_for_widget.length).to eq(2) end it "can have multiple destruction records" do @@ -887,19 +890,19 @@ PaperTrail::Version.with_item_keys("Widget", widget.id) } widget = described_class.create - assert_equal 1, widget.versions.length + expect(widget.versions.length).to eq(1) widget.destroy - assert_equal 2, versions.call(widget).length + expect(versions.call(widget).length).to eq(2) widget = widget.version.reify widget.save - assert_equal 3, versions.call(widget).length + expect(versions.call(widget).length).to eq(3) widget.destroy - assert_equal 4, versions.call(widget).length - assert_equal 2, versions.call(widget).where(event: "destroy").length + expect(versions.call(widget).length).to eq(4) + expect(versions.call(widget).where(event: "destroy").length).to eq(2) end end - describe "#paper_trail.originator", versioning: true do + describe "#paper_trail.originator", :versioning do let(:widget) { described_class.create! name: "Bob", an_integer: 1 } describe "return value" do @@ -913,7 +916,7 @@ it "returns the originator for the model at a given state" do expect(widget.paper_trail).to be_live expect(widget.paper_trail.originator).to eq(orig_name) - ::PaperTrail.request(whodunnit: new_name) { + PaperTrail.request(whodunnit: new_name) { widget.update(name: "Elizabeth") } expect(widget.paper_trail.originator).to eq(new_name) @@ -939,7 +942,7 @@ end end - describe "#version_at", versioning: true do + describe "#version_at", :versioning do let(:widget) { described_class.create! name: "Bob", an_integer: 1 } context "when Timestamp argument is AFTER object has been destroyed" do @@ -951,7 +954,7 @@ end end - describe "touch", versioning: true do + describe "touch", :versioning do let(:widget) { described_class.create! name: "Bob", an_integer: 1 } it "creates a version" do @@ -969,7 +972,7 @@ end end - describe ".paper_trail.update_columns", versioning: true do + describe ".paper_trail.update_columns", :versioning do it "creates a version record" do widget = described_class.create expect(widget.versions.count).to eq(1) @@ -987,14 +990,14 @@ end end - describe "#update", versioning: true do + describe "#update", :versioning do let(:widget) { described_class.create! name: "Bob", an_integer: 1 } it "creates a version record" do widget = described_class.create - assert_equal 1, widget.versions.length + expect(widget.versions.length).to eq(1) widget.update(name: "Bugle") - assert_equal 2, widget.versions.length + expect(widget.versions.length).to eq(2) end end end diff --git a/spec/models/wotsit_spec.rb b/spec/models/wotsit_spec.rb index e72fbefac..808bc7a2a 100644 --- a/spec/models/wotsit_spec.rb +++ b/spec/models/wotsit_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe Wotsit, versioning: true do +RSpec.describe Wotsit, :versioning do it "update! records timestamps" do wotsit = described_class.create!(name: "wotsit") wotsit.update!(name: "changed") diff --git a/spec/paper_trail/cleaner_spec.rb b/spec/paper_trail/cleaner_spec.rb index 33d954f4f..6ecd16079 100644 --- a/spec/paper_trail/cleaner_spec.rb +++ b/spec/paper_trail/cleaner_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" module PaperTrail - ::RSpec.describe Cleaner, versioning: true do + ::RSpec.describe Cleaner, :versioning do describe "clean_versions!" do let(:animal) { ::Animal.new } let(:dog) { ::Dog.new } @@ -41,7 +41,7 @@ module PaperTrail context "when keeping 2" do it "keeps two records, instead of the usual one" do PaperTrail.clean_versions!(keeping: 2) - expect(PaperTrail::Version.all.count).to(eq(6)) + expect(PaperTrail::Version.count).to(eq(6)) animals.each { |animal| expect(animal.versions.size).to(eq(2)) } end end @@ -49,13 +49,13 @@ module PaperTrail context "with the :date option" do it "only deletes versions created on the given date" do animal.versions.each do |ver| - ver.update_attribute(:created_at, (ver.created_at - 1.day)) + ver.update_attribute(:created_at, ver.created_at - 1.day) end date = animal.versions.first.created_at.to_date animal.update_attribute(:name, FFaker::Name.name) expect(PaperTrail::Version.count).to(eq(10)) expect(animal.versions.size).to(eq(4)) - expect(animal.paper_trail.versions_between(date, (date + 1.day)).size).to(eq(3)) + expect(animal.paper_trail.versions_between(date, date + 1.day).size).to(eq(3)) PaperTrail.clean_versions!(date: date) expect(PaperTrail::Version.count).to(eq(8)) expect(animal.versions.reload.size).to(eq(2)) @@ -90,7 +90,7 @@ module PaperTrail before do [animal, dog].each do |animal| animal.versions.each do |ver| - ver.update_attribute(:created_at, (ver.created_at - 1.day)) + ver.update_attribute(:created_at, ver.created_at - 1.day) end animal.update_attribute(:name, FFaker::Name.name) end @@ -101,7 +101,7 @@ module PaperTrail expect(PaperTrail::Version.count).to(eq(11)) [animal, dog].each do |animal| expect(animal.versions.size).to(eq(4)) - expect(animal.versions.between(date, (date + 1.day)).size).to(eq(3)) + expect(animal.versions.between(date, date + 1.day).size).to(eq(3)) end end @@ -112,7 +112,7 @@ module PaperTrail [animal, dog].each do |animal| animal.versions.reload expect(animal.versions.size).to(eq(3)) - expect(animal.versions.between(date, (date + 1.day)).size).to(eq(2)) + expect(animal.versions.between(date, date + 1.day).size).to(eq(2)) end expect(PaperTrail::Version.count).to(eq(9)) end @@ -124,7 +124,7 @@ module PaperTrail PaperTrail.clean_versions!(date: date, item_id: dog.id) dog.versions.reload expect(dog.versions.size).to(eq(2)) - expect(dog.versions.between(date, (date + 1.day)).size).to(eq(1)) + expect(dog.versions.between(date, date + 1.day).size).to(eq(1)) expect(PaperTrail::Version.count).to(eq(9)) end end @@ -135,7 +135,7 @@ module PaperTrail PaperTrail.clean_versions!(date: date, item_id: dog.id, keeping: 2) dog.versions.reload expect(dog.versions.size).to(eq(3)) - expect(dog.versions.between(date, (date + 1.day)).size).to(eq(2)) + expect(dog.versions.between(date, date + 1.day).size).to(eq(2)) expect(PaperTrail::Version.count).to(eq(10)) end end diff --git a/spec/paper_trail/compatibility_spec.rb b/spec/paper_trail/compatibility_spec.rb index f2c30e2b8..7f940b3f0 100644 --- a/spec/paper_trail/compatibility_spec.rb +++ b/spec/paper_trail/compatibility_spec.rb @@ -5,7 +5,7 @@ module PaperTrail describe ".check_activerecord" do context "when compatible" do it "does not produce output" do - ar_version = ::Gem::Version.new("6.1.0") + ar_version = ::Gem::Version.new("7.1.0") expect { described_class.check_activerecord(ar_version) }.not_to output.to_stderr @@ -14,7 +14,7 @@ module PaperTrail context "when incompatible" do it "writes a warning to stderr" do - ar_version = ::Gem::Version.new("8.1.0") + ar_version = ::Gem::Version.new("8.2.0") expect { described_class.check_activerecord(ar_version) }.to output(/not compatible/).to_stderr diff --git a/spec/paper_trail/config_spec.rb b/spec/paper_trail/config_spec.rb index 9a31cf579..d3ae03217 100644 --- a/spec/paper_trail/config_spec.rb +++ b/spec/paper_trail/config_spec.rb @@ -17,7 +17,7 @@ module PaperTrail end end - describe ".version_error_behavior", versioning: true do + describe ".version_error_behavior", :versioning do let(:logger) { instance_double(Logger) } before do @@ -238,7 +238,7 @@ module PaperTrail end end - describe ".version_limit", versioning: true do + describe ".version_limit", :versioning do after { PaperTrail.config.version_limit = nil } it "limits the number of versions to 3 (2 plus the created at event)" do @@ -271,7 +271,7 @@ module PaperTrail limited_bike = LimitedBicycle.create!(name: "Limited") limited_bike.name = "A new name" limited_bike.save - assert_equal 2, limited_bike.versions.length + expect(limited_bike.versions.length).to eq(2) end end end diff --git a/spec/paper_trail/events/base_spec.rb b/spec/paper_trail/events/base_spec.rb index fd504cec9..39f9c77d5 100644 --- a/spec/paper_trail/events/base_spec.rb +++ b/spec/paper_trail/events/base_spec.rb @@ -5,7 +5,7 @@ module PaperTrail module Events ::RSpec.describe Base do - describe "#changed_notably?", versioning: true do + describe "#changed_notably?", :versioning do context "with a new record" do it "returns true" do g = Gadget.new(created_at: Time.current) @@ -49,7 +49,7 @@ module Events end end - describe "#nonskipped_attributes_before_change", versioning: true do + describe "#nonskipped_attributes_before_change", :versioning do it "returns a hash lacking the skipped attribute" do # Skipper has_paper_trail(..., skip: [:another_timestamp]) skipper = Skipper.create!(another_timestamp: Time.current) diff --git a/spec/paper_trail/events/destroy_spec.rb b/spec/paper_trail/events/destroy_spec.rb index f75d3ef50..81b302810 100644 --- a/spec/paper_trail/events/destroy_spec.rb +++ b/spec/paper_trail/events/destroy_spec.rb @@ -5,7 +5,7 @@ module PaperTrail module Events ::RSpec.describe Destroy do - describe "#data", versioning: true do + describe "#data", :versioning do it "includes correct item_subtype" do carter = Family::CelebrityFamily.new( name: "Carter", diff --git a/spec/paper_trail/events/update_spec.rb b/spec/paper_trail/events/update_spec.rb index 06942cdfd..41ace588a 100644 --- a/spec/paper_trail/events/update_spec.rb +++ b/spec/paper_trail/events/update_spec.rb @@ -5,7 +5,7 @@ module PaperTrail module Events ::RSpec.describe Update do - describe "#data", versioning: true do + describe "#data", :versioning do context "when is_touch false" do it "object_changes is present" do carter = Family::CelebrityFamily.create( diff --git a/spec/paper_trail/model_config_spec.rb b/spec/paper_trail/model_config_spec.rb index c23d2b78d..2cae0814b 100644 --- a/spec/paper_trail/model_config_spec.rb +++ b/spec/paper_trail/model_config_spec.rb @@ -52,8 +52,8 @@ module PaperTrail klass = Class.new(ApplicationRecord) do has_paper_trail versions: { autosave: true, validate: true } end - expect(klass.reflect_on_association(:versions).options[:autosave]).to eq true - expect(klass.reflect_on_association(:versions).options[:validate]).to eq true + expect(klass.reflect_on_association(:versions).options[:autosave]).to be true + expect(klass.reflect_on_association(:versions).options[:validate]).to be true end it "can even override options that PaperTrail adds to has_many" do diff --git a/spec/paper_trail/request_spec.rb b/spec/paper_trail/request_spec.rb index 5e2dae54f..a923fb408 100644 --- a/spec/paper_trail/request_spec.rb +++ b/spec/paper_trail/request_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" module PaperTrail - ::RSpec.describe(Request, versioning: true) do + ::RSpec.describe(Request, :versioning) do describe ".enabled_for_model?" do it "returns true" do expect(PaperTrail.request.enabled_for_model?(Widget)).to eq(true) @@ -111,10 +111,10 @@ module PaperTrail described_class.with(whodunnit: "foo", enabled_for_Widget: false) do expect(described_class.whodunnit).to eq("foo") - expect(described_class.enabled_for_model?(Widget)).to eq false + expect(described_class.enabled_for_model?(Widget)).to be false end expect(described_class.whodunnit).to eq "some_whodunnit" - expect(described_class.enabled_for_model?(Widget)).to eq true + expect(described_class.enabled_for_model?(Widget)).to be true end it "sets options only for the current thread" do @@ -123,12 +123,12 @@ module PaperTrail described_class.with(whodunnit: "foo", enabled_for_Widget: false) do expect(described_class.whodunnit).to eq("foo") - expect(described_class.enabled_for_model?(Widget)).to eq false + expect(described_class.enabled_for_model?(Widget)).to be false Thread.new { expect(described_class.whodunnit).to be_nil }.join - Thread.new { expect(described_class.enabled_for_model?(Widget)).to eq true }.join + Thread.new { expect(described_class.enabled_for_model?(Widget)).to be true }.join end expect(described_class.whodunnit).to eq "some_whodunnit" - expect(described_class.enabled_for_model?(Widget)).to eq true + expect(described_class.enabled_for_model?(Widget)).to be true end end diff --git a/spec/paper_trail/serializer_spec.rb b/spec/paper_trail/serializer_spec.rb index 227dc9e06..ea26bbb8b 100644 --- a/spec/paper_trail/serializer_spec.rb +++ b/spec/paper_trail/serializer_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" require "support/custom_json_serializer" -RSpec.describe("PaperTrail serializers", versioning: true) do +RSpec.describe("PaperTrail serializers", :versioning) do context "with YAML serializer" do it "saves the expected YAML in the object column" do customer = Customer.create(name: "Some text.") diff --git a/spec/paper_trail/serializers/custom_yaml_serializer_spec.rb b/spec/paper_trail/serializers/custom_yaml_serializer_spec.rb index 2a14a329f..cb0b33850 100644 --- a/spec/paper_trail/serializers/custom_yaml_serializer_spec.rb +++ b/spec/paper_trail/serializers/custom_yaml_serializer_spec.rb @@ -6,9 +6,9 @@ module CustomYamlSerializer extend PaperTrail::Serializers::YAML def self.load(string) - parsed_value = super(string) + parsed_value = super if parsed_value.is_a?(Hash) - parsed_value.reject { |k, v| (k.blank? || v.blank?) } + parsed_value.reject { |k, v| k.blank? || v.blank? } else parsed_value end @@ -22,7 +22,7 @@ def self.dump(object) RSpec.describe CustomYamlSerializer do let(:word_hash) { { - "key1" => ::FFaker::Lorem.word, + "key1" => FFaker::Lorem.word, "key2" => nil, "tkey" => nil, "" => "foo" @@ -32,7 +32,7 @@ def self.dump(object) describe ".load" do it("deserializes YAML to Ruby, removing pairs with blank keys or values") do expect(described_class.load(word_hash.to_yaml)).to eq( - word_hash.reject { |k, v| (k.blank? || v.blank?) } + word_hash.reject { |k, v| k.blank? || v.blank? } ) end end diff --git a/spec/paper_trail/serializers/json_spec.rb b/spec/paper_trail/serializers/json_spec.rb index b8f0fcd37..412557b7a 100644 --- a/spec/paper_trail/serializers/json_spec.rb +++ b/spec/paper_trail/serializers/json_spec.rb @@ -29,7 +29,7 @@ module Serializers it "construct correct WHERE query" do matches = described_class. where_object_condition(PaperTrail::Version.arel_table[:object], :arg1, "Val 1") - expect(matches.instance_of?(Arel::Nodes::Matches)).to(eq(true)) + expect(matches.instance_of?(Arel::Nodes::Matches)).to(be(true)) expect(arel_value(matches.right)).to eq("%\"arg1\":\"Val 1\"%") end end @@ -38,7 +38,7 @@ module Serializers it "construct correct WHERE query" do matches = described_class. where_object_condition(PaperTrail::Version.arel_table[:object], :arg1, nil) - expect(matches.instance_of?(Arel::Nodes::Matches)).to(eq(true)) + expect(matches.instance_of?(Arel::Nodes::Matches)).to(be(true)) expect(arel_value(matches.right)).to(eq("%\"arg1\":null%")) end end @@ -47,7 +47,7 @@ module Serializers it "construct correct WHERE query" do grouping = described_class. where_object_condition(PaperTrail::Version.arel_table[:object], :arg1, -3.5) - expect(grouping.instance_of?(Arel::Nodes::Grouping)).to(eq(true)) + expect(grouping.instance_of?(Arel::Nodes::Grouping)).to(be(true)) disjunction = grouping.expr expect(disjunction).to be_an(Arel::Nodes::Or) dj_left = disjunction.left diff --git a/spec/paper_trail/serializers/yaml_spec.rb b/spec/paper_trail/serializers/yaml_spec.rb index 31fa45357..053216cb7 100644 --- a/spec/paper_trail/serializers/yaml_spec.rb +++ b/spec/paper_trail/serializers/yaml_spec.rb @@ -4,7 +4,7 @@ module PaperTrail module Serializers - ::RSpec.describe(YAML, versioning: true) do + ::RSpec.describe(YAML, :versioning) do let(:array) { ::Array.new(10) { ::FFaker::Lorem.word } } let(:hash) { { @@ -15,7 +15,7 @@ module Serializers float: 4.2 } } - let(:hash_with_indifferent_access) { HashWithIndifferentAccess.new(hash) } + let(:hash_with_indifferent_access) { ActiveSupport::HashWithIndifferentAccess.new(hash) } describe ".load" do it "deserializes YAML to Ruby" do @@ -56,7 +56,7 @@ module Serializers matches = described_class.where_object_condition( ::PaperTrail::Version.arel_table[:object], :arg1, "Val 1" ) - expect(matches.instance_of?(Arel::Nodes::Matches)).to(eq(true)) + expect(matches.instance_of?(Arel::Nodes::Matches)).to(be(true)) expect(arel_value(matches.right)).to eq("%\narg1: Val 1\n%") end end diff --git a/spec/paper_trail/version_limit_spec.rb b/spec/paper_trail/version_limit_spec.rb index 4899ff5ea..f69ef4cce 100644 --- a/spec/paper_trail/version_limit_spec.rb +++ b/spec/paper_trail/version_limit_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" module PaperTrail - ::RSpec.describe Cleaner, versioning: true do + ::RSpec.describe Cleaner, :versioning do after do PaperTrail.config.version_limit = nil end @@ -74,7 +74,7 @@ module PaperTrail expect(widget.reload.versions.count).to eq(4) # 1 create + 4 updates # Exclude the create, because the create will return nil for `#reify`. - last_names = widget.versions.not_creates.map(&:reify).map(&:name) + last_names = widget.versions.not_creates.map { |x| x.reify.name } expect(last_names).to eq(["Name 3", "Name 4", "Name 5"]) # No matter what order the version records are returned it, we should # always keep the most-recent changes. diff --git a/spec/paper_trail/version_spec.rb b/spec/paper_trail/version_spec.rb index 749a144c8..8a0efb5ac 100644 --- a/spec/paper_trail/version_spec.rb +++ b/spec/paper_trail/version_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" module PaperTrail - ::RSpec.describe(Version, versioning: true) do + ::RSpec.describe(Version, :versioning) do describe ".creates" do it "returns only create events" do animal = Animal.create(name: "Foo") diff --git a/spec/paper_trail_spec.rb b/spec/paper_trail_spec.rb index 5ff6d9aae..d96088824 100644 --- a/spec/paper_trail_spec.rb +++ b/spec/paper_trail_spec.rb @@ -9,7 +9,7 @@ end end - describe "#config", versioning: true do + describe "#config", :versioning do it "allows for config values to be set" do expect(described_class.config.enabled).to eq(true) described_class.config.enabled = false @@ -34,8 +34,8 @@ describe ".gem_version" do it "returns a Gem::Version" do v = described_class.gem_version - expect(v).to be_a(::Gem::Version) - expect(v.to_s).to eq(::PaperTrail::VERSION::STRING) + expect(v).to be_a(Gem::Version) + expect(v.to_s).to eq(PaperTrail::VERSION::STRING) end end @@ -46,7 +46,7 @@ it "affects all threads" do Thread.new { described_class.enabled = false }.join - assert_equal false, described_class.enabled? + expect(described_class.enabled?).to eq(false) end end @@ -72,7 +72,7 @@ end end - context "with `versioning: true`", versioning: true do + context "with `versioning: true`", :versioning do it "has versioning on by default" do expect(described_class).to be_enabled end diff --git a/spec/requests/articles_spec.rb b/spec/requests/articles_spec.rb index 9bffdd367..7cba86cf0 100644 --- a/spec/requests/articles_spec.rb +++ b/spec/requests/articles_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe "Articles management", type: :request, order: :defined do +RSpec.describe "Articles management", order: :defined do let(:valid_params) { { article: { title: "Doh", content: FFaker::Lorem.sentence } } } context "with versioning disabled" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6f3011c93..9bca1d31b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -60,7 +60,8 @@ Bundler.setup # Then, the chosen components of Rails would be loaded. In our case, we only -# test with AR and AC. +# test with AR and AC. We require `logger` because `active_record` needs it. +require "logger" require "active_record/railtie" require "action_controller/railtie" @@ -77,7 +78,7 @@ # Now that AR has a connection pool, we can migrate the database. require_relative "support/paper_trail_spec_migrator" -::PaperTrailSpecMigrator.new.migrate +PaperTrailSpecMigrator.new.migrate # This final section resembles what might be dummy_app's spec_helper, if it # had one. @@ -85,4 +86,5 @@ RSpec.configure do |config| config.fixture_path = nil # we use factories, not fixtures config.use_transactional_fixtures = true + config.infer_spec_type_from_file_location! end diff --git a/spec/support/custom_json_serializer.rb b/spec/support/custom_json_serializer.rb index 33206b77f..72fdc7c99 100644 --- a/spec/support/custom_json_serializer.rb +++ b/spec/support/custom_json_serializer.rb @@ -5,7 +5,7 @@ module CustomJsonSerializer extend PaperTrail::Serializers::JSON def self.load(string) - parsed_value = super(string) + parsed_value = super parsed_value.is_a?(Hash) ? parsed_value.reject { |k, v| k.blank? || v.blank? } : parsed_value end diff --git a/spec/support/performance_helpers.rb b/spec/support/performance_helpers.rb index 01c79b081..7cb9b6861 100644 --- a/spec/support/performance_helpers.rb +++ b/spec/support/performance_helpers.rb @@ -32,7 +32,7 @@ end failure_message do - "expected that example will allocate less than #{expected}#{@scale},"\ - " but allocated #{@allocated}#{@scale}" + "expected that example will allocate less than #{expected}#{@scale}, " \ + "but allocated #{@allocated}#{@scale}" end end diff --git a/spec/support/shared_examples/active_record_encryption.rb b/spec/support/shared_examples/active_record_encryption.rb index 2967afb9f..cbbb3f354 100644 --- a/spec/support/shared_examples/active_record_encryption.rb +++ b/spec/support/shared_examples/active_record_encryption.rb @@ -1,58 +1,56 @@ # frozen_string_literal: true RSpec.shared_examples "active_record_encryption" do |model| - if PaperTrail.active_record_gte_7_0? - context "when ActiveRecord Encryption is enabled", versioning: true do - let(:record) { model.create(supplier: "ABC", name: "Tomato") } - - before do - ActiveRecord::Encryption.configure( - primary_key: "test", - deterministic_key: "test", - key_derivation_salt: "test" - ) - end + context "when ActiveRecord Encryption is enabled", :versioning do + let(:record) { model.create(supplier: "ABC", name: "Tomato") } + + before do + ActiveRecord::Encryption.configure( + primary_key: "test", + deterministic_key: "test", + key_derivation_salt: "test" + ) + end - it "is versioned with encrypted values" do - original_supplier, original_name = record.values_at(:supplier, :name) - - # supplier is encrypted, name is not - record.update!(supplier: "XYZ", name: "Avocado") - - expect(record.versions.count).to be 2 - expect(record.versions.pluck(:event)).to include("create", "update") - - # versioned encrypted value should be something like - # "{\"p\":\"zDQU\",\"h\":{\"iv\":\"h2OADmJT3DfK1EZc\",\"at\":\"Urcd0mGSwyu9rGT1vrE5cg==\"}}" - - # check paper trail object - object = record.versions.last.object - expect(object.to_s).not_to include("XYZ") - versioned_supplier, versioned_name = object.values_at("supplier", "name") - # encrypted column should be versioned with encrypted value - expect(versioned_supplier).not_to eq(original_supplier) - # non-encrypted column should be versioned with the original value - expect(versioned_name).to eq(original_name) - parsed_versioned_supplier = JSON.parse(versioned_supplier) - expect(parsed_versioned_supplier) - .to match(hash_including("p", "h" => hash_including("iv", "at"))) - - # check paper trail object_changes - object_changes = record.versions.last.object_changes - expect(object_changes.to_s).not_to include("XYZ") - supplier_changes, name_changes = object_changes.values_at("supplier", "name") - expect(supplier_changes).not_to eq([original_supplier, "XYZ"]) - expect(name_changes).to eq([original_name, "Avocado"]) - supplier_changes.each do |supplier| - parsed_supplier = JSON.parse(supplier) - expect(parsed_supplier).to match(hash_including("p", "h" => hash_including("iv", "at"))) - end + it "is versioned with encrypted values" do + original_supplier, original_name = record.values_at(:supplier, :name) + + # supplier is encrypted, name is not + record.update!(supplier: "XYZ", name: "Avocado") + + expect(record.versions.count).to be 2 + expect(record.versions.pluck(:event)).to include("create", "update") + + # versioned encrypted value should be something like + # "{\"p\":\"zDQU\",\"h\":{\"iv\":\"h2OADmJT3DfK1EZc\",\"at\":\"Urcd0mGSwyu9rGT1vrE5cg==\"}}" + + # check paper trail object + object = record.versions.last.object + expect(object.to_s).not_to include("XYZ") + versioned_supplier, versioned_name = object.values_at("supplier", "name") + # encrypted column should be versioned with encrypted value + expect(versioned_supplier).not_to eq(original_supplier) + # non-encrypted column should be versioned with the original value + expect(versioned_name).to eq(original_name) + parsed_versioned_supplier = JSON.parse(versioned_supplier) + expect(parsed_versioned_supplier) + .to match(hash_including("p", "h" => hash_including("iv", "at"))) + + # check paper trail object_changes + object_changes = record.versions.last.object_changes + expect(object_changes.to_s).not_to include("XYZ") + supplier_changes, name_changes = object_changes.values_at("supplier", "name") + expect(supplier_changes).not_to eq([original_supplier, "XYZ"]) + expect(name_changes).to eq([original_name, "Avocado"]) + supplier_changes.each do |supplier| + parsed_supplier = JSON.parse(supplier) + expect(parsed_supplier).to match(hash_including("p", "h" => hash_including("iv", "at"))) end + end - it "reifies encrypted values to decrypted values" do - record.update!(supplier: "XYZ", name: "Avocado") - expect(record.versions.last.reify.supplier).to eq "ABC" - end + it "reifies encrypted values to decrypted values" do + record.update!(supplier: "XYZ", name: "Avocado") + expect(record.versions.last.reify.supplier).to eq "ABC" end end end diff --git a/spec/support/shared_examples/queries.rb b/spec/support/shared_examples/queries.rb index 6264826c2..7bfbf332f 100644 --- a/spec/support/shared_examples/queries.rb +++ b/spec/support/shared_examples/queries.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "support/custom_object_changes_adapter" + RSpec.shared_examples "queries" do |column_type, model, name_of_integer_column| let(:record) { model.new } let(:name) { FFaker::Name.first_name } @@ -9,7 +11,7 @@ PaperTrail.serializer = PaperTrail::Serializers::YAML end - describe "#where_attribute_changes", versioning: true do + describe "#where_attribute_changes", :versioning do it "requires its argument to be a string or a symbol" do expect { model.paper_trail.version_class.where_attribute_changes({}) @@ -25,7 +27,7 @@ end it "calls the adapter's where_attribute_changes method" do - adapter = instance_spy("CustomObjectChangesAdapter") + adapter = instance_spy(CustomObjectChangesAdapter) bicycle = model.create!(name: "abc") bicycle.update!(name: "xyz") @@ -36,7 +38,7 @@ PaperTrail.config.object_changes_adapter = adapter expect( bicycle.versions.where_attribute_changes(:name) - ).to match_array([bicycle.versions[0], bicycle.versions[1]]) + ).to contain_exactly(bicycle.versions[0], bicycle.versions[1]) expect(adapter).to have_received(:where_attribute_changes) end @@ -50,13 +52,13 @@ expect { bicycle.versions.where_attribute_changes(:name) }.to raise_error( - ::PaperTrail::UnsupportedColumnType, + PaperTrail::UnsupportedColumnType, "where_attribute_changes expected json or jsonb column, got text" ) else expect( bicycle.versions.where_attribute_changes(:name) - ).to match_array([bicycle.versions[0], bicycle.versions[1]]) + ).to contain_exactly(bicycle.versions[0], bicycle.versions[1]) end end end @@ -66,7 +68,7 @@ expect { record.versions.where_attribute_changes(:name).to_a }.to raise_error( - ::PaperTrail::UnsupportedColumnType, + PaperTrail::UnsupportedColumnType, "where_attribute_changes expected json or jsonb column, got text" ) end @@ -89,7 +91,7 @@ end end - describe "#where_object", versioning: true do + describe "#where_object", :versioning do it "requires its argument to be a Hash" do record.update!(name: name, name_of_integer_column => int) record.update!(name: "foobar", name_of_integer_column => 100) @@ -140,7 +142,7 @@ end end - describe "#where_object_changes", versioning: true do + describe "#where_object_changes", :versioning do it "requires its argument to be a Hash" do expect { model.paper_trail.version_class.where_object_changes(:foo) @@ -156,7 +158,7 @@ end it "calls the adapter's where_object_changes method" do - adapter = instance_spy("CustomObjectChangesAdapter") + adapter = instance_spy(CustomObjectChangesAdapter) bicycle = model.create!(name: "abc") allow(adapter).to( receive(:where_object_changes).with(model.paper_trail.version_class, { name: "abc" }) @@ -176,7 +178,7 @@ expect { bicycle.versions.where_object_changes(name: "abc") }.to raise_error( - ::PaperTrail::UnsupportedColumnType, + PaperTrail::UnsupportedColumnType, "where_object_changes expected json or jsonb column, got text" ) else @@ -192,7 +194,7 @@ expect { record.versions.where_object_changes(name: "foo").to_a }.to raise_error( - ::PaperTrail::UnsupportedColumnType, + PaperTrail::UnsupportedColumnType, "where_object_changes expected json or jsonb column, got text" ) end @@ -217,7 +219,7 @@ end end - describe "#where_object_changes_from", versioning: true do + describe "#where_object_changes_from", :versioning do it "requires its argument to be a Hash" do expect { model.paper_trail.version_class.where_object_changes_from(:foo) @@ -233,7 +235,7 @@ end it "calls the adapter's where_object_changes_from method" do - adapter = instance_spy("CustomObjectChangesAdapter") + adapter = instance_spy(CustomObjectChangesAdapter) bicycle = model.create!(name: "abc") bicycle.update!(name: "xyz") @@ -244,7 +246,7 @@ PaperTrail.config.object_changes_adapter = adapter expect( bicycle.versions.where_object_changes_from(name: "abc") - ).to match_array([bicycle.versions[1]]) + ).to contain_exactly(bicycle.versions[1]) expect(adapter).to have_received(:where_object_changes_from) end @@ -258,13 +260,13 @@ expect { bicycle.versions.where_object_changes_from(name: "abc") }.to raise_error( - ::PaperTrail::UnsupportedColumnType, + PaperTrail::UnsupportedColumnType, "where_object_changes_from expected json or jsonb column, got text" ) else expect( bicycle.versions.where_object_changes_from(name: "abc") - ).to match_array([bicycle.versions[1]]) + ).to contain_exactly(bicycle.versions[1]) end end end @@ -274,7 +276,7 @@ expect { record.versions.where_object_changes_from(name: "foo").to_a }.to raise_error( - ::PaperTrail::UnsupportedColumnType, + PaperTrail::UnsupportedColumnType, "where_object_changes_from expected json or jsonb column, got text" ) end @@ -300,7 +302,7 @@ end end - describe "#where_object_changes_to", versioning: true do + describe "#where_object_changes_to", :versioning do it "requires its argument to be a Hash" do expect { model.paper_trail.version_class.where_object_changes_to(:foo) @@ -316,7 +318,7 @@ end it "calls the adapter's where_object_changes_to method" do - adapter = instance_spy("CustomObjectChangesAdapter") + adapter = instance_spy(CustomObjectChangesAdapter) bicycle = model.create!(name: "abc") bicycle.update!(name: "xyz") @@ -327,7 +329,7 @@ PaperTrail.config.object_changes_adapter = adapter expect( bicycle.versions.where_object_changes_to(name: "xyz") - ).to match_array([bicycle.versions[1]]) + ).to contain_exactly(bicycle.versions[1]) expect(adapter).to have_received(:where_object_changes_to) end @@ -341,13 +343,13 @@ expect { bicycle.versions.where_object_changes_to(name: "xyz") }.to raise_error( - ::PaperTrail::UnsupportedColumnType, + PaperTrail::UnsupportedColumnType, "where_object_changes_to expected json or jsonb column, got text" ) else expect( bicycle.versions.where_object_changes_to(name: "xyz") - ).to match_array([bicycle.versions[1]]) + ).to contain_exactly(bicycle.versions[1]) end end end @@ -357,7 +359,7 @@ expect { record.versions.where_object_changes_to(name: "foo").to_a }.to raise_error( - ::PaperTrail::UnsupportedColumnType, + PaperTrail::UnsupportedColumnType, "where_object_changes_to expected json or jsonb column, got text" ) end