-
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
Add paper_trail.update_columns #1037
Add paper_trail.update_columns #1037
Conversation
Add paper_trail.update_columns so you can record a version when using update_columns (which skips all callbacks, including the after_update callback).
Hi Tyler. I don't have time to review this right now, but a few quick thoughts:
Sorry, that's not much of a review, but it's all I have time for right now. |
argument so that we don't need an exception to Style/MethodCallWithoutArgsParentheses
record_update_columns, at @jaredbeck's request
As you wish. I've added back the duplication. No, it was only necessary to allow reuse without duplication. |
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 is close, nice work. Please also add an entry to the changelog under Unreleased -> Added.
spec/models/widget_spec.rb
Outdated
expect(widget.versions.count).to eq(2) | ||
expect(widget.versions.last.event).to(eq("update")) | ||
expect(widget.versions.last.changeset[:name]).to eq([nil, "Bugle"]) | ||
expect(widget.versions.last.created_at.to_i).to eq(Time.now.to_i) |
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.
We can't guarantee that this expectation will pass. The operation could take more than a second, or cross a second-boundary.
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.
True... should I just use a Timecop.freeze
to keep Time.now
fixed? (That's what I did.)
Looks like there's at least one other expectation with the same problem:
expect(widget.paper_trail.version_at(Time.now)).to be_nil
spec/models/widget_spec.rb
Outdated
expect(widget.versions.count).to eq(1) | ||
Timecop.travel 1.second.since # because MySQL lacks fractional seconds precision | ||
widget.paper_trail.update_columns(name: "Bugle") | ||
# widget.update_attributes(name: "Bugle") |
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.
Please delete all commented-out code.
spec/models/widget_spec.rb
Outdated
# widget.update_attributes(name: "Bugle") | ||
expect(widget.versions.count).to eq(2) | ||
expect(widget.versions.last.event).to(eq("update")) | ||
expect(widget.versions.last.changeset[:name]).to eq([nil, "Bugle"]) |
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.
Good, detailed test.
widget = Widget.create | ||
assert_equal 1, widget.versions.length | ||
widget.paper_trail.update_columns(name: "Bugle") | ||
assert_equal 2, widget.versions.length |
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.
Just FYI, there is a nice way to "expect a change" in RSpec. You don't have to use it, what you have is fine, just thought I'd share.
expect {
widget.paper_trail.update_columns(name: "Bugle")
}.to(change { widget.versions.length }).from(1).to(2)
lib/paper_trail/record_trail.rb
Outdated
end | ||
|
||
def record_update_columns(changes) | ||
if enabled? |
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'd prefer a "guard clause" here.
return unless enabled?
lib/paper_trail/record_trail.rb
Outdated
versions_assoc = @record.send(@record.class.versions_association_name) | ||
version = versions_assoc.create(data_for_update_columns(changes)) | ||
if version.errors.any? | ||
log_version_errors(version, :update) |
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.
Nice job including details like this error logging, and the enabled?
check. 👍
lib/paper_trail/record_trail.rb
Outdated
object: recordable_object, | ||
whodunnit: PaperTrail.whodunnit | ||
} | ||
data[:created_at] = Time.now |
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.
Why do we have to set created_at? Won't AR do this?
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 don't remember. I think it may have been necessary in an earlier version I had but it appears to work fine without it, so I'll remove it. Good catch.
lib/paper_trail/record_trail.rb
Outdated
changes = {} | ||
attributes.each do |k, v| | ||
changes[k] = [@record[k], v] | ||
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.
Let's add a comment somewhere here explaining that we are constructing a hash that is compatible with the hash returned by ActiveModel::Dirty#changes
.
Thanks for the detailed review, @jaredbeck. I've pushed up another commit to fix the things you suggested. |
dfb5983
to
8b8d9a4
Compare
- Use a guard in `record_update_columns` - Use Timecop.freeze so that we can guarantee that an expectation will pass - Add some comments
8b8d9a4
to
3b9ee26
Compare
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.
Looks good. I think we can merge this as is, but maybe we should follow up with some documentation in the readme also, or else people will not know this feature exists.
Merged. Thanks for your contribution, Tyler!
Please follow up with a docs PR, so other people can discover this feature. Thanks! |
This is excellent, we were just looking at how to solve this issue cleanly. |
@jaredbeck Can you please provide documentation for this? |
As I said above, contributions of documentation are welcome, thanks! |
* Add paper_trail.update_columns Add paper_trail.update_columns so you can record a version when using update_columns (which skips all callbacks, including the after_update callback). * Change recordable_object_changes to not have a default for the changes argument so that we don't need an exception to Style/MethodCallWithoutArgsParentheses * Add back the duplication between record_update and record_update_columns, at @jaredbeck's request * - Add Changelog entry for `paper_trail.update_columns` - Use a guard in `record_update_columns` - Use Timecop.freeze so that we can guarantee that an expectation will pass - Add some comments
Add paper_trail.update_columns so you can record a version when using
update_columns
(which skips all callbacks, including theafter_update
callback).