PT-AT is now responsible for testing itself

See historical overview in README
This commit is contained in:
Jared Beck 2019-08-06 01:34:27 -04:00
parent 4f06eb3771
commit 24f3fbcd8f
20 changed files with 15 additions and 1224 deletions

View File

@ -115,12 +115,6 @@ RSpec/DescribedClass:
RSpec/ExampleLength:
Enabled: false
RSpec/FilePath:
Exclude:
- spec/paper_trail/association_reify_error_behaviour/error.rb
- spec/paper_trail/association_reify_error_behaviour/warn.rb
- spec/paper_trail/association_reify_error_behaviour/ignore.rb
# In an ideal world, each example has a single expectation. In the real world,
# sometimes setup is a pain and you don't want to duplicate setup in multiple
# examples, or make the specs more confusing with many nested `context`s, and

View File

@ -29,17 +29,8 @@ RSpec/HooksBeforeExamples:
- spec/paper_trail/request_spec.rb
- spec/paper_trail_spec.rb
RSpec/InstanceVariable:
Exclude:
- spec/paper_trail/associations/belongs_to_spec.rb
- spec/paper_trail/associations/habtm_spec.rb
- spec/paper_trail/associations/has_many_through_spec.rb
RSpec/NestedGroups:
Exclude:
- spec/paper_trail/associations/belongs_to_spec.rb
- spec/paper_trail/associations/habtm_spec.rb
- spec/paper_trail/associations/has_many_through_spec.rb
- spec/paper_trail/model_spec.rb
# It may be possible for us to use safe_load, but we'd have to pass the

View File

