mirror of
https://github.com/paper-trail-gem/paper_trail.git
synced 2022-11-09 11:33:19 -05:00
Merge pull request #1062 from airblade/various_2018-03-11
Various 2018-03-11
This commit is contained in:
commit
87f097c05f
5 changed files with 79 additions and 20 deletions
|
@ -12,7 +12,6 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/).
|
||||||
- PaperTrail now uses `frozen_string_literal`, so you should assume that all
|
- PaperTrail now uses `frozen_string_literal`, so you should assume that all
|
||||||
strings it returns are frozen.
|
strings it returns are frozen.
|
||||||
- Using `where_object_changes` to read YAML from a text column will now raise
|
- Using `where_object_changes` to read YAML from a text column will now raise
|
||||||
- [#1051](https://github.com/airblade/paper_trail/issues/1051) Fix `touch_with_version` does not work with conditional options
|
|
||||||
error, was deprecated in 8.1.0.
|
error, was deprecated in 8.1.0.
|
||||||
|
|
||||||
### Breaking Changes, Minor
|
### Breaking Changes, Minor
|
||||||
|
@ -44,11 +43,17 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/).
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- [#1051](https://github.com/airblade/paper_trail/issues/1051) - `touch_with_version`
|
||||||
|
should always create a version, regardles of the `:only` option
|
||||||
- [#1047](https://github.com/airblade/paper_trail/issues/1047) - A rare issue
|
- [#1047](https://github.com/airblade/paper_trail/issues/1047) - A rare issue
|
||||||
where `touch_with_version` saved less data than expected, but only when the
|
where `touch_with_version` saved less data than expected, but only when the
|
||||||
update callback was not installed, eg. `has_paper_trail(on: [])`
|
update callback was not installed, eg. `has_paper_trail(on: [])`
|
||||||
- [#1042](https://github.com/airblade/paper_trail/issues/1042) - A rare issue
|
- [#1042](https://github.com/airblade/paper_trail/issues/1042) - A rare issue
|
||||||
with load order when using PT outside of rails
|
with load order when using PT outside of rails
|
||||||
|
- [#594](https://github.com/airblade/paper_trail/issues/594) - Improved the
|
||||||
|
error message for a very rare issue in the experimentatl association tracking
|
||||||
|
feature involving two has_one associations, referencing STI models with the
|
||||||
|
same base class, and the same foreign_key.
|
||||||
|
|
||||||
## 8.1.2 (2017-12-22)
|
## 8.1.2 (2017-12-22)
|
||||||
|
|
||||||
|
|
|
@ -805,8 +805,8 @@ string, please try the [paper_trail-globalid][37] gem.
|
||||||
|
|
||||||
### 4.b. Associations
|
### 4.b. Associations
|
||||||
|
|
||||||
**Experimental feature**, not recommended for production. See known issues
|
**Experimental feature with many known issues. Not recommended for production.**
|
||||||
below.
|
See known issues below.
|
||||||
|
|
||||||
PaperTrail can restore three types of associations: Has-One, Has-Many, and
|
PaperTrail can restore three types of associations: Has-One, Has-Many, and
|
||||||
Has-Many-Through. In order to do this, you will need to do two things:
|
Has-Many-Through. In order to do this, you will need to do two things:
|
||||||
|
@ -913,6 +913,8 @@ Associations are an **experimental feature** and have the following known
|
||||||
issues, in order of descending importance.
|
issues, in order of descending importance.
|
||||||
|
|
||||||
1. PaperTrail only reifies the first level of associations.
|
1. PaperTrail only reifies the first level of associations.
|
||||||
|
1. Does not fully support STI (For example, see `spec/models/person_spec.rb` and
|
||||||
|
`PaperTrail::Reifiers::HasOne::FoundMoreThanOne` error)
|
||||||
1. [#542](https://github.com/airblade/paper_trail/issues/542) -
|
1. [#542](https://github.com/airblade/paper_trail/issues/542) -
|
||||||
Not compatible with [transactional tests][34], aka. transactional fixtures.
|
Not compatible with [transactional tests][34], aka. transactional fixtures.
|
||||||
1. Requires database timestamp columns with fractional second precision.
|
1. Requires database timestamp columns with fractional second precision.
|
||||||
|
|
|
@ -5,10 +5,42 @@ module PaperTrail
|
||||||
# Reify a single `has_one` association of `model`.
|
# Reify a single `has_one` association of `model`.
|
||||||
# @api private
|
# @api private
|
||||||
module HasOne
|
module HasOne
|
||||||
|
# A more helpful error message, instead of the AssociationTypeMismatch
|
||||||
|
# you would get if, eg. we were to try to assign a Bicycle to the :car
|
||||||
|
# association (before, if there were multiple records we would just take
|
||||||
|
# the first and hope for the best).
|
||||||
|
# @api private
|
||||||
|
class FoundMoreThanOne < RuntimeError
|
||||||
|
MESSAGE_FMT = <<~STR
|
||||||
|
Unable to reify has_one association. Expected to find one %s,
|
||||||
|
but found %d.
|
||||||
|
|
||||||
|
This is a known issue, and a good example of why association tracking
|
||||||
|
is an experimental feature that should not be used in production.
|
||||||
|
|
||||||
|
That said, this is a rare error. In spec/models/person_spec.rb we
|
||||||
|
reproduce it by having two STI models with the same foreign_key (Car
|
||||||
|
and Bicycle are both Vehicles and the FK for both is owner_id)
|
||||||
|
|
||||||
|
If you'd like to help fix this error, please read
|
||||||
|
https://github.com/airblade/paper_trail/issues/594
|
||||||
|
and see spec/models/person_spec.rb
|
||||||
|
STR
|
||||||
|
|
||||||
|
def initialize(base_class_name, num_records_found)
|
||||||
|
@base_class_name = base_class_name.to_s
|
||||||
|
@num_records_found = num_records_found.to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def message
|
||||||
|
format(MESSAGE_FMT, @base_class_name, @num_records_found)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
# @api private
|
# @api private
|
||||||
def reify(assoc, model, options, transaction_id)
|
def reify(assoc, model, options, transaction_id)
|
||||||
version = load_version_for_has_one(assoc, model, transaction_id, options[:version_at])
|
version = load_version(assoc, model, transaction_id, options[:version_at])
|
||||||
return unless version
|
return unless version
|
||||||
if version.event == "create"
|
if version.event == "create"
|
||||||
create_event(assoc, model, options)
|
create_event(assoc, model, options)
|
||||||
|
@ -33,15 +65,29 @@ module PaperTrail
|
||||||
# Given a has-one association `assoc` on `model`, return the version
|
# Given a has-one association `assoc` on `model`, return the version
|
||||||
# record from the point in time identified by `transaction_id` or `version_at`.
|
# record from the point in time identified by `transaction_id` or `version_at`.
|
||||||
# @api private
|
# @api private
|
||||||
def load_version_for_has_one(assoc, model, transaction_id, version_at)
|
def load_version(assoc, model, transaction_id, version_at)
|
||||||
|
base_class_name = assoc.klass.base_class.name
|
||||||
|
versions = load_versions(assoc, model, transaction_id, version_at, base_class_name)
|
||||||
|
case versions.length
|
||||||
|
when 0
|
||||||
|
nil
|
||||||
|
when 1
|
||||||
|
versions.first
|
||||||
|
else
|
||||||
|
raise FoundMoreThanOne.new(base_class_name, versions.length)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# @api private
|
||||||
|
def load_versions(assoc, model, transaction_id, version_at, base_class_name)
|
||||||
version_table_name = model.class.paper_trail.version_class.table_name
|
version_table_name = model.class.paper_trail.version_class.table_name
|
||||||
model.class.paper_trail.version_class.joins(:version_associations).
|
model.class.paper_trail.version_class.joins(:version_associations).
|
||||||
where("version_associations.foreign_key_name = ?", assoc.foreign_key).
|
where("version_associations.foreign_key_name = ?", assoc.foreign_key).
|
||||||
where("version_associations.foreign_key_id = ?", model.id).
|
where("version_associations.foreign_key_id = ?", model.id).
|
||||||
where("#{version_table_name}.item_type = ?", assoc.klass.base_class.name).
|
where("#{version_table_name}.item_type = ?", base_class_name).
|
||||||
where("created_at >= ? OR transaction_id = ?", version_at, transaction_id).
|
where("created_at >= ? OR transaction_id = ?", version_at, transaction_id).
|
||||||
order("#{version_table_name}.id ASC").
|
order("#{version_table_name}.id ASC").
|
||||||
first
|
load
|
||||||
end
|
end
|
||||||
|
|
||||||
# @api private
|
# @api private
|
||||||
|
|
|
@ -174,14 +174,6 @@ RSpec.describe Article, type: :model, versioning: true do
|
||||||
).to(eq("Some text here."))
|
).to(eq("Some text here."))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "creates a version with touch_with_version & :only option combined" do
|
|
||||||
article = described_class.create
|
|
||||||
|
|
||||||
expect { article.paper_trail.touch_with_version }.to change {
|
|
||||||
PaperTrail::Version.count
|
|
||||||
}.by(+1)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context "#destroy" do
|
context "#destroy" do
|
||||||
|
@ -193,4 +185,13 @@ RSpec.describe Article, type: :model, versioning: true do
|
||||||
expect(article.versions.map(&:event)).to(match_array(%w[create destroy]))
|
expect(article.versions.map(&:event)).to(match_array(%w[create destroy]))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#touch_with_version" do
|
||||||
|
it "creates a version, ignoring the :only option" do
|
||||||
|
article = described_class.create
|
||||||
|
expect { article.paper_trail.touch_with_version }.to change {
|
||||||
|
::PaperTrail::Version.count
|
||||||
|
}.by(+1)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ RSpec.describe Person, type: :model, versioning: true do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#cars and bicycles" do
|
describe "#cars and bicycles" do
|
||||||
it "can be reified", skip: "Known Issue #594" do
|
it "can be reified" do
|
||||||
person = Person.create(name: "Frank")
|
person = Person.create(name: "Frank")
|
||||||
car = Car.create(name: "BMW 325")
|
car = Car.create(name: "BMW 325")
|
||||||
bicycle = Bicycle.create(name: "BMX 1.0")
|
bicycle = Bicycle.create(name: "BMX 1.0")
|
||||||
|
@ -23,11 +23,16 @@ RSpec.describe Person, type: :model, versioning: true do
|
||||||
|
|
||||||
expect(person.reload.versions.length).to(eq(3))
|
expect(person.reload.versions.length).to(eq(3))
|
||||||
|
|
||||||
# Will fail because the correct sub type of the STI model is not present at query.
|
|
||||||
# See https://github.com/airblade/paper_trail/issues/594
|
# See https://github.com/airblade/paper_trail/issues/594
|
||||||
second_version = person.reload.versions.second.reify(has_one: true)
|
expect {
|
||||||
expect(second_version.car.name).to(eq("BMW 325"))
|
person.reload.versions.second.reify(has_one: true)
|
||||||
expect(second_version.bicycle.name).to(eq("BMX 1.0"))
|
}.to(
|
||||||
|
raise_error(::PaperTrail::Reifiers::HasOne::FoundMoreThanOne) do |err|
|
||||||
|
expect(err.message.squish).to match(
|
||||||
|
/Expected to find one Vehicle, but found 2/
|
||||||
|
)
|
||||||
|
end
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue