Introduce config.object_changes_adapter
Allows users to write third-party adapters giving them complete control over the contents of the `object_changes` column.
This commit is contained in:
parent
28aea8b271
commit
776189de57
|
@ -11,7 +11,11 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/).
|
|||
|
||||
### Added
|
||||
|
||||
- None
|
||||
- [#1093](https://github.com/paper-trail-gem/paper_trail/pull/1093) -
|
||||
`PaperTrail.config.object_changes_adapter` - Allows specifying an adapter that will
|
||||
determine how the changes for each version are stored in the object_changes column.
|
||||
An example of this implementation using the hashdiff gem can be found here:
|
||||
[paper_trail-hashdiff](https://github.com/hashwin/paper_trail-hashdiff)
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
18
README.md
18
README.md
|
@ -54,6 +54,7 @@ has been destroyed.
|
|||
- [6. Extensibility](#6-extensibility)
|
||||
- [6.a. Custom Version Classes](#6a-custom-version-classes)
|
||||
- [6.b. Custom Serializer](#6b-custom-serializer)
|
||||
- [6.c. Custom Object Changes](#6c-custom-object-changes)
|
||||
- [7. Testing](#7-testing)
|
||||
- [7.a. Minitest](#7a-minitest)
|
||||
- [7.b. RSpec](#7b-rspec)
|
||||
|
@ -1378,6 +1379,23 @@ class ConvertVersionsObjectToJson < ActiveRecord::Migration
|
|||
end
|
||||
```
|
||||
|
||||
### 6.c. Custom Object Changes
|
||||
|
||||
By default, PaperTrail stores object changes in a before/after array of objects
|
||||
containing keys of columns that have changed in that particular version. You can
|
||||
override this behaviour by using the object_changes_adapter config option:
|
||||
|
||||
```ruby
|
||||
PaperTrail.config.object_changes_adapter = MyObjectChangesAdapter.new
|
||||
```
|
||||
|
||||
A valid adapter is a class that contains the following methods:
|
||||
1. diff: Returns the changeset in the desired format given the changeset in the original format
|
||||
2. load_changeset: Returns the changeset for a given version object
|
||||
3. where_object_changes: Returns the records resulting from the given hash of attributes.
|
||||
|
||||
For an example of such an implementation, see [paper_trail-hashdiff](https://github.com/hashwin/paper_trail-hashdiff)
|
||||
|
||||
## 7. Testing
|
||||
|
||||
You may want to turn PaperTrail off to speed up your tests. See [Turning
|
||||
|
|
|
@ -26,7 +26,8 @@ module PaperTrail
|
|||
STR
|
||||
|
||||
include Singleton
|
||||
attr_accessor :serializer, :version_limit, :association_reify_error_behaviour
|
||||
attr_accessor :serializer, :version_limit, :association_reify_error_behaviour,
|
||||
:object_changes_adapter
|
||||
|
||||
def initialize
|
||||
# Variables which affect all threads, whose access is synchronized.
|
||||
|
|
|
@ -23,6 +23,11 @@ module PaperTrail
|
|||
|
||||
# @api private
|
||||
def execute
|
||||
if PaperTrail.config.object_changes_adapter
|
||||
return PaperTrail.config.object_changes_adapter.where_object_changes(
|
||||
@version_model_class, @attributes
|
||||
)
|
||||
end
|
||||
case @version_model_class.columns_hash["object_changes"].type
|
||||
when :jsonb
|
||||
jsonb
|
||||
|
|
|
@ -418,6 +418,10 @@ module PaperTrail
|
|||
#
|
||||
# @api private
|
||||
def recordable_object_changes(changes)
|
||||
if PaperTrail.config.object_changes_adapter
|
||||
changes = PaperTrail.config.object_changes_adapter.diff(changes)
|
||||
end
|
||||
|
||||
if @record.class.paper_trail.version_class.object_changes_col_is_json?
|
||||
changes
|
||||
else
|
||||
|
|
|
@ -265,6 +265,10 @@ module PaperTrail
|
|||
|
||||
# @api private
|
||||
def load_changeset
|
||||
if PaperTrail.config.object_changes_adapter
|
||||
return PaperTrail.config.object_changes_adapter.load_changeset(self)
|
||||
end
|
||||
|
||||
# First, deserialize the `object_changes` column.
|
||||
changes = HashWithIndifferentAccess.new(object_changes_deserialized)
|
||||
|
||||
|
|
|
@ -16,6 +16,28 @@ module PaperTrail
|
|||
end
|
||||
end
|
||||
|
||||
context "with object_changes_adapter" do
|
||||
let(:adapter) { instance_spy("CustomObjectChangesAdapter") }
|
||||
|
||||
before do
|
||||
PaperTrail.config.object_changes_adapter = adapter
|
||||
allow(adapter).to(
|
||||
receive(:diff).with(
|
||||
hash_including("name" => [nil, "Dashboard"])
|
||||
).and_return([["name", nil, "Dashboard"]])
|
||||
)
|
||||
end
|
||||
|
||||
after do
|
||||
PaperTrail.config.object_changes_adapter = nil
|
||||
end
|
||||
|
||||
it "creates a version with custom changes" do
|
||||
expect(widget.versions.last.object_changes).to eq("---\n- - name\n - \n - Dashboard\n")
|
||||
expect(adapter).to have_received(:diff)
|
||||
end
|
||||
end
|
||||
|
||||
context "serializer is JSON" do
|
||||
before do
|
||||
PaperTrail.serializer = PaperTrail::Serializers::JSON
|
||||
|
@ -202,6 +224,25 @@ module PaperTrail
|
|||
}.to raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
context "with object_changes_adapter configured" do
|
||||
after do
|
||||
PaperTrail.config.object_changes_adapter = nil
|
||||
end
|
||||
|
||||
it "calls the adapter's where_object_changes method" do
|
||||
adapter = instance_spy("CustomObjectChangesAdapter")
|
||||
bicycle = Bicycle.create!(name: "abc")
|
||||
allow(adapter).to(
|
||||
receive(:where_object_changes).with(Version, name: "abc")
|
||||
).and_return(bicycle.versions[0..1])
|
||||
PaperTrail.config.object_changes_adapter = adapter
|
||||
expect(
|
||||
bicycle.versions.where_object_changes(name: "abc")
|
||||
).to match_array(bicycle.versions[0..1])
|
||||
expect(adapter).to have_received(:where_object_changes)
|
||||
end
|
||||
end
|
||||
|
||||
# Only test json and jsonb columns. where_object_changes no longer
|
||||
# supports text columns.
|
||||
if column_datatype_override
|
||||
|
|
|
@ -51,6 +51,28 @@ RSpec.describe(::PaperTrail, versioning: true) do
|
|||
expect(changeset["updated_at"][0]).to be_nil
|
||||
expect(changeset["updated_at"][1].to_i).to eq(@widget.updated_at.to_i)
|
||||
end
|
||||
|
||||
context "custom object_changes_adapter" do
|
||||
let(:adapter) { instance_spy("CustomObjectChangesAdapter") }
|
||||
|
||||
before do
|
||||
PaperTrail.config.object_changes_adapter = adapter
|
||||
allow(adapter).to(
|
||||
receive(:load_changeset).with(@widget.versions.last).and_return(a: "b", c: "d")
|
||||
)
|
||||
end
|
||||
|
||||
after do
|
||||
PaperTrail.config.object_changes_adapter = nil
|
||||
end
|
||||
|
||||
it "calls the adapter's load_changeset method" do
|
||||
changeset = @widget.versions.last.changeset
|
||||
expect(changeset[:a]).to eq("b")
|
||||
expect(changeset[:c]).to eq("d")
|
||||
expect(adapter).to have_received(:load_changeset)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "and then updated without any changes" do
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This custom serializer excludes nil values
|
||||
class CustomObjectChangesAdapter
|
||||
def diff(changes)
|
||||
changes
|
||||
end
|
||||
|
||||
def load_changeset(version)
|
||||
version.changeset
|
||||
end
|
||||
|
||||
def where_object_changes(klass, attributes)
|
||||
klass.where(attributes)
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue