make the object column optional

This commit is contained in:
Sean Linsley 2018-07-24 10:04:53 -05:00
parent ac710b3197
commit 68ecf779da
8 changed files with 80 additions and 4 deletions

View File

@ -11,7 +11,9 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/).
### Added
- None
- [#1099](https://github.com/paper-trail-gem/paper_trail/issues/1099) -
Ability to save ~50% storage space by making the `object` column optional.
Note that this disables `reify` and `where_object`.
### Fixed

View File

@ -1212,6 +1212,12 @@ A valid adapter is a class that contains the following methods:
For an example of such an implementation, see [paper_trail-hashdiff](https://github.com/hashwin/paper_trail-hashdiff)
### 6.d. Excluding the Object Column
The `object` column ends up storing a lot of duplicate data if you have models that have many columns,
and that are updated many times. You can save ~50% of storage space by removing the column from the
versions table. It's important to note that this will disable `reify` and `where_object`.
## 7. Testing
You may want to turn PaperTrail off to speed up your tests. See [Turning

View File

@ -254,6 +254,13 @@ module PaperTrail
@record.class.paper_trail.version_class.column_names.include?("object_changes")
end
# Returns a boolean indicating whether to store the original object during save.
#
# @api private
def record_object?
@record.class.paper_trail.version_class.column_names.include?("object")
end
# Returns an object which can be assigned to the `object` attribute of a
# nascent version record. If the `object` column is a postgres `json`
# column, then a hash can be used in the assignment, otherwise the column

View File

@ -16,9 +16,11 @@ module PaperTrail
item_id: @record.id,
item_type: @record.class.base_class.name,
event: @record.paper_trail_event || "destroy",
object: recordable_object(false),
whodunnit: PaperTrail.request.whodunnit
}
if record_object?
data[:object] = recordable_object(false)
end
merge_metadata_into(data)
end
end

View File

@ -27,12 +27,14 @@ module PaperTrail
def data
data = {
event: @record.paper_trail_event || "update",
object: recordable_object(@is_touch),
whodunnit: PaperTrail.request.whodunnit
}
if @record.respond_to?(:updated_at)
data[:created_at] = @record.updated_at
end
if record_object?
data[:object] = recordable_object(@is_touch)
end
if record_object_changes?
data[:object_changes] = recordable_object_changes(@changes)
end

View File

@ -18,7 +18,10 @@ module PaperTrail
# @api private
def execute
case @version_model_class.columns_hash["object"].type
column = @version_model_class.columns_hash["object"]
raise "where_object can't be called without an object column" unless column
case column.type
when :jsonb
jsonb
when :json

View File

@ -205,6 +205,9 @@ module PaperTrail
# - `:preserve` - Attributes undefined in version record are not modified.
#
def reify(options = {})
unless self.class.column_names.include? "object"
raise "reify can't be called without an object column"
end
return nil if object.nil?
::PaperTrail::Reifier.reify(self, options)
end

View File

@ -877,4 +877,55 @@ RSpec.describe(::PaperTrail, versioning: true) do
expect(widget.versions.empty?).to(eq(true))
end
end
context "without the object column" do
# rubocop:disable RSpec/BeforeAfterAll
before :all do
ActiveRecord::Migration.remove_column :versions, :object
PaperTrail::Version.reset_column_information
end
after :all do
ActiveRecord::Migration.add_column :versions, :object, :text
PaperTrail::Version.reset_column_information
end
# rubocop:enable RSpec/BeforeAfterAll
it "versions are created" do
song = Song.create length: 4
version = song.versions.last.attributes
expect(version).not_to include "object"
expect(version["event"]).to eq "create"
expect(version["object_changes"]).to start_with("---")
song.update length: 5
version = song.versions.last.attributes
expect(version).not_to include "object"
expect(version["event"]).to eq "update"
expect(version["object_changes"]).to start_with("---")
song.destroy
version = song.versions.last.attributes
expect(version).not_to include "object"
expect(version["event"]).to eq "destroy"
expect(version["object_changes"]).to eq nil
end
it "reify doesn't work" do
song = Song.create length: 4
song.update length: 5
expect do
song.versions.first.reify
end.to raise_error "reify can't be called without an object column"
end
it "where_object doesn't work" do
song = Song.create length: 4
song.update length: 5
expect do
song.versions.where_object length: 4
end.to raise_error "where_object can't be called without an object column"
end
end
end