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:
parent
febd62ae57
commit
3960d7ff75
4 changed files with 78 additions and 4 deletions
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue