class DraftApprove::Persistor
Logic for writing a Draft
to the database, and for applying changes contained within a single Draft
and saving them to the database.
@api private
Constants
- CREATE_METHOD
IMPORTANT NOTE: These constants are written to the database, so cannot be updated without requiring a migration of existing draft data. Such a migration may be very slow, since these constants are embedded in the JSON generated by this serializer!
- DEFAULT_CREATE_METHOD
- DEFAULT_DELETE_METHOD
- DEFAULT_UPDATE_METHOD
- DELETE_METHOD
- UPDATE_METHOD
Public Class Methods
Write a Draft
object to the database to persist any changes to the given model.
@param action_type [String] the type of action this draft will represent -
+CREATE+, +UPDATE+, or +DELETE+
@param model [Object] the acts_as_draftable
ActiveRecord model whose
changes will be saved to the database
@param options [Hash] the options to use when saving this draft, see
+DraftApprove::Draftable::InstanceMethods#draft_save!+ and +DraftApprove::Draftable::InstanceMethods#draft_destroy!+ for details of valid options
@return [Draft, false] the Draft
record which was created, or false
if
there were no changes (ie. the result would have been a 'no-op' change)
@see DraftApprove::Draftable::InstanceMethods#draft_save!
@see DraftApprove::Draftable::InstanceMethods#draft_destroy!
# File lib/draft_approve/persistor.rb, line 43 def self.write_draft_from_model(action_type, model, options = nil) raise(ArgumentError, 'model argument must be present') unless model.present? if validate_model?(options) && model.invalid? raise(ActiveRecord::RecordInvalid, model) end DraftApprove::Transaction.ensure_in_draft_transaction do # Now we're in a Transaction, ensure we don't get multiple drafts for the same object if model.persisted? && Draft.pending_approval.where(draftable: model).count > 0 raise(DraftApprove::Errors::ExistingDraftError, "#{model} has existing draft") end case action_type when Draft::CREATE raise(DraftApprove::Errors::AlreadyPersistedModelError, "#{model} is already persisted") if model.persisted? draftable_type = model.class.name draftable_id = nil when Draft::UPDATE raise(DraftApprove::Errors::UnpersistedModelError, "#{model} isn't persisted") unless model.persisted? draftable_type = model.class.name draftable_id = model.id when Draft::DELETE raise(DraftApprove::Errors::UnpersistedModelError, "#{model} isn't persisted") unless model.persisted? draftable_type = model.class.name draftable_id = model.id else raise(ArgumentError, "Unknown draft_action_type #{action_type}") end draft_transaction = DraftApprove::Transaction.current_draft_transaction! draft_options = sanitize_options_for_db(options) serializer = serializer_class(draft_transaction) changes = serializer.changes_for_model(model) # Don't write no-op updates! return false if changes.empty? && action_type == Draft::UPDATE return model.draft_pending_approval = Draft.create!( draft_transaction: draft_transaction, draftable_type: draftable_type, draftable_id: draftable_id, draft_action_type: action_type, draft_changes: changes, draft_options: draft_options ) end end
Write the changes represented by the given Draft
object to the database.
Depending upon the type of Draft
, this method may create a new record in the database, update an existing record, or delete a record.
@param draft [Draft] the Draft
object whose changes should be applied
and persisted to the database
@return [Object] the acts_as_draftable
ActiveRecord model which has been
created, updated, or deleted
# File lib/draft_approve/persistor.rb, line 102 def self.write_model_from_draft(draft) serializer = serializer_class(draft.draft_transaction) new_values_hash = serializer.new_values_for_draft(draft) options = draft.draft_options || {} case draft.draft_action_type when Draft::CREATE raise(DraftApprove::Errors::NoDraftableError, "No draftable_type for #{draft}") if draft.draftable_type.blank? create_method = (options.include?(CREATE_METHOD) ? options[CREATE_METHOD] : DEFAULT_CREATE_METHOD) model_class = Object.const_get(draft.draftable_type) model = model_class.send(create_method, new_values_hash) # We've only just persisted the model, the draft can't have referenced it before! draft.update!(draftable: model) return model when Draft::UPDATE raise(DraftApprove::Errors::NoDraftableError, "No draftable for #{draft}") if draft.draftable.blank? update_method = (options.include?(UPDATE_METHOD) ? options[UPDATE_METHOD] : DEFAULT_UPDATE_METHOD) model = draft.draftable model.send(update_method, new_values_hash) return model when Draft::DELETE raise(DraftApprove::Errors::NoDraftableError, "No draftable for #{draft}") if draft.draftable.blank? delete_method = (options.include?(DELETE_METHOD) ? options[DELETE_METHOD] : DEFAULT_DELETE_METHOD) model = draft.draftable model.send(delete_method) return model else raise(ArgumentError, "Unknown draft_action_type #{draft.draft_action_type}") end end
Private Class Methods
Helper to remove invalid options before they get persisted to the database
# File lib/draft_approve/persistor.rb, line 155 def self.sanitize_options_for_db(options) return nil if !options || options.empty? draft_options_keys = [CREATE_METHOD, UPDATE_METHOD, DELETE_METHOD] accepted_options = options.each_with_object({}) do |(key, value), accepted_opts| accepted_opts[key.to_s] = value if draft_options_keys.include?(key.to_s) end return (accepted_options.empty? ? nil : accepted_options) end
Helper to get the serialization class to use
# File lib/draft_approve/persistor.rb, line 150 def self.serializer_class(draft_transaction) draft_transaction.serialization_module.get_serializer end
Helper to determine whether to validate a model before writing a draft
# File lib/draft_approve/persistor.rb, line 144 def self.validate_model?(options) options ||= {} options.fetch(:validate, true) end