draft_approve 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/.yardopts +6 -0
- data/Appraisals +3 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +91 -0
- data/LICENSE.md +21 -0
- data/README.md +329 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/draft_approve.gemspec +56 -0
- data/lib/draft_approve.rb +5 -0
- data/lib/draft_approve/draft_changes_proxy.rb +242 -0
- data/lib/draft_approve/draftable/base_class_methods.rb +33 -0
- data/lib/draft_approve/draftable/class_methods.rb +119 -0
- data/lib/draft_approve/draftable/instance_methods.rb +80 -0
- data/lib/draft_approve/errors.rb +19 -0
- data/lib/draft_approve/models/draft.rb +75 -0
- data/lib/draft_approve/models/draft_transaction.rb +109 -0
- data/lib/draft_approve/persistor.rb +167 -0
- data/lib/draft_approve/serialization/json.rb +16 -0
- data/lib/draft_approve/serialization/json/constants.rb +21 -0
- data/lib/draft_approve/serialization/json/draft_changes_proxy.rb +317 -0
- data/lib/draft_approve/serialization/json/serializer.rb +181 -0
- data/lib/draft_approve/transaction.rb +125 -0
- data/lib/draft_approve/version.rb +3 -0
- data/lib/generators/draft_approve/migration/migration_generator.rb +41 -0
- data/lib/generators/draft_approve/migration/templates/create_draft_approve_tables.rb +25 -0
- metadata +253 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'draft_approve/persistor'
|
2
|
+
|
3
|
+
module DraftApprove
|
4
|
+
module Draftable
|
5
|
+
|
6
|
+
# Instance methods automatically added to an ActiveRecord model when
|
7
|
+
# +acts_as_draftable+ is called
|
8
|
+
module InstanceMethods
|
9
|
+
##### Basic DraftApprove instance methods #####
|
10
|
+
|
11
|
+
# Whether this object is draftable. Helper method to identify draftable
|
12
|
+
# objects.
|
13
|
+
#
|
14
|
+
# @return [Boolean] true
|
15
|
+
def draftable?
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
# Saves any changes to the object as a draft.
|
20
|
+
#
|
21
|
+
# This method may be called both on a new object which has not been
|
22
|
+
# persisted yet, and on objects which have already been persisted.
|
23
|
+
#
|
24
|
+
# @param options [Hash] the options to save the draft with
|
25
|
+
# @option options [Symbol] :validate whether to validate the model before
|
26
|
+
# draft changes are saved, defaults to +true+
|
27
|
+
# @option options [Symbol] :create_method the method to use when creating
|
28
|
+
# a new object from this draft, eg. +:find_or_create_by!+. This must be
|
29
|
+
# a method available on the object, and must accept a hash of attribute
|
30
|
+
# names to attribute values. The default is +create!+. Ignored if this
|
31
|
+
# draft is for an object which has already been persisted.
|
32
|
+
# @option options [Symbol] :update_method the method to use when updating
|
33
|
+
# an existing object from this draft, eg. +:update_columns+. This must
|
34
|
+
# be a method available on the object, and must accept a hash of
|
35
|
+
# attribute names to attribute values. The default is +update!+. Ignored
|
36
|
+
# if this draft is for an object which has not yet been persisted.
|
37
|
+
#
|
38
|
+
# @return [Draft, nil] the +Draft+ object which was created, or +nil+ if
|
39
|
+
# there were no changes to the object
|
40
|
+
def draft_save!(options = nil)
|
41
|
+
if self.new_record?
|
42
|
+
DraftApprove::Persistor.write_draft_from_model(Draft::CREATE, self, options)
|
43
|
+
else
|
44
|
+
DraftApprove::Persistor.write_draft_from_model(Draft::UPDATE, self, options)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Marks this object to be destroyed when this draft change is approved.
|
49
|
+
#
|
50
|
+
# This method should only be called on objects which have already been
|
51
|
+
# persisted.
|
52
|
+
#
|
53
|
+
# @param options [Hash] the options to save the draft with
|
54
|
+
# @option options [Symbol] :delete_method the method to use to delete
|
55
|
+
# the object when this draft is approved, eg. +:delete+. This must be
|
56
|
+
# a method available on the object. The default is +destroy!+.
|
57
|
+
#
|
58
|
+
# @return [Draft] the +Draft+ object which was created
|
59
|
+
def draft_destroy!(options = nil)
|
60
|
+
DraftApprove::Persistor.write_draft_from_model(Draft::DELETE, self, options)
|
61
|
+
end
|
62
|
+
|
63
|
+
##### Additional convenience DraftApprove instance methods #####
|
64
|
+
|
65
|
+
# Updates an existing object with the given attributes, and saves the
|
66
|
+
# updates as a draft.
|
67
|
+
#
|
68
|
+
# @param attributes [Hash] a hash of attribute names to attribute values,
|
69
|
+
# like the hash expected by the ActiveRecord +update+ / +update!+
|
70
|
+
# methods
|
71
|
+
#
|
72
|
+
# @return [Draft, nil] the +Draft+ object which was created, or +nil+ if
|
73
|
+
# there were no changes to the object
|
74
|
+
def draft_update!(attributes)
|
75
|
+
self.assign_attributes(attributes)
|
76
|
+
self.draft_save!
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module DraftApprove
|
2
|
+
module Errors
|
3
|
+
class DraftTransactionError < StandardError; end
|
4
|
+
class NestedDraftTransactionError < DraftTransactionError; end
|
5
|
+
class NoDraftTransactionError < DraftTransactionError; end
|
6
|
+
|
7
|
+
class DraftSaveError < StandardError; end
|
8
|
+
class ExistingDraftError < DraftSaveError; end
|
9
|
+
class AlreadyPersistedModelError < DraftSaveError; end
|
10
|
+
class UnpersistedModelError < DraftSaveError; end
|
11
|
+
|
12
|
+
class ChangeSerializationError < StandardError; end
|
13
|
+
class AssociationUnsavedError < ChangeSerializationError; end
|
14
|
+
|
15
|
+
class ApplyDraftChangesError < StandardError; end
|
16
|
+
class NoDraftableError < ApplyDraftChangesError; end
|
17
|
+
class PriorDraftNotAppliedError < ApplyDraftChangesError; end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# @note It is strongly recommended that you do not directly create +Draft+
|
2
|
+
# objects, and instead use the supported public interface for doing so. See
|
3
|
+
# +DraftApprove::Draftable::ClassMethods+,
|
4
|
+
# +DraftApprove::Draftable::InstanceMethods+, and the README docs for this.
|
5
|
+
#
|
6
|
+
# ActiveRecord model for persisting data about draft changes.
|
7
|
+
#
|
8
|
+
# Each +Draft+ must be linked to a +DraftTransaction+, and must have a
|
9
|
+
# +draft_action_type+ which specifies whether this draft is to create a new
|
10
|
+
# record, update a record, or delete a record.
|
11
|
+
#
|
12
|
+
# If the draft is to update or delete an existing record in the database, the
|
13
|
+
# +Draft+ will also have a link to the +acts_as_draftable+ instance to which it
|
14
|
+
# relates, via the polymorphic +draftable+ association.
|
15
|
+
#
|
16
|
+
# Linking to the +acts_as_draftable+ instance is not possible for drafts which
|
17
|
+
# create new records, since the new record does not yet exist in the database!
|
18
|
+
# In these cases, the +draftable_type+ column is still set to the name of the
|
19
|
+
# class which is to be created, but the +draftable_id+ is +nil+.
|
20
|
+
#
|
21
|
+
# The +draft_changes+ attribute is a serialized representation of the draft
|
22
|
+
# changes. The representation is delegated to a +DraftApprove::Serialization+
|
23
|
+
# module. At present, there is only a JSON implementation, suitable for use with
|
24
|
+
# PostgreSQL databases.
|
25
|
+
#
|
26
|
+
# Note that saving 'no-op' +Draft+s is generally avoided by this library
|
27
|
+
# (specifically by the +DraftApprove::Persistor+ class).
|
28
|
+
#
|
29
|
+
# @see DraftTransaction
|
30
|
+
# @see DraftApprove::Draftable::ClassMethods
|
31
|
+
# @see DraftApprove::Draftable::InstanceMethods
|
32
|
+
class Draft < ActiveRecord::Base
|
33
|
+
# IMPORTANT NOTE: These constants are written to the database, so cannot be
|
34
|
+
# updated without requiring a migration of existing draft data
|
35
|
+
CREATE = 'create'.freeze
|
36
|
+
UPDATE = 'update'.freeze
|
37
|
+
DELETE = 'delete'.freeze
|
38
|
+
|
39
|
+
belongs_to :draft_transaction
|
40
|
+
belongs_to :draftable, polymorphic: true, optional: true
|
41
|
+
|
42
|
+
validates :draft_action_type, inclusion: {
|
43
|
+
in: [CREATE, UPDATE, DELETE],
|
44
|
+
message: "%{value} is not a valid Draft.draft_action_type"
|
45
|
+
}
|
46
|
+
|
47
|
+
scope :pending_approval, -> { joins(:draft_transaction).merge(DraftTransaction.pending_approval) }
|
48
|
+
scope :approved, -> { joins(:draft_transaction).merge(DraftTransaction.approved) }
|
49
|
+
scope :rejected, -> { joins(:draft_transaction).merge(DraftTransaction.rejected) }
|
50
|
+
scope :approval_error, -> { joins(:draft_transaction).merge(DraftTransaction.approval_error) }
|
51
|
+
|
52
|
+
# @return [Boolean] +true+ if this +Draft+ is to create a new record, +false+
|
53
|
+
# otherwise
|
54
|
+
def create?
|
55
|
+
draft_action_type == CREATE
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [Boolean] +true+ if this +Draft+ is to update an existing record,
|
59
|
+
# +false+ otherwise
|
60
|
+
def update?
|
61
|
+
draft_action_type == UPDATE
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Boolean] +true+ if this +Draft+ is to delete an existing record,
|
65
|
+
# +false+ otherwise
|
66
|
+
def delete?
|
67
|
+
draft_action_type == DELETE
|
68
|
+
end
|
69
|
+
|
70
|
+
# Apply the changes in this draft, writing them to the database
|
71
|
+
# @api private
|
72
|
+
def apply_changes!
|
73
|
+
DraftApprove::Persistor.write_model_from_draft(self)
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# @note It is strongly recommended that you do not directly create
|
2
|
+
# +DraftTransaction+ objects, and instead use the supported public interface
|
3
|
+
# for doing so. See +DraftApprove::Draftable::ClassMethods+ and the README
|
4
|
+
# docs for this.
|
5
|
+
#
|
6
|
+
# ActiveRecord model for persisting data about a group of draft changes which
|
7
|
+
# should be approved or rejected as a single, transactional group.
|
8
|
+
#
|
9
|
+
# Each +DraftTransaction+ has many linked +Draft+ objects.
|
10
|
+
#
|
11
|
+
# When a +DraftTransaction+ is first created, it has +status+ of
|
12
|
+
# +pending_approval+. The changes should then be reviewed and either approved or
|
13
|
+
# rejected.
|
14
|
+
#
|
15
|
+
# +DraftTransaction+ objects also have optional attribute for storing who or
|
16
|
+
# what created the transaction and the group of draft changes (+created_by+
|
17
|
+
# attribute), who or what reviewed the changes (+reviewed_by+ attribute), and
|
18
|
+
# the reason given for approving or rejecting the changes (+review_reason+
|
19
|
+
# attribute), and finally the stack trace of any error which occurred during
|
20
|
+
# the process of applying the changes (+error+ attribute).
|
21
|
+
#
|
22
|
+
# Arbitrary extra data can also be stored in the +extra_data+ attribute.
|
23
|
+
#
|
24
|
+
# Note that saving 'no-op' +DraftTransaction+s is generally avoided by this
|
25
|
+
# library (specifically by the +DraftApprove::Transaction+ class).
|
26
|
+
#
|
27
|
+
# @see Draft
|
28
|
+
# @see DraftApprove::Draftable::ClassMethods
|
29
|
+
class DraftTransaction < ActiveRecord::Base
|
30
|
+
# IMPORTANT NOTE: These constants are written to the database, so cannot be
|
31
|
+
# updated without requiring a migration of existing draft data
|
32
|
+
PENDING_APPROVAL = 'pending_approval'.freeze
|
33
|
+
APPROVED = 'approved'.freeze
|
34
|
+
REJECTED = 'rejected'.freeze
|
35
|
+
APPROVAL_ERROR = 'approval_error'.freeze
|
36
|
+
|
37
|
+
has_many :drafts
|
38
|
+
|
39
|
+
validates :status, inclusion: {
|
40
|
+
in: [PENDING_APPROVAL, APPROVED, REJECTED, APPROVAL_ERROR],
|
41
|
+
message: "%{value} is not a valid DraftTransaction.status"
|
42
|
+
}
|
43
|
+
|
44
|
+
scope :pending_approval, -> { where(status: PENDING_APPROVAL) }
|
45
|
+
scope :approved, -> { where(status: APPROVED) }
|
46
|
+
scope :rejected, -> { where(status: REJECTED) }
|
47
|
+
scope :approval_error, -> { where(status: APPROVAL_ERROR) }
|
48
|
+
|
49
|
+
# Approve all changes in this +DraftTransaction+ and immediately apply them
|
50
|
+
# to the database.
|
51
|
+
#
|
52
|
+
# Note that applying the changes occurs within a database transaction.
|
53
|
+
#
|
54
|
+
# @param reviewed_by [String] the user or process which approved these changes
|
55
|
+
# @param review_reason [String] the reason for approving these changes
|
56
|
+
#
|
57
|
+
# @return [Boolean] +true+ if the changes were successfully applied
|
58
|
+
def approve_changes!(reviewed_by: nil, review_reason: nil)
|
59
|
+
begin
|
60
|
+
ActiveRecord::Base.transaction do
|
61
|
+
self.lock! # Avoid multiple threads applying changes concurrently
|
62
|
+
return false unless self.status == PENDING_APPROVAL
|
63
|
+
|
64
|
+
drafts.order(:created_at, :id).each do |draft|
|
65
|
+
draft.apply_changes!
|
66
|
+
end
|
67
|
+
|
68
|
+
self.update!(status: APPROVED, reviewed_by: reviewed_by, review_reason: review_reason)
|
69
|
+
return true
|
70
|
+
end
|
71
|
+
rescue StandardError => e
|
72
|
+
# Log the error in the database table and re-raise
|
73
|
+
self.update!(status: APPROVAL_ERROR, error: "#{e.inspect}\n#{e.backtrace.join("\n")}")
|
74
|
+
raise
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Reject all changes in this +DraftTransaction+.
|
79
|
+
#
|
80
|
+
# @param reviewed_by [String] the user or process which rejected these changes
|
81
|
+
# @param review_reason [String] the reason for rejecting these changes
|
82
|
+
#
|
83
|
+
# @return [Boolean] +true+ if the changes were successfully rejected
|
84
|
+
def reject_changes!(reviewed_by: nil, review_reason: nil)
|
85
|
+
self.update!(status: REJECTED, reviewed_by: reviewed_by, review_reason: review_reason)
|
86
|
+
return true
|
87
|
+
end
|
88
|
+
|
89
|
+
# Get a +DraftChangesProxy+ for the given object in the scope of this
|
90
|
+
# +DraftTransaction+.
|
91
|
+
#
|
92
|
+
# @param object [Object] the +Draft+ or +acts_as_draftable+ object to
|
93
|
+
# create a +DraftChangesProxy+ for
|
94
|
+
#
|
95
|
+
# @return [DraftChangesProxy] a proxy to get changes drafted to the given
|
96
|
+
# object and related objects, within the scope of this +DraftTransaction+
|
97
|
+
#
|
98
|
+
# @see DraftApprove::DraftChangesProxy
|
99
|
+
def draft_proxy_for(object)
|
100
|
+
serialization_module.get_draft_changes_proxy.new(object, self)
|
101
|
+
end
|
102
|
+
|
103
|
+
# @return the module used for serialization by this +DraftTransaction+.
|
104
|
+
#
|
105
|
+
# @api private
|
106
|
+
def serialization_module
|
107
|
+
Object.const_get(self.serialization)
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'draft_approve/errors'
|
2
|
+
require 'draft_approve/models/draft'
|
3
|
+
require 'draft_approve/serialization/json'
|
4
|
+
|
5
|
+
module DraftApprove
|
6
|
+
|
7
|
+
# Logic for writing a +Draft+ to the database, and for applying changes
|
8
|
+
# contained within a single +Draft+ and saving them to the database.
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
class Persistor
|
12
|
+
DEFAULT_CREATE_METHOD = 'create!'.freeze
|
13
|
+
DEFAULT_UPDATE_METHOD = 'update!'.freeze
|
14
|
+
DEFAULT_DELETE_METHOD = 'destroy!'.freeze
|
15
|
+
private_constant :DEFAULT_CREATE_METHOD, :DEFAULT_UPDATE_METHOD, :DEFAULT_DELETE_METHOD
|
16
|
+
|
17
|
+
# IMPORTANT NOTE: These constants are written to the database, so cannot be
|
18
|
+
# updated without requiring a migration of existing draft data. Such a
|
19
|
+
# migration may be very slow, since these constants are embedded in the
|
20
|
+
# JSON generated by this serializer!
|
21
|
+
CREATE_METHOD = 'create_method'.freeze
|
22
|
+
UPDATE_METHOD = 'update_method'.freeze
|
23
|
+
DELETE_METHOD = 'delete_method'.freeze
|
24
|
+
private_constant :CREATE_METHOD, :UPDATE_METHOD, :DELETE_METHOD
|
25
|
+
|
26
|
+
# Write a +Draft+ object to the database to persist any changes to the
|
27
|
+
# given model.
|
28
|
+
#
|
29
|
+
# @param action_type [String] the type of action this draft will represent -
|
30
|
+
# +CREATE+, +UPDATE+, or +DELETE+
|
31
|
+
# @param model [Object] the +acts_as_draftable+ ActiveRecord model whose
|
32
|
+
# changes will be saved to the database
|
33
|
+
# @param options [Hash] the options to use when saving this draft, see
|
34
|
+
# +DraftApprove::Draftable::InstanceMethods#draft_save!+ and
|
35
|
+
# +DraftApprove::Draftable::InstanceMethods#draft_destroy!+ for details of
|
36
|
+
# valid options
|
37
|
+
#
|
38
|
+
# @return [Draft, false] the +Draft+ record which was created, or +false+ if
|
39
|
+
# there were no changes (ie. the result would have been a 'no-op' change)
|
40
|
+
#
|
41
|
+
# @see DraftApprove::Draftable::InstanceMethods#draft_save!
|
42
|
+
# @see DraftApprove::Draftable::InstanceMethods#draft_destroy!
|
43
|
+
def self.write_draft_from_model(action_type, model, options = nil)
|
44
|
+
raise(ArgumentError, 'model argument must be present') unless model.present?
|
45
|
+
|
46
|
+
if validate_model?(options) && model.invalid?
|
47
|
+
raise(ActiveRecord::RecordInvalid, model)
|
48
|
+
end
|
49
|
+
|
50
|
+
DraftApprove::Transaction.ensure_in_draft_transaction do
|
51
|
+
# Now we're in a Transaction, ensure we don't get multiple drafts for the same object
|
52
|
+
if model.persisted? && Draft.pending_approval.where(draftable: model).count > 0
|
53
|
+
raise(DraftApprove::Errors::ExistingDraftError, "#{model} has existing draft")
|
54
|
+
end
|
55
|
+
|
56
|
+
case action_type
|
57
|
+
when Draft::CREATE
|
58
|
+
raise(DraftApprove::Errors::AlreadyPersistedModelError, "#{model} is already persisted") if model.persisted?
|
59
|
+
draftable_type = model.class.name
|
60
|
+
draftable_id = nil
|
61
|
+
when Draft::UPDATE
|
62
|
+
raise(DraftApprove::Errors::UnpersistedModelError, "#{model} isn't persisted") unless model.persisted?
|
63
|
+
draftable_type = model.class.name
|
64
|
+
draftable_id = model.id
|
65
|
+
when Draft::DELETE
|
66
|
+
raise(DraftApprove::Errors::UnpersistedModelError, "#{model} isn't persisted") unless model.persisted?
|
67
|
+
draftable_type = model.class.name
|
68
|
+
draftable_id = model.id
|
69
|
+
else
|
70
|
+
raise(ArgumentError, "Unknown draft_action_type #{action_type}")
|
71
|
+
end
|
72
|
+
|
73
|
+
draft_transaction = DraftApprove::Transaction.current_draft_transaction!
|
74
|
+
draft_options = sanitize_options_for_db(options)
|
75
|
+
serializer = serializer_class(draft_transaction)
|
76
|
+
changes = serializer.changes_for_model(model)
|
77
|
+
|
78
|
+
# Don't write no-op updates!
|
79
|
+
return false if changes.empty? && action_type == Draft::UPDATE
|
80
|
+
|
81
|
+
return model.draft_pending_approval = Draft.create!(
|
82
|
+
draft_transaction: draft_transaction,
|
83
|
+
draftable_type: draftable_type,
|
84
|
+
draftable_id: draftable_id,
|
85
|
+
draft_action_type: action_type,
|
86
|
+
draft_changes: changes,
|
87
|
+
draft_options: draft_options
|
88
|
+
)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Write the changes represented by the given +Draft+ object to the database.
|
93
|
+
#
|
94
|
+
# Depending upon the type of +Draft+, this method may create a new record in
|
95
|
+
# the database, update an existing record, or delete a record.
|
96
|
+
#
|
97
|
+
# @param draft [Draft] the +Draft+ object whose changes should be applied
|
98
|
+
# and persisted to the database
|
99
|
+
#
|
100
|
+
# @return [Object] the +acts_as_draftable+ ActiveRecord model which has been
|
101
|
+
# created, updated, or deleted
|
102
|
+
def self.write_model_from_draft(draft)
|
103
|
+
serializer = serializer_class(draft.draft_transaction)
|
104
|
+
new_values_hash = serializer.new_values_for_draft(draft)
|
105
|
+
options = draft.draft_options || {}
|
106
|
+
|
107
|
+
case draft.draft_action_type
|
108
|
+
when Draft::CREATE
|
109
|
+
raise(DraftApprove::Errors::NoDraftableError, "No draftable_type for #{draft}") if draft.draftable_type.blank?
|
110
|
+
|
111
|
+
create_method = (options.include?(CREATE_METHOD) ? options[CREATE_METHOD] : DEFAULT_CREATE_METHOD)
|
112
|
+
|
113
|
+
model_class = Object.const_get(draft.draftable_type)
|
114
|
+
model = model_class.send(create_method, new_values_hash)
|
115
|
+
|
116
|
+
# We've only just persisted the model, the draft can't have referenced it before!
|
117
|
+
draft.update!(draftable: model)
|
118
|
+
|
119
|
+
return model
|
120
|
+
when Draft::UPDATE
|
121
|
+
raise(DraftApprove::Errors::NoDraftableError, "No draftable for #{draft}") if draft.draftable.blank?
|
122
|
+
|
123
|
+
update_method = (options.include?(UPDATE_METHOD) ? options[UPDATE_METHOD] : DEFAULT_UPDATE_METHOD)
|
124
|
+
|
125
|
+
model = draft.draftable
|
126
|
+
model.send(update_method, new_values_hash)
|
127
|
+
return model
|
128
|
+
when Draft::DELETE
|
129
|
+
raise(DraftApprove::Errors::NoDraftableError, "No draftable for #{draft}") if draft.draftable.blank?
|
130
|
+
|
131
|
+
delete_method = (options.include?(DELETE_METHOD) ? options[DELETE_METHOD] : DEFAULT_DELETE_METHOD)
|
132
|
+
|
133
|
+
model = draft.draftable
|
134
|
+
model.send(delete_method)
|
135
|
+
return model
|
136
|
+
else
|
137
|
+
raise(ArgumentError, "Unknown draft_action_type #{draft.draft_action_type}")
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
# Helper to determine whether to validate a model before writing a draft
|
144
|
+
def self.validate_model?(options)
|
145
|
+
options ||= {}
|
146
|
+
options.fetch(:validate, true)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Helper to get the serialization class to use
|
150
|
+
def self.serializer_class(draft_transaction)
|
151
|
+
draft_transaction.serialization_module.get_serializer
|
152
|
+
end
|
153
|
+
|
154
|
+
# Helper to remove invalid options before they get persisted to the database
|
155
|
+
def self.sanitize_options_for_db(options)
|
156
|
+
return nil if !options || options.empty?
|
157
|
+
|
158
|
+
draft_options_keys = [CREATE_METHOD, UPDATE_METHOD, DELETE_METHOD]
|
159
|
+
|
160
|
+
accepted_options = options.each_with_object({}) do |(key, value), accepted_opts|
|
161
|
+
accepted_opts[key.to_s] = value if draft_options_keys.include?(key.to_s)
|
162
|
+
end
|
163
|
+
|
164
|
+
return (accepted_options.empty? ? nil : accepted_options)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|