1
0
Fork 0
mirror of https://github.com/paper-trail-gem/paper_trail.git synced 2022-11-09 11:33:19 -05:00

Add paper_trail.update_columns (#1037)

* 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
This commit is contained in:
Tyler Rick 2018-01-22 19:21:08 -08:00 committed by Jared Beck
parent febd62ae57
commit 3960d7ff75
4 changed files with 78 additions and 4 deletions

View file

@ -26,6 +26,7 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/).
### Added
- [#1037](https://github.com/airblade/paper_trail/pull/1037) Add `paper_trail.update_columns`
- [#961](https://github.com/airblade/paper_trail/issues/961) - Instead of
crashing when misconfigured Custom Version Classes are used, an error will be
raised earlier, with a much more helpful message.

View file

@ -226,7 +226,7 @@ module PaperTrail
data[:created_at] = @record.updated_at
end
if record_object_changes? && changed_notably?
data[:object_changes] = recordable_object_changes
data[:object_changes] = recordable_object_changes(changes)
end
add_transaction_id_to(data)
merge_metadata_into(data)
@ -284,7 +284,7 @@ module PaperTrail
@in_after_callback = false
end
# Returns data for record update
# Returns data for record_update
# @api private
def data_for_update
data = {
@ -296,7 +296,35 @@ module PaperTrail
data[:created_at] = @record.updated_at
end
if record_object_changes?
data[:object_changes] = recordable_object_changes
data[:object_changes] = recordable_object_changes(changes)
end
add_transaction_id_to(data)
merge_metadata_into(data)
end
# @api private
def record_update_columns(changes)
return unless enabled?
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)
else
update_transaction_id(version)
save_associations(version)
end
end
# Returns data for record_update_columns
# @api private
def data_for_update_columns(changes)
data = {
event: @record.paper_trail_event || "update",
object: recordable_object,
whodunnit: PaperTrail.whodunnit
}
if record_object_changes?
data[:object_changes] = recordable_object_changes(changes)
end
add_transaction_id_to(data)
merge_metadata_into(data)
@ -322,7 +350,7 @@ module PaperTrail
# otherwise the column is a `text` column, and we must perform the
# serialization here, using `PaperTrail.serializer`.
# @api private
def recordable_object_changes
def recordable_object_changes(changes)
if @record.class.paper_trail.version_class.object_changes_col_is_json?
changes
else
@ -406,6 +434,28 @@ module PaperTrail
@record.save!(validate: false)
end
# Like the `update_column` method from `ActiveRecord::Persistence`, but also
# creates a version to record those changes.
# @api public
def update_column(name, value)
update_columns(name => value)
end
# Like the `update_columns` method from `ActiveRecord::Persistence`, but also
# creates a version to record those changes.
# @api public
def update_columns(attributes)
# `@record.update_columns` skips dirty tracking, so we can't just use `@record.changes` or
# @record.saved_changes` from `ActiveModel::Dirty`. We need to build our own hash with the
# changes that will be made directly to the database.
changes = {}
attributes.each do |k, v|
changes[k] = [@record[k], v]
end
@record.update_columns(attributes)
record_update_columns(changes)
end
# Returns the object (not a Version) as it was at the given timestamp.
def version_at(timestamp, reify_options = {})
# Because a version stores how its object looked *before* the change,

View file

@ -21,6 +21,15 @@ module On
end
end
describe ".paper_trail.update_columns" do
it "creates a version record" do
widget = Widget.create
assert_equal 1, widget.versions.length
widget.paper_trail.update_columns(name: "Bugle")
assert_equal 2, widget.versions.length
end
end
describe "#update_attributes" do
it "does not create any version records" do
record = described_class.create(name: "Alice")

View file

@ -285,6 +285,20 @@ RSpec.describe Widget, type: :model do
end
end
describe ".paper_trail.update_columns", versioning: true do
it "creates a version record" do
widget = Widget.create
expect(widget.versions.count).to eq(1)
Timecop.freeze Time.now do
widget.paper_trail.update_columns(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"])
expect(widget.versions.last.created_at.to_i).to eq(Time.now.to_i)
end
end
end
describe "#update", versioning: true do
it "creates a version record" do
widget = Widget.create