-
Notifications
You must be signed in to change notification settings - Fork 899
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
How to force metadata saving in case when there is no changes in model #447
Comments
You can always trigger a manual version creation just by mimicing what the source code is doing. So you could make a method like this (on your model): def generate_version!
object_attrs = object_attrs_for_paper_trail(self)
object_value = self.class.paper_trail_version_class.object_col_is_json? ? object_attrs : PaperTrail.serializer.dump(object_attrs)
data = {
event: 'associations_change', # or some custom event name?
object: object_value,
whodunnit: PaperTrail.whodunnit
}
send(self.class.versions_association_name).create! merge_metadata(data)
end |
Thank you very much for your answer. I spent a lot of time to find the better solution for my task. I post it, probably someone will find it usefull. I need to track not only record fields and belongs_to item but also has_many relations. Main requirement was to save all changes in one version. We use I added There is a concern which implements main logic: module WithVersions
extend ActiveSupport::Concern
included do
# Will save new version of the object
after_commit do
generate_version!
end
# Cache object changes to access it from after_commit
after_update do
@event = 'update'
object_changes
end
# Cache all details to access it from after_commit
before_destroy do
@event = 'destroy'
object_value
object_changes
object_metadata
end
after_create do
@event = 'create'
end
end
module ClassMethods
def has_paper_trail_with_relations(&block)
has_paper_trail on: [:nothing], meta: {
related_objects: :related_objects_snapshot_as_json,
related_objects_changes: :related_objects_snapshot_changes_as_json
}
self.class_eval do
define_method(:related_objects_snapshot) { self.instance_eval &block }
end
end
end
def related_objects_snapshot_as_json
related_objects_snapshot.to_json
end
def related_objects_snapshot_changes
prev_version = self.versions.last
if prev_version
old_snapshot = JSON.parse(prev_version.related_objects).with_indifferent_access
end
old_snapshot ||= {}
new_snapshot = related_objects_snapshot.with_indifferent_access
new_snapshot.keys.reduce({}) do |diff, key|
old_value = old_snapshot[key]
new_value = new_snapshot[key]
if old_value.class == new_value.class
relation_diff = {}
added = new_value - old_value
relation_diff[:+] = added unless added.empty?
removed = old_value - new_value
relation_diff[:-] = removed unless removed.empty?
diff[key] = relation_diff unless relation_diff.empty?
end
diff
end
end
def related_objects_snapshot_changes_as_json
related_objects_snapshot_changes.to_json
end
def object_value
@object_value ||= begin
object_attrs = object_attrs_for_paper_trail(self)
self.class.paper_trail_version_class.object_col_is_json? ? object_attrs : PaperTrail.serializer.dump(object_attrs)
end
end
def object_changes
@object_changes ||= begin
self.class.paper_trail_version_class.object_changes_col_is_json? ? changes_for_paper_trail : PaperTrail.serializer.dump(changes_for_paper_trail)
end
end
def object_metadata
@object_metadata ||= merge_metadata({})
end
def generate_version!
data = {
event: @event,
object: object_value,
object_changes: object_changes,
whodunnit: PaperTrail.whodunnit,
item_type: self.class.name,
item_id: self.id,
}
if @event == 'create' && respond_to?(:created_at)
data[PaperTrail.timestamp_field] = created_at
elsif @event == 'update' && respond_to?(:updated_at)
data[PaperTrail.timestamp_field] = updated_at
end
PaperTrail::Version.create! data.merge!(object_metadata)
end
end Sample of usage class Movie < ActiveRecord::Base
# ...
include WithVersions
has_paper_trail_with_relations do
{
cast_members: cast_members.map { |c| { role: c.role, character_name: c.character_name, person: c.person.id } },
genres: genres.map(&:id),
images: images.map(&:url),
}
end Also there is a some lines of code for rendering extended |
If you are trying to track associations, you may want to give the |
It isn't paper_trail issue, it's a question.
I have papertrailed model, and I use metadata fileds to save related objects snapshot changes. But if there is no changes in the main object, no versions with metadata is saved.
I need some way to take part in paper_trail's decision about save/not save the new version of object
The text was updated successfully, but these errors were encountered: