+ def manage_versioning
+ should_preserve_version = should_preserve_version? # Time sensitive, cache value
+ return(yield) unless (should_preserve_version || syncable_updates.any?)
+
+ # Put aside the changes because with_lock does a record reload
+ changes = self.changes
+ snapshot = nil
+ restore_attributes
+ with_lock do
+ # Copy the original state to save it as old version
+ if should_preserve_version
+ snapshot = self.dup
+ snapshot.uuid = nil # Reset UUID so it's created as a new record
+ snapshot.created_at = self.created_at
+ snapshot.modified_at = self.modified_at_was
+ end
+
+ # Restore requested changes on the current version
+ changes.keys.each do |attr|
+ if attr == 'preserve_version' && changes[attr].last == false && !should_preserve_version
+ next # Ignore false assignment, once true it'll be true until next version
+ end
+ self.attributes = {attr => changes[attr].last}
+ if attr == 'uuid'
+ # Also update the current version reference
+ self.attributes = {'current_version_uuid' => changes[attr].last}
+ end
+ end
+
+ if should_preserve_version
+ self.version += 1
+ end
+
+ yield
+
+ sync_past_versions if syncable_updates.any?
+ if snapshot
+ snapshot.attributes = self.syncable_updates
+ leave_modified_by_user_alone do
+ leave_modified_at_alone do
+ act_as_system_user do
+ snapshot.save
+ end
+ end
+ end
+ end
+ end