-
Notifications
You must be signed in to change notification settings - Fork 901
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Track changes on destroy #1123
Track changes on destroy #1123
Conversation
Regarding the config option, PaperTrail.config.record_changes_on_destroy = true You were planning on doing a normal deprecation cycle, which would normally be the right call,
but the next release will be a major version, so I'd rather just start always tracking changes on destroy, and not bother with the intermediate step of having a config option. I don't understand the rationale for removing
Right, but people might want to keep |
When trying to save disk space, Do we know if anyone is using |
99c6e60
to
f80e5c0
Compare
I updated this PR with a refactor of the serialization code to work around the fact that Rails doesn't provide Sometimes the test fails in this way: 1) PaperTrail record_changes_on_destroy works
Failure/Error: expect(changes).to eq "id" => [nil, song.id], "length" => [nil, 240]
expected: {"id"=>[nil, 1], "length"=>[nil, 240]}
got: {"length"=>[nil, 4], "id"=>[nil, 1]}
(compared using ==)
Diff:
@@ -1,3 +1,3 @@
"id" => [nil, 1],
-"length" => [nil, 240],
+"length" => [nil, 4], ... and sometimes it passes. For me locally, it has always passed. Which doesn't make sense: the test is setting |
f80e5c0
to
9c9e97b
Compare
Ah, the song model has this code in it: paper_trail/spec/dummy_app/app/models/song.rb Lines 21 to 31 in 3f9b5be
So we should be expecting a length of 240 to be saved in every case. But with Rails 4.2, a length of 4 is incorrectly being stored. That might've made sense on the destroy event b/c this PR calls |
OK, makes sense. Let's drop
If you want to do that as a separate PR against the |
In that case a deprecation release is probably overkill since there's a clear path if they want the old behavior. Though we'll need to document this in the upgrade guide:
We also need to figure out this test failure. AFAICT, it's signalling a bug in Rails 4.2. Should I just update the test to use a different model? |
I can try to help with the |
Yes, sorry, that'd be removed. |
5c06395
to
48d344b
Compare
Okay this should be complete, code-wise. I also added a commit to remove I'm planning on testing these changes in my application next week. If this ends up getting merged before then, I'll submit another PR with upgrade steps (particularly, a migration to populate data on old destroy events). |
I like how you've split this into two commits, but can you please split it into two (new) PRs? I have separate comments for each commit and I think they'll be easier to discuss as separate PRs. Thanks. |
Sure. I broke it out into #1130. Now to update this PR... |
48d344b
to
4ec48ef
Compare
lib/paper_trail/events/create.rb
Outdated
data[:object_changes] = recordable_object_changes(changes) | ||
changes = serialize_changes(notable_changes) | ||
changes = recordable_object_changes(changes) | ||
data[:object_changes] = changes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm concerned this changes the order of operations. Previously, object_changes_adapter
happened first, before AttributeSerializers::ObjectChangesAttribute
. Now it happens after. Was that intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, that's not how I'm reading it. Previously we had this code:
data[:object_changes] = recordable_object_changes(changes)
changes
used to find the notable changes and serialize them. then recordable_object_changes
, the last operation, is what called object_changes_adapter
.
lib/paper_trail/events/destroy.rb
Outdated
changes = serialize_changes(changes) | ||
changes = recordable_object_changes(changes) | ||
data[:object_changes] = changes | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is getting a bit long. I think we can extract some part of this into a new method. Maybe something like
if record_object_changes?
data[:object_changes] = object_changes
end
# ..
private
# Rails' implementation returns nothing on destroy, so we build our own hash.
def object_changes
changes = @record.attributes.map { |attr, value| [attr, [value, nil]] }.to_h
changes = serialize_changes(changes)
recordable_object_changes(changes)
end
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous level of abstraction we had in changes
clearly wasn't right, since both the update and destroy events would customize the underlying changeset.
Currently every event has these two lines in common w/out any modifications:
changes = serialize_changes(changes)
changes = recordable_object_changes(changes)
What if we introduced a method to wrap those?
# destroy.rb
if record_object_changes?
changes = @record.attributes.map { |attr, value| [attr, [value, nil]] }.to_h
data[:object_changes] = prepare_object_changes(changes)
end
# base.rb
def prepare_object_changes(changes)
recordable_object_changes(serialize_changes(changes))
end
spec/paper_trail/model_spec.rb
Outdated
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"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The predominant code style for this repo uses an additional set of parentheses for the eq
matcher, and sometimes for the to
as well.
expect(changes).to eq("id" => [nil, book.id], "title" => [nil, "A"])
In my experience, using parentheses for neither is hard for many people to follow.
spec/paper_trail/model_spec.rb
Outdated
|
||
book.destroy | ||
changes = YAML.load book.versions.last.attributes["object_changes"] | ||
expect(changes).to eq "id" => [book.id, nil], "title" => ["B", nil] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test looks sufficient, but it'd be great to also have unit tests in eg. spec/paper_trail/events/destroy_spec.rb
. Not a requirement, use your judgement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't look like the unit tests for the various events currently cover object
or object_changes
. There's an argument to be made that a lot of the tests in mode_spec.rb
should be moved out, but I think that's beyond the scope of this PR. This test is probably enough for now.
4ec48ef
to
a4477d6
Compare
this involved refactoring the internal serialization code so it'd be possible to run it on non-Rails-provided changes
a4477d6
to
c33661e
Compare
I went ahead and refactored this PR. Let me know if there's anything else that needs changing. |
🤔
|
"Expect to be a kind of Time" is the name of my memoirs. |
Thanks for your work on this, Sean.
That'd be great, thanks.
How important is such a migration? Should we wait for it before releasing PT 10? |
🎉 In the changelog I noted to check #1099 for a backport migration, so a new release doesn't need to wait on the migration. |
this involved refactoring the internal serialization code so it'd be possible to run it on non-Rails-provided changes
Part 2 of #1099, followup to #1122.
Rails'
changes
method doesn't return any attributes on destroy, so this PR generates them by hand (though create and update still rely on Rails). In order to still apply serialization to the changes, some of the internal API has been refactored.object_changes
on historical data