Skip to content

Commit

Permalink
add the ability to track changes on destroy
Browse files Browse the repository at this point in the history
this involved refactoring the internal serialization code so it'd be possible to run it on non-Rails-provided changes
  • Loading branch information
seanlinsley committed Aug 6, 2018
1 parent 3f9b5be commit 9c9e97b
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ 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`.
- Ability to track `object_changes` on destroy, to complement ⬆️.

### Fixed

Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,14 @@ The `object` column ends up storing a lot of duplicate data if you have models t
and that are updated many times. You can save ~50% of storage space by removing the column from the
versions table. It's important to note that this will disable `reify` and `where_object`.

Removing the `object` column also makes it so that destroy events don't store any attributes, since
the default behavior on destroy is only to populate the `object` column.
That can be fixed with this setting:

```ruby
PaperTrail.config.record_changes_on_destroy = true
```

## 7. Testing

You may want to turn PaperTrail off to speed up your tests. See [Turning
Expand Down
1 change: 1 addition & 0 deletions lib/paper_trail/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Config
:classes_warned_about_sti_item_types,
:i_have_updated_my_existing_item_types,
:object_changes_adapter,
:record_changes_on_destroy,
:serializer,
:version_limit
)
Expand Down
16 changes: 10 additions & 6 deletions lib/paper_trail/events/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,18 @@ def changed_in_latest_version
end

# @api private
def changes
notable_changes = changes_in_latest_version.delete_if { |k, _v|
!notably_changed.include?(k)
}
def serialize_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
Expand Down
4 changes: 3 additions & 1 deletion lib/paper_trail/events/create.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ def data
data[:created_at] = @record.updated_at
end
if record_object_changes? && changed_notably?
data[:object_changes] = recordable_object_changes(changes)
changes = serialize_changes(notable_changes)
changes = recordable_object_changes(changes)
data[:object_changes] = changes
end
merge_metadata_into(data)
end
Expand Down
7 changes: 7 additions & 0 deletions lib/paper_trail/events/destroy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ def data
if record_object?
data[:object] = recordable_object(false)
end
if record_object_changes? && PaperTrail.config.record_changes_on_destroy
# Rails' implementation returns nothing on destroy :/
changes = @record.attributes.map { |attr, value| [attr, [value, nil]] }.to_h
changes = serialize_changes(changes)
changes = recordable_object_changes(changes)
data[:object_changes] = changes
end
merge_metadata_into(data)
end
end
Expand Down
7 changes: 5 additions & 2 deletions lib/paper_trail/events/update.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Update < Base
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.
Expand All @@ -35,7 +35,10 @@ def data
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
changes = serialize_changes(changes)
changes = recordable_object_changes(changes)
data[:object_changes] = changes
end
merge_metadata_into(data)
end
Expand Down
15 changes: 15 additions & 0 deletions spec/paper_trail/model_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -928,4 +928,19 @@
end.to raise_error "where_object can't be called without an object column"
end
end

context "record_changes_on_destroy" do
before { PaperTrail.config.record_changes_on_destroy = true }
after { PaperTrail.config.record_changes_on_destroy = nil }

it "works" do
song = Song.create(length: 4)
changes = YAML.load song.versions.last.attributes["object_changes"]
expect(changes).to eq "id" => [nil, song.id], "length" => [nil, 240]

song.destroy
changes = YAML.load song.versions.last.attributes["object_changes"]
expect(changes).to eq "id" => [song.id, nil], "length" => [240, nil], "name" => [nil, nil]
end
end
end

0 comments on commit 9c9e97b

Please sign in to comment.