module Leaf::Editable extend ActiveSupport::Concern MINIMUM_TIME_BETWEEN_VERSIONS = 10.minutes included do has_many :edits, dependent: :delete_all after_update :record_moved_to_trash, if: :was_trashed? end def edit(leafable_params: {}, leaf_params: {}) if record_new_edit?(leafable_params) update_and_record_edit leaf_params, leafable_params else update_without_recording_edit leaf_params, leafable_params end end private def record_new_edit?(leafable_params) will_change_leafable?(leafable_params) && last_edit_old? end def last_edit_old? edits.empty? || edits.last.created_at.before?(MINIMUM_TIME_BETWEEN_VERSIONS.ago) end def will_change_leafable?(leafable_params) leafable_params.select do |key, value| leafable.attributes[key.to_s] != value end.present? end def update_without_recording_edit(leaf_params, leafable_params) transaction do leafable.update!(leafable_params) edits.last&.touch update! leaf_params end end def update_and_record_edit(leaf_params, leafable_params) transaction do new_leafable = dup_leafable_with_attachments leafable new_leafable.update!(leafable_params) edits.revision.create!(leafable: leafable) update! leaf_params.merge(leafable: new_leafable) end end def dup_leafable_with_attachments(leafable) leafable.dup.tap do |new| leafable.attachment_reflections.each do |name, _| new.send(name).attach(leafable.send(name).blob) end end end def record_moved_to_trash edits.trash.create!(leafable: leafable) end def was_trashed? trashed? && previous_changes.include?(:status) end end