@ -97,7 +97,7 @@ are welcome.
| 2 | 2.7-stable | >= 1.8.7 | >= 3.0, < 4 |
| 1 | rails2 | >= 1.8.7 | >= 2.3, < 3 |
Experts: to install incompatible versions of activerecord, see
Experts: to install incompatible versions of activerecord, see
`paper_trail/compatibility.rb`.
### 1.b. Installation
@ -109,7 +109,7 @@ Experts: to install incompatible versions of activerecord, see
1. Add a `versions` table to your database:
```
bundle exec rails generate paper_trail:install [--with-changes] [--with-associations]
bundle exec rails generate paper_trail:install [--with-changes]
```
For more information on this generator, see [section 5.c.
@ -889,17 +889,19 @@ string, please try the [paper_trail-globalid][37] gem.
To track and reify associations, use [paper_trail-association_tracking][6] (PT-AT).
From 2014 to 2018, association tracking was part of PT core as an experimental
feature, but many issues were discovered. To attract new volunteers to address
these issues, PT-AT was extracted (see
https://github.com/paper-trail-gem/paper_trail/issues/1070).
From 2014 to 2018, association tracking was an experimental feature, but many
issues were discovered. To attract new volunteers to address these issues, PT-AT
was extracted (see https://github.com/paper-trail-gem/paper_trail/issues/1070).
Even though this has always been an experimental feature, we don't want the
extraction of PT-AT to be a breaking change. In PT 9, PT-AT was kept as a
runtime dependency. In PT 10, it is a development dependency, so if you use it
you must add it to your own `Gemfile`. We will keep PT-AT as a development
dependency and continue running the existing tests related to association
tracking for as long as is practical.
Even though it had always been an experimental feature, we didn't want the
extraction of PT-AT to be a breaking change, so great care was taken to remove
it slowly.
- In PT 9, PT-AT was kept as a runtime dependency.
- In PT 10, it became a development dependency (If you use it you must add it to
your own `Gemfile`) and we kept running all of its tests.
- In PT 11, it will no longer be a development dependency, and it is responsible
for its own tests.
#### 4.b.1 The optional `item_subtype` column
@ -1225,7 +1227,7 @@ add_column :versions, :new_object, :jsonb # or :json
PaperTrail::Version.where.not(object: nil).find_each do |version|
version.update_column(:new_object, YAML.load(version.object))
# if version.object_changes
# version.update_column(
# :new_object_changes,

View File

@ -9,14 +9,6 @@ module PaperTrail
class Config
include Singleton
E_PT_AT_REMOVED = <<-EOS.squish
Association Tracking for PaperTrail has been extracted to a separate gem.
To use it, please add `paper_trail-association_tracking` to your Gemfile.
If you don't use it (most people don't, that's the default) and you set
`track_associations = false` somewhere (probably a rails initializer) you
can remove that line now.
EOS
attr_accessor(
:association_reify_error_behaviour,
:object_changes_adapter,
@ -43,30 +35,5 @@ module PaperTrail
def enabled=(enable)
@mutex.synchronize { @enabled = enable }
end
# In PT 10, the paper_trail-association_tracking gem was changed from a
# runtime dependency to a development dependency. We raise an error about
# this for the people who don't read changelogs.
#
# We raise a generic RuntimeError instead of a specific PT error class
# because there is no known use case where someone would want to rescue
# this. If we think of such a use case in the future we can revisit this
# decision.
#
# @override If PT-AT is `require`d, it will replace this method with its
# own implementation.
def track_associations=(value)
if value
raise E_PT_AT_REMOVED
else
::Kernel.warn(E_PT_AT_REMOVED)
end
end
# @override If PT-AT is `require`d, it will replace this method with its
# own implementation.
def track_associations?
raise E_PT_AT_REMOVED
end
end
end

View File

@ -205,18 +205,8 @@ module PaperTrail
# Restore the item from this version.
#
# Optionally this can also restore all :has_one and :has_many (including
# has_many :through) associations as they were "at the time", if they are
# also being versioned by PaperTrail.
#
# Options:
#
# - :has_one
# - `true` - Also reify has_one associations.
# - `false - Default.
# - :has_many
# - `true` - Also reify has_many and has_many :through associations.
# - `false` - Default.
# - :mark_for_destruction
# - `true` - Mark the has_one/has_many associations that did not exist in
# the reified version for destruction, instead of removing them.

View File

@ -9,12 +9,6 @@ require "action_controller/railtie"
Bundler.require(:default, Rails.env)
require "paper_trail"
# As of PT 10, PT-AT is a development dependency in paper_trail.gemspec
# https://github.com/paper-trail-gem/paper_trail/issues/1070
# https://github.com/westonganger/paper_trail-association_tracking/issues/2
# https://github.com/westonganger/paper_trail-association_tracking/issues/7
require "paper_trail-association_tracking"
module Dummy
class Application < Rails::Application
config.encoding = "utf-8"

View File

@ -1,3 +0,0 @@
# frozen_string_literal: true
::PaperTrail.config.track_associations = true

View File

@ -101,17 +101,6 @@ class SetUpTestTables < (
end
add_index :versions, %i[item_type item_id]
create_table :version_associations do |t|
t.integer :version_id
t.string :foreign_key_name, null: false
t.integer :foreign_key_id
t.string :foreign_type, null: false
end
add_index :version_associations, [:version_id]
add_index :version_associations,
%i[foreign_key_name foreign_key_id foreign_type],
name: "index_version_associations_on_foreign_key"
create_table :post_versions, force: true do |t|
t.string :item_type, null: false
t.integer :item_id, null: false

View File

@ -1,81 +0,0 @@
# frozen_string_literal: true
require "spec_helper"
module Family
RSpec.describe Family, type: :model, versioning: true do
describe "#reify" do
context "belongs_to" do
it "uses the correct item_type in queries" do
parent = described_class.new(name: "parent1")
parent.children.build(name: "child1")
parent.save!
parent.update!(
name: "parent2",
children_attributes: { id: parent.children.first.id, name: "child2" }
)
last_child_version = parent.children.first.versions.last
# We expect `reify` to look for item_type 'Family::Family', not
# '::Family::Family'. See PR #996
previous_children = last_child_version.reify(belongs_to: true)
expect(previous_children.parent.name).to eq "parent1"
end
end
context "has_many" do
it "uses the correct item_type in queries" do
parent = described_class.new(name: "parent1")
parent.children.build(name: "child1")
parent.save!
parent.name = "parent2"
parent.children.build(name: "child2")
parent.save!
# We expect `reify` to look for item_type 'Family::Family', not
# '::Family::Family'. See PR #996
previous_parent = parent.versions.last.reify(has_many: true)
previous_children = previous_parent.children
expect(previous_children.size).to eq 1
expect(previous_children.first.name).to eq "child1"
end
end
context "has_many through" do
it "uses the correct item_type in queries" do
parent = described_class.new(name: "parent1")
parent.grandsons.build(name: "grandson1")
parent.save!
parent.name = "parent2"
parent.grandsons.build(name: "grandson2")
parent.save!
# We expect `reify` to look for item_type 'Family::Family', not
# '::Family::Family'. See PR #996
previous_parent = parent.versions.last.reify(has_many: true)
previous_grandsons = previous_parent.grandsons
expect(previous_grandsons.size).to eq 1
expect(previous_grandsons.first.name).to eq "grandson1"
end
end
context "has_one" do
it "uses the correct item_type in queries" do
parent = described_class.new(name: "parent1")
parent.build_mentee(name: "partner1")
parent.save!
parent.update(
name: "parent2",
mentee_attributes: { id: parent.mentee.id, name: "partner2" }
)
# We expect `reify` to look for item_type 'Family::Family', not
# '::Family::Family'. See PR #996
previous_parent = parent.versions.last.reify(has_one: true)
previous_partner = previous_parent.mentee
expect(previous_partner.name).to eq "partner1"
end
end
end
end
end

View File

@ -22,26 +22,6 @@ RSpec.describe Pet, type: :model, versioning: true do
person.update(name: "Peter")
expect(person.reload.versions.length).to(eq(3))
second_version = person.reload.versions.second.reify(has_many: true)
expect(second_version.pets.length).to(eq(2))
expect(second_version.animals.length).to(eq(2))
expect(second_version.animals.map { |a| a.class.name }).to(eq(%w[Dog Cat]))
expect(second_version.pets.map { |p| p.animal.class.name }).to(eq(%w[Dog Cat]))
expect(second_version.animals.first.name).to(eq("Snoopy"))
expect(second_version.dogs.first.name).to(eq("Snoopy"))
expect(second_version.animals.second.name).to(eq("Garfield"))
expect(second_version.cats.first.name).to(eq("Garfield"))
last_version = person.reload.versions.last.reify(has_many: true)
expect(last_version.pets.length).to(eq(2))
expect(last_version.animals.length).to(eq(2))
expect(last_version.animals.map { |a| a.class.name }).to(eq(%w[Dog Cat]))
expect(last_version.pets.map { |p| p.animal.class.name }).to(eq(%w[Dog Cat]))
expect(last_version.animals.first.name).to(eq("Beethoven"))
expect(last_version.dogs.first.name).to(eq("Beethoven"))
expect(last_version.animals.second.name).to(eq("Sylvester"))
expect(last_version.cats.first.name).to(eq("Sylvester"))
end
context "Older version entry present where item_type refers to the base_class" do

View File

@ -1,40 +0,0 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe PaperTrail, versioning: true do
it "baseline test setup" do
expect(Person.new).to be_versioned
end
# See https://github.com/paper-trail-gem/paper_trail/issues/594
describe "#association reify error behaviour" do
it "association reify error behaviour = :error" do
::PaperTrail.config.association_reify_error_behaviour = :error
person = Person.create(name: "Frank")
car = Car.create(name: "BMW 325")
bicycle = Bicycle.create(name: "BMX 1.0")
person.car = car
person.bicycle = bicycle
person.update(name: "Steve")
car.update(name: "BMW 330")
bicycle.update(name: "BMX 2.0")
person.update(name: "Peter")
expect(person.reload.versions.length).to(eq(3))
expect {
person.reload.versions.second.reify(has_one: true)
}.to(
raise_error(::PaperTrailAssociationTracking::Reifiers::HasOne::FoundMoreThanOne) do |err|
expect(err.message.squish).to match(
/Expected to find one Vehicle, but found 2/
)
end
)
end
end
end

View File

@ -1,40 +0,0 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe PaperTrail, versioning: true do
it "baseline test setup" do
expect(Person.new).to be_versioned
end
# See https://github.com/paper-trail-gem/paper_trail/issues/594
describe "#association reify error behaviour" do
it "association reify error behaviour = :ignore" do
::PaperTrail.config.association_reify_error_behaviour = :ignore
person = Person.create(name: "Frank")
thing = Thing.create(name: "BMW 325")
thing2 = Thing.create(name: "BMX 1.0")
person.thing = thing
person.thing_2 = thing2
person.update(name: "Steve")
thing.update(name: "BMW 330")
thing.update(name: "BMX 2.0")
person.update(name: "Peter")
expect(person.reload.versions.length).to(eq(3))
logger = person.versions.first.logger
allow(logger).to receive(:warn)
person.reload.versions.second.reify(has_one: true)
expect(logger).not_to(
have_received(:warn).with(/Unable to reify has_one association/)
)
end
end
end

View File

@ -1,40 +0,0 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe PaperTrail, versioning: true do
it "baseline test setup" do
expect(Person.new).to be_versioned
end
# See https://github.com/paper-trail-gem/paper_trail/issues/594
describe "#association reify error behaviour" do
it "association reify error behaviour = :warn" do
::PaperTrail.config.association_reify_error_behaviour = :warn
person = Person.create(name: "Frank")
thing = Thing.create(name: "BMW 325")
thing2 = Thing.create(name: "BMX 1.0")
person.thing = thing
person.thing_2 = thing2
person.update(name: "Steve")
thing.update(name: "BMW 330")
thing.update(name: "BMX 2.0")
person.update(name: "Peter")
expect(person.reload.versions.length).to(eq(3))
logger = person.versions.first.logger
allow(logger).to receive(:warn)
person.reload.versions.second.reify(has_one: true)
expect(logger).to(
have_received(:warn).with(/Unable to reify has_one association/).twice
)
end
end
end

View File

@ -1,133 +0,0 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe(::PaperTrail, versioning: true) do
context "wotsit belongs_to widget" do
before { @widget = Widget.create(name: "widget_0") }
context "where the association is created between model versions" do
before do
@wotsit = Wotsit.create(name: "wotsit_0")
@wotsit.update(widget_id: @widget.id, name: "wotsit_1")
end
context "when reified" do
before { @wotsit0 = @wotsit.versions.last.reify(belongs_to: true) }
it "see the associated as it was at the time" do
expect(@wotsit0.widget).to be_nil
end
it "not persist changes to the live association" do
expect(@wotsit.reload.widget).to(eq(@widget))
end
end
context "and then the associated is updated between model versions" do
before do
@widget.update(name: "widget_1")
@widget.update(name: "widget_2")
@wotsit.update(name: "wotsit_2")
@widget.update(name: "widget_3")
end
context "when reified" do
before { @wotsit1 = @wotsit.versions.last.reify(belongs_to: true) }
it "see the associated as it was at the time" do
expect(@wotsit1.widget.name).to(eq("widget_2"))
end
it "not persist changes to the live association" do
expect(@wotsit.reload.widget.name).to(eq("widget_3"))
end
end
context "when reified opting out of belongs_to reification" do
before { @wotsit1 = @wotsit.versions.last.reify(belongs_to: false) }
it "see the associated as it is live" do
expect(@wotsit1.widget.name).to(eq("widget_3"))
end
end
end
context "and then the associated is destroyed" do
before do
@wotsit.update(name: "wotsit_2")
@widget.destroy
end
context "when reified with belongs_to: true" do
before { @wotsit2 = @wotsit.versions.last.reify(belongs_to: true) }
it "see the associated as it was at the time" do
expect(@wotsit2.widget).to(eq(@widget))
end
it "not persist changes to the live association" do
expect(@wotsit.reload.widget).to be_nil
end
it "be able to persist the reified record" do
expect { @wotsit2.save! }.not_to(raise_error)
end
end
context "when reified with belongs_to: false" do
before { @wotsit2 = @wotsit.versions.last.reify(belongs_to: false) }
it "save should not re-create the widget record" do
@wotsit2.save!
expect(::Widget.find_by(id: @widget.id)).to be_nil
end
end
context "and then the model is updated" do
before do
@wotsit.update(name: "wotsit_3")
end
context "when reified" do
before { @wotsit2 = @wotsit.versions.last.reify(belongs_to: true) }
it "see the associated as it was the time" do
expect(@wotsit2.widget).to be_nil
end
end
end
end
end
context "where the association is changed between model versions" do
before do
@wotsit = @widget.create_wotsit(name: "wotsit_0")
@new_widget = Widget.create(name: "new_widget")
@wotsit.update(widget_id: @new_widget.id, name: "wotsit_1")
end
context "when reified" do
before { @wotsit0 = @wotsit.versions.last.reify(belongs_to: true) }
it "see the association as it was at the time" do
expect(@wotsit0.widget.name).to(eq("widget_0"))
end
it "not persist changes to the live association" do
expect(@wotsit.reload.widget).to(eq(@new_widget))
end
end
context "when reified with option mark_for_destruction" do
before do
@wotsit0 = @wotsit.versions.last.reify(belongs_to: true, mark_for_destruction: true)
end
it "does not mark the new associated for destruction" do
expect(@new_widget.marked_for_destruction?).to(eq(false))
end
end
end
end
end

View File

@ -1,123 +0,0 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe(::PaperTrail, versioning: true) do
context "foo and bar" do
before do
@foo = FooHabtm.create(name: "foo")
end
context "where the association is created between model versions" do
before do
@foo.update(name: "foo1", bar_habtms: [BarHabtm.create(name: "bar")])
end
context "when reified" do
before do
@reified = @foo.versions.last.reify(has_and_belongs_to_many: true)
end
it "see the associated as it was at the time" do
expect(@reified.bar_habtms.length).to(eq(0))
end
it "not persist changes to the live association" do
expect(@foo.reload.bar_habtms).not_to(eq(@reified.bar_habtms))
end
end
end
context "where the association is changed between model versions" do
before do
@foo.update(name: "foo2", bar_habtms: [BarHabtm.create(name: "bar2")])
@foo.update(name: "foo3", bar_habtms: [BarHabtm.create(name: "bar3")])
end
context "when reified" do
before do
@reified = @foo.versions.last.reify(has_and_belongs_to_many: true)
end
it "see the association as it was at the time" do
expect(@reified.bar_habtms.first.name).to(eq("bar2"))
end
it "not persist changes to the live association" do
expect(@foo.reload.bar_habtms.first).not_to(eq(@reified.bar_habtms.first))
end
end
context "when reified with has_and_belongs_to_many: false" do
before { @reified = @foo.versions.last.reify }
it "see the association as it is now" do
expect(@reified.bar_habtms.first.name).to(eq("bar3"))
end
end
end
context "where the association is destroyed between model versions" do
before do
@foo.update(name: "foo2", bar_habtms: [BarHabtm.create(name: "bar2")])
@foo.update(name: "foo3", bar_habtms: [])
end
context "when reified" do
before do
@reified = @foo.versions.last.reify(has_and_belongs_to_many: true)
end
it "see the association as it was at the time" do
expect(@reified.bar_habtms.first.name).to(eq("bar2"))
end
it "not persist changes to the live association" do
expect(@foo.reload.bar_habtms.first).not_to(eq(@reified.bar_habtms.first))
end
end
end
context "where the unassociated model changes" do
before do
@bar = BarHabtm.create(name: "bar2")
@foo.update(name: "foo2", bar_habtms: [@bar])
@foo.update(name: "foo3", bar_habtms: [BarHabtm.create(name: "bar4")])
@bar.update(name: "bar3")
end
context "when reified" do
before do
@reified = @foo.versions.last.reify(has_and_belongs_to_many: true)
end
it "see the association as it was at the time" do
expect(@reified.bar_habtms.first.name).to(eq("bar2"))
end
it "not persist changes to the live association" do
expect(@foo.reload.bar_habtms.first).not_to(eq(@reified.bar_habtms.first))
end
end
end
end
context "updated via nested attributes" do
before do
@foo = FooHabtm.create(name: "foo", bar_habtms_attributes: [{ name: "bar" }])
@foo.update(
name: "foo2",
bar_habtms_attributes: [{ id: @foo.bar_habtms.first.id, name: "bar2" }]
)
@reified = @foo.versions.last.reify(has_and_belongs_to_many: true)
end
it "see the associated object as it was at the time" do
expect(@reified.bar_habtms.first.name).to(eq("bar"))
end
it "not persist changes to the live object" do
expect(@foo.reload.bar_habtms.first.name).not_to(eq(@reified.bar_habtms.first.name))
end
end
end

View File

@ -1,144 +0,0 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe(::PaperTrail, versioning: true) do
describe "customer, reified from version before order created" do
it "has no orders" do
customer = Customer.create(name: "customer_0")
customer.update!(name: "customer_1")
customer.orders.create!(order_date: Date.today)
customer0 = customer.versions.last.reify(has_many: true)
expect(customer0.orders).to(eq([]))
expect(customer.orders.reload).not_to(eq([]))
end
end
describe "customer, reified with mark_for_destruction, from version before order" do
it "has orders, but they are marked for destruction" do
customer = Customer.create(name: "customer_0")
customer.update!(name: "customer_1")
customer.orders.create!(order_date: Date.today)
customer0 = customer.versions.last.reify(has_many: true, mark_for_destruction: true)
expect(customer0.orders.map(&:marked_for_destruction?)).to(eq([true]))
end
end
describe "customer, reified from version after order created" do
it "has the expected order" do
customer = Customer.create(name: "customer_0")
customer.orders.create!(order_date: "order_date_0")
customer.update(name: "customer_1")
customer0 = customer.versions.last.reify(has_many: true)
expect(customer0.orders.map(&:order_date)).to(eq(["order_date_0"]))
end
end
describe "customer, reified from version after order line_items created" do
it "has the expected line item" do
customer = Customer.create(name: "customer_0")
order = customer.orders.create!(order_date: "order_date_0")
customer.update(name: "customer_1")
order.line_items.create!(product: "product_0")
customer0 = customer.versions.last.reify(has_many: true)
expect(customer0.orders.first.line_items.map(&:product)).to(eq(["product_0"]))
end
end
describe "customer, reified from version after order is updated" do
it "has the updated order_date" do
customer = Customer.create(name: "customer_0")
order = customer.orders.create!(order_date: "order_date_0")
customer.update(name: "customer_1")
order.update(order_date: "order_date_1")
order.update(order_date: "order_date_2")
customer.update(name: "customer_2")
order.update(order_date: "order_date_3")
customer1 = customer.versions.last.reify(has_many: true)
expect(customer1.orders.map(&:order_date)).to(eq(["order_date_2"]))
expect(customer.orders.reload.map(&:order_date)).to(eq(["order_date_3"]))
end
end
describe "customer, reified with has_many: false" do
it "has the latest order from the database" do
# TODO: This can be tested with fewer db records
customer = Customer.create(name: "customer_0")
order = customer.orders.create!(order_date: "order_date_0")
customer.update(name: "customer_1")
order.update(order_date: "order_date_1")
order.update(order_date: "order_date_2")
customer.update(name: "customer_2")
order.update(order_date: "order_date_3")
customer1 = customer.versions.last.reify(has_many: false)
expect(customer1.orders.map(&:order_date)).to(eq(["order_date_3"]))
end
end
describe "customer, reified from version before order is destroyed" do
it "has the order" do
# TODO: This can be tested with fewer db records
customer = Customer.create(name: "customer_0")
order = customer.orders.create!(order_date: "order_date_0")
customer.update(name: "customer_1")
order.update(order_date: "order_date_1")
order.update(order_date: "order_date_2")
customer.update(name: "customer_2")
order.update(order_date: "order_date_3")
order.destroy
customer1 = customer.versions.last.reify(has_many: true)
expect(customer1.orders.map(&:order_date)).to(eq(["order_date_2"]))
expect(customer.orders.reload).to(eq([]))
end
end
describe "customer, reified from version before order is destroyed" do
it "has the order" do
customer = Customer.create(name: "customer_0")
order = customer.orders.create!(order_date: "order_date_0")
customer.update(name: "customer_1")
order.destroy
customer1 = customer.versions.last.reify(has_many: true)
expect(customer1.orders.map(&:order_date)).to(eq([order.order_date]))
expect(customer.orders.reload).to(eq([]))
end
end
describe "customer, reified from version after order is destroyed" do
it "does not have the order" do
customer = Customer.create(name: "customer_0")
order = customer.orders.create!(order_date: "order_date_0")
customer.update(name: "customer_1")
order.destroy
customer.update(name: "customer_2")
customer1 = customer.versions.last.reify(has_many: true)
expect(customer1.orders).to(eq([]))
end
end
describe "customer, reified from version before order was updated" do
it "has the old order_date" do
customer = Customer.create(name: "customer_0")
customer.orders.create!(order_date: "order_date_0")
customer.update(name: "customer_1")
customer.orders.create!(order_date: "order_date_1")
customer0 = customer.versions.last.reify(has_many: true)
expect(customer0.orders.map(&:order_date)).to(eq(["order_date_0"]))
expect(
customer.orders.reload.map(&:order_date)
).to match_array(%w[order_date_0 order_date_1])
end
end
describe "customer, reified w/ mark_for_destruction, from version before 2nd order created" do
it "has both orders, and the second is marked for destruction" do
customer = Customer.create(name: "customer_0")
customer.orders.create!(order_date: "order_date_0")
customer.update(name: "customer_1")
customer.orders.create!(order_date: "order_date_1")
customer0 = customer.versions.last.reify(has_many: true, mark_for_destruction: true)
order = customer0.orders.detect { |o| o.order_date == "order_date_1" }
expect(order).to be_marked_for_destruction
end
end
end

View File

@ -1,408 +0,0 @@
# frozen_string_literal: true
require "spec_helper"
CHAPTER_NAMES = [
"Down the Rabbit-Hole",
"The Pool of Tears",
"A Caucus-Race and a Long Tale",
"The Rabbit Sends in a Little Bill",
"Advice from a Caterpillar",
"Pig and Pepper",
"A Mad Tea-Party",
"The Queen's Croquet-Ground",
"The Mock Turtle's Story",
"The Lobster Quadrille",
"Who Stole the Tarts?",
"Alice's Evidence"
].freeze
RSpec.describe(::PaperTrail, versioning: true) do
context "Books, Authors, and Authorships" do
before { @book = Book.create(title: "book_0") }
context "updated before the associated was created" do
before do
@book.update!(title: "book_1")
@book.authors.create!(name: "author_0")
end
context "when reified" do
before { @book0 = @book.versions.last.reify(has_many: true) }
it "see the associated as it was at the time" do
expect(@book0.authors).to(eq([]))
end
it "not persist changes to the live association" do
expect(@book.authors.reload.map(&:name)).to(eq(["author_0"]))
end
end
context "when reified with option mark_for_destruction" do
before do
@book0 = @book.versions.last.reify(has_many: true, mark_for_destruction: true)
end
it "mark the associated for destruction" do
expect(@book0.authors.map(&:marked_for_destruction?)).to(eq([true]))
end
it "mark the associated-through for destruction" do
expect(@book0.authorships.map(&:marked_for_destruction?)).to(eq([true]))
end
end
end
context "updated before it is associated with an existing one" do
before do
person_existing = Person.create(name: "person_existing")
@book.update!(title: "book_1")
(@book.authors << person_existing)
end
context "when reified" do
before { @book0 = @book.versions.last.reify(has_many: true) }
it "see the associated as it was at the time" do
expect(@book0.authors).to(eq([]))
end
end
context "when reified with option mark_for_destruction" do
before do
@book0 = @book.versions.last.reify(has_many: true, mark_for_destruction: true)
end
it "not mark the associated for destruction" do
expect(@book0.authors.map(&:marked_for_destruction?)).to(eq([false]))
end
it "mark the associated-through for destruction" do
expect(@book0.authorships.map(&:marked_for_destruction?)).to(eq([true]))
end
end
end
context "where the association is created between model versions" do
before do
@author = @book.authors.create!(name: "author_0")
@person_existing = Person.create(name: "person_existing")
@book.update!(title: "book_1")
end
context "when reified" do
before { @book0 = @book.versions.last.reify(has_many: true) }
it "see the associated as it was at the time" do
expect(@book0.authors.map(&:name)).to(eq(["author_0"]))
end
end
context "and then the associated is updated between model versions" do
before do
@author.update(name: "author_1")
@author.update(name: "author_2")
@book.update(title: "book_2")
@author.update(name: "author_3")
end
context "when reified" do
before { @book1 = @book.versions.last.reify(has_many: true) }
it "see the associated as it was at the time" do
expect(@book1.authors.map(&:name)).to(eq(["author_2"]))
end
it "not persist changes to the live association" do
expect(@book.authors.reload.map(&:name)).to(eq(["author_3"]))
end
end
context "when reified opting out of has_many reification" do
before { @book1 = @book.versions.last.reify(has_many: false) }
it "see the associated as it is live" do
expect(@book1.authors.map(&:name)).to(eq(["author_3"]))
end
end
end
context "and then the associated is destroyed" do
before { @author.destroy }
context "when reified" do
before { @book1 = @book.versions.last.reify(has_many: true) }
it "see the associated as it was at the time" do
expect(@book1.authors.map(&:name)).to(eq([@author.name]))
end
it "not persist changes to the live association" do
expect(@book.authors.reload).to(eq([]))
end
end
end
context "and then the associated is destroyed between model versions" do
before do
@author.destroy
@book.update(title: "book_2")
end
context "when reified" do
before { @book1 = @book.versions.last.reify(has_many: true) }
it "see the associated as it was at the time" do
expect(@book1.authors).to(eq([]))
end
end
end
context "and then the associated is dissociated between model versions" do
before do
@book.authors = []
@book.update(title: "book_2")
end
context "when reified" do
before { @book1 = @book.versions.last.reify(has_many: true) }
it "see the associated as it was at the time" do
expect(@book1.authors).to(eq([]))
end
end
end
context "and then another associated is created" do
before { @book.authors.create!(name: "author_1") }
context "when reified" do
before { @book0 = @book.versions.last.reify(has_many: true) }
it "only see the first associated" do
expect(@book0.authors.map(&:name)).to(eq(["author_0"]))
end
it "not persist changes to the live association" do
expect(@book.authors.reload.map(&:name)).to(eq(%w[author_0 author_1]))
end
end
context "when reified with option mark_for_destruction" do
before do
@book0 = @book.versions.last.reify(has_many: true, mark_for_destruction: true)
end
it "mark the newly associated for destruction" do
author = @book0.authors.detect { |a| a.name == "author_1" }
expect(author).to be_marked_for_destruction
end
it "mark the newly associated-through for destruction" do
authorship = @book0.authorships.detect { |as| as.author.name == "author_1" }
expect(authorship).to be_marked_for_destruction
end
end
end
context "and then an existing one is associated" do
before { (@book.authors << @person_existing) }
context "when reified" do
before { @book0 = @book.versions.last.reify(has_many: true) }
it "only see the first associated" do
expect(@book0.authors.map(&:name)).to(eq(["author_0"]))
end
it "not persist changes to the live association" do
expect(@book.authors.reload.map(&:name).sort).to(eq(%w[author_0 person_existing]))
end
end
context "when reified with option mark_for_destruction" do
before do
@book0 = @book.versions.last.reify(has_many: true, mark_for_destruction: true)
end
it "not mark the newly associated for destruction" do
author = @book0.authors.detect { |a| a.name == "person_existing" }
expect(author).not_to be_marked_for_destruction
end
it "mark the newly associated-through for destruction" do
authorship = @book0.authorships.detect { |as| as.author.name == "person_existing" }
expect(authorship).to be_marked_for_destruction
end
end
end
end
context "updated before the associated without paper_trail was created" do
before do
@book.update!(title: "book_1")
@book.editors.create!(name: "editor_0")
end
context "when reified" do
before { @book0 = @book.versions.last.reify(has_many: true) }
it "see the live association" do
expect(@book0.editors.map(&:name)).to(eq(["editor_0"]))
end
end
end
end
context "Chapters, Sections, Paragraphs, Quotations, and Citations" do
before { @chapter = Chapter.create(name: CHAPTER_NAMES[0]) }
context "before any associations are created" do
before { @chapter.update(name: CHAPTER_NAMES[1]) }
it "not reify any associations" do
chapter_v1 = @chapter.versions[1].reify(has_many: true)
expect(chapter_v1.name).to(eq(CHAPTER_NAMES[0]))
expect(chapter_v1.sections).to(eq([]))
expect(chapter_v1.paragraphs).to(eq([]))
end
end
context "after the first has_many through relationship is created" do
before do
@chapter.update(name: CHAPTER_NAMES[1])
@chapter.sections.create(name: "section 1")
@chapter.sections.first.update(name: "section 2")
@chapter.update(name: CHAPTER_NAMES[2])
@chapter.sections.first.update(name: "section 3")
end
context "version 1" do
it "have no sections" do
chapter_v1 = @chapter.versions[1].reify(has_many: true)
expect(chapter_v1.sections).to(eq([]))
end
end
context "version 2" do
it "have one section" do
chapter_v2 = @chapter.versions[2].reify(has_many: true)
expect(chapter_v2.sections.size).to(eq(1))
expect(chapter_v2.sections.map(&:name)).to(eq(["section 2"]))
expect(chapter_v2.name).to(eq(CHAPTER_NAMES[1]))
end
end
context "version 2, before the section was destroyed" do
before do
@chapter.update(name: CHAPTER_NAMES[2])
@chapter.sections.destroy_all
end
it "have the one section" do
chapter_v2 = @chapter.versions[2].reify(has_many: true)
expect(chapter_v2.sections.map(&:name)).to(eq(["section 2"]))
end
end
context "version 3, after the section was destroyed" do
before do
@chapter.sections.destroy_all
@chapter.update(name: CHAPTER_NAMES[3])
end
it "have no sections" do
chapter_v3 = @chapter.versions[3].reify(has_many: true)
expect(chapter_v3.sections.size).to(eq(0))
end
end
context "after creating a paragraph" do
before do
@section = @chapter.sections.first
@paragraph = @section.paragraphs.create(name: "para1")
end
context "new chapter version" do
it "have one paragraph" do
initial_section_name = @section.name
initial_paragraph_name = @paragraph.name
@chapter.update(name: CHAPTER_NAMES[4])
expect(@chapter.versions.size).to(eq(4))
@paragraph.update(name: "para3")
chapter_v3 = @chapter.versions[3].reify(has_many: true)
expect(chapter_v3.sections.map(&:name)).to(eq([initial_section_name]))
paragraphs = chapter_v3.sections.first.paragraphs
expect(paragraphs.size).to(eq(1))
expect(paragraphs.map(&:name)).to(eq([initial_paragraph_name]))
end
end
context "the version before a section is destroyed" do
it "have the section and paragraph" do
@chapter.update(name: CHAPTER_NAMES[3])
expect(@chapter.versions.size).to(eq(4))
@section.destroy
expect(@chapter.versions.size).to(eq(4))
chapter_v3 = @chapter.versions[3].reify(has_many: true)
expect(chapter_v3.name).to(eq(CHAPTER_NAMES[2]))
expect(chapter_v3.sections).to(eq([@section]))
expect(chapter_v3.sections[0].paragraphs).to(eq([@paragraph]))
expect(chapter_v3.paragraphs).to(eq([@paragraph]))
end
end
context "the version after a section is destroyed" do
it "not have any sections or paragraphs" do
@section.destroy
@chapter.update(name: CHAPTER_NAMES[5])
expect(@chapter.versions.size).to(eq(4))
chapter_v3 = @chapter.versions[3].reify(has_many: true)
expect(chapter_v3.sections.size).to(eq(0))
expect(chapter_v3.paragraphs.size).to(eq(0))
end
end
context "the version before a paragraph is destroyed" do
it "have the one paragraph" do
initial_paragraph_name = @section.paragraphs.first.name
@chapter.update(name: CHAPTER_NAMES[5])
@paragraph.destroy
chapter_v3 = @chapter.versions[3].reify(has_many: true)
paragraphs = chapter_v3.sections.first.paragraphs
expect(paragraphs.size).to(eq(1))
expect(paragraphs.first.name).to(eq(initial_paragraph_name))
end
end
context "the version after a paragraph is destroyed" do
it "have no paragraphs" do
@paragraph.destroy
@chapter.update(name: CHAPTER_NAMES[5])
chapter_v3 = @chapter.versions[3].reify(has_many: true)
expect(chapter_v3.paragraphs.size).to(eq(0))
expect(chapter_v3.sections.first.paragraphs).to(eq([]))
end
end
end
end
context "a chapter with one paragraph and one citation" do
it "reify paragraphs and citations" do
chapter = Chapter.create(name: CHAPTER_NAMES[0])
section = Section.create(name: "Section One", chapter: chapter)
paragraph = Paragraph.create(name: "Paragraph One", section: section)
quotation = Quotation.create(chapter: chapter)
citation = Citation.create(quotation: quotation)
chapter.update(name: CHAPTER_NAMES[1])
expect(chapter.versions.count).to(eq(2))
paragraph.destroy
citation.destroy
reified = chapter.versions[1].reify(has_many: true)
expect(reified.sections.first.paragraphs).to(eq([paragraph]))
expect(reified.quotations.first.citations).to(eq([citation]))
end
end
end
end

View File

@ -1,79 +0,0 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe(::PaperTrail, versioning: true) do
describe "widget, reified from a version prior to creation of wotsit" do
it "has a nil wotsit" do
widget = Widget.create(name: "widget_0")
widget.update(name: "widget_1")
widget.create_wotsit(name: "wotsit_0")
widget0 = widget.versions.last.reify(has_one: true)
expect(widget0.wotsit).to be_nil
end
end
describe "widget, reified from a version after creation of wotsit" do
it "has the expected wotsit" do
widget = Widget.create(name: "widget_0")
wotsit = widget.create_wotsit(name: "wotsit_0")
widget.update(name: "widget_1")
widget0 = widget.versions.last.reify(has_one: true)
expect(widget0.wotsit.name).to(eq("wotsit_0"))
expect(widget.reload.wotsit).to(eq(wotsit))
end
end
describe "widget, reified from a version after its wotsit has been updated" do
it "has the expected wotsit" do
widget = Widget.create(name: "widget_0")
wotsit = widget.create_wotsit(name: "wotsit_0")
widget.update(name: "widget_1")
wotsit.update(name: "wotsit_1")
wotsit.update(name: "wotsit_2")
widget.update(name: "widget_2")
wotsit.update(name: "wotsit_3")
widget1 = widget.versions.last.reify(has_one: true)
expect(widget1.wotsit.name).to(eq("wotsit_2"))
expect(widget.reload.wotsit.name).to(eq("wotsit_3"))
end
end
describe "widget, reified with has_one: false" do
it "has the latest wotsit in the database" do
widget = Widget.create(name: "widget_0")
wotsit = widget.create_wotsit(name: "wotsit_0")
widget.update(name: "widget_1")
wotsit.update(name: "wotsit_1")
wotsit.update(name: "wotsit_2")
widget.update(name: "widget_2")
wotsit.update(name: "wotsit_3")
widget1 = widget.versions.last.reify(has_one: false)
expect(widget1.wotsit.name).to(eq("wotsit_3"))
end
end
describe "widget, reified from a version prior to the destruction of its wotsit" do
it "has the wotsit" do
widget = Widget.create(name: "widget_0")
wotsit = widget.create_wotsit(name: "wotsit_0")
widget.update(name: "widget_1")
wotsit.destroy
widget1 = widget.versions.last.reify(has_one: true)
expect(widget1.wotsit).to(eq(wotsit))
expect(widget.reload.wotsit).to be_nil
end
end
describe "widget, refied from version after its wotsit was destroyed" do
it "has a nil wotsit" do
widget = Widget.create(name: "widget_0")
wotsit = widget.create_wotsit(name: "wotsit_0")
widget.update(name: "widget_1")
wotsit.destroy
widget.update(name: "widget_3")
widget2 = widget.versions.last.reify(has_one: true)
expect(widget2.wotsit).to be_nil
end
end
end

View File

@ -17,22 +17,6 @@ module PaperTrail
end
end
describe "track_associations?" do
context "@track_associations is nil" do
it "returns false and prints a deprecation warning" do
config = described_class.instance
config.track_associations = nil
expect(config.track_associations?).to eq(false)
end
after do
::ActiveSupport::Deprecation.silence do
PaperTrail.config.track_associations = true
end
end
end
end
describe ".version_limit", versioning: true do
after { PaperTrail.config.version_limit = nil }

View File

@ -156,15 +156,6 @@ RSpec.describe(::PaperTrail, versioning: true) do
expect(reified_widget.wotsit).to eq(wotsit)
expect(widget.reload.wotsit).to eq(wotsit)
end
it "copy the has_one association when reifying with :has_one => true" do
widget = Widget.create(name: "Henry")
widget.update(name: "Harry")
wotsit = widget.create_wotsit name: "John"
reified_widget = widget.versions.last.reify(has_one: true)
expect(reified_widget.wotsit).to(be_nil)
expect(widget.reload.wotsit).to eq(wotsit)
end
end
context "and has many associated objects" do