From 6157c8103fc967db0eeadc83e7282efae20f0236 Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Mon, 30 Aug 2021 00:10:16 -0400 Subject: [PATCH 01/24] Release 12.1.0 --- CHANGELOG.md | 19 +++++++++++++++++-- README.md | 2 +- lib/paper_trail/version_number.rb | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e74f0bea..c837711e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,21 @@ This project follows [semver 2.0.0](http://semver.org/spec/v2.0.0.html) and the recommendations of [keepachangelog.com](http://keepachangelog.com/). ## Unreleased -- [#1309](https://github.com/paper-trail-gem/paper_trail/pull/1309) - - Removes `item_subtype` requirement when specifying model-specific limits. + +### Breaking Changes + +- None + +### Added + +- None + +### Fixed + +- None + +## 12.1.0 (2021-08-30) + ### Breaking Changes - None @@ -24,6 +37,8 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/). - [#1285](https://github.com/paper-trail-gem/paper_trail/pull/1285) - For ActiveRecord >= 6.0, the `touch` callback will no longer create a new `Version` for skipped or ignored attributes. +- [#1309](https://github.com/paper-trail-gem/paper_trail/pull/1309) - + Removes `item_subtype` requirement when specifying model-specific limits. - [#1333](https://github.com/paper-trail-gem/paper_trail/pull/1333) - Improve reification of STI models that use `find_sti_class`/`sti_class_for` to customize single table inheritance. diff --git a/README.md b/README.md index 021128f8..6b62cc51 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.0](https://github.com/paper-trail-gem/paper_trail/blob/v12.0.0/README.md), +[12.1](https://github.com/paper-trail-gem/paper_trail/blob/v12.1.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), diff --git a/lib/paper_trail/version_number.rb b/lib/paper_trail/version_number.rb index cf2cfae1..97a5be13 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 = 0 + MINOR = 1 TINY = 0 # Set PRE to nil unless it's a pre-release (beta, rc, etc.) From 559211a06efdb39b5fd6160aabdb7fbb0a04e872 Mon Sep 17 00:00:00 2001 From: Hugo Hache Date: Mon, 5 Jul 2021 09:31:00 +0200 Subject: [PATCH 02/24] Use an app specific name for abstract version class to avoid autoloading conflict --- README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 4b4123c7..9ae0d924 100644 --- a/README.md +++ b/README.md @@ -1216,17 +1216,20 @@ class PostVersion < PaperTrail::Version end ``` -If you only use custom version classes and don't have a `versions` table, you -must let ActiveRecord know that the `PaperTrail::Version` class is an -`abstract_class`. +If you only use custom version classes and don't have a `versions` table, you must +let ActiveRecord know that your base version class (eg. `ApplicationVersion` below) +class is an `abstract_class`. ```ruby -# app/models/paper_trail/version.rb -module PaperTrail - class Version < ActiveRecord::Base - include PaperTrail::VersionConcern - self.abstract_class = true - end +# app/models/application_version.rb +class ApplicationVersion < ActiveRecord::Base + include PaperTrail::VersionConcern + self.abstract_class = true +end + +class PostVersion < ApplicationVersion + self.table_name = :post_versions + self.sequence_name = :post_versions_id_seq end ``` From 7e02f975c96baedb8c7e828bef156cc53551ed27 Mon Sep 17 00:00:00 2001 From: Tony Drake Date: Sun, 5 Sep 2021 10:19:18 -0400 Subject: [PATCH 03/24] Support Psych version 4 Fixes #1335 Psych's `.load` method uses `.safe_load` by default which is not compatible with papertail needs to load. This implements the suggested fix in the issue which is a similar fix that Rails uses throughout its codebase when it needs to load YAML content. --- CHANGELOG.md | 3 ++- lib/paper_trail/serializers/yaml.rb | 2 +- spec/paper_trail/serializers/yaml_spec.rb | 13 +++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c837711e..b46b1f6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,8 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/). ### Fixed -- None +- [#1338](https://github.com/paper-trail-gem/paper_trail/pull/1338) - + Support Psych version 4 ## 12.1.0 (2021-08-30) diff --git a/lib/paper_trail/serializers/yaml.rb b/lib/paper_trail/serializers/yaml.rb index 31ffc9d8..0530c990 100644 --- a/lib/paper_trail/serializers/yaml.rb +++ b/lib/paper_trail/serializers/yaml.rb @@ -9,7 +9,7 @@ module PaperTrail extend self # makes all instance methods become module methods as well def load(string) - ::YAML.load string + ::YAML.respond_to?(:unsafe_load) ? ::YAML.unsafe_load(string) : ::YAML.load(string) end # @param object (Hash | HashWithIndifferentAccess) - Coming from diff --git a/spec/paper_trail/serializers/yaml_spec.rb b/spec/paper_trail/serializers/yaml_spec.rb index 92ef85a7..94aef5c6 100644 --- a/spec/paper_trail/serializers/yaml_spec.rb +++ b/spec/paper_trail/serializers/yaml_spec.rb @@ -22,6 +22,19 @@ module PaperTrail expect(described_class.load(hash.to_yaml)).to eq(hash) expect(described_class.load(array.to_yaml)).to eq(array) end + + it "calls the expected load method based on Psych version" do + # Psych 4+ implements .unsafe_load + if ::YAML.respond_to?(:unsafe_load) + allow(::YAML).to receive(:unsafe_load) + described_class.load("string") + expect(::YAML).to have_received(:unsafe_load) + else # Psych < 4 + allow(::YAML).to receive(:load) + described_class.load("string") + expect(::YAML).to have_received(:load) + end + end end describe ".dump" do From 0ab65735a9caa9233cd54b09d16ca46fec63acae Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Fri, 22 Oct 2021 10:40:06 +0200 Subject: [PATCH 04/24] Add specific instructions for MySQL migration The currently generated migration mentions that MySQL users should explicitly enable "fractional seconds precision", but doesn't provide specific steps to enable this. The user has to click the links to figure out what to do. This change adds the specific migration statement that can be used. --- .../paper_trail/install/templates/create_versions.rb.erb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 268d9290..677d1b73 100644 --- a/lib/generators/paper_trail/install/templates/create_versions.rb.erb +++ b/lib/generators/paper_trail/install/templates/create_versions.rb.erb @@ -28,7 +28,9 @@ class CreateVersions < ActiveRecord::Migration<%= migration_version %> # MySQL users should also upgrade to at least rails 4.2, which is the first # version of ActiveRecord with support for fractional seconds in MySQL. # (https://github.com/rails/rails/pull/14359) - # + # + # MySQL users should use the following line for `created_at` + # t.datetime :created_at, limit: 6 t.datetime :created_at end add_index :versions, %i(item_type item_id) From 4e96041c078a273ab26820861df80900a1d9c083 Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Mon, 25 Oct 2021 10:54:46 -0400 Subject: [PATCH 05/24] Drop support for ruby 2.5 Ruby 2.5 reached EoL on 2021-03-31. --- .github/ISSUE_TEMPLATE/bug-report.md | 6 +++--- .github/workflows/test.yml | 4 ++-- .rubocop.yml | 2 +- CHANGELOG.md | 5 +++++ lib/paper_trail/compatibility.rb | 2 +- paper_trail.gemspec | 4 +--- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index dff3b543..41223030 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -32,11 +32,11 @@ require "bundler/inline" # STEP ONE: What versions are you using? gemfile(true) do - ruby "2.5.1" + ruby "3.0.2" source "https://rubygems.org" - gem "activerecord", "5.2.0" + gem "activerecord", "6.1.4.1" gem "minitest", "5.11.3" - gem "paper_trail", "9.2.0", require: false + gem "paper_trail", "12.1.0", require: false gem "sqlite3", "1.3.13" end diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8ecdb41b..16811db1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: uses: ruby/setup-ruby@v1 with: # See "Lowest supported ruby version" in CONTRIBUTING.md - ruby-version: '2.5' + ruby-version: '2.6' - name: Bundle run: | gem install bundler @@ -63,7 +63,7 @@ jobs: # in case it still produces any deprecation warnings. # # See "Lowest supported ruby version" in CONTRIBUTING.md - ruby: [ '2.5', '2.7', '3.0' ] + ruby: [ '2.6', '2.7', '3.0' ] exclude: # rails 5.2 requires ruby < 3.0 diff --git a/.rubocop.yml b/.rubocop.yml index 549ef8b2..477c42af 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -22,7 +22,7 @@ AllCops: NewCops: enable # See "Lowest supported ruby version" in CONTRIBUTING.md - TargetRubyVersion: 2.5 + TargetRubyVersion: 2.6 Bundler/OrderedGems: Exclude: diff --git a/CHANGELOG.md b/CHANGELOG.md index b46b1f6e..0cc7c9c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,13 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/). ### Fixed +- None + +### Dependencies + - [#1338](https://github.com/paper-trail-gem/paper_trail/pull/1338) - Support Psych version 4 +- ruby >= 2.6 (was >= 2.5). Ruby 2.5 reached EoL on 2021-03-31. ## 12.1.0 (2021-08-30) diff --git a/lib/paper_trail/compatibility.rb b/lib/paper_trail/compatibility.rb index 85f74cb7..fb8163db 100644 --- a/lib/paper_trail/compatibility.rb +++ b/lib/paper_trail/compatibility.rb @@ -8,7 +8,7 @@ module PaperTrail # # It is not safe to assume that a new version of rails will be compatible with # PaperTrail. PT is only compatible with the versions of rails that it is - # tested against. See `.travis.yml`. + # tested against. See `.github/workflows/test.yml`. # # However, as of # [#1213](https://github.com/paper-trail-gem/paper_trail/pull/1213) our diff --git a/paper_trail.gemspec b/paper_trail.gemspec index e04f8f86..00ae9c0b 100644 --- a/paper_trail.gemspec +++ b/paper_trail.gemspec @@ -43,9 +43,7 @@ has been destroyed. # about 3 years, per https://www.ruby-lang.org/en/downloads/branches/ # # See "Lowest supported ruby version" in CONTRIBUTING.md - # - # Ruby 2.5 reaches EoL on 2021-03-31. - s.required_ruby_version = ">= 2.5.0" + s.required_ruby_version = ">= 2.6.0" # We no longer specify a maximum activerecord version. # See discussion in paper_trail/compatibility.rb From dd230c71826ff447d60731d9c9f67d9856903c22 Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Mon, 25 Oct 2021 11:43:22 -0400 Subject: [PATCH 06/24] Update dev. dependencies --- .rubocop.yml | 4 +++- paper_trail.gemspec | 16 ++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 477c42af..9915bd43 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -31,8 +31,10 @@ Bundler/OrderedGems: Layout/ArgumentAlignment: EnforcedStyle: with_fixed_indentation +# This cop has a bug in 1.22.2 (https://github.com/rubocop/rubocop/issues/10210) +# When the bug is fixed, we'll return to using `EnforcedStyle: trailing`. Layout/DotPosition: - EnforcedStyle: trailing + Enabled: false # Avoid blank lines inside methods. They are a sign that the method is too big. Layout/EmptyLineAfterGuardClause: diff --git a/paper_trail.gemspec b/paper_trail.gemspec index 00ae9c0b..b2f61093 100644 --- a/paper_trail.gemspec +++ b/paper_trail.gemspec @@ -51,8 +51,8 @@ has been destroyed. s.add_dependency "request_store", "~> 1.1" s.add_development_dependency "appraisal", "~> 2.4.1" - s.add_development_dependency "byebug", "~> 11.0" - s.add_development_dependency "ffaker", "~> 2.19.0" + s.add_development_dependency "byebug", "~> 11.1" + s.add_development_dependency "ffaker", "~> 2.20" s.add_development_dependency "generator_spec", "~> 0.9.4" s.add_development_dependency "memory_profiler", "~> 1.0.0" @@ -63,13 +63,13 @@ has been destroyed. s.add_development_dependency "rake", "~> 13.0" s.add_development_dependency "rspec-rails", "~> 5.0.2" - s.add_development_dependency "rubocop", "~> 1.20.0" + 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.11.3" + 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.4.0" - s.add_development_dependency "simplecov", ">= 0.21", "< 0.22" + s.add_development_dependency "rubocop-rspec", "~> 2.5.0" + s.add_development_dependency "simplecov", "~> 0.21.2" # ## Database Adapters # @@ -81,7 +81,7 @@ has been destroyed. # Currently, all versions of rails we test against are consistent. In the past, # when we tested against rails 4.2, we had to specify database adapters in # `Appraisals`. - s.add_development_dependency "mysql2", "~> 0.5" - s.add_development_dependency "pg", ">= 0.18", "< 2.0" + s.add_development_dependency "mysql2", "~> 0.5.3" + s.add_development_dependency "pg", "~> 1.2" s.add_development_dependency "sqlite3", "~> 1.4" end From 845c6ef7ef2a21d511ff3fbebe76bc3d4110aa9d Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Mon, 25 Oct 2021 12:01:30 -0400 Subject: [PATCH 07/24] Lint: RSpec/DescribedClass --- .rubocop.yml | 5 - spec/models/animal_spec.rb | 8 +- spec/models/article_spec.rb | 2 +- spec/models/book_spec.rb | 10 +- spec/models/boolit_spec.rb | 4 +- spec/models/car_spec.rb | 2 +- spec/models/custom_primary_key_record_spec.rb | 4 +- spec/models/document_spec.rb | 10 +- spec/models/foo_widget_spec.rb | 4 +- spec/models/fruit_spec.rb | 2 +- spec/models/gadget_spec.rb | 2 +- spec/models/joined_version_spec.rb | 10 +- spec/models/no_object_spec.rb | 4 +- spec/models/person_spec.rb | 30 ++-- spec/models/pet_spec.rb | 6 +- spec/models/plant_spec.rb | 8 +- spec/models/post_spec.rb | 8 +- spec/models/skipper_spec.rb | 16 +- spec/models/thing_spec.rb | 4 +- spec/models/version_spec.rb | 54 +++---- spec/models/widget_spec.rb | 140 +++++++++--------- spec/models/wotsit_spec.rb | 4 +- spec/paper_trail/events/base_spec.rb | 12 +- spec/paper_trail/events/destroy_spec.rb | 4 +- spec/paper_trail/events/update_spec.rb | 4 +- 25 files changed, 176 insertions(+), 181 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 9915bd43..ecf9d481 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -138,11 +138,6 @@ Rails/SkipsModelValidations: RSpec/DescribeClass: Enabled: false -# This cop has a bug in 1.35.0 -# https://github.com/rubocop-hq/rubocop-rspec/issues/799 -RSpec/DescribedClass: - Enabled: false - # Yes, ideally examples would be short. Is it possible to pick a limit and say, # "no example will ever be longer than this"? Hard to say. Sometimes they're # quite long. diff --git a/spec/models/animal_spec.rb b/spec/models/animal_spec.rb index e1e1ed62..20b74851 100644 --- a/spec/models/animal_spec.rb +++ b/spec/models/animal_spec.rb @@ -4,8 +4,8 @@ require "spec_helper" RSpec.describe Animal, type: :model, versioning: true do it "baseline test setup" do - expect(Animal.new).to be_versioned - expect(Animal.inheritance_column).to eq("species") + expect(described_class.new).to be_versioned + expect(described_class.inheritance_column).to eq("species") end describe "#descends_from_active_record?" do @@ -15,7 +15,7 @@ RSpec.describe Animal, type: :model, versioning: true do end it "works with custom STI inheritance column" do - animal = Animal.create(name: "Animal") + animal = described_class.create(name: "Animal") animal.update(name: "Animal from the Muppets") animal.update(name: "Animal Muppet") animal.destroy @@ -46,7 +46,7 @@ RSpec.describe Animal, type: :model, versioning: true do it "allows the inheritance_column (species) to be updated" do cat = Cat.create!(name: "Leo") cat.update(name: "Spike", species: "Dog") - dog = Animal.find(cat.id) + dog = described_class.find(cat.id) expect(dog).to be_instance_of(Dog) end diff --git a/spec/models/article_spec.rb b/spec/models/article_spec.rb index 2fb0a998..d8ec2b14 100644 --- a/spec/models/article_spec.rb +++ b/spec/models/article_spec.rb @@ -187,7 +187,7 @@ RSpec.describe Article, type: :model, versioning: true do end context "with an item" do - let(:article) { Article.new(title: initial_title) } + let(:article) { described_class.new(title: initial_title) } let(:initial_title) { "Foobar" } context "when it is created" do diff --git a/spec/models/book_spec.rb b/spec/models/book_spec.rb index 77e57c7c..d51f15fd 100644 --- a/spec/models/book_spec.rb +++ b/spec/models/book_spec.rb @@ -5,7 +5,7 @@ require "spec_helper" RSpec.describe Book, versioning: true do context "with :has_many :through" do it "store version on source <<" do - book = Book.create(title: "War and Peace") + book = described_class.create(title: "War and Peace") dostoyevsky = Person.create(name: "Dostoyevsky") Person.create(name: "Solzhenitsyn") count = PaperTrail::Version.count @@ -15,7 +15,7 @@ RSpec.describe Book, versioning: true do end it "store version on source create" do - book = Book.create(title: "War and Peace") + book = described_class.create(title: "War and Peace") Person.create(name: "Dostoyevsky") Person.create(name: "Solzhenitsyn") count = PaperTrail::Version.count @@ -27,7 +27,7 @@ RSpec.describe Book, versioning: true do end it "store version on join destroy" do - book = Book.create(title: "War and Peace") + book = described_class.create(title: "War and Peace") dostoyevsky = Person.create(name: "Dostoyevsky") Person.create(name: "Solzhenitsyn") (book.authors << dostoyevsky) @@ -39,7 +39,7 @@ RSpec.describe Book, versioning: true do end it "store version on join clear" do - book = Book.create(title: "War and Peace") + book = described_class.create(title: "War and Peace") dostoyevsky = Person.create(name: "Dostoyevsky") Person.create(name: "Solzhenitsyn") book.authors << dostoyevsky @@ -53,7 +53,7 @@ RSpec.describe Book, versioning: true do context "when a persisted record is updated then destroyed" do it "has changes" do - book = Book.create! title: "A" + book = described_class.create! title: "A" changes = YAML.load book.versions.last.attributes["object_changes"] expect(changes).to eq("id" => [nil, book.id], "title" => [nil, "A"]) diff --git a/spec/models/boolit_spec.rb b/spec/models/boolit_spec.rb index 6e648906..c022bce7 100644 --- a/spec/models/boolit_spec.rb +++ b/spec/models/boolit_spec.rb @@ -4,7 +4,7 @@ require "spec_helper" require "support/custom_json_serializer" RSpec.describe Boolit, type: :model, versioning: true do - let(:boolit) { Boolit.create! } + let(:boolit) { described_class.create! } before { boolit.update!(name: FFaker::Name.name) } @@ -20,7 +20,7 @@ RSpec.describe Boolit, type: :model, versioning: true do before { boolit.update!(scoped: false) } it "is NOT scoped" do - expect(Boolit.first).to be_nil + expect(described_class.first).to be_nil end it "still can be reified and persisted" do diff --git a/spec/models/car_spec.rb b/spec/models/car_spec.rb index cf0e1d9d..9cab7305 100644 --- a/spec/models/car_spec.rb +++ b/spec/models/car_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Car, type: :model do describe "changeset", versioning: true do it "has the expected keys (see issue 738)" do - car = Car.create!(name: "Alice") + car = described_class.create!(name: "Alice") car.update(name: "Bob") assert_includes car.versions.last.changeset.keys, "name" end diff --git a/spec/models/custom_primary_key_record_spec.rb b/spec/models/custom_primary_key_record_spec.rb index 519f0ffe..1c1a125d 100644 --- a/spec/models/custom_primary_key_record_spec.rb +++ b/spec/models/custom_primary_key_record_spec.rb @@ -12,9 +12,9 @@ RSpec.describe CustomPrimaryKeyRecord, type: :model do version = custom_primary_key_record.versions.last expect(version).to be_a(CustomPrimaryKeyRecordVersion) version_from_db = CustomPrimaryKeyRecordVersion.last - expect(version_from_db.reify).to be_a(CustomPrimaryKeyRecord) + expect(version_from_db.reify).to be_a(described_class) custom_primary_key_record.destroy - expect(CustomPrimaryKeyRecordVersion.last.reify).to be_a(CustomPrimaryKeyRecord) + expect(CustomPrimaryKeyRecordVersion.last.reify).to be_a(described_class) end end end diff --git a/spec/models/document_spec.rb b/spec/models/document_spec.rb index 59e0a530..19bbe802 100644 --- a/spec/models/document_spec.rb +++ b/spec/models/document_spec.rb @@ -5,7 +5,7 @@ require "spec_helper" RSpec.describe Document, type: :model, versioning: true do describe "have_a_version_with matcher" do it "works with custom versions association" do - document = Document.create!(name: "Foo") + document = described_class.create!(name: "Foo") document.update!(name: "Bar") expect(document).to have_a_version_with(name: "Foo") end @@ -13,7 +13,7 @@ RSpec.describe Document, type: :model, versioning: true do describe "#paper_trail.next_version" do it "returns the expected document" do - doc = Document.create + doc = described_class.create doc.update(name: "Doc 1") reified = doc.paper_trail_versions.last.reify expect(doc.name).to(eq(reified.paper_trail.next_version.name)) @@ -22,7 +22,7 @@ RSpec.describe Document, type: :model, versioning: true do describe "#paper_trail.previous_version" do it "returns the expected document" do - doc = Document.create + doc = described_class.create doc.update(name: "Doc 1") doc.update(name: "Doc 2") expect(doc.paper_trail_versions.length).to(eq(3)) @@ -32,7 +32,7 @@ RSpec.describe Document, type: :model, versioning: true do describe "#paper_trail_versions" do it "returns the expected version records" do - doc = Document.create + doc = described_class.create doc.update(name: "Doc 1") expect(doc.paper_trail_versions.length).to(eq(2)) expect(doc.paper_trail_versions.map(&:event)).to( @@ -43,7 +43,7 @@ RSpec.describe Document, type: :model, versioning: true do describe "#versions" do it "does not respond to versions method" do - doc = Document.create + doc = described_class.create doc.update(name: "Doc 1") expect(doc).not_to respond_to(:versions) end diff --git a/spec/models/foo_widget_spec.rb b/spec/models/foo_widget_spec.rb index 4e571bb0..8e3bbfcf 100644 --- a/spec/models/foo_widget_spec.rb +++ b/spec/models/foo_widget_spec.rb @@ -5,7 +5,7 @@ require "support/performance_helpers" RSpec.describe(FooWidget, versioning: true) do context "with a subclass" do - let(:foo) { FooWidget.create } + let(:foo) { described_class.create } before do foo.update!(name: "Foo") @@ -26,7 +26,7 @@ RSpec.describe(FooWidget, versioning: true) do before { foo.destroy } it "reify with the correct type" do - assert_kind_of(FooWidget, foo.versions.last.reify) + assert_kind_of(described_class, foo.versions.last.reify) expect(PaperTrail::Version.last.previous).to(eq(foo.versions[1])) expect(PaperTrail::Version.last.next).to(be_nil) end diff --git a/spec/models/fruit_spec.rb b/spec/models/fruit_spec.rb index acb70fca..66a9c5b2 100644 --- a/spec/models/fruit_spec.rb +++ b/spec/models/fruit_spec.rb @@ -9,7 +9,7 @@ if ENV["DB"] == "postgres" || JsonVersion.table_exists? # As of PT 9.0.0, with_version_changes only supports json(b) columns, # so that's why were testing the have_a_version_with_changes matcher # here. - banana = Fruit.create!(color: "Red", name: "Banana") + banana = described_class.create!(color: "Red", name: "Banana") banana.update!(color: "Yellow") expect(banana).to have_a_version_with_changes(color: "Yellow") expect(banana).not_to have_a_version_with_changes(color: "Pink") diff --git a/spec/models/gadget_spec.rb b/spec/models/gadget_spec.rb index f71817a7..1c794da2 100644 --- a/spec/models/gadget_spec.rb +++ b/spec/models/gadget_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" RSpec.describe Gadget, type: :model do - let(:gadget) { Gadget.create!(name: "Wrench", brand: "Acme") } + let(:gadget) { described_class.create!(name: "Wrench", brand: "Acme") } it { is_expected.to be_versioned } diff --git a/spec/models/joined_version_spec.rb b/spec/models/joined_version_spec.rb index d144cfea..8046f1d4 100644 --- a/spec/models/joined_version_spec.rb +++ b/spec/models/joined_version_spec.rb @@ -4,10 +4,10 @@ require "spec_helper" RSpec.describe JoinedVersion, type: :model, versioning: true do let(:widget) { Widget.create!(name: FFaker::Name.name) } - let(:version) { JoinedVersion.first } + let(:version) { described_class.first } describe "default_scope" do - it { expect(JoinedVersion.default_scopes).not_to be_empty } + it { expect(described_class.default_scopes).not_to be_empty } end describe "VersionConcern::ClassMethods" do @@ -15,19 +15,19 @@ RSpec.describe JoinedVersion, type: :model, versioning: true do describe "#subsequent" do it "does not raise error when there is a default_scope that joins" do - JoinedVersion.subsequent(version).first + described_class.subsequent(version).first end end describe "#preceding" do it "does not raise error when there is a default scope that joins" do - JoinedVersion.preceding(version).first + described_class.preceding(version).first end end describe "#between" do it "does not raise error when there is a default scope that joins" do - JoinedVersion.between(Time.current, 1.minute.from_now).first + described_class.between(Time.current, 1.minute.from_now).first end end end diff --git a/spec/models/no_object_spec.rb b/spec/models/no_object_spec.rb index 900af397..85e4dd11 100644 --- a/spec/models/no_object_spec.rb +++ b/spec/models/no_object_spec.rb @@ -38,7 +38,7 @@ RSpec.describe NoObject, versioning: true do describe "reify" do it "raises error" do - n = NoObject.create!(letter: "A") + n = described_class.create!(letter: "A") v = n.versions.last expect { v.reify }.to( raise_error( @@ -51,7 +51,7 @@ RSpec.describe NoObject, versioning: true do describe "where_object" do it "raises error" do - n = NoObject.create!(letter: "A") + n = described_class.create!(letter: "A") expect { n.versions.where_object(foo: "bar") }.to( diff --git a/spec/models/person_spec.rb b/spec/models/person_spec.rb index df47fa18..bd44f19e 100644 --- a/spec/models/person_spec.rb +++ b/spec/models/person_spec.rb @@ -9,21 +9,21 @@ require "spec_helper" RSpec.describe Person, type: :model, versioning: true do describe "#time_zone" do it "returns an ActiveSupport::TimeZone" do - person = Person.new(time_zone: "Samoa") + person = described_class.new(time_zone: "Samoa") expect(person.time_zone.class).to(eq(ActiveSupport::TimeZone)) end end context "when the model is saved" do it "version.object_changes should store long serialization of TimeZone object" do - person = Person.new(time_zone: "Samoa") + person = described_class.new(time_zone: "Samoa") person.save! len = person.versions.last.object_changes.length expect((len < 105)).to(be_truthy) end it "version.object_changes attribute should have stored the value from serializer" do - person = Person.new(time_zone: "Samoa") + person = described_class.new(time_zone: "Samoa") person.save! as_stored_in_version = HashWithIndifferentAccess[ YAML.load(person.versions.last.object_changes) @@ -34,14 +34,14 @@ RSpec.describe Person, type: :model, versioning: true do end it "version.changeset should convert attribute to original, unserialized value" do - person = Person.new(time_zone: "Samoa") + person = described_class.new(time_zone: "Samoa") person.save! unserialized_value = Person::TimeZoneSerializer.load(person.time_zone) expect(person.versions.last.changeset[:time_zone].last).to(eq(unserialized_value)) end it "record.changes (before save) returns the original, unserialized values" do - person = Person.new(time_zone: "Samoa") + person = described_class.new(time_zone: "Samoa") changes_before_save = person.changes.dup person.save! expect( @@ -50,7 +50,7 @@ RSpec.describe Person, type: :model, versioning: true do end it "version.changeset should be the same as record.changes was before the save" do - person = Person.new(time_zone: "Samoa") + person = described_class.new(time_zone: "Samoa") changes_before_save = person.changes.dup person.save! actual = person.versions.last.changeset.delete_if { |k, _v| (k.to_sym == :id) } @@ -61,7 +61,7 @@ RSpec.describe Person, type: :model, versioning: true do context "when that attribute is updated" do it "object should not store long serialization of TimeZone object" do - person = Person.new(time_zone: "Samoa") + person = described_class.new(time_zone: "Samoa") person.save! person.assign_attributes(time_zone: "Pacific Time (US & Canada)") person.save! @@ -70,7 +70,7 @@ RSpec.describe Person, type: :model, versioning: true do end it "object_changes should not store long serialization of TimeZone object" do - person = Person.new(time_zone: "Samoa") + person = described_class.new(time_zone: "Samoa") person.save! person.assign_attributes(time_zone: "Pacific Time (US & Canada)") person.save! @@ -79,7 +79,7 @@ RSpec.describe Person, type: :model, versioning: true do end it "version.object attribute should have stored value from serializer" do - person = Person.new(time_zone: "Samoa") + person = described_class.new(time_zone: "Samoa") person.save! attribute_value_before_change = person.time_zone person.assign_attributes(time_zone: "Pacific Time (US & Canada)") @@ -93,7 +93,7 @@ RSpec.describe Person, type: :model, versioning: true do end it "version.object_changes attribute should have stored value from serializer" do - person = Person.new(time_zone: "Samoa") + person = described_class.new(time_zone: "Samoa") person.save! person.assign_attributes(time_zone: "Pacific Time (US & Canada)") person.save! @@ -106,7 +106,7 @@ RSpec.describe Person, type: :model, versioning: true do end it "version.reify should convert attribute to original, unserialized value" do - person = Person.new(time_zone: "Samoa") + person = described_class.new(time_zone: "Samoa") person.save! attribute_value_before_change = person.time_zone person.assign_attributes(time_zone: "Pacific Time (US & Canada)") @@ -116,7 +116,7 @@ RSpec.describe Person, type: :model, versioning: true do end it "version.changeset should convert attribute to original, unserialized value" do - person = Person.new(time_zone: "Samoa") + person = described_class.new(time_zone: "Samoa") person.save! person.assign_attributes(time_zone: "Pacific Time (US & Canada)") person.save! @@ -125,7 +125,7 @@ RSpec.describe Person, type: :model, versioning: true do end it "record.changes (before save) returns the original, unserialized values" do - person = Person.new(time_zone: "Samoa") + person = described_class.new(time_zone: "Samoa") person.save! person.assign_attributes(time_zone: "Pacific Time (US & Canada)") changes_before_save = person.changes.dup @@ -136,7 +136,7 @@ RSpec.describe Person, type: :model, versioning: true do end it "version.changeset should be the same as record.changes was before the save" do - person = Person.new(time_zone: "Samoa") + person = described_class.new(time_zone: "Samoa") person.save! person.assign_attributes(time_zone: "Pacific Time (US & Canada)") changes_before_save = person.changes.dup @@ -151,7 +151,7 @@ RSpec.describe Person, type: :model, versioning: true do describe "#cars and bicycles" do it "can be reified" do - person = Person.create(name: "Frank") + person = described_class.create(name: "Frank") car = Car.create(name: "BMW 325") bicycle = Bicycle.create(name: "BMX 1.0") diff --git a/spec/models/pet_spec.rb b/spec/models/pet_spec.rb index 68362a7d..061e86b2 100644 --- a/spec/models/pet_spec.rb +++ b/spec/models/pet_spec.rb @@ -5,7 +5,7 @@ require "rails/generators" RSpec.describe Pet, type: :model, versioning: true do it "baseline test setup" do - expect(Pet.new).to be_versioned + expect(described_class.new).to be_versioned end it "can be reified" do @@ -13,8 +13,8 @@ RSpec.describe Pet, type: :model, versioning: true do dog = Dog.create(name: "Snoopy") cat = Cat.create(name: "Garfield") - person.pets << Pet.create(animal: dog) - person.pets << Pet.create(animal: cat) + person.pets << described_class.create(animal: dog) + person.pets << described_class.create(animal: cat) person.update(name: "Steve") dog.update(name: "Beethoven") diff --git a/spec/models/plant_spec.rb b/spec/models/plant_spec.rb index 004b6505..4656b1f3 100644 --- a/spec/models/plant_spec.rb +++ b/spec/models/plant_spec.rb @@ -4,8 +4,8 @@ require "spec_helper" RSpec.describe Plant, type: :model, versioning: true do it "baseline test setup" do - expect(Plant.new).to be_versioned - expect(Plant.inheritance_column).to eq("species") + expect(described_class.new).to be_versioned + expect(described_class.inheritance_column).to eq("species") end describe "#descends_from_active_record?" do @@ -15,14 +15,14 @@ RSpec.describe Plant, type: :model, versioning: true do end it "works with non standard STI column contents" do - plant = Plant.create + plant = described_class.create plant.destroy tomato = Tomato.create tomato.destroy reified = plant.versions.last.reify - expect(reified.class).to eq(Plant) + expect(reified.class).to eq(described_class) reified = tomato.versions.last.reify expect(reified.class).to eq(Tomato) diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index 57cec579..59bf49f4 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -5,7 +5,7 @@ require "spec_helper" # The `Post` model uses a custom version class, `PostVersion` RSpec.describe Post, type: :model, versioning: true do it "inserts records into the correct table, post_versions" do - post = Post.create + post = described_class.create expect(PostVersion.count).to(eq(1)) post.update(content: "Some new content") expect(PostVersion.count).to(eq(2)) @@ -14,20 +14,20 @@ RSpec.describe Post, type: :model, versioning: true do context "with the first version" do it "have the correct index" do - post = Post.create + post = described_class.create version = post.versions.first expect(version.index).to(eq(0)) end end it "have versions of the custom class" do - post = Post.create + post = described_class.create expect(post.versions.first.class.name).to(eq("PostVersion")) end describe "#changeset" do it "returns nil because the object_changes column doesn't exist" do - post = Post.create + post = described_class.create post.update(content: "Some new content") expect(post.versions.last.changeset).to(be_nil) end diff --git a/spec/models/skipper_spec.rb b/spec/models/skipper_spec.rb index 29e2f363..2e4189ed 100644 --- a/spec/models/skipper_spec.rb +++ b/spec/models/skipper_spec.rb @@ -11,7 +11,7 @@ RSpec.describe Skipper, type: :model, versioning: true do let(:t2) { Time.zone.local(2015, 7, 15, 20, 34, 30) } it "does not create a version" do - skipper = Skipper.create!(another_timestamp: t1) + skipper = described_class.create!(another_timestamp: t1) expect { skipper.update!(another_timestamp: t2) }.not_to(change { skipper.versions.length }) @@ -25,28 +25,28 @@ RSpec.describe Skipper, type: :model, versioning: true do if ActiveRecord.gem_version >= Gem::Version.new("6") it "does not create a version for skipped attributes" do - skipper = Skipper.create!(another_timestamp: t1) + skipper = described_class.create!(another_timestamp: t1) expect { skipper.touch(:another_timestamp, time: t2) }.not_to(change { skipper.versions.length }) end it "does not create a version for ignored attributes" do - skipper = Skipper.create!(created_at: t1) + skipper = described_class.create!(created_at: t1) expect { skipper.touch(:created_at, time: t2) }.not_to(change { skipper.versions.length }) end else it "creates a version even for skipped attributes" do - skipper = Skipper.create!(another_timestamp: t1) + skipper = described_class.create!(another_timestamp: t1) expect { skipper.touch(:another_timestamp, time: t2) }.to(change { skipper.versions.length }) end it "creates a version even for ignored attributes" do - skipper = Skipper.create!(created_at: t1) + skipper = described_class.create!(created_at: t1) expect { skipper.touch(:created_at, time: t2) }.to(change { skipper.versions.length }) @@ -54,7 +54,7 @@ RSpec.describe Skipper, type: :model, versioning: true do end it "creates a version for non-skipped timestamps" do - skipper = Skipper.create! + skipper = described_class.create! expect { skipper.touch }.to(change { skipper.versions.length }) @@ -67,7 +67,7 @@ RSpec.describe Skipper, type: :model, versioning: true do context "without preserve (default)" do it "has no timestamp" do - skipper = Skipper.create!(another_timestamp: t1) + 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) @@ -76,7 +76,7 @@ RSpec.describe Skipper, type: :model, versioning: true do context "with preserve" do it "preserves its timestamp" do - skipper = Skipper.create!(another_timestamp: t1) + skipper = described_class.create!(another_timestamp: t1) skipper.update!(another_timestamp: t2, name: "Foobar") skipper = skipper.versions.last.reify(unversioned_attributes: :preserve) expect(skipper.another_timestamp).to eq(t2) diff --git a/spec/models/thing_spec.rb b/spec/models/thing_spec.rb index ac7b8521..3030abd7 100644 --- a/spec/models/thing_spec.rb +++ b/spec/models/thing_spec.rb @@ -4,10 +4,10 @@ require "spec_helper" RSpec.describe Thing, type: :model do describe "#versions", versioning: true do - let(:thing) { Thing.create! } + let(:thing) { described_class.create! } it "applies the scope option" do - expect(Thing.reflect_on_association(:versions).scope).to be_a Proc + expect(described_class.reflect_on_association(:versions).scope).to be_a Proc expect(thing.versions.to_sql).to end_with "ORDER BY id desc" end diff --git a/spec/models/version_spec.rb b/spec/models/version_spec.rb index 9ecfdbce..03bfb976 100644 --- a/spec/models/version_spec.rb +++ b/spec/models/version_spec.rb @@ -60,7 +60,7 @@ module PaperTrail describe "#paper_trail_originator" do context "with no previous versions" do it "returns nil" do - expect(PaperTrail::Version.new.paper_trail_originator).to be_nil + expect(described_class.new.paper_trail_originator).to be_nil end end @@ -78,7 +78,7 @@ module PaperTrail describe "#previous" do context "with no previous versions" do it "returns nil" do - expect(PaperTrail::Version.new.previous).to be_nil + expect(described_class.new.previous).to be_nil end end @@ -88,7 +88,7 @@ module PaperTrail widget = Widget.create!(name: FFaker::Name.name) widget.versions.first.update!(whodunnit: name) widget.update!(name: FFaker::Name.first_name) - expect(widget.versions.last.previous).to be_instance_of(PaperTrail::Version) + expect(widget.versions.last.previous).to be_instance_of(described_class) end end end @@ -96,14 +96,14 @@ module PaperTrail describe "#terminator" do it "is an alias for the `whodunnit` attribute" do attributes = { whodunnit: FFaker::Name.first_name } - version = PaperTrail::Version.new(attributes) + version = described_class.new(attributes) expect(version.terminator).to eq(attributes[:whodunnit]) end end describe "#version_author" do it "is an alias for the `terminator` method" do - version = PaperTrail::Version.new + version = described_class.new expect(version.method(:version_author)).to eq(version.method(:terminator)) end end @@ -135,7 +135,7 @@ module PaperTrail "ALTER TABLE versions ADD COLUMN #{column} #{column_datatype_override};" ) end - PaperTrail::Version.reset_column_information + described_class.reset_column_information end end @@ -144,17 +144,17 @@ module PaperTrail if column_datatype_override ActiveRecord::Base.connection.execute("ROLLBACK TO SAVEPOINT pgtest;") - PaperTrail::Version.reset_column_information + described_class.reset_column_information end end describe "#where_attribute_changes", versioning: true do it "requires its argument to be a string or a symbol" do expect { - PaperTrail::Version.where_attribute_changes({}) + described_class.where_attribute_changes({}) }.to raise_error(ArgumentError) expect { - PaperTrail::Version.where_attribute_changes([]) + described_class.where_attribute_changes([]) }.to raise_error(ArgumentError) end @@ -169,7 +169,7 @@ module PaperTrail bicycle.update!(name: "xyz") allow(adapter).to( - receive(:where_attribute_changes).with(Version, :name) + receive(:where_attribute_changes).with(described_class, :name) ).and_return([bicycle.versions[0], bicycle.versions[1]]) PaperTrail.config.object_changes_adapter = adapter @@ -235,10 +235,10 @@ module PaperTrail widget.update!(name: "foobar", an_integer: 100) widget.update!(name: FFaker::Name.last_name, an_integer: 15) expect { - PaperTrail::Version.where_object(:foo) + described_class.where_object(:foo) }.to raise_error(ArgumentError) expect { - PaperTrail::Version.where_object([]) + described_class.where_object([]) }.to raise_error(ArgumentError) end @@ -249,13 +249,13 @@ module PaperTrail widget.update!(name: "foobar", an_integer: 100) widget.update!(name: FFaker::Name.last_name, an_integer: 15) expect( - PaperTrail::Version.where_object(an_integer: int) + described_class.where_object(an_integer: int) ).to eq([widget.versions[1]]) expect( - PaperTrail::Version.where_object(name: name) + described_class.where_object(name: name) ).to eq([widget.versions[1]]) expect( - PaperTrail::Version.where_object(an_integer: 100) + described_class.where_object(an_integer: 100) ).to eq([widget.versions[2]]) end end @@ -268,13 +268,13 @@ module PaperTrail widget.update!(name: "foobar", an_integer: 100) widget.update!(name: FFaker::Name.last_name, an_integer: 15) expect( - PaperTrail::Version.where_object(an_integer: int) + described_class.where_object(an_integer: int) ).to eq([widget.versions[1]]) expect( - PaperTrail::Version.where_object(name: name) + described_class.where_object(name: name) ).to eq([widget.versions[1]]) expect( - PaperTrail::Version.where_object(an_integer: 100) + described_class.where_object(an_integer: 100) ).to eq([widget.versions[2]]) end end @@ -283,10 +283,10 @@ module PaperTrail describe "#where_object_changes", versioning: true do it "requires its argument to be a Hash" do expect { - PaperTrail::Version.where_object_changes(:foo) + described_class.where_object_changes(:foo) }.to raise_error(ArgumentError) expect { - PaperTrail::Version.where_object_changes([]) + described_class.where_object_changes([]) }.to raise_error(ArgumentError) end @@ -299,7 +299,7 @@ module PaperTrail adapter = instance_spy("CustomObjectChangesAdapter") bicycle = Bicycle.create!(name: "abc") allow(adapter).to( - receive(:where_object_changes).with(Version, name: "abc") + receive(:where_object_changes).with(described_class, name: "abc") ).and_return(bicycle.versions[0..1]) PaperTrail.config.object_changes_adapter = adapter expect( @@ -362,10 +362,10 @@ module PaperTrail describe "#where_object_changes_from", versioning: true do it "requires its argument to be a Hash" do expect { - PaperTrail::Version.where_object_changes_from(:foo) + described_class.where_object_changes_from(:foo) }.to raise_error(ArgumentError) expect { - PaperTrail::Version.where_object_changes_from([]) + described_class.where_object_changes_from([]) }.to raise_error(ArgumentError) end @@ -380,7 +380,7 @@ module PaperTrail bicycle.update!(name: "xyz") allow(adapter).to( - receive(:where_object_changes_from).with(Version, name: "abc") + receive(:where_object_changes_from).with(described_class, name: "abc") ).and_return([bicycle.versions[1]]) PaperTrail.config.object_changes_adapter = adapter @@ -447,10 +447,10 @@ module PaperTrail describe "#where_object_changes_to", versioning: true do it "requires its argument to be a Hash" do expect { - PaperTrail::Version.where_object_changes_to(:foo) + described_class.where_object_changes_to(:foo) }.to raise_error(ArgumentError) expect { - PaperTrail::Version.where_object_changes_to([]) + described_class.where_object_changes_to([]) }.to raise_error(ArgumentError) end @@ -465,7 +465,7 @@ module PaperTrail bicycle.update!(name: "xyz") allow(adapter).to( - receive(:where_object_changes_to).with(Version, name: "xyz") + receive(:where_object_changes_to).with(described_class, name: "xyz") ).and_return([bicycle.versions[1]]) PaperTrail.config.object_changes_adapter = adapter diff --git a/spec/models/widget_spec.rb b/spec/models/widget_spec.rb index 56b86b04..7c22cbbd 100644 --- a/spec/models/widget_spec.rb +++ b/spec/models/widget_spec.rb @@ -6,7 +6,7 @@ require "support/performance_helpers" RSpec.describe Widget, type: :model, versioning: true do describe "#changeset" do it "has expected values" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") changeset = widget.versions.last.changeset expect(changeset["name"]).to eq([nil, "Henry"]) expect(changeset["id"]).to eq([nil, widget.id]) @@ -24,7 +24,7 @@ RSpec.describe Widget, type: :model, versioning: true do end it "calls the adapter's load_changeset method" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") adapter = instance_spy("CustomObjectChangesAdapter") PaperTrail.config.object_changes_adapter = adapter allow(adapter).to( @@ -39,7 +39,7 @@ RSpec.describe Widget, type: :model, versioning: true do it "defaults to the original behavior" do adapter = Class.new.new PaperTrail.config.object_changes_adapter = adapter - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") changeset = widget.versions.last.changeset expect(changeset[:name]).to eq([nil, "Henry"]) end @@ -48,44 +48,44 @@ RSpec.describe Widget, type: :model, versioning: true do context "with a new record" do it "not have any previous versions" do - expect(Widget.new.versions).to(eq([])) + expect(described_class.new.versions).to(eq([])) end it "be live" do - expect(Widget.new.paper_trail.live?).to(eq(true)) + expect(described_class.new.paper_trail.live?).to(eq(true)) end end context "with a persisted record" do it "have one previous version" do - widget = Widget.create(name: "Henry", created_at: (Time.current - 1.day)) + widget = described_class.create(name: "Henry", created_at: (Time.current - 1.day)) expect(widget.versions.length).to(eq(1)) end it "be nil in its previous version" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") expect(widget.versions.first.object).to(be_nil) expect(widget.versions.first.reify).to(be_nil) end it "record the correct event" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") expect(widget.versions.first.event).to(match(/create/i)) end it "be live" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") expect(widget.paper_trail.live?).to(eq(true)) end it "use the widget `updated_at` as the version's `created_at`" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") expect(widget.versions.first.created_at.to_i).to(eq(widget.updated_at.to_i)) end context "when updated without any changes" do it "to have two previous versions" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.touch expect(widget.versions.length).to eq(2) end @@ -93,13 +93,13 @@ RSpec.describe Widget, type: :model, versioning: true do context "when updated with changes" do it "have three previous versions" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") expect(widget.versions.length).to(eq(2)) end it "be available in its previous version" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") expect(widget.name).to(eq("Harry")) expect(widget.versions.last.object).not_to(be_nil) @@ -109,19 +109,19 @@ RSpec.describe Widget, type: :model, versioning: true do end it "have the same ID in its previous version" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") expect(widget.versions.last.reify.id).to(eq(widget.id)) end it "record the correct event" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") expect(widget.versions.last.event).to(match(/update/i)) end it "have versions that are not live" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") widget.versions.map(&:reify).compact.each do |v| expect(v.paper_trail).not_to be_live @@ -129,7 +129,7 @@ RSpec.describe Widget, type: :model, versioning: true do end it "have stored changes" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") last_obj_changes = widget.versions.last.object_changes actual = PaperTrail.serializer.load(last_obj_changes).reject do |k, _v| @@ -141,7 +141,7 @@ RSpec.describe Widget, type: :model, versioning: true do end it "return changes with indifferent access" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") expect(widget.versions.last.changeset[:name]).to(eq(%w[Henry Harry])) expect(widget.versions.last.changeset["name"]).to(eq(%w[Henry Harry])) @@ -150,7 +150,7 @@ RSpec.describe Widget, type: :model, versioning: true do context "when updated, and has one associated object" do it "not copy the has_one association by default when reifying" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") wotsit = widget.create_wotsit name: "John" reified_widget = widget.versions.last.reify @@ -161,7 +161,7 @@ RSpec.describe Widget, type: :model, versioning: true do context "when updated, and has many associated objects" do it "copy the has_many associations when reifying" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") widget.fluxors.create(name: "f-zero") widget.fluxors.create(name: "f-one") @@ -175,7 +175,7 @@ RSpec.describe Widget, type: :model, versioning: true do context "when updated, and has many associated polymorphic objects" do it "copy the has_many associations when reifying" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") widget.whatchamajiggers.create(name: "f-zero") widget.whatchamajiggers.create(name: "f-zero") @@ -189,7 +189,7 @@ RSpec.describe Widget, type: :model, versioning: true do context "when updated, polymorphic objects by themselves" do it "not fail with a nil pointer on the polymorphic association" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") widget = Whatchamajigger.new(name: "f-zero") widget.save! @@ -198,21 +198,21 @@ RSpec.describe Widget, type: :model, versioning: true do context "when updated, and then destroyed" do it "record the correct event" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") widget.destroy expect(PaperTrail::Version.last.event).to(match(/destroy/i)) end it "have three previous versions" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") widget.destroy expect(PaperTrail::Version.with_item_keys("Widget", widget.id).length).to(eq(3)) end it "returns the expected attributes for the reified widget" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") widget.destroy reified_widget = PaperTrail::Version.last.reify @@ -239,7 +239,7 @@ RSpec.describe Widget, type: :model, versioning: true do end it "be re-creatable from its previous version" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") widget.destroy reified_widget = PaperTrail::Version.last.reify @@ -247,7 +247,7 @@ RSpec.describe Widget, type: :model, versioning: true do end it "restore its associations on its previous version" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") widget.fluxors.create(name: "flux") widget.destroy @@ -257,7 +257,7 @@ RSpec.describe Widget, type: :model, versioning: true do end it "have nil item for last version" do - widget = Widget.create(name: "Henry") + widget = described_class.create(name: "Henry") widget.update(name: "Harry") widget.destroy expect(widget.versions.last.item).to be_nil @@ -270,7 +270,7 @@ RSpec.describe Widget, type: :model, versioning: true do let!(:t0) { Time.current } let(:previous_widget) { widget.versions.last.reify } let(:widget) { - Widget.create( + described_class.create( name: "Warble", a_text: "The quick brown fox", an_integer: 42, @@ -338,7 +338,7 @@ RSpec.describe Widget, type: :model, versioning: true do let(:last_version) { widget.versions.last } it "reify previous version" do - assert_kind_of(Widget, last_version.reify) + assert_kind_of(described_class, last_version.reify) end it "restore all forward-compatible attributes" do @@ -362,7 +362,7 @@ RSpec.describe Widget, type: :model, versioning: true do after { PaperTrail.enabled = true } it "not add to its trail" do - widget = Widget.create(name: "Zaphod") + widget = described_class.create(name: "Zaphod") PaperTrail.enabled = false count = widget.versions.length widget.update(name: "Beeblebrox") @@ -372,23 +372,23 @@ RSpec.describe Widget, type: :model, versioning: true do context "with its paper trail turned off, when updated" do after do - PaperTrail.request.enable_model(Widget) + PaperTrail.request.enable_model(described_class) end it "not add to its trail" do - widget = Widget.create(name: "Zaphod") - PaperTrail.request.disable_model(Widget) + widget = described_class.create(name: "Zaphod") + PaperTrail.request.disable_model(described_class) count = widget.versions.length widget.update(name: "Beeblebrox") expect(widget.versions.length).to(eq(count)) end it "add to its trail" do - widget = Widget.create(name: "Zaphod") - PaperTrail.request.disable_model(Widget) + widget = described_class.create(name: "Zaphod") + PaperTrail.request.disable_model(described_class) count = widget.versions.length widget.update(name: "Beeblebrox") - PaperTrail.request.enable_model(Widget) + PaperTrail.request.enable_model(described_class) widget.update(name: "Ford") expect(widget.versions.length).to(eq((count + 1))) end @@ -398,7 +398,7 @@ RSpec.describe Widget, type: :model, versioning: true do context "with somebody making changes" do context "when a record is created" do it "tracks who made the change" do - widget = Widget.new(name: "Fidget") + widget = described_class.new(name: "Fidget") PaperTrail.request.whodunnit = "Alice" widget.save version = widget.versions.last @@ -411,7 +411,7 @@ RSpec.describe Widget, type: :model, versioning: true do context "when created, then updated" do it "tracks who made the change" do - widget = Widget.new(name: "Fidget") + widget = described_class.new(name: "Fidget") PaperTrail.request.whodunnit = "Alice" widget.save PaperTrail.request.whodunnit = "Bob" @@ -426,7 +426,7 @@ RSpec.describe Widget, type: :model, versioning: true do context "when created, updated, and destroyed" do it "tracks who made the change" do - widget = Widget.new(name: "Fidget") + widget = described_class.new(name: "Fidget") PaperTrail.request.whodunnit = "Alice" widget.save PaperTrail.request.whodunnit = "Bob" @@ -444,7 +444,7 @@ RSpec.describe Widget, type: :model, versioning: true do context "with an item with versions" do context "when the versions were created over time" do - let(:widget) { Widget.create(name: "Widget") } + let(:widget) { described_class.create(name: "Widget") } let(:t0) { 2.days.ago } let(:t1) { 1.day.ago } let(:t2) { 1.hour.ago } @@ -501,7 +501,7 @@ RSpec.describe Widget, type: :model, versioning: true do describe ".versions_between" do it "return versions in the time period" do - widget = Widget.create(name: "Widget") + widget = described_class.create(name: "Widget") widget.update(name: "Fidget") widget.update(name: "Digit") widget.versions[0].update(created_at: 30.days.ago) @@ -524,11 +524,11 @@ RSpec.describe Widget, type: :model, versioning: true do end context "with the first version" do - let(:widget) { Widget.create(name: "Widget") } + let(:widget) { described_class.create(name: "Widget") } let(:version) { widget.versions.last } before do - widget = Widget.create(name: "Widget") + widget = described_class.create(name: "Widget") widget.update(name: "Fidget") widget.update(name: "Digit") end @@ -547,7 +547,7 @@ RSpec.describe Widget, type: :model, versioning: true do end context "with the last version" do - let(:widget) { Widget.create(name: "Widget") } + let(:widget) { described_class.create(name: "Widget") } let(:version) { widget.versions.last } before do @@ -571,7 +571,7 @@ RSpec.describe Widget, type: :model, versioning: true do context "with a reified item" do it "know which version it came from, and return its previous self" do - widget = Widget.create(name: "Bob") + widget = described_class.create(name: "Bob") %w[Tom Dick Jane].each do |name| widget.update(name: name) end @@ -585,7 +585,7 @@ RSpec.describe Widget, type: :model, versioning: true do describe "#next_version" do context "with a reified item" do it "returns the object (not a Version) as it became next" do - widget = Widget.create(name: "Bob") + widget = described_class.create(name: "Bob") %w[Tom Dick Jane].each do |name| widget.update(name: name) end @@ -598,7 +598,7 @@ RSpec.describe Widget, type: :model, versioning: true do context "with a non-reified item" do it "always returns nil because cannot ever have a next version" do - widget = Widget.new + widget = described_class.new expect(widget.paper_trail.next_version).to(be_nil) widget.save %w[Tom Dick Jane].each do |name| @@ -612,7 +612,7 @@ RSpec.describe Widget, type: :model, versioning: true do describe "#previous_version" do context "with a reified item" do it "returns the object (not a Version) as it was most recently" do - widget = Widget.create(name: "Bob") + widget = described_class.create(name: "Bob") %w[Tom Dick Jane].each do |name| widget.update(name: name) end @@ -625,7 +625,7 @@ RSpec.describe Widget, type: :model, versioning: true do context "with a non-reified item" do it "returns the object (not a Version) as it was most recently" do - widget = Widget.new + widget = described_class.new expect(widget.paper_trail.previous_version).to(be_nil) widget.save %w[Tom Dick Jane].each do |name| @@ -638,7 +638,7 @@ RSpec.describe Widget, type: :model, versioning: true do context "with an unsaved record" do it "not have a version created on destroy" do - widget = Widget.new + widget = described_class.new widget.destroy expect(widget.versions.empty?).to(eq(true)) end @@ -646,7 +646,7 @@ RSpec.describe Widget, type: :model, versioning: true do context "when measuring the memory allocation of" do let(:widget) do - Widget.new( + described_class.new( name: "Warble", a_text: "The quick brown fox", an_integer: 42, @@ -725,7 +725,7 @@ RSpec.describe Widget, type: :model, versioning: true do end describe "`have_a_version_with` matcher", versioning: true do - let(:widget) { Widget.create! name: "Bob", an_integer: 1 } + let(:widget) { described_class.create! name: "Bob", an_integer: 1 } before do widget.update!(name: "Leonard", an_integer: 1) @@ -743,21 +743,21 @@ RSpec.describe Widget, type: :model, versioning: true do describe "versioning option" do context "when enabled", versioning: true do it "enables versioning" do - widget = Widget.create! name: "Bob", an_integer: 1 + widget = described_class.create! name: "Bob", an_integer: 1 expect(widget.versions.size).to eq(1) end end context "when disabled", versioning: false do it "does not enable versioning" do - widget = Widget.create! name: "Bob", an_integer: 1 + widget = described_class.create! name: "Bob", an_integer: 1 expect(widget.versions.size).to eq(0) end end end describe "Callbacks", versioning: true do - let(:widget) { Widget.create! name: "Bob", an_integer: 1 } + let(:widget) { described_class.create! name: "Bob", an_integer: 1 } describe "before_save" do it "resets value for timestamp attrs for update so that value gets updated properly" do @@ -768,7 +768,7 @@ RSpec.describe Widget, type: :model, versioning: true do end describe "after_create" do - let(:widget) { Widget.create!(name: "Foobar", created_at: Time.current - 1.week) } + let(:widget) { described_class.create!(name: "Foobar", created_at: Time.current - 1.week) } 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) @@ -832,7 +832,7 @@ RSpec.describe Widget, type: :model, versioning: true do end describe "Association", versioning: true do - let(:widget) { Widget.create! name: "Bob", an_integer: 1 } + let(:widget) { described_class.create! name: "Bob", an_integer: 1 } describe "sort order" do it "sorts by the timestamp order from the `VersionConcern`" do @@ -844,19 +844,19 @@ RSpec.describe Widget, type: :model, versioning: true do end describe "#create", versioning: true do - let(:widget) { Widget.create! name: "Bob", an_integer: 1 } + let(:widget) { described_class.create! name: "Bob", an_integer: 1 } it "creates a version record" do - wordget = Widget.create + wordget = described_class.create assert_equal 1, wordget.versions.length end end describe "#destroy", versioning: true do - let(:widget) { Widget.create! name: "Bob", an_integer: 1 } + let(:widget) { described_class.create! name: "Bob", an_integer: 1 } it "creates a version record" do - widget = Widget.create + widget = described_class.create assert_equal 1, widget.versions.length widget.destroy versions_for_widget = PaperTrail::Version.with_item_keys("Widget", widget.id) @@ -869,7 +869,7 @@ RSpec.describe Widget, type: :model, versioning: true do # the `widget.versions` association, instead of `with_item_keys`. PaperTrail::Version.with_item_keys("Widget", widget.id) } - widget = Widget.create + widget = described_class.create assert_equal 1, widget.versions.length widget.destroy assert_equal 2, versions.call(widget).length @@ -883,7 +883,7 @@ RSpec.describe Widget, type: :model, versioning: true do end describe "#paper_trail.originator", versioning: true do - let(:widget) { Widget.create! name: "Bob", an_integer: 1 } + let(:widget) { described_class.create! name: "Bob", an_integer: 1 } describe "return value" do let(:orig_name) { FFaker::Name.name } @@ -923,7 +923,7 @@ RSpec.describe Widget, type: :model, versioning: true do end describe "#version_at", versioning: true do - let(:widget) { Widget.create! name: "Bob", an_integer: 1 } + let(:widget) { described_class.create! name: "Bob", an_integer: 1 } context "when Timestamp argument is AFTER object has been destroyed" do it "returns nil" do @@ -935,7 +935,7 @@ RSpec.describe Widget, type: :model, versioning: true do end describe "touch", versioning: true do - let(:widget) { Widget.create! name: "Bob", an_integer: 1 } + let(:widget) { described_class.create! name: "Bob", an_integer: 1 } it "creates a version" do expect { widget.touch }.to change { @@ -953,10 +953,10 @@ RSpec.describe Widget, type: :model, versioning: true do end describe ".paper_trail.update_columns", versioning: true do - let(:widget) { Widget.create! name: "Bob", an_integer: 1 } + let(:widget) { described_class.create! name: "Bob", an_integer: 1 } it "creates a version record" do - widget = Widget.create + widget = described_class.create expect(widget.versions.count).to eq(1) widget.paper_trail.update_columns(name: "Bugle") expect(widget.versions.count).to eq(2) @@ -966,10 +966,10 @@ RSpec.describe Widget, type: :model, versioning: true do end describe "#update", versioning: true do - let(:widget) { Widget.create! name: "Bob", an_integer: 1 } + let(:widget) { described_class.create! name: "Bob", an_integer: 1 } it "creates a version record" do - widget = Widget.create + widget = described_class.create assert_equal 1, widget.versions.length widget.update(name: "Bugle") assert_equal 2, widget.versions.length diff --git a/spec/models/wotsit_spec.rb b/spec/models/wotsit_spec.rb index 7f12eb0b..e72fbefa 100644 --- a/spec/models/wotsit_spec.rb +++ b/spec/models/wotsit_spec.rb @@ -4,7 +4,7 @@ require "spec_helper" RSpec.describe Wotsit, versioning: true do it "update! records timestamps" do - wotsit = Wotsit.create!(name: "wotsit") + wotsit = described_class.create!(name: "wotsit") wotsit.update!(name: "changed") reified = wotsit.versions.last.reify expect(reified.created_at).not_to(be_nil) @@ -12,7 +12,7 @@ RSpec.describe Wotsit, versioning: true do end it "update! does not raise error" do - wotsit = Wotsit.create!(name: "name1") + wotsit = described_class.create!(name: "name1") expect { wotsit.update!(name: "name2") }.not_to(raise_error) end end diff --git a/spec/paper_trail/events/base_spec.rb b/spec/paper_trail/events/base_spec.rb index da7e1e62..fd504cec 100644 --- a/spec/paper_trail/events/base_spec.rb +++ b/spec/paper_trail/events/base_spec.rb @@ -9,7 +9,7 @@ module PaperTrail context "with a new record" do it "returns true" do g = Gadget.new(created_at: Time.current) - event = PaperTrail::Events::Base.new(g, false) + event = described_class.new(g, false) expect(event.changed_notably?).to eq(true) end end @@ -18,14 +18,14 @@ module PaperTrail it "only acknowledges non-ignored attrs" do gadget = Gadget.create!(created_at: Time.current) gadget.name = "Wrench" - event = PaperTrail::Events::Base.new(gadget, false) + event = described_class.new(gadget, false) expect(event.changed_notably?).to eq(true) end it "does not acknowledge ignored attr (brand)" do gadget = Gadget.create!(created_at: Time.current) gadget.brand = "Acme" - event = PaperTrail::Events::Base.new(gadget, false) + event = described_class.new(gadget, false) expect(event.changed_notably?).to eq(false) end end @@ -35,7 +35,7 @@ module PaperTrail gadget = Gadget.create!(created_at: Time.current) gadget.name = "Wrench" gadget.updated_at = Time.current - event = PaperTrail::Events::Base.new(gadget, false) + event = described_class.new(gadget, false) expect(event.changed_notably?).to eq(true) end @@ -43,7 +43,7 @@ module PaperTrail gadget = Gadget.create!(created_at: Time.current) gadget.brand = "Acme" gadget.updated_at = Time.current - event = PaperTrail::Events::Base.new(gadget, false) + event = described_class.new(gadget, false) expect(event.changed_notably?).to eq(false) end end @@ -53,7 +53,7 @@ module PaperTrail it "returns a hash lacking the skipped attribute" do # Skipper has_paper_trail(..., skip: [:another_timestamp]) skipper = Skipper.create!(another_timestamp: Time.current) - event = PaperTrail::Events::Base.new(skipper, false) + event = described_class.new(skipper, false) attributes = event.send(:nonskipped_attributes_before_change, false) expect(attributes).not_to have_key("another_timestamp") end diff --git a/spec/paper_trail/events/destroy_spec.rb b/spec/paper_trail/events/destroy_spec.rb index c63accdd..7a6340a5 100644 --- a/spec/paper_trail/events/destroy_spec.rb +++ b/spec/paper_trail/events/destroy_spec.rb @@ -11,14 +11,14 @@ module PaperTrail name: "Carter", path_to_stardom: "Mexican radio" ) - data = PaperTrail::Events::Destroy.new(carter, true).data + data = described_class.new(carter, true).data expect(data[:item_type]).to eq("Family::Family") expect(data[:item_subtype]).to eq("Family::CelebrityFamily") end context "with skipper" do let(:skipper) { Skipper.create!(another_timestamp: Time.current) } - let(:data) { PaperTrail::Events::Destroy.new(skipper, false).data } + let(:data) { described_class.new(skipper, false).data } it "includes `object` without skipped attributes" do object = YAML.load(data[:object]) diff --git a/spec/paper_trail/events/update_spec.rb b/spec/paper_trail/events/update_spec.rb index e4133ba4..06942cdf 100644 --- a/spec/paper_trail/events/update_spec.rb +++ b/spec/paper_trail/events/update_spec.rb @@ -13,7 +13,7 @@ module PaperTrail path_to_stardom: "Mexican radio" ) carter.path_to_stardom = "Johnny" - data = PaperTrail::Events::Update.new(carter, false, false, nil).data + data = described_class.new(carter, false, false, nil).data expect(data[:object_changes]).to eq( <<~YAML --- @@ -32,7 +32,7 @@ module PaperTrail path_to_stardom: "Mexican radio" ) carter.path_to_stardom = "Johnny" - data = PaperTrail::Events::Update.new(carter, false, true, nil).data + data = described_class.new(carter, false, true, nil).data expect(data[:object_changes]).to be_nil end end From ff9e0abfb5da378aaeacf9c8584956ddbe5c095a Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Mon, 25 Oct 2021 12:05:14 -0400 Subject: [PATCH 08/24] Lint: organize configuration of excluded files --- .rubocop.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index ecf9d481..13419b5e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -14,9 +14,10 @@ inherit_from: .rubocop_todo.yml # - Only include permanent config; temporary goes in .rubocop_todo.yml AllCops: + # Generated files, like schema.rb, are out of our control. Exclude: - - gemfiles/vendor/bundle/**/* # This dir only shows up on travis ¯\_(ツ)_/¯ - - spec/dummy_app/db/schema.rb # Generated, out of our control + - gemfiles/* + - spec/dummy_app/db/schema.rb # Enable pending cops so we can adopt the code before they are switched on. NewCops: enable @@ -24,10 +25,6 @@ AllCops: # See "Lowest supported ruby version" in CONTRIBUTING.md TargetRubyVersion: 2.6 -Bundler/OrderedGems: - Exclude: - - gemfiles/* # generated by Appraisal - Layout/ArgumentAlignment: EnforcedStyle: with_fixed_indentation From ba7ad96215cb2df7d5369cf0270056d0b3866de2 Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Mon, 25 Oct 2021 12:06:55 -0400 Subject: [PATCH 09/24] Lint: remove redundant config `Enabled: true` cops are redundant after `NewCops: enable` --- .rubocop.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 13419b5e..68a54611 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -56,20 +56,11 @@ Layout/MultilineOperationIndentation: Layout/ParameterAlignment: EnforcedStyle: with_fixed_indentation -Layout/SpaceAroundMethodCallOperator: - Enabled: true - # Use exactly one space on each side of an operator. Do not align operators # because it makes the code harder to edit, and makes lines unnecessarily long. Layout/SpaceAroundOperators: AllowForAlignment: false -Lint/RaiseException: - Enabled: true - -Lint/StructNewOverride: - Enabled: true - # Migrations often contain long up/down methods, and extracting smaller methods # from these is of questionable value. Metrics/AbcSize: From 21f370e9d269ef1fb9b6147917c051f2dcb5529b Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Mon, 25 Oct 2021 12:17:27 -0400 Subject: [PATCH 10/24] Lint: defunct config --- .rubocop.yml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 68a54611..226b6498 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -90,12 +90,9 @@ Naming/FileName: - Appraisals # Heredocs are usually assigned to a variable or constant, which already has a -# name, so naming the heredoc doesn't add much value. Feel free to name -# heredocs that are used as anonymous values (not a variable, constant, or -# named parameter). -# -# All heredocs containing SQL should be named SQL, to support editor syntax -# highlighting. +# name, so naming the delimiter doesn't add much value unless doing so improves +# syntax highlighting. For example, all heredocs containing SQL should be named +# SQL, to support editor syntax highlighting. Naming/HeredocDelimiterNaming: Enabled: false @@ -149,10 +146,6 @@ Style/BlockDelimiters: Style/DoubleNegation: Enabled: false -# This cop is unimportant in this repo. -Style/ExponentialNotation: - Enabled: false - # Avoid annotated tokens except in desperately complicated format strings. # In 99% of format strings they actually make it less readable. Style/FormatStringToken: From cb0af4f2422ccdcc9c20b6a73253e3013e792cdf Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Mon, 25 Oct 2021 13:10:10 -0400 Subject: [PATCH 11/24] Extract private method: Events::Base#evaluate_only --- lib/paper_trail/events/base.rb | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/paper_trail/events/base.rb b/lib/paper_trail/events/base.rb index 58fb259b..e11194db 100644 --- a/lib/paper_trail/events/base.rb +++ b/lib/paper_trail/events/base.rb @@ -116,6 +116,20 @@ module PaperTrail @changes_in_latest_version ||= load_changes_in_latest_version end + # @api private + def evaluate_only + only = @record.paper_trail_options[:only].dup + # Remove Hash arguments and then evaluate whether the attributes (the + # keys of the hash) should also get pushed into the collection. + only.delete_if do |obj| + obj.is_a?(Hash) && + obj.each { |attr, condition| + only << attr if condition.respond_to?(:call) && condition.call(@record) + } + end + only + end + # An attributed is "ignored" if it is listed in the `:ignore` option # and/or the `:skip` option. Returns true if an ignored attribute has # changed. @@ -207,16 +221,9 @@ module PaperTrail def notably_changed # Memoized to reduce memory usage @notably_changed ||= begin - only = @record.paper_trail_options[:only].dup - # Remove Hash arguments and then evaluate whether the attributes (the - # keys of the hash) should also get pushed into the collection. - only.delete_if do |obj| - obj.is_a?(Hash) && - obj.each { |attr, condition| - only << attr if condition.respond_to?(:call) && condition.call(@record) - } - end - only.empty? ? changed_and_not_ignored : (changed_and_not_ignored & only) + only = evaluate_only + cani = changed_and_not_ignored + only.empty? ? cani : (cani & only) end end From 0ed023ccdeb3440ea294cbe10a1cd09577108dec Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Mon, 25 Oct 2021 13:10:43 -0400 Subject: [PATCH 12/24] Extract private method: Events::Base#metadatum_from_model_method --- lib/paper_trail/events/base.rb | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/paper_trail/events/base.rb b/lib/paper_trail/events/base.rb index e11194db..f50aa9ec 100644 --- a/lib/paper_trail/events/base.rb +++ b/lib/paper_trail/events/base.rb @@ -196,20 +196,28 @@ module PaperTrail if value.respond_to?(:call) value.call(@record) elsif value.is_a?(Symbol) && @record.respond_to?(value, true) - # If it is an attribute that is changing in an existing object, - # be sure to grab the current version. - if event != "create" && - @record.has_attribute?(value) && - attribute_changed_in_latest_version?(value) - attribute_in_previous_version(value, false) - else - @record.send(value) - end + metadatum_from_model_method(event, value) else value end end + # The model method can either be an attribute or a non-attribute method. + # + # If it is an attribute that is changing in an existing object, + # be sure to grab the correct version. + # + # @api private + def metadatum_from_model_method(event, method) + if event != "create" && + @record.has_attribute?(method) && + attribute_changed_in_latest_version?(method) + attribute_in_previous_version(method, false) + else + @record.send(method) + end + end + # @api private def notable_changes changes_in_latest_version.delete_if { |k, _v| From 870f01d244d85eca412ce18fd06347ff54133e55 Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Mon, 25 Oct 2021 13:11:32 -0400 Subject: [PATCH 13/24] Extract private method: Events::Update#merge_object_changes_into --- lib/paper_trail/events/update.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/paper_trail/events/update.rb b/lib/paper_trail/events/update.rb index 8516bc34..89407226 100644 --- a/lib/paper_trail/events/update.rb +++ b/lib/paper_trail/events/update.rb @@ -35,16 +35,21 @@ module PaperTrail if record_object? data[:object] = recordable_object(@is_touch) end - if record_object_changes? - changes = @force_changes.nil? ? notable_changes : @force_changes - data[:object_changes] = prepare_object_changes(changes) - end + merge_object_changes_into(data) merge_item_subtype_into(data) merge_metadata_into(data) end private + # @api private + def merge_object_changes_into(data) + if record_object_changes? + changes = @force_changes.nil? ? notable_changes : @force_changes + data[:object_changes] = prepare_object_changes(changes) + end + end + # `touch` cannot record `object_changes` because rails' `touch` does not # perform dirty-tracking. Specifically, methods from `Dirty`, like # `saved_changes`, return the same values before and after `touch`. From 5e97568f7d671ece99cdf9a73223313e02e4b431 Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Mon, 25 Oct 2021 13:12:13 -0400 Subject: [PATCH 14/24] Extract private method: ModelConfig#append_option_uniquely --- lib/paper_trail/model_config.rb | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/paper_trail/model_config.rb b/lib/paper_trail/model_config.rb index 60cef85a..fda751d7 100644 --- a/lib/paper_trail/model_config.rb +++ b/lib/paper_trail/model_config.rb @@ -40,8 +40,7 @@ module PaperTrail @model_class.after_create { |r| r.paper_trail.record_create if r.paper_trail.save_version? } - return if @model_class.paper_trail_options[:on].include?(:create) - @model_class.paper_trail_options[:on] << :create + append_option_uniquely(:on, :create) end # Adds a callback that records a version before or after a "destroy" event. @@ -49,7 +48,6 @@ module PaperTrail # @api public def on_destroy(recording_order = "before") assert_valid_recording_order_for_on_destroy(recording_order) - @model_class.send( "#{recording_order}_destroy", lambda do |r| @@ -57,9 +55,7 @@ module PaperTrail r.paper_trail.record_destroy(recording_order) end ) - - return if @model_class.paper_trail_options[:on].include?(:destroy) - @model_class.paper_trail_options[:on] << :destroy + append_option_uniquely(:on, :destroy) end # Adds a callback that records a version after an "update" event. @@ -81,8 +77,7 @@ module PaperTrail @model_class.after_update { |r| r.paper_trail.clear_version_instance } - return if @model_class.paper_trail_options[:on].include?(:update) - @model_class.paper_trail_options[:on] << :update + append_option_uniquely(:on, :update) end # Adds a callback that records a version after a "touch" event. @@ -127,6 +122,13 @@ module PaperTrail RAILS_LT_6_0 = ::ActiveRecord.gem_version < ::Gem::Version.new("6.0.0") private_constant :RAILS_LT_6_0 + # @api private + def append_option_uniquely(option, value) + collection = @model_class.paper_trail_options.fetch(option) + return if collection.include?(value) + collection << value + end + # Raises an error if the provided class is an `abstract_class`. # @api private def assert_concrete_activerecord_class(class_name) From a1a9c0c00f566cba26505b95743cc39164fc8379 Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Mon, 25 Oct 2021 13:12:53 -0400 Subject: [PATCH 15/24] Extract private method: ModelConfig#event_attribute_option --- lib/paper_trail/model_config.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/paper_trail/model_config.rb b/lib/paper_trail/model_config.rb index fda751d7..20e6e862 100644 --- a/lib/paper_trail/model_config.rb +++ b/lib/paper_trail/model_config.rb @@ -207,6 +207,14 @@ module PaperTrail options end + # Process an `ignore`, `skip`, or `only` option. + def event_attribute_option(option_name) + [@model_class.paper_trail_options[option_name]]. + flatten. + compact. + map { |attr| attr.is_a?(Hash) ? attr.stringify_keys : attr.to_s } + end + def get_versions_scope(options) options[:versions][:scope] || -> { order(model.timestamp_sort_order) } end @@ -241,12 +249,8 @@ module PaperTrail @model_class.paper_trail_options = options.dup %i[ignore skip only].each do |k| - @model_class.paper_trail_options[k] = [@model_class.paper_trail_options[k]]. - flatten. - compact. - map { |attr| attr.is_a?(Hash) ? attr.stringify_keys : attr.to_s } + @model_class.paper_trail_options[k] = event_attribute_option(k) end - @model_class.paper_trail_options[:meta] ||= {} end end From d90ba94540eac2b1717d98c0b14e9a2d6c8061c6 Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Mon, 25 Oct 2021 13:13:12 -0400 Subject: [PATCH 16/24] Extract local variable --- lib/paper_trail/version_concern.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/paper_trail/version_concern.rb b/lib/paper_trail/version_concern.rb index 1c5e5244..fc6181c9 100644 --- a/lib/paper_trail/version_concern.rb +++ b/lib/paper_trail/version_concern.rb @@ -376,10 +376,11 @@ module PaperTrail # # @api private def version_limit - if limit_option?(item.class) - item.class.paper_trail_options[:limit] - elsif base_class_limit_option?(item.class) - item.class.base_class.paper_trail_options[:limit] + klass = item.class + if limit_option?(klass) + klass.paper_trail_options[:limit] + elsif base_class_limit_option?(klass) + klass.base_class.paper_trail_options[:limit] else PaperTrail.config.version_limit end From 58ec764db13ba0c5aefa71af865111115b42f261 Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Mon, 25 Oct 2021 12:18:34 -0400 Subject: [PATCH 17/24] Lint: ABCSize et. al. --- .rubocop_todo.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 22d8b95e..a83dd96a 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -6,21 +6,6 @@ # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 5 -# Configuration parameters: IgnoredMethods, CountRepeatedAttributes. -Metrics/AbcSize: - Max: 17.5 # Goal: 17, the default - -# Offense count: 1 -# Configuration parameters: IgnoredMethods. -Metrics/CyclomaticComplexity: - Max: 8 # Goal: 7, the default - -# Offense count: 1 -# Configuration parameters: IgnoredMethods. -Metrics/PerceivedComplexity: - Max: 9 # Goal: 8, the default - # Offense count: 56 # Cop supports --auto-correct. Rails/ApplicationRecord: From 94e167b5ac95b2ead6999f88397dad01264d0cb5 Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Sat, 27 Nov 2021 22:05:13 -0500 Subject: [PATCH 18/24] Touch should honour if and unless options (#1354) * Honour if: and unless: options in on_touch The on_touch callback was not honouring the if: or unless: options that were set in the model config. * Fix some test descriptions These tests were actually testing the opposite of what they said they were testing. * Doc: changelog entry for #1349 [ci skip] Co-authored-by: Nigel Williams --- CHANGELOG.md | 3 ++- lib/paper_trail/model_config.rb | 12 +++++++----- spec/models/translation_spec.rb | 28 ++++++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cc7c9c6..e255068e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,8 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/). ### Added -- None +- [#1349](https://github.com/paper-trail-gem/paper_trail/pull/1349) - + `if:` and `unless:` work with `touch` events now. ### Fixed diff --git a/lib/paper_trail/model_config.rb b/lib/paper_trail/model_config.rb index 20e6e862..41bbcbca 100644 --- a/lib/paper_trail/model_config.rb +++ b/lib/paper_trail/model_config.rb @@ -91,11 +91,13 @@ module PaperTrail # @api public def on_touch @model_class.after_touch { |r| - r.paper_trail.record_update( - force: RAILS_LT_6_0, - in_after_callback: true, - is_touch: true - ) + if r.paper_trail.save_version? + r.paper_trail.record_update( + force: RAILS_LT_6_0, + in_after_callback: true, + is_touch: true + ) + end } end diff --git a/spec/models/translation_spec.rb b/spec/models/translation_spec.rb index 51413d5d..3f3cf891 100644 --- a/spec/models/translation_spec.rb +++ b/spec/models/translation_spec.rb @@ -24,6 +24,14 @@ RSpec.describe Translation, type: :model, versioning: true do expect(PaperTrail::Version.count).to(eq(0)) end end + + context "when after touch" do + it "not change the number of versions" do + translation = described_class.create!(headline: "Headline") + translation.touch + expect(PaperTrail::Version.count).to(eq(0)) + end + end end context "with US translations" do @@ -44,6 +52,15 @@ RSpec.describe Translation, type: :model, versioning: true do translation.update(content: "Content") expect(PaperTrail::Version.count).to(eq(0)) end + + it "touch does not change the number of versions" do + translation = described_class.new(headline: "Headline") + translation.language_code = "US" + translation.type = "DRAFT" + translation.save! + translation.touch + expect(PaperTrail::Version.count).to(eq(0)) + end end context "with non-drafts" do @@ -52,14 +69,21 @@ RSpec.describe Translation, type: :model, versioning: true do expect(PaperTrail::Version.count).to(eq(1)) end - it "update does not change the number of versions" do + it "update changes the number of versions" do translation = described_class.create!(headline: "Headline", language_code: "US") translation.update(content: "Content") expect(PaperTrail::Version.count).to(eq(2)) expect(translation.versions.size).to(eq(2)) end - it "destroy does not change the number of versions" do + it "touch changes the number of versions" do + translation = described_class.create!(headline: "Headline", language_code: "US") + translation.touch + expect(PaperTrail::Version.count).to(eq(2)) + expect(translation.versions.size).to(eq(2)) + end + + it "destroy changes the number of versions" do translation = described_class.new(headline: "Headline") translation.language_code = "US" translation.save! From ccb1b9a5631d2d3c704af950c24e597aa7bd7a80 Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Sat, 27 Nov 2021 22:13:56 -0500 Subject: [PATCH 19/24] Test: rename misleading column --- spec/dummy_app/app/models/translation.rb | 6 +----- .../db/migrate/20110208155312_set_up_test_tables.rb | 4 ++-- spec/models/translation_spec.rb | 6 +++--- spec/spec_helper.rb | 2 +- spec/support/paper_trail_spec_migrator.rb | 2 +- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/spec/dummy_app/app/models/translation.rb b/spec/dummy_app/app/models/translation.rb index cdcc3b3b..b1c00e5e 100644 --- a/spec/dummy_app/app/models/translation.rb +++ b/spec/dummy_app/app/models/translation.rb @@ -2,12 +2,8 @@ # Demonstrates the `if` and `unless` configuration options. class Translation < ActiveRecord::Base - # Has a `type` column, but it's not used for STI. - # TODO: rename column - self.inheritance_column = nil - has_paper_trail( if: proc { |t| t.language_code == "US" }, - unless: proc { |t| t.type == "DRAFT" } + unless: proc { |t| t.draft_status == "DRAFT" } ) 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 9505b581..82e28753 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 @@ -249,10 +249,10 @@ class SetUpTestTables < ::ActiveRecord::Migration::Current end create_table :translations, force: true do |t| - t.string :headline t.string :content + t.string :draft_status + t.string :headline t.string :language_code - t.string :type end create_table :gadgets, force: true do |t| diff --git a/spec/models/translation_spec.rb b/spec/models/translation_spec.rb index 3f3cf891..23bb1450 100644 --- a/spec/models/translation_spec.rb +++ b/spec/models/translation_spec.rb @@ -39,7 +39,7 @@ RSpec.describe Translation, type: :model, versioning: true do it "creation does not change the number of versions" do translation = described_class.new(headline: "Headline") translation.language_code = "US" - translation.type = "DRAFT" + translation.draft_status = "DRAFT" translation.save! expect(PaperTrail::Version.count).to(eq(0)) end @@ -47,7 +47,7 @@ RSpec.describe Translation, type: :model, versioning: true do it "update does not change the number of versions" do translation = described_class.new(headline: "Headline") translation.language_code = "US" - translation.type = "DRAFT" + translation.draft_status = "DRAFT" translation.save! translation.update(content: "Content") expect(PaperTrail::Version.count).to(eq(0)) @@ -56,7 +56,7 @@ RSpec.describe Translation, type: :model, versioning: true do it "touch does not change the number of versions" do translation = described_class.new(headline: "Headline") translation.language_code = "US" - translation.type = "DRAFT" + translation.draft_status = "DRAFT" translation.save! translation.touch expect(PaperTrail::Version.count).to(eq(0)) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8cdc6a04..97cc8fb3 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -53,7 +53,7 @@ end # in `dummy_app/config/*`. By consolidating it here, # # - It can better be understood, and documented in one place -# - It can more closely resememble a conventional app boot. For example, loading +# - It can more closely resemble a conventional app boot. For example, loading # gems (like rspec-rails) _before_ loading the app. # First, `config/boot.rb` would add gems to $LOAD_PATH. diff --git a/spec/support/paper_trail_spec_migrator.rb b/spec/support/paper_trail_spec_migrator.rb index edd8f430..317dd381 100644 --- a/spec/support/paper_trail_spec_migrator.rb +++ b/spec/support/paper_trail_spec_migrator.rb @@ -18,7 +18,7 @@ class PaperTrailSpecMigrator @migrations_path = dummy_app_migrations_dir end - # Looks like the API for programatically running migrations will change + # Looks like the API for programmatically running migrations will change # in rails 5.2. This is an undocumented change, AFAICT. Then again, # how many people use the programmatic interface? Most people probably # just use rake. Maybe we're doing it wrong. From f343ff70eb0d93dce89155c8fb031f3fbd3e2738 Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Sat, 27 Nov 2021 23:21:27 -0500 Subject: [PATCH 20/24] Test: Rewrite the query examples in version_spec.rb The "query" specs (for methods such as where_object) have bothered me for years because they changed the datatype of database columns _during the test suite_. I'm happy to have addressed that concern. The schema no longer changes during the suite. I'm not thrilled (never have been) with RSpec's "Shared Examples" feature. My primary complaint is that it breaks relative constant lookup. For example, I must write `::PaperTrail::UnsupportedColumnType` instead of the relative `UnsupportedColumnType`. Secondarily, I don't like having two files open in order to understand a test. So, in the future I may break `shared_examples/queries.rb` into smaller pieces, or find a completely different technique of "example reuse". --- spec/dummy_app/app/models/fruit.rb | 3 +- spec/dummy_app/app/models/vegetable.rb | 8 + spec/dummy_app/app/versions/jsonb_version.rb | 5 + .../20110208155312_set_up_test_tables.rb | 30 +- spec/models/version_spec.rb | 442 +----------------- spec/spec_helper.rb | 2 +- spec/support/shared_examples/queries.rb | 388 +++++++++++++++ 7 files changed, 444 insertions(+), 434 deletions(-) create mode 100644 spec/dummy_app/app/models/vegetable.rb create mode 100644 spec/dummy_app/app/versions/jsonb_version.rb create mode 100644 spec/support/shared_examples/queries.rb diff --git a/spec/dummy_app/app/models/fruit.rb b/spec/dummy_app/app/models/fruit.rb index 1237612c..ce119127 100644 --- a/spec/dummy_app/app/models/fruit.rb +++ b/spec/dummy_app/app/models/fruit.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true +# See also `Vegetable` which uses `JsonbVersion`. class Fruit < ActiveRecord::Base - if ENV["DB"] == "postgres" || JsonVersion.table_exists? + if ENV["DB"] == "postgres" has_paper_trail versions: { class_name: "JsonVersion" } end end diff --git a/spec/dummy_app/app/models/vegetable.rb b/spec/dummy_app/app/models/vegetable.rb new file mode 100644 index 00000000..56659373 --- /dev/null +++ b/spec/dummy_app/app/models/vegetable.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# See also `Fruit` which uses `JsonVersion`. +class Vegetable < ActiveRecord::Base + if ENV["DB"] == "postgres" + has_paper_trail versions: { class_name: "JsonbVersion" } + end +end diff --git a/spec/dummy_app/app/versions/jsonb_version.rb b/spec/dummy_app/app/versions/jsonb_version.rb new file mode 100644 index 00000000..eaf83bdf --- /dev/null +++ b/spec/dummy_app/app/versions/jsonb_version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class JsonbVersion < PaperTrail::Version + self.table_name = "jsonb_versions" +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 82e28753..9731c85b 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 @@ -128,16 +128,19 @@ class SetUpTestTables < ::ActiveRecord::Migration::Current add_index :no_object_versions, %i[item_type item_id] if ENV["DB"] == "postgres" - create_table :json_versions, force: true do |t| - t.string :item_type, null: false - t.integer :item_id, null: false - t.string :event, null: false - t.string :whodunnit - t.json :object - t.json :object_changes - t.datetime :created_at, limit: 6 + %w[json jsonb].each do |j| + table_name = j + "_versions" + create_table table_name, force: true do |t| + t.string :item_type, null: false + t.integer :item_id, null: false + t.string :event, null: false + t.string :whodunnit + t.public_send j, :object + t.public_send j, :object_changes + t.datetime :created_at, limit: 6 + end + add_index table_name, %i[item_type item_id] end - add_index :json_versions, %i[item_type item_id] end create_table :not_on_updates, force: true do |t| @@ -277,8 +280,9 @@ class SetUpTestTables < ::ActiveRecord::Migration::Current end create_table :fruits, force: true do |t| - t.string :name t.string :color + t.integer :mass + t.string :name end create_table :boolits, force: true do |t| @@ -358,6 +362,12 @@ class SetUpTestTables < ::ActiveRecord::Migration::Current t.integer :parent_id t.integer :partner_id end + + create_table :vegetables, force: true do |t| + t.string :color + t.integer :mass + t.string :name + end end def down diff --git a/spec/models/version_spec.rb b/spec/models/version_spec.rb index 03bfb976..4263476b 100644 --- a/spec/models/version_spec.rb +++ b/spec/models/version_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "spec_helper" +require "support/shared_examples/queries" module PaperTrail ::RSpec.describe Version, type: :model do @@ -108,430 +109,27 @@ module PaperTrail end end - context "when changing the data type of database columns on the fly" do - # TODO: Changing the data type of these database columns in the middle - # of the test suite adds a fair amount of complexity. Is there a better - # way? We already have a `json_versions` table in our tests, maybe we - # could use that and add a `jsonb_versions` table? - column_overrides = [false] - if ENV["DB"] == "postgres" - column_overrides += %w[json jsonb] + context "with text columns", versioning: true do + include_examples "queries", :text, ::Widget, :an_integer + end + + if ENV["DB"] == "postgres" + context "with json columns", versioning: true do + include_examples( + "queries", + :json, + ::Fruit, # uses JsonVersion + :mass + ) end - column_overrides.shuffle.each do |column_datatype_override| - context "with a #{column_datatype_override || 'text'} column" do - let(:widget) { Widget.new } - let(:name) { FFaker::Name.first_name } - let(:int) { column_datatype_override ? 1 : rand(2..6) } - - before do - if column_datatype_override - ActiveRecord::Base.connection.execute("SAVEPOINT pgtest;") - %w[object object_changes].each do |column| - ActiveRecord::Base.connection.execute( - "ALTER TABLE versions DROP COLUMN #{column};" - ) - ActiveRecord::Base.connection.execute( - "ALTER TABLE versions ADD COLUMN #{column} #{column_datatype_override};" - ) - end - described_class.reset_column_information - end - end - - after do - PaperTrail.serializer = PaperTrail::Serializers::YAML - - if column_datatype_override - ActiveRecord::Base.connection.execute("ROLLBACK TO SAVEPOINT pgtest;") - described_class.reset_column_information - end - end - - describe "#where_attribute_changes", versioning: true do - it "requires its argument to be a string or a symbol" do - expect { - described_class.where_attribute_changes({}) - }.to raise_error(ArgumentError) - expect { - described_class.where_attribute_changes([]) - }.to raise_error(ArgumentError) - end - - context "with object_changes_adapter configured" do - after do - PaperTrail.config.object_changes_adapter = nil - end - - it "calls the adapter's where_attribute_changes method" do - adapter = instance_spy("CustomObjectChangesAdapter") - bicycle = Bicycle.create!(name: "abc") - bicycle.update!(name: "xyz") - - allow(adapter).to( - receive(:where_attribute_changes).with(described_class, :name) - ).and_return([bicycle.versions[0], bicycle.versions[1]]) - - PaperTrail.config.object_changes_adapter = adapter - expect( - bicycle.versions.where_attribute_changes(:name) - ).to match_array([bicycle.versions[0], bicycle.versions[1]]) - expect(adapter).to have_received(:where_attribute_changes) - end - - it "defaults to the original behavior" do - adapter = Class.new.new - PaperTrail.config.object_changes_adapter = adapter - bicycle = Bicycle.create!(name: "abc") - bicycle.update!(name: "xyz") - - if column_datatype_override - expect( - bicycle.versions.where_attribute_changes(:name) - ).to match_array([bicycle.versions[0], bicycle.versions[1]]) - else - expect { - bicycle.versions.where_attribute_changes(:name) - }.to raise_error( - UnsupportedColumnType, - "where_attribute_changes expected json or jsonb column, got text" - ) - end - end - end - - # Only test json and jsonb columns. where_attribute_changes does - # not support text columns. - if column_datatype_override - it "locates versions according to their object_changes contents" do - widget.update!(name: "foobar", an_integer: 100) - widget.update!(an_integer: 17) - - expect( - widget.versions.where_attribute_changes(:name) - ).to eq([widget.versions[0]]) - expect( - widget.versions.where_attribute_changes("an_integer") - ).to eq([widget.versions[0], widget.versions[1]]) - expect( - widget.versions.where_attribute_changes(:a_float) - ).to eq([]) - end - else - it "raises error" do - expect { - widget.versions.where_attribute_changes(:name).to_a - }.to raise_error( - UnsupportedColumnType, - "where_attribute_changes expected json or jsonb column, got text" - ) - end - end - end - - describe "#where_object", versioning: true do - it "requires its argument to be a Hash" do - widget.update!(name: name, an_integer: int) - widget.update!(name: "foobar", an_integer: 100) - widget.update!(name: FFaker::Name.last_name, an_integer: 15) - expect { - described_class.where_object(:foo) - }.to raise_error(ArgumentError) - expect { - described_class.where_object([]) - }.to raise_error(ArgumentError) - end - - context "with YAML serializer" do - it "locates versions according to their `object` contents" do - expect(PaperTrail.serializer).to be PaperTrail::Serializers::YAML - widget.update!(name: name, an_integer: int) - widget.update!(name: "foobar", an_integer: 100) - widget.update!(name: FFaker::Name.last_name, an_integer: 15) - expect( - described_class.where_object(an_integer: int) - ).to eq([widget.versions[1]]) - expect( - described_class.where_object(name: name) - ).to eq([widget.versions[1]]) - expect( - described_class.where_object(an_integer: 100) - ).to eq([widget.versions[2]]) - end - end - - context "with JSON serializer" do - it "locates versions according to their `object` contents" do - PaperTrail.serializer = PaperTrail::Serializers::JSON - expect(PaperTrail.serializer).to be PaperTrail::Serializers::JSON - widget.update!(name: name, an_integer: int) - widget.update!(name: "foobar", an_integer: 100) - widget.update!(name: FFaker::Name.last_name, an_integer: 15) - expect( - described_class.where_object(an_integer: int) - ).to eq([widget.versions[1]]) - expect( - described_class.where_object(name: name) - ).to eq([widget.versions[1]]) - expect( - described_class.where_object(an_integer: 100) - ).to eq([widget.versions[2]]) - end - end - end - - describe "#where_object_changes", versioning: true do - it "requires its argument to be a Hash" do - expect { - described_class.where_object_changes(:foo) - }.to raise_error(ArgumentError) - expect { - described_class.where_object_changes([]) - }.to raise_error(ArgumentError) - end - - context "with object_changes_adapter configured" do - after do - PaperTrail.config.object_changes_adapter = nil - end - - it "calls the adapter's where_object_changes method" do - adapter = instance_spy("CustomObjectChangesAdapter") - bicycle = Bicycle.create!(name: "abc") - allow(adapter).to( - receive(:where_object_changes).with(described_class, name: "abc") - ).and_return(bicycle.versions[0..1]) - PaperTrail.config.object_changes_adapter = adapter - expect( - bicycle.versions.where_object_changes(name: "abc") - ).to match_array(bicycle.versions[0..1]) - expect(adapter).to have_received(:where_object_changes) - end - - it "defaults to the original behavior" do - adapter = Class.new.new - PaperTrail.config.object_changes_adapter = adapter - bicycle = Bicycle.create!(name: "abc") - if column_datatype_override - expect( - bicycle.versions.where_object_changes(name: "abc") - ).to match_array(bicycle.versions[0..1]) - else - expect { - bicycle.versions.where_object_changes(name: "abc") - }.to raise_error( - UnsupportedColumnType, - "where_object_changes expected json or jsonb column, got text" - ) - end - end - end - - # Only test json and jsonb columns. where_object_changes no longer - # supports text columns. - if column_datatype_override - it "locates versions according to their object_changes contents" do - widget.update!(name: name, an_integer: 0) - widget.update!(name: "foobar", an_integer: 100) - widget.update!(name: FFaker::Name.last_name, an_integer: int) - expect( - widget.versions.where_object_changes(name: name) - ).to eq(widget.versions[0..1]) - expect( - widget.versions.where_object_changes(an_integer: 100) - ).to eq(widget.versions[1..2]) - expect( - widget.versions.where_object_changes(an_integer: int) - ).to eq([widget.versions.last]) - expect( - widget.versions.where_object_changes(an_integer: 100, name: "foobar") - ).to eq(widget.versions[1..2]) - end - else - it "raises error" do - expect { - widget.versions.where_object_changes(name: "foo").to_a - }.to raise_error( - UnsupportedColumnType, - "where_object_changes expected json or jsonb column, got text" - ) - end - end - end - - describe "#where_object_changes_from", versioning: true do - it "requires its argument to be a Hash" do - expect { - described_class.where_object_changes_from(:foo) - }.to raise_error(ArgumentError) - expect { - described_class.where_object_changes_from([]) - }.to raise_error(ArgumentError) - end - - context "with object_changes_adapter configured" do - after do - PaperTrail.config.object_changes_adapter = nil - end - - it "calls the adapter's where_object_changes_from method" do - adapter = instance_spy("CustomObjectChangesAdapter") - bicycle = Bicycle.create!(name: "abc") - bicycle.update!(name: "xyz") - - allow(adapter).to( - receive(:where_object_changes_from).with(described_class, name: "abc") - ).and_return([bicycle.versions[1]]) - - PaperTrail.config.object_changes_adapter = adapter - expect( - bicycle.versions.where_object_changes_from(name: "abc") - ).to match_array([bicycle.versions[1]]) - expect(adapter).to have_received(:where_object_changes_from) - end - - it "defaults to the original behavior" do - adapter = Class.new.new - PaperTrail.config.object_changes_adapter = adapter - bicycle = Bicycle.create!(name: "abc") - bicycle.update!(name: "xyz") - - if column_datatype_override - expect( - bicycle.versions.where_object_changes_from(name: "abc") - ).to match_array([bicycle.versions[1]]) - else - expect { - bicycle.versions.where_object_changes_from(name: "abc") - }.to raise_error( - UnsupportedColumnType, - "where_object_changes_from expected json or jsonb column, got text" - ) - end - end - end - - # Only test json and jsonb columns. where_object_changes_from does - # not support text columns. - if column_datatype_override - it "locates versions according to their object_changes contents" do - widget.update!(name: name, an_integer: 0) - widget.update!(name: "foobar", an_integer: 100) - widget.update!(name: FFaker::Name.last_name, an_integer: int) - - expect( - widget.versions.where_object_changes_from(name: name) - ).to eq([widget.versions[1]]) - expect( - widget.versions.where_object_changes_from(an_integer: 100) - ).to eq([widget.versions[2]]) - expect( - widget.versions.where_object_changes_from(an_integer: int) - ).to eq([]) - expect( - widget.versions.where_object_changes_from(an_integer: 100, name: "foobar") - ).to eq([widget.versions[2]]) - end - else - it "raises error" do - expect { - widget.versions.where_object_changes_from(name: "foo").to_a - }.to raise_error( - UnsupportedColumnType, - "where_object_changes_from expected json or jsonb column, got text" - ) - end - end - end - - describe "#where_object_changes_to", versioning: true do - it "requires its argument to be a Hash" do - expect { - described_class.where_object_changes_to(:foo) - }.to raise_error(ArgumentError) - expect { - described_class.where_object_changes_to([]) - }.to raise_error(ArgumentError) - end - - context "with object_changes_adapter configured" do - after do - PaperTrail.config.object_changes_adapter = nil - end - - it "calls the adapter's where_object_changes_to method" do - adapter = instance_spy("CustomObjectChangesAdapter") - bicycle = Bicycle.create!(name: "abc") - bicycle.update!(name: "xyz") - - allow(adapter).to( - receive(:where_object_changes_to).with(described_class, name: "xyz") - ).and_return([bicycle.versions[1]]) - - PaperTrail.config.object_changes_adapter = adapter - expect( - bicycle.versions.where_object_changes_to(name: "xyz") - ).to match_array([bicycle.versions[1]]) - expect(adapter).to have_received(:where_object_changes_to) - end - - it "defaults to the original behavior" do - adapter = Class.new.new - PaperTrail.config.object_changes_adapter = adapter - bicycle = Bicycle.create!(name: "abc") - bicycle.update!(name: "xyz") - - if column_datatype_override - expect( - bicycle.versions.where_object_changes_to(name: "xyz") - ).to match_array([bicycle.versions[1]]) - else - expect { - bicycle.versions.where_object_changes_to(name: "xyz") - }.to raise_error( - UnsupportedColumnType, - "where_object_changes_to expected json or jsonb column, got text" - ) - end - end - end - - # Only test json and jsonb columns. where_object_changes_to does - # not support text columns. - if column_datatype_override - it "locates versions according to their object_changes contents" do - widget.update!(name: name, an_integer: 0) - widget.update!(name: "foobar", an_integer: 100) - widget.update!(name: FFaker::Name.last_name, an_integer: int) - - expect( - widget.versions.where_object_changes_to(name: name) - ).to eq([widget.versions[0]]) - expect( - widget.versions.where_object_changes_to(an_integer: 100) - ).to eq([widget.versions[1]]) - expect( - widget.versions.where_object_changes_to(an_integer: int) - ).to eq([widget.versions[2]]) - expect( - widget.versions.where_object_changes_to(an_integer: 100, name: "foobar") - ).to eq([widget.versions[1]]) - expect( - widget.versions.where_object_changes_to(an_integer: -1) - ).to eq([]) - end - else - it "raises error" do - expect { - widget.versions.where_object_changes_to(name: "foo").to_a - }.to raise_error( - UnsupportedColumnType, - "where_object_changes_to expected json or jsonb column, got text" - ) - end - end - end - end + context "with jsonb columns", versioning: true do + include_examples( + "queries", + :jsonb, + ::Vegetable, # uses JsonbVersion + :mass + ) end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 97cc8fb3..aaf56a92 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -7,7 +7,7 @@ require "simplecov" SimpleCov.start do add_filter "spec" end -SimpleCov.minimum_coverage 92.4 +SimpleCov.minimum_coverage(ENV["DB"] == "postgres" ? 97.3 : 92.4) require "byebug" require_relative "support/pt_arel_helpers" diff --git a/spec/support/shared_examples/queries.rb b/spec/support/shared_examples/queries.rb new file mode 100644 index 00000000..7cad5bd1 --- /dev/null +++ b/spec/support/shared_examples/queries.rb @@ -0,0 +1,388 @@ +# frozen_string_literal: true + +RSpec.shared_examples "queries" do |column_type, model, name_of_integer_column| + let(:record) { model.new } + let(:name) { FFaker::Name.first_name } + let(:int) { column_type == :text ? 1 : rand(2..6) } + + after do + PaperTrail.serializer = PaperTrail::Serializers::YAML + end + + describe "#where_attribute_changes", versioning: true do + it "requires its argument to be a string or a symbol" do + expect { + model.paper_trail.version_class.where_attribute_changes({}) + }.to raise_error(ArgumentError) + expect { + model.paper_trail.version_class.where_attribute_changes([]) + }.to raise_error(ArgumentError) + end + + context "with object_changes_adapter configured" do + after do + PaperTrail.config.object_changes_adapter = nil + end + + it "calls the adapter's where_attribute_changes method" do + adapter = instance_spy("CustomObjectChangesAdapter") + bicycle = model.create!(name: "abc") + bicycle.update!(name: "xyz") + + allow(adapter).to( + receive(:where_attribute_changes).with(model.paper_trail.version_class, :name) + ).and_return([bicycle.versions[0], bicycle.versions[1]]) + + PaperTrail.config.object_changes_adapter = adapter + expect( + bicycle.versions.where_attribute_changes(:name) + ).to match_array([bicycle.versions[0], bicycle.versions[1]]) + expect(adapter).to have_received(:where_attribute_changes) + end + + it "defaults to the original behavior" do + adapter = Class.new.new + PaperTrail.config.object_changes_adapter = adapter + bicycle = model.create!(name: "abc") + bicycle.update!(name: "xyz") + + if column_type == :text + expect { + bicycle.versions.where_attribute_changes(:name) + }.to raise_error( + ::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]]) + end + end + end + + if column_type == :text + it "raises error" do + expect { + record.versions.where_attribute_changes(:name).to_a + }.to raise_error( + ::PaperTrail::UnsupportedColumnType, + "where_attribute_changes expected json or jsonb column, got text" + ) + end + else + it "locates versions according to their object_changes contents" do + record.update!(name: "foobar", name_of_integer_column => 100) + record.update!(name_of_integer_column => 17) + + expect( + record.versions.where_attribute_changes(:name) + ).to eq([record.versions[0]]) + expect( + record.versions.where_attribute_changes(name_of_integer_column.to_s) + ).to eq([record.versions[0], record.versions[1]]) + expect(record.class.column_names).to include("color") + expect( + record.versions.where_attribute_changes(:color) + ).to eq([]) + end + end + end + + describe "#where_object", versioning: true 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) + record.update!(name: FFaker::Name.last_name, name_of_integer_column => 15) + expect { + model.paper_trail.version_class.where_object(:foo) + }.to raise_error(ArgumentError) + expect { + model.paper_trail.version_class.where_object([]) + }.to raise_error(ArgumentError) + end + + context "with YAML serializer" do + it "locates versions according to their `object` contents" do + expect(PaperTrail.serializer).to be PaperTrail::Serializers::YAML + record.update!(name: name, name_of_integer_column => int) + record.update!(name: "foobar", name_of_integer_column => 100) + record.update!(name: FFaker::Name.last_name, name_of_integer_column => 15) + expect( + model.paper_trail.version_class.where_object(name_of_integer_column => int) + ).to eq([record.versions[1]]) + expect( + model.paper_trail.version_class.where_object(name: name) + ).to eq([record.versions[1]]) + expect( + model.paper_trail.version_class.where_object(name_of_integer_column => 100) + ).to eq([record.versions[2]]) + end + end + + context "with JSON serializer" do + it "locates versions according to their `object` contents" do + PaperTrail.serializer = PaperTrail::Serializers::JSON + expect(PaperTrail.serializer).to be PaperTrail::Serializers::JSON + record.update!(name: name, name_of_integer_column => int) + record.update!(name: "foobar", name_of_integer_column => 100) + record.update!(name: FFaker::Name.last_name, name_of_integer_column => 15) + expect( + model.paper_trail.version_class.where_object(name_of_integer_column => int) + ).to eq([record.versions[1]]) + expect( + model.paper_trail.version_class.where_object(name: name) + ).to eq([record.versions[1]]) + expect( + model.paper_trail.version_class.where_object(name_of_integer_column => 100) + ).to eq([record.versions[2]]) + end + end + end + + describe "#where_object_changes", versioning: true do + it "requires its argument to be a Hash" do + expect { + model.paper_trail.version_class.where_object_changes(:foo) + }.to raise_error(ArgumentError) + expect { + model.paper_trail.version_class.where_object_changes([]) + }.to raise_error(ArgumentError) + end + + context "with object_changes_adapter configured" do + after do + PaperTrail.config.object_changes_adapter = nil + end + + it "calls the adapter's where_object_changes method" do + adapter = instance_spy("CustomObjectChangesAdapter") + bicycle = model.create!(name: "abc") + allow(adapter).to( + receive(:where_object_changes).with(model.paper_trail.version_class, name: "abc") + ).and_return(bicycle.versions[0..1]) + PaperTrail.config.object_changes_adapter = adapter + expect( + bicycle.versions.where_object_changes(name: "abc") + ).to match_array(bicycle.versions[0..1]) + expect(adapter).to have_received(:where_object_changes) + end + + it "defaults to the original behavior" do + adapter = Class.new.new + PaperTrail.config.object_changes_adapter = adapter + bicycle = model.create!(name: "abc") + if column_type == :text + expect { + bicycle.versions.where_object_changes(name: "abc") + }.to raise_error( + ::PaperTrail::UnsupportedColumnType, + "where_object_changes expected json or jsonb column, got text" + ) + else + expect( + bicycle.versions.where_object_changes(name: "abc") + ).to match_array(bicycle.versions[0..1]) + end + end + end + + if column_type == :text + it "raises error" do + expect { + record.versions.where_object_changes(name: "foo").to_a + }.to raise_error( + ::PaperTrail::UnsupportedColumnType, + "where_object_changes expected json or jsonb column, got text" + ) + end + else + it "locates versions according to their object_changes contents" do + record.update!(name: name, name_of_integer_column => 0) + record.update!(name: "foobar", name_of_integer_column => 100) + record.update!(name: FFaker::Name.last_name, name_of_integer_column => int) + expect( + record.versions.where_object_changes(name: name) + ).to eq(record.versions[0..1]) + expect( + record.versions.where_object_changes(name_of_integer_column => 100) + ).to eq(record.versions[1..2]) + expect( + record.versions.where_object_changes(name_of_integer_column => int) + ).to eq([record.versions.last]) + expect( + record.versions.where_object_changes(name_of_integer_column => 100, name: "foobar") + ).to eq(record.versions[1..2]) + end + end + end + + describe "#where_object_changes_from", versioning: true do + it "requires its argument to be a Hash" do + expect { + model.paper_trail.version_class.where_object_changes_from(:foo) + }.to raise_error(ArgumentError) + expect { + model.paper_trail.version_class.where_object_changes_from([]) + }.to raise_error(ArgumentError) + end + + context "with object_changes_adapter configured" do + after do + PaperTrail.config.object_changes_adapter = nil + end + + it "calls the adapter's where_object_changes_from method" do + adapter = instance_spy("CustomObjectChangesAdapter") + bicycle = model.create!(name: "abc") + bicycle.update!(name: "xyz") + + allow(adapter).to( + receive(:where_object_changes_from).with(model.paper_trail.version_class, name: "abc") + ).and_return([bicycle.versions[1]]) + + PaperTrail.config.object_changes_adapter = adapter + expect( + bicycle.versions.where_object_changes_from(name: "abc") + ).to match_array([bicycle.versions[1]]) + expect(adapter).to have_received(:where_object_changes_from) + end + + it "defaults to the original behavior" do + adapter = Class.new.new + PaperTrail.config.object_changes_adapter = adapter + bicycle = model.create!(name: "abc") + bicycle.update!(name: "xyz") + + if column_type == :text + expect { + bicycle.versions.where_object_changes_from(name: "abc") + }.to raise_error( + ::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]]) + end + end + end + + if column_type == :text + it "raises error" do + expect { + record.versions.where_object_changes_from(name: "foo").to_a + }.to raise_error( + ::PaperTrail::UnsupportedColumnType, + "where_object_changes_from expected json or jsonb column, got text" + ) + end + else + it "locates versions according to their object_changes contents" do + record.update!(name: name, name_of_integer_column => 0) + record.update!(name: "foobar", name_of_integer_column => 100) + record.update!(name: FFaker::Name.last_name, name_of_integer_column => int) + + expect( + record.versions.where_object_changes_from(name: name) + ).to eq([record.versions[1]]) + expect( + record.versions.where_object_changes_from(name_of_integer_column => 100) + ).to eq([record.versions[2]]) + expect( + record.versions.where_object_changes_from(name_of_integer_column => int) + ).to eq([]) + expect( + record.versions.where_object_changes_from(name_of_integer_column => 100, name: "foobar") + ).to eq([record.versions[2]]) + end + end + end + + describe "#where_object_changes_to", versioning: true do + it "requires its argument to be a Hash" do + expect { + model.paper_trail.version_class.where_object_changes_to(:foo) + }.to raise_error(ArgumentError) + expect { + model.paper_trail.version_class.where_object_changes_to([]) + }.to raise_error(ArgumentError) + end + + context "with object_changes_adapter configured" do + after do + PaperTrail.config.object_changes_adapter = nil + end + + it "calls the adapter's where_object_changes_to method" do + adapter = instance_spy("CustomObjectChangesAdapter") + bicycle = model.create!(name: "abc") + bicycle.update!(name: "xyz") + + allow(adapter).to( + receive(:where_object_changes_to).with(model.paper_trail.version_class, name: "xyz") + ).and_return([bicycle.versions[1]]) + + PaperTrail.config.object_changes_adapter = adapter + expect( + bicycle.versions.where_object_changes_to(name: "xyz") + ).to match_array([bicycle.versions[1]]) + expect(adapter).to have_received(:where_object_changes_to) + end + + it "defaults to the original behavior" do + adapter = Class.new.new + PaperTrail.config.object_changes_adapter = adapter + bicycle = model.create!(name: "abc") + bicycle.update!(name: "xyz") + + if column_type == :text + expect { + bicycle.versions.where_object_changes_to(name: "xyz") + }.to raise_error( + ::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]]) + end + end + end + + if column_type == :text + it "raises error" do + expect { + record.versions.where_object_changes_to(name: "foo").to_a + }.to raise_error( + ::PaperTrail::UnsupportedColumnType, + "where_object_changes_to expected json or jsonb column, got text" + ) + end + else + it "locates versions according to their object_changes contents" do + record.update!(name: name, name_of_integer_column => 0) + record.update!(name: "foobar", name_of_integer_column => 100) + record.update!(name: FFaker::Name.last_name, name_of_integer_column => int) + + expect( + record.versions.where_object_changes_to(name: name) + ).to eq([record.versions[0]]) + expect( + record.versions.where_object_changes_to(name_of_integer_column => 100) + ).to eq([record.versions[1]]) + expect( + record.versions.where_object_changes_to(name_of_integer_column => int) + ).to eq([record.versions[2]]) + expect( + record.versions.where_object_changes_to(name_of_integer_column => 100, name: "foobar") + ).to eq([record.versions[1]]) + expect( + record.versions.where_object_changes_to(name_of_integer_column => -1) + ).to eq([]) + end + end + end +end From ace1b1bccd748e1433eda9155dcb9925048fe1ff Mon Sep 17 00:00:00 2001 From: Alfonso Uceda Date: Wed, 29 Dec 2021 09:52:12 +0100 Subject: [PATCH 21/24] Add ruby 3.1 to the matrix --- .github/workflows/test.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 16811db1..c260fdd0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,13 +63,15 @@ jobs: # in case it still produces any deprecation warnings. # # See "Lowest supported ruby version" in CONTRIBUTING.md - ruby: [ '2.6', '2.7', '3.0' ] + ruby: [ '2.6', '2.7', '3.0', '3.1' ] exclude: # rails 5.2 requires ruby < 3.0 # https://github.com/rails/rails/issues/40938 - ruby: '3.0' gemfile: 'rails_5.2' + - ruby: '3.1' + gemfile: 'rails_5.2' steps: - name: Checkout source uses: actions/checkout@v2 From b36ba3216f9b32d247343235d27d3cfbecc4ee24 Mon Sep 17 00:00:00 2001 From: Alfonso Uceda Date: Wed, 29 Dec 2021 10:50:09 +0100 Subject: [PATCH 22/24] Use unsafe_load instead of load --- spec/models/gadget_spec.rb | 6 +++++- spec/models/no_object_spec.rb | 6 +++++- spec/paper_trail/events/destroy_spec.rb | 12 ++++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/spec/models/gadget_spec.rb b/spec/models/gadget_spec.rb index 1c794da2..5c8bd06f 100644 --- a/spec/models/gadget_spec.rb +++ b/spec/models/gadget_spec.rb @@ -35,7 +35,11 @@ RSpec.describe Gadget, type: :model do gadget.update_attribute(:updated_at, Time.current + 1) }.to(change { gadget.versions.size }.by(1)) expect( - YAML.load(gadget.versions.last.object_changes).keys + if ::YAML.respond_to?(:unsafe_load) + YAML.unsafe_load(gadget.versions.last.object_changes).keys + else + YAML.load(gadget.versions.last.object_changes).keys + end ).to eq(["updated_at"]) end end diff --git a/spec/models/no_object_spec.rb b/spec/models/no_object_spec.rb index 85e4dd11..95f62492 100644 --- a/spec/models/no_object_spec.rb +++ b/spec/models/no_object_spec.rb @@ -27,7 +27,11 @@ RSpec.describe NoObject, versioning: true do # New feature: destroy populates object_changes # https://github.com/paper-trail-gem/paper_trail/pull/1123 - h = YAML.load a["object_changes"] + h = if ::YAML.respond_to?(:unsafe_load) + YAML.unsafe_load a["object_changes"] + else + YAML.load a["object_changes"] + end expect(h["id"]).to eq([n.id, nil]) expect(h["letter"]).to eq([n.letter, nil]) expect(h["created_at"][0]).to be_present diff --git a/spec/paper_trail/events/destroy_spec.rb b/spec/paper_trail/events/destroy_spec.rb index 7a6340a5..f75d3ef5 100644 --- a/spec/paper_trail/events/destroy_spec.rb +++ b/spec/paper_trail/events/destroy_spec.rb @@ -21,7 +21,11 @@ module PaperTrail let(:data) { described_class.new(skipper, false).data } it "includes `object` without skipped attributes" do - object = YAML.load(data[:object]) + object = if ::YAML.respond_to?(:unsafe_load) + YAML.unsafe_load(data[:object]) + else + YAML.load(data[:object]) + end expect(object["id"]).to eq(skipper.id) expect(object).to have_key("updated_at") expect(object).to have_key("created_at") @@ -29,7 +33,11 @@ module PaperTrail end it "includes `object_changes` without skipped and ignored attributes" do - changes = YAML.load(data[:object_changes]) + changes = if ::YAML.respond_to?(:unsafe_load) + YAML.unsafe_load(data[:object_changes]) + else + YAML.load(data[:object_changes]) + end expect(changes["id"]).to eq([skipper.id, nil]) expect(changes["updated_at"][0]).to be_present expect(changes["updated_at"][1]).to be_nil From c10a8573f1080d8835c5efca1411235135bcddc9 Mon Sep 17 00:00:00 2001 From: David Furber Date: Fri, 21 Jan 2022 00:10:53 -0500 Subject: [PATCH 23/24] Rails 7.0 Compatibility (#1365) * Make paper_trail work with Rails 7.0 * from class_methods do back to module ClassMethods * add spec for PostgresArraySerializer to boost coverage * lint the spec for PostgresArraySerializer * lint the spec for PostgresArraySerializer again * and now make that linted spec pass again * test object change scopes a bit * round out json and jsonb testing of object scopes * test some other code paths to increase coverage * linting * linting * mess with yaml loading in test * oddball cop for double quotes * use Rails public API for compatibility rather than instance_variable_set Co-authored-by: dfurber --- Appraisals | 5 +++ CHANGELOG.md | 2 + Rakefile | 13 +++--- gemfiles/rails_7.0.gemfile | 8 ++++ lib/paper_trail.rb | 2 + .../cast_attribute_serializer.rb | 6 +++ lib/paper_trail/compatibility.rb | 2 +- lib/paper_trail/version_concern.rb | 2 +- spec/dummy_app/app/models/car.rb | 2 + spec/dummy_app/app/models/vegetable.rb | 6 +-- spec/dummy_app/app/versions/jsonb_version.rb | 4 +- .../20110208155312_set_up_test_tables.rb | 8 ++-- spec/models/car_spec.rb | 19 ++++++++ spec/models/fruit_spec.rb | 37 +++++++++++++++- spec/models/json_version_spec.rb | 2 +- spec/models/vegetable_spec.rb | 43 +++++++++++++++++++ spec/models/widget_spec.rb | 2 + spec/paper_trail/compatibility_spec.rb | 2 +- .../postgres_array_serializer_spec.rb | 32 ++++++++++++++ 19 files changed, 179 insertions(+), 18 deletions(-) create mode 100644 gemfiles/rails_7.0.gemfile create mode 100644 spec/models/vegetable_spec.rb create mode 100644 spec/paper_trail/type_serializers/postgres_array_serializer_spec.rb diff --git a/Appraisals b/Appraisals index b4051e70..7d16c71c 100644 --- a/Appraisals +++ b/Appraisals @@ -24,3 +24,8 @@ 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.0" + gem "rails-controller-testing", "~> 1.0.5" +end diff --git a/CHANGELOG.md b/CHANGELOG.md index e255068e..7541e3fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/). ### Added +- [#1365](https://github.com/paper-trail-gem/paper_trail/pull/1365) - + Support Rails 7.0 - [#1349](https://github.com/paper-trail-gem/paper_trail/pull/1349) - `if:` and `unless:` work with `touch` events now. diff --git a/Rakefile b/Rakefile index 3f814c5f..0c7ed0c0 100644 --- a/Rakefile +++ b/Rakefile @@ -38,11 +38,8 @@ task :clean do end end -desc <<~EOS - Write a database.yml for the specified RDBMS, and create database. Does not - migrate. Migration happens later in spec_helper. -EOS -task prepare: %i[clean install_database_yml] do +desc "Create the database." +task :create_db do puts format("creating %s database", ENV["DB"]) case ENV["DB"] when "mysql" @@ -59,6 +56,12 @@ task prepare: %i[clean install_database_yml] do end end +desc <<~EOS + Write a database.yml for the specified RDBMS, and create database. Does not + migrate. Migration happens later in spec_helper. +EOS +task prepare: %i[clean install_database_yml create_db] + require "rspec/core/rake_task" desc "Run tests on PaperTrail with RSpec" task(:spec).clear diff --git a/gemfiles/rails_7.0.gemfile b/gemfiles/rails_7.0.gemfile new file mode 100644 index 00000000..ed927c64 --- /dev/null +++ b/gemfiles/rails_7.0.gemfile @@ -0,0 +1,8 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "rails", "~> 7.0.0" +gem "rails-controller-testing", "~> 1.0.5" + +gemspec path: "../" diff --git a/lib/paper_trail.rb b/lib/paper_trail.rb index 5df47023..8559f556 100644 --- a/lib/paper_trail.rb +++ b/lib/paper_trail.rb @@ -26,6 +26,8 @@ module PaperTrail named created_at. EOS + RAILS_GTE_7_0 = ::ActiveRecord.gem_version >= ::Gem::Version.new("7.0.0") + extend PaperTrail::Cleaner class << self diff --git a/lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb b/lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb index 5fcdb05c..471a2ce8 100644 --- a/lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb +++ b/lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb @@ -32,6 +32,12 @@ module PaperTrail 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::RAILS_GTE_7_0 && 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 + # https://github.com/rails/rails/issues/43966 + val.instance_variable_get(:@time) else AttributeSerializerFactory.for(@klass, attr).deserialize(val) end diff --git a/lib/paper_trail/compatibility.rb b/lib/paper_trail/compatibility.rb index fb8163db..5a5ee0df 100644 --- a/lib/paper_trail/compatibility.rb +++ b/lib/paper_trail/compatibility.rb @@ -18,7 +18,7 @@ module PaperTrail # versions. module Compatibility ACTIVERECORD_GTE = ">= 5.2" # enforced in gemspec - ACTIVERECORD_LT = "< 7.0" # not enforced in gemspec + ACTIVERECORD_LT = "< 7.1" # 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/version_concern.rb b/lib/paper_trail/version_concern.rb index fc6181c9..1e4a96c0 100644 --- a/lib/paper_trail/version_concern.rb +++ b/lib/paper_trail/version_concern.rb @@ -16,7 +16,7 @@ module PaperTrail extend ::ActiveSupport::Concern included do - belongs_to :item, polymorphic: true, optional: true + belongs_to :item, polymorphic: true, optional: true, inverse_of: false validates_presence_of :event after_create :enforce_version_limit! end diff --git a/spec/dummy_app/app/models/car.rb b/spec/dummy_app/app/models/car.rb index ea3faa5d..a6064a83 100644 --- a/spec/dummy_app/app/models/car.rb +++ b/spec/dummy_app/app/models/car.rb @@ -2,4 +2,6 @@ class Car < Vehicle has_paper_trail + attribute :color, type: ActiveModel::Type::String + attr_accessor :top_speed end diff --git a/spec/dummy_app/app/models/vegetable.rb b/spec/dummy_app/app/models/vegetable.rb index 56659373..a07a24c6 100644 --- a/spec/dummy_app/app/models/vegetable.rb +++ b/spec/dummy_app/app/models/vegetable.rb @@ -2,7 +2,7 @@ # See also `Fruit` which uses `JsonVersion`. class Vegetable < ActiveRecord::Base - if ENV["DB"] == "postgres" - has_paper_trail versions: { class_name: "JsonbVersion" } - end + has_paper_trail versions: { + class_name: ENV["DB"] == "postgres" ? "JsonbVersion" : "PaperTrail::Version" + }, on: %i[create update] end diff --git a/spec/dummy_app/app/versions/jsonb_version.rb b/spec/dummy_app/app/versions/jsonb_version.rb index eaf83bdf..59040471 100644 --- a/spec/dummy_app/app/versions/jsonb_version.rb +++ b/spec/dummy_app/app/versions/jsonb_version.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true -class JsonbVersion < PaperTrail::Version +class JsonbVersion < ActiveRecord::Base + include PaperTrail::VersionConcern + self.table_name = "jsonb_versions" 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 9731c85b..377889ad 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 @@ -131,10 +131,10 @@ class SetUpTestTables < ::ActiveRecord::Migration::Current %w[json jsonb].each do |j| table_name = j + "_versions" create_table table_name, force: true do |t| - t.string :item_type, null: false - t.integer :item_id, null: false - t.string :event, null: false - t.string :whodunnit + t.string :item_type, null: false + t.bigint :item_id, null: false + t.string :event, null: false + t.string :whodunnit t.public_send j, :object t.public_send j, :object_changes t.datetime :created_at, limit: 6 diff --git a/spec/models/car_spec.rb b/spec/models/car_spec.rb index 9cab7305..37f98c88 100644 --- a/spec/models/car_spec.rb +++ b/spec/models/car_spec.rb @@ -12,4 +12,23 @@ RSpec.describe Car, type: :model do assert_includes car.versions.last.changeset.keys, "name" end end + + describe "attributes and accessors", versioning: true do + it "reifies attributes that are not AR attributes" do + car = described_class.create name: "Pinto", color: "green" + car.update color: "yellow" + car.update color: "brown" + expect(car.versions.second.reify.color).to eq("yellow") + end + + it "reifies attributes that once were attributes but now just attr_accessor" do + car = described_class.create name: "Pinto", color: "green" + car.update color: "yellow" + changes = PaperTrail::Serializers::YAML.load(car.versions.last.attributes["object"]) + changes[:top_speed] = 80 + car.versions.first.update object: changes.to_yaml + car.reload + expect(car.versions.first.reify.top_speed).to eq(80) + end + end end diff --git a/spec/models/fruit_spec.rb b/spec/models/fruit_spec.rb index 66a9c5b2..10e12bc9 100644 --- a/spec/models/fruit_spec.rb +++ b/spec/models/fruit_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -if ENV["DB"] == "postgres" || JsonVersion.table_exists? +if ENV["DB"] == "postgres" && JsonVersion.table_exists? RSpec.describe Fruit, type: :model, versioning: true do describe "have_a_version_with_changes matcher" do it "works with Fruit because Fruit uses JsonVersion" do @@ -16,5 +16,40 @@ if ENV["DB"] == "postgres" || JsonVersion.table_exists? expect(banana).not_to have_a_version_with_changes(color: "Yellow", name: "Kiwi") end end + + describe "queries of versions", versioning: true do + let!(:fruit) { described_class.create(name: "Apple", mass: 1, color: "green") } + + before do + described_class.create(name: "Pear") + fruit.update(name: "Fidget") + fruit.update(name: "Digit") + end + + it "return the fruit whose name has changed" do + result = JsonVersion.where_attribute_changes(:name).map(&:item) + expect(result).to include(fruit) + end + + it "returns the fruit whose name was Fidget" do + result = JsonVersion.where_object_changes_from({ name: "Fidget" }).map(&:item) + expect(result).to include(fruit) + end + + it "returns the fruit whose name became Digit" do + result = JsonVersion.where_object_changes_to({ name: "Digit" }).map(&:item) + expect(result).to include(fruit) + end + + it "returns the fruit where the object was named Fidget before it changed" do + result = JsonVersion.where_object({ name: "Fidget" }).map(&:item) + expect(result).to include(fruit) + end + + it "returns the fruit that changed to Fidget" do + result = JsonVersion.where_object_changes({ name: "Fidget" }).map(&:item) + expect(result).to include(fruit) + end + end end end diff --git a/spec/models/json_version_spec.rb b/spec/models/json_version_spec.rb index b4649b21..72400e3f 100644 --- a/spec/models/json_version_spec.rb +++ b/spec/models/json_version_spec.rb @@ -5,7 +5,7 @@ require "spec_helper" # 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 do + RSpec.describe JsonVersion, type: :model, versioning: true do it "includes the VersionConcern module" do expect(described_class).to include(PaperTrail::VersionConcern) end diff --git a/spec/models/vegetable_spec.rb b/spec/models/vegetable_spec.rb new file mode 100644 index 00000000..7ab422a8 --- /dev/null +++ b/spec/models/vegetable_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require "spec_helper" +require "support/performance_helpers" + +if ENV["DB"] == "postgres" && JsonbVersion.table_exists? + ::RSpec.describe Vegetable do + describe "queries of versions", versioning: true do + let!(:vegetable) { described_class.create(name: "Veggie", mass: 1, color: "green") } + + before do + vegetable.update(name: "Fidget") + vegetable.update(name: "Digit") + described_class.create(name: "Cucumber") + end + + it "return the vegetable whose name has changed" do + result = JsonbVersion.where_attribute_changes(:name).map(&:item) + expect(result).to include(vegetable) + end + + it "returns the vegetable whose name was Fidget" do + result = JsonbVersion.where_object_changes_from({ name: "Fidget" }).map(&:item) + expect(result).to include(vegetable) + end + + it "returns the vegetable whose name became Digit" do + result = JsonbVersion.where_object_changes_to({ name: "Digit" }).map(&:item) + expect(result).to include(vegetable) + end + + it "returns the vegetable where the object was named Fidget before it changed" do + result = JsonbVersion.where_object({ name: "Fidget" }).map(&:item) + expect(result).to include(vegetable) + end + + it "returns the vegetable that changed to Fidget" do + result = JsonbVersion.where_object_changes({ name: "Fidget" }).map(&:item) + expect(result).to include(vegetable) + end + end + end +end diff --git a/spec/models/widget_spec.rb b/spec/models/widget_spec.rb index 7c22cbbd..72e2393a 100644 --- a/spec/models/widget_spec.rb +++ b/spec/models/widget_spec.rb @@ -260,6 +260,8 @@ RSpec.describe Widget, type: :model, versioning: true do widget = described_class.create(name: "Henry") widget.update(name: "Harry") widget.destroy + expect(widget.versions.first.item.object_id).not_to eq(widget.object_id) + expect(widget.versions.last.item.object_id).not_to eq(widget.object_id) expect(widget.versions.last.item).to be_nil end end diff --git a/spec/paper_trail/compatibility_spec.rb b/spec/paper_trail/compatibility_spec.rb index dae02398..79c65976 100644 --- a/spec/paper_trail/compatibility_spec.rb +++ b/spec/paper_trail/compatibility_spec.rb @@ -14,7 +14,7 @@ module PaperTrail context "when incompatible" do it "writes a warning to stderr" do - ar_version = ::Gem::Version.new("7.0.0") + ar_version = ::Gem::Version.new("7.1.0") expect { described_class.check_activerecord(ar_version) }.to output(/not compatible/).to_stderr diff --git a/spec/paper_trail/type_serializers/postgres_array_serializer_spec.rb b/spec/paper_trail/type_serializers/postgres_array_serializer_spec.rb new file mode 100644 index 00000000..4bec2e84 --- /dev/null +++ b/spec/paper_trail/type_serializers/postgres_array_serializer_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require "spec_helper" + +module PaperTrail + module TypeSerializers + ::RSpec.describe PostgresArraySerializer do + let(:word_array) { [].fill(0, rand(4..8)) { ::FFaker::Lorem.word } } + let(:word_array_as_string) { word_array.join("|") } + + let(:the_thing) { described_class.new("foo", "bar") } + + describe ".deserialize" do + it "deserializes array to Ruby" do + expect(the_thing.deserialize(word_array)).to eq(word_array) + end + + it "deserializes string to Ruby array" do + allow(the_thing).to receive(:deserialize_with_ar).and_return(word_array) + expect(the_thing.deserialize(word_array_as_string)).to eq(word_array) + expect(the_thing).to have_received(:deserialize_with_ar) + end + end + + describe ".dump" do + it "serializes Ruby to JSON" do + expect(the_thing.serialize(word_array)).to eq(word_array) + end + end + end + end +end From 6ec1468b213d182567095adddf1e2bc2c1a967c4 Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Fri, 21 Jan 2022 00:16:52 -0500 Subject: [PATCH 24/24] Release 12.2.0 --- CHANGELOG.md | 14 ++++++++++++++ README.md | 2 +- lib/paper_trail/version_number.rb | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 757b9600..be3f4129 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,20 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/). ### Added +- None + +### Fixed + +- None + +## 12.2.0 (2022-01-21) + +### Breaking Changes + +- None + +### Added + - [#1365](https://github.com/paper-trail-gem/paper_trail/pull/1365) - Support Rails 7.0 - [#1349](https://github.com/paper-trail-gem/paper_trail/pull/1349) - diff --git a/README.md b/README.md index 9a5cdc26..627fec76 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.1](https://github.com/paper-trail-gem/paper_trail/blob/v12.1.0/README.md), +[12.2](https://github.com/paper-trail-gem/paper_trail/blob/v12.2.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), diff --git a/lib/paper_trail/version_number.rb b/lib/paper_trail/version_number.rb index 97a5be13..0b24f405 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 = 1 + MINOR = 2 TINY = 0 # Set PRE to nil unless it's a pre-release (beta, rc, etc.)