diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c260fdd0..18ec7b99 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,7 +56,7 @@ 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_5.2', 'rails_6.0', 'rails_6.1' ] + gemfile: [ 'rails_5.2', 'rails_6.0', 'rails_6.1', 'rails_7.0' ] # To keep matrix size down, only test highest and lowest rubies. # Ruby 3.0 is an exception. For now, let's continue to test against 2.7 @@ -72,6 +72,9 @@ jobs: gemfile: 'rails_5.2' - ruby: '3.1' gemfile: 'rails_5.2' + # rails 7 requires ruby > 2.7 + - ruby: '2.6' + gemfile: 'rails_7.0' steps: - name: Checkout source uses: actions/checkout@v2 @@ -79,10 +82,7 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - - name: Bundle - run: | - gem install bundler - bundle install --jobs 4 --retry 3 + bundler-cache: true # 'bundle install' and cache env: BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile diff --git a/CHANGELOG.md b/CHANGELOG.md index be3f4129..1c7b93f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,31 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/). - None +## 12.3.0 (2022-03-13) + +### Breaking Changes + +- None + +### Added + +- [#1371](https://github.com/paper-trail-gem/paper_trail/pull/1371) - Added + `in_after_callback` argument to `PaperTrail::RecordTrail#save_with_version`, + to allow the caller to indicate if this method is being called during an + `after` callback. Defaults to `false`. +- [#1374](https://github.com/paper-trail-gem/paper_trail/pull/1374) - Added + option `--uuid` when generating new migration. This can be used to set the + type of item_id column to uuid for use with paper_trail on a database that + uses uuid as primary key. + +### Fixed + +- [#1373](https://github.com/paper-trail-gem/paper_trail/issues/1373) - Add + CLI option to use uuid type for item_id when generating migration. +- [#1376](https://github.com/paper-trail-gem/paper_trail/pull/1376) - Create a + version record when associated object is touched. Restores the behavior of + PaperTrail < v12.1.0. + ## 12.2.0 (2022-01-21) ### Breaking Changes @@ -32,7 +57,8 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/). ### Fixed -- None +- [#1366](https://github.com/paper-trail-gem/paper_trail/pull/1366) - + Fixed a bug where the `create_versions` migration lead to a broken `db/schema.rb` for Ruby 3 ### Dependencies diff --git a/README.md b/README.md index 627fec76..0e26168d 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ This is the _user guide_. See also, the Choose version: [Unreleased](https://github.com/paper-trail-gem/paper_trail/blob/master/README.md), -[12.2](https://github.com/paper-trail-gem/paper_trail/blob/v12.2.0/README.md), +[12.3](https://github.com/paper-trail-gem/paper_trail/blob/v12.3.0/README.md), [11.1](https://github.com/paper-trail-gem/paper_trail/blob/v11.1.0/README.md), [10.3](https://github.com/paper-trail-gem/paper_trail/blob/v10.3.1/README.md), [9.2](https://github.com/paper-trail-gem/paper_trail/blob/v9.2.0/README.md), @@ -88,9 +88,9 @@ Choose version: ### 1.a. Compatibility | paper_trail | branch | ruby | activerecord | -| -------------- | ---------- | -------- | ------------- | -| unreleased | master | >= 2.5.0 | >= 5.2, < 6.2 | -| 12 | 12-stable | >= 2.5.0 | >= 5.2, < 6.2 | +| -------------- | ---------- |----------|---------------| +| unreleased | master | >= 2.6.0 | >= 5.2, < 7.1 | +| 12 | 12-stable | >= 2.6.0 | >= 5.2, < 7.1 | | 11 | 11-stable | >= 2.4.0 | >= 5.2, < 6.1 | | 10 | 10-stable | >= 2.3.0 | >= 4.2, < 6.1 | | 9 | 9-stable | >= 2.3.0 | >= 4.2, < 5.3 | @@ -108,7 +108,7 @@ Experts: to install incompatible versions of activerecord, see ### 1.b. Installation -1. Add PaperTrail to your `Gemfile`. +1. Add PaperTrail to your `Gemfile` and run [`bundle`][57]. `gem 'paper_trail'` @@ -117,6 +117,11 @@ Experts: to install incompatible versions of activerecord, see ``` bundle exec rails generate paper_trail:install [--with-changes] ``` + + If tables in your project use `uuid` instead of `integers` for `id`, then use: + ``` + bundle exec rails generate paper_trail:install [--uuid] + ``` See [section 5.c. Generators](#5c-generators) for details. @@ -1135,6 +1140,7 @@ Usage: Options: [--with-changes], [--no-with-changes] # Store changeset (diff) with each version + [--uuid] # To use paper_trail with projects using uuid for id Runtime options: -f, [--force] # Overwrite files that already exist @@ -1772,3 +1778,4 @@ Released under the MIT licence. [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 [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/lib/generators/paper_trail/install/install_generator.rb b/lib/generators/paper_trail/install/install_generator.rb index 6fc82fe7..13786636 100644 --- a/lib/generators/paper_trail/install/install_generator.rb +++ b/lib/generators/paper_trail/install/install_generator.rb @@ -20,6 +20,12 @@ module PaperTrail default: false, desc: "Store changeset (diff) with each version" ) + class_option( + :uuid, + type: :boolean, + default: false, + 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." @@ -28,7 +34,8 @@ module PaperTrail add_paper_trail_migration( "create_versions", item_type_options: item_type_options, - versions_table_options: versions_table_options + versions_table_options: versions_table_options, + item_id_type_options: item_id_type_options ) if options.with_changes? add_paper_trail_migration("add_object_changes_to_versions") @@ -37,13 +44,18 @@ module PaperTrail private + # To use uuid instead of integer for primary key + def item_id_type_options + options.uuid? ? "string" : "bigint" + end + # MySQL 5.6 utf8mb4 limit is 191 chars for keys used in indexes. # See https://github.com/paper-trail-gem/paper_trail/issues/651 def item_type_options if mysql? - ", { null: false, limit: 191 }" + ", null: false, limit: 191" else - ", { null: false }" + ", null: false" 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 677d1b73..a5dc710f 100644 --- a/lib/generators/paper_trail/install/templates/create_versions.rb.erb +++ b/lib/generators/paper_trail/install/templates/create_versions.rb.erb @@ -11,7 +11,7 @@ class CreateVersions < ActiveRecord::Migration<%= migration_version %> def change create_table :versions<%= versions_table_options %> do |t| t.string :item_type<%= item_type_options %> - t.bigint :item_id, null: false + t.<%= item_id_type_options %> :item_id, null: false t.string :event, null: false t.string :whodunnit t.text :object, limit: TEXT_BYTES diff --git a/lib/paper_trail/events/update.rb b/lib/paper_trail/events/update.rb index 89407226..5c9123d7 100644 --- a/lib/paper_trail/events/update.rb +++ b/lib/paper_trail/events/update.rb @@ -40,6 +40,20 @@ module PaperTrail merge_metadata_into(data) end + # If it is a touch event, and changed are empty, it is assumed to be + # implicit `touch` mutation, and will a version is created. + # + # See https://github.com/rails/rails/commit/dcb825902d79d0f6baba956f7c6ec5767611353e + # + # @api private + def changed_notably? + if @is_touch && changes_in_latest_version.empty? + true + else + super + end + end + private # @api private diff --git a/lib/paper_trail/record_trail.rb b/lib/paper_trail/record_trail.rb index c29348d4..66c52896 100644 --- a/lib/paper_trail/record_trail.rb +++ b/lib/paper_trail/record_trail.rb @@ -194,15 +194,17 @@ module PaperTrail # Save, and create a version record regardless of options such as `:on`, # `:if`, or `:unless`. # - # Arguments are passed to `save`. + # `in_after_callback`: Indicates if this method is being called within an + # `after` callback. Defaults to `false`. + # `options`: Optional arguments passed to `save`. # # This is an "update" event. That is, we record the same data we would in # the case of a normal AR `update`. - def save_with_version(**options) + def save_with_version(in_after_callback: false, **options) ::PaperTrail.request(enabled: false) do @record.save(**options) end - record_update(force: true, in_after_callback: false, is_touch: false) + record_update(force: true, in_after_callback: in_after_callback, is_touch: false) end # Like the `update_column` method from `ActiveRecord::Persistence`, but also diff --git a/lib/paper_trail/version_number.rb b/lib/paper_trail/version_number.rb index 0b24f405..791de755 100644 --- a/lib/paper_trail/version_number.rb +++ b/lib/paper_trail/version_number.rb @@ -8,7 +8,7 @@ module PaperTrail # People are encouraged to use `PaperTrail.gem_version` instead. module VERSION MAJOR = 12 - MINOR = 2 + MINOR = 3 TINY = 0 # Set PRE to nil unless it's a pre-release (beta, rc, etc.) diff --git a/spec/dummy_app/app/models/order.rb b/spec/dummy_app/app/models/order.rb index f272874d..d8913768 100644 --- a/spec/dummy_app/app/models/order.rb +++ b/spec/dummy_app/app/models/order.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Order < ActiveRecord::Base - belongs_to :customer + belongs_to :customer, touch: :touched_at has_many :line_items has_paper_trail end 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 377889ad..a8b8058b 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 @@ -144,6 +144,7 @@ class SetUpTestTables < ::ActiveRecord::Migration::Current end create_table :not_on_updates, force: true do |t| + t.string :name t.timestamps null: true, limit: 6 end @@ -267,6 +268,7 @@ class SetUpTestTables < ::ActiveRecord::Migration::Current create_table :customers, force: true do |t| t.string :name + t.datetime :touched_at, limit: 6 end create_table :orders, force: true do |t| diff --git a/spec/generators/paper_trail/install_generator_spec.rb b/spec/generators/paper_trail/install_generator_spec.rb index 10365b2a..34f1386c 100644 --- a/spec/generators/paper_trail/install_generator_spec.rb +++ b/spec/generators/paper_trail/install_generator_spec.rb @@ -33,9 +33,9 @@ RSpec.describe PaperTrail::InstallGenerator, type: :generator do }.call expected_item_type_options = lambda { if described_class::MYSQL_ADAPTERS.include?(ActiveRecord::Base.connection.class.name) - ", { null: false, limit: 191 }" + ", null: false, limit: 191" else - ", { null: false }" + ", null: false" end }.call expect(destination_root).to( @@ -102,4 +102,26 @@ RSpec.describe PaperTrail::InstallGenerator, type: :generator do ) end end + + describe "`--uuid` option set to `true`" do + before do + prepare_destination + run_generator %w[--uuid] + end + + it "generates a migration for creating the 'versions' table with item_id type uuid" do + expected_item_id_type = "string" + expect(destination_root).to( + have_structure { + directory("db") { + directory("migrate") { + migration("create_versions") { + contains "t.#{expected_item_id_type} :item_id, null: false" + } + } + } + } + ) + end + end end diff --git a/spec/models/not_on_update_spec.rb b/spec/models/not_on_update_spec.rb index 2ab15d72..c811c56c 100644 --- a/spec/models/not_on_update_spec.rb +++ b/spec/models/not_on_update_spec.rb @@ -11,5 +11,12 @@ RSpec.describe NotOnUpdate, type: :model do PaperTrail::Version.count }.by(+1) end + + it "captures changes when in_after_callback is true" do + record.name = "test" + record.paper_trail.save_with_version(in_after_callback: true) + changeset = record.versions.last.changeset + expect(changeset[:name]).to eq([nil, "test"]) + end end end diff --git a/spec/models/order_spec.rb b/spec/models/order_spec.rb new file mode 100644 index 00000000..99665acc --- /dev/null +++ b/spec/models/order_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Order, type: :model, versioning: true do + context "when the record destroyed" do + it "creates a version record for association" do + customer = Customer.create! + described_class.create!(customer: customer) + described_class.destroy_all + + expect(customer.versions.count).to(eq(3)) + end + end +end