Track changes on destroy (#1123)

this involved refactoring the internal serialization code so it'd be possible to run it on non-Rails-provided changes
This commit is contained in:
Sean Linsley 2018-08-13 20:11:24 -05:00 committed by Jared Beck
parent 40fa4c0b4b
commit c145cc46c2
7 changed files with 53 additions and 15 deletions

View File

@ -31,6 +31,9 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/).
- [#1099](https://github.com/paper-trail-gem/paper_trail/issues/1099) -
Ability to save ~50% storage space by making the `object` column optional.
Note that this disables `reify` and `where_object`.
- `object_changes` is now populated on destroy in order to make
`where_object_changes` usable when you've dropped the `object` column.
See the issue for a backport migration.
### Fixed

View File

@ -119,14 +119,25 @@ module PaperTrail
end
# @api private
def changes
notable_changes = changes_in_latest_version.delete_if { |k, _v|
!notably_changed.include?(k)
}
def prepare_object_changes(changes)
changes = serialize_object_changes(changes)
changes = recordable_object_changes(changes)
changes
end
# @api private
def serialize_object_changes(changes)
AttributeSerializers::ObjectChangesAttribute.
new(@record.class).
serialize(notable_changes)
notable_changes.to_hash
serialize(changes)
changes.to_hash
end
# @api private
def notable_changes
changes_in_latest_version.delete_if { |k, _v|
!notably_changed.include?(k)
}
end
# Rails 5.1 changed the API of `ActiveRecord::Dirty`. See

View File

@ -20,7 +20,8 @@ module PaperTrail
data[:created_at] = @record.updated_at
end
if record_object_changes? && changed_notably?
data[:object_changes] = recordable_object_changes(changes)
changes = notable_changes
data[:object_changes] = prepare_object_changes(changes)
end
merge_metadata_into(data)
end

View File

@ -21,6 +21,11 @@ module PaperTrail
if record_object?
data[:object] = recordable_object(false)
end
if record_object_changes?
# Rails' implementation returns nothing on destroy :/
changes = @record.attributes.map { |attr, value| [attr, [value, nil]] }.to_h
data[:object_changes] = prepare_object_changes(changes)
end
merge_metadata_into(data)
end
end

View File

@ -17,7 +17,7 @@ module PaperTrail
def initialize(record, in_after_callback, is_touch, force_changes)
super(record, in_after_callback)
@is_touch = is_touch
@changes = force_changes.nil? ? changes : force_changes
@force_changes = force_changes
end
# Return attributes of nascent `Version` record.
@ -35,7 +35,8 @@ module PaperTrail
data[:object] = recordable_object(@is_touch)
end
if record_object_changes?
data[:object_changes] = recordable_object_changes(@changes)
changes = @force_changes.nil? ? notable_changes : @force_changes
data[:object_changes] = prepare_object_changes(changes)
end
merge_metadata_into(data)
end

View File

@ -22,8 +22,18 @@ RSpec.describe NoObject, versioning: true do
a = n.versions.last.attributes
expect(a).not_to include "object"
expect(a["event"]).to eq "destroy"
expect(a["object_changes"]).to be_nil
expect(a["object_changes"]).to start_with("---")
expect(a["metadatum"]).to eq(42)
# New feature: destroy populates object_changes
# https://github.com/paper-trail-gem/paper_trail/pull/1123
h = YAML.safe_load(a["object_changes"], [::Time])
expect(h["id"]).to eq([n.id, nil])
expect(h["letter"]).to eq([n.letter, nil])
expect(h["created_at"][0]).to be_present
expect(h["created_at"][1]).to be_nil
expect(h["updated_at"][0]).to be_present
expect(h["updated_at"][1]).to be_nil
end
describe "reify" do

View File

@ -264,11 +264,18 @@ RSpec.describe(::PaperTrail, versioning: true) do
expect(widget.versions.last.item).to be_nil
end
it "not have changes" do
widget = Widget.create(name: "Henry")
widget.update_attributes(name: "Harry")
widget.destroy
expect(widget.versions.last.changeset).to eq({})
it "has changes" do
book = Book.create! title: "A"
changes = YAML.load book.versions.last.attributes["object_changes"]
expect(changes).to eq("id" => [nil, book.id], "title" => [nil, "A"])
book.update! title: "B"
changes = YAML.load book.versions.last.attributes["object_changes"]
expect(changes).to eq("title" => %w[A B])
book.destroy
changes = YAML.load book.versions.last.attributes["object_changes"]
expect(changes).to eq("id" => [book.id, nil], "title" => ["B", nil])
end
end
end