activeinteractor 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +17 -0
- data/LICENSE +21 -0
- data/README.md +661 -0
- data/lib/active_interactor.rb +63 -0
- data/lib/active_interactor/base.rb +18 -0
- data/lib/active_interactor/configuration.rb +34 -0
- data/lib/active_interactor/context.rb +300 -0
- data/lib/active_interactor/interactor.rb +97 -0
- data/lib/active_interactor/interactor/callbacks.rb +273 -0
- data/lib/active_interactor/interactor/context.rb +59 -0
- data/lib/active_interactor/interactor/execution.rb +25 -0
- data/lib/active_interactor/interactor/worker.rb +89 -0
- data/lib/active_interactor/organizer.rb +47 -0
- data/lib/active_interactor/version.rb +7 -0
- data/lib/rails/generators/active_interactor.rb +35 -0
- data/lib/rails/generators/active_interactor/install_generator.rb +31 -0
- data/lib/rails/generators/interactor/interactor_generator.rb +13 -0
- data/lib/rails/generators/interactor/organizer_generator.rb +18 -0
- data/lib/rails/generators/interactor/rspec_generator.rb +15 -0
- data/lib/rails/generators/interactor/test_unit_generator.rb +15 -0
- data/lib/rails/generators/templates/application_interactor.erb +4 -0
- data/lib/rails/generators/templates/initializer.rb +8 -0
- data/lib/rails/generators/templates/interactor.erb +12 -0
- data/lib/rails/generators/templates/organizer.erb +10 -0
- data/lib/rails/generators/templates/rspec.erb +7 -0
- data/lib/rails/generators/templates/test_unit.erb +9 -0
- metadata +269 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model'
|
4
|
+
|
5
|
+
require 'active_interactor/version'
|
6
|
+
|
7
|
+
# Copyright (c) 2019 Aaron Allen
|
8
|
+
#
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
10
|
+
# of this software and associated documentation files (the "Software"), to deal
|
11
|
+
# in the Software without restriction, including without limitation the rights
|
12
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
13
|
+
# copies of the Software, and to permit persons to whom the Software is
|
14
|
+
# furnished to do so, subject to the following conditions:
|
15
|
+
#
|
16
|
+
# The above copyright notice and this permission notice shall be included in
|
17
|
+
# all copies or substantial portions of the Software.
|
18
|
+
#
|
19
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
20
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
21
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
22
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
23
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
24
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
25
|
+
# THE SOFTWARE.
|
26
|
+
#
|
27
|
+
# @author Aaron Allen <hello@aaronmallen.me>
|
28
|
+
# @since 0.0.1
|
29
|
+
# @version 0.1
|
30
|
+
module ActiveInteractor
|
31
|
+
extend ActiveSupport::Autoload
|
32
|
+
|
33
|
+
autoload :Base
|
34
|
+
autoload :Configuration
|
35
|
+
autoload :Context
|
36
|
+
autoload :Interactor
|
37
|
+
|
38
|
+
class << self
|
39
|
+
# The ActiveInteractor configuration
|
40
|
+
# @return [ActiveInteractor::Configuration] the configuration instance
|
41
|
+
def configuration
|
42
|
+
@configuration ||= Configuration.new
|
43
|
+
end
|
44
|
+
|
45
|
+
# Configures the ActiveInteractor gem
|
46
|
+
#
|
47
|
+
# @example Configure ActiveInteractor
|
48
|
+
# ActiveInteractor.configure do |config|
|
49
|
+
# config.logger = Rails.logger
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# @yield [ActiveInteractor#configuration]
|
53
|
+
def configure
|
54
|
+
yield(configuration)
|
55
|
+
end
|
56
|
+
|
57
|
+
# The ActiveInteractor logger object
|
58
|
+
# @return [Logger] the configured logger instance
|
59
|
+
def logger
|
60
|
+
configuration.logger
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
# The Base Interactor class inherited by all interactors
|
5
|
+
#
|
6
|
+
# @author Aaron Allen <hello@aaronmallen.me>
|
7
|
+
# @since 0.0.1
|
8
|
+
# @version 0.1
|
9
|
+
class Base
|
10
|
+
include Interactor
|
11
|
+
# A new instance of {Base}
|
12
|
+
# @param context [Hash, nil] the properties of the context
|
13
|
+
# @return [ActiveInteractor::Base] a new instance of {Base}
|
14
|
+
def initialize(context = {})
|
15
|
+
@context = self.class.context_class.new(self, context)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
# The Configuration object for the ActiveInteractor gem
|
5
|
+
#
|
6
|
+
# @author Aaron Allen <hello@aaronmallen.me>
|
7
|
+
# @since 0.0.1
|
8
|
+
# @version 0.1
|
9
|
+
#
|
10
|
+
# @!attribute [rw] logger
|
11
|
+
# @return [Logger] an instance of Logger
|
12
|
+
class Configuration
|
13
|
+
# The default configuration options for {Configuration}
|
14
|
+
# @return [Hash{Symbol => *}]
|
15
|
+
DEFAULTS = {
|
16
|
+
logger: Logger.new(STDOUT)
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
attr_accessor :logger
|
20
|
+
|
21
|
+
# A new instance of {Configuration}
|
22
|
+
# @param options [Hash{Symbol => *}] the options to initialize the
|
23
|
+
# configuration with.
|
24
|
+
# @option options [Logger] :logger the logger ActiveInteractor should
|
25
|
+
# use for logging
|
26
|
+
# @return [ActiveInteractor::Configuration] a new instance of {Configuration}
|
27
|
+
def initialize(options = {})
|
28
|
+
options = DEFAULTS.merge(options.dup || {}).slice(*DEFAULTS.keys)
|
29
|
+
options.each_key do |attribute|
|
30
|
+
instance_variable_set("@#{attribute}", options[attribute])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,300 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/class/attribute'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
module ActiveInteractor
|
7
|
+
# ActiveInteractor::Context module
|
8
|
+
#
|
9
|
+
# @author Aaron Allen <hello@aaronmallen.me>
|
10
|
+
# @since 0.0.1
|
11
|
+
# @version 0.1
|
12
|
+
module Context
|
13
|
+
# Raised when an interactor context fails
|
14
|
+
#
|
15
|
+
# @author Aaron Allen <hello@aaronmallen.me>
|
16
|
+
# @since 0.0.1
|
17
|
+
# @version 0.1
|
18
|
+
#
|
19
|
+
# @!attribute [r] context
|
20
|
+
# @return [Base] an instance of {Base}
|
21
|
+
class Failure < StandardError
|
22
|
+
attr_reader :context
|
23
|
+
|
24
|
+
# A new instance of {Failure}
|
25
|
+
# @param context [Hash] an instance of {Base}
|
26
|
+
# @return [Failure] a new instance of {Failure}
|
27
|
+
def initialize(context = {})
|
28
|
+
@context = context
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# The base context class inherited by all {Interactor::Context} classes
|
34
|
+
#
|
35
|
+
# @author Aaron Allen <hello@aaronmallen.me>
|
36
|
+
# @since 0.0.1
|
37
|
+
# @version 0.1
|
38
|
+
class Base < OpenStruct
|
39
|
+
include ActiveModel::Validations
|
40
|
+
|
41
|
+
class_attribute :__default_attributes, instance_writer: false, default: []
|
42
|
+
|
43
|
+
# A new instance of {Base}
|
44
|
+
# @param interactor [ActiveInteractor::Base] an interactor instance
|
45
|
+
# @param attributes [Hash, nil] the attributes of the context
|
46
|
+
# @return [ActiveInteractor::Context::Base] a new instance of {Base}
|
47
|
+
def initialize(interactor, attributes = {})
|
48
|
+
copy_flags!(attributes)
|
49
|
+
@interactor = interactor
|
50
|
+
super(attributes)
|
51
|
+
end
|
52
|
+
|
53
|
+
class << self
|
54
|
+
# Attributes defined on the context class
|
55
|
+
#
|
56
|
+
# @example Get attributes defined on a context class
|
57
|
+
# MyInteractor::Context.attributes
|
58
|
+
# #=> [:first_name, :last_name]
|
59
|
+
#
|
60
|
+
# @return [Array<Symbol>] the defined attributes
|
61
|
+
def attributes
|
62
|
+
__default_attributes
|
63
|
+
.concat(_validate_callbacks.map(&:filter).map(&:attributes).flatten)
|
64
|
+
.flatten
|
65
|
+
.uniq
|
66
|
+
end
|
67
|
+
|
68
|
+
# Set attributes on a context class
|
69
|
+
# @param [Array<Symbol, String>] attributes
|
70
|
+
#
|
71
|
+
# @example Define attributes on a context class
|
72
|
+
# MyInteractor::Context.attributes = :first_name, :last_name
|
73
|
+
# #=> [:first_name, :last_name]
|
74
|
+
#
|
75
|
+
# @return [Array<Symbol>] the defined attributes
|
76
|
+
def attributes=(*attributes)
|
77
|
+
self.__default_attributes = self.attributes.concat(attributes.flatten.map(&:to_sym)).uniq
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Attributes defined on the instance
|
82
|
+
#
|
83
|
+
# @example Get attributes defined on an instance
|
84
|
+
# MyInteractor::Context.attributes = :first_name, :last_name
|
85
|
+
# #=> [:first_name, :last_name]
|
86
|
+
#
|
87
|
+
# context = MyInteractor::Context.new(first_name: 'Aaron', last_name: 'Allen')
|
88
|
+
# #=> <#MyInteractor::Context first_name='Aaron', last_name='Allen'>
|
89
|
+
#
|
90
|
+
# context.attributes
|
91
|
+
# #=> { first_name: 'Aaron', last_name: 'Allen' }
|
92
|
+
#
|
93
|
+
# @example Get attributes defined on an instance with unknown attribute
|
94
|
+
# MyInteractor::Context.attributes = :first_name, :last_name
|
95
|
+
# #=> [:first_name, :last_name]
|
96
|
+
#
|
97
|
+
# context = MyInteractor::Context.new(first_name: 'Aaron', last_name: 'Allen', unknown: 'unknown')
|
98
|
+
# #=> <#MyInteractor::Context first_name='Aaron', last_name='Allen', unknown='unknown'>
|
99
|
+
#
|
100
|
+
# context.attributes
|
101
|
+
# #=> { first_name: 'Aaron', last_name: 'Allen' }
|
102
|
+
#
|
103
|
+
# context.unknown
|
104
|
+
# #=> 'unknown'
|
105
|
+
#
|
106
|
+
# @return [Hash{Symbol => *}] the defined attributes and values
|
107
|
+
def attributes
|
108
|
+
self.class.attributes.each_with_object({}) do |attribute, hash|
|
109
|
+
hash[attribute] = self[attribute] if self[attribute]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Track that an Interactor has been called. The {#called!} method
|
114
|
+
# is used by the interactor being invoked with this context. After an
|
115
|
+
# interactor is successfully called, the interactor instance is tracked in
|
116
|
+
# the context for the purpose of potential future rollback
|
117
|
+
#
|
118
|
+
# @return [Array<ActiveInteractor::Base>] all called interactors
|
119
|
+
def called!
|
120
|
+
_called << interactor
|
121
|
+
end
|
122
|
+
|
123
|
+
# Removes properties from the instance that are not
|
124
|
+
# explicitly defined in the context instance {#attributes}
|
125
|
+
#
|
126
|
+
# @example Clean an instance of Context with unknown attribute
|
127
|
+
# MyInteractor::Context.attributes = :first_name, :last_name
|
128
|
+
# #=> [:first_name, :last_name]
|
129
|
+
#
|
130
|
+
# context = MyInteractor::Context.new(first_name: 'Aaron', last_name: 'Allen', unknown: 'unknown')
|
131
|
+
# #=> <#MyInteractor::Context first_name='Aaron', last_name='Allen', unknown='unknown'>
|
132
|
+
#
|
133
|
+
# context.unknown
|
134
|
+
# #=> 'unknown'
|
135
|
+
#
|
136
|
+
# context.clean!
|
137
|
+
# #=> { unknown: 'unknown' }
|
138
|
+
#
|
139
|
+
# context.unknown
|
140
|
+
# #=> nil
|
141
|
+
#
|
142
|
+
# @return [Hash{Symbol => *}] the deleted attributes
|
143
|
+
def clean!
|
144
|
+
deleted = {}
|
145
|
+
return deleted if keys.empty?
|
146
|
+
|
147
|
+
keys.reject { |key| self.class.attributes.include?(key) }.each do |attribute|
|
148
|
+
deleted[attribute] = self[attribute] if self[attribute]
|
149
|
+
delete_field(key.to_s)
|
150
|
+
end
|
151
|
+
deleted
|
152
|
+
end
|
153
|
+
|
154
|
+
# Fail the context instance. Failing a context raises an error
|
155
|
+
# that may be rescued by the calling interactor. The context is also flagged
|
156
|
+
# as having failed
|
157
|
+
#
|
158
|
+
# @example Fail an interactor context
|
159
|
+
# interactor = MyInteractor.new(name: 'Aaron')
|
160
|
+
# #=> <#MyInteractor name='Aaron'>
|
161
|
+
#
|
162
|
+
# interactor.context.fail!
|
163
|
+
# #=> ActiveInteractor::Context::Failure: <#MyInteractor::Context name='Aaron'>
|
164
|
+
#
|
165
|
+
# @param errors [ActiveModel::Errors, Hash] errors to add to the context on failure
|
166
|
+
# @see https://api.rubyonrails.org/classes/ActiveModel/Errors.html ActiveModel::Errors
|
167
|
+
# @raise [Failure]
|
168
|
+
def fail!(errors = {})
|
169
|
+
self.errors.merge!(errors) unless errors.empty?
|
170
|
+
@_failed = true
|
171
|
+
raise Failure, self
|
172
|
+
end
|
173
|
+
|
174
|
+
# Whether the context instance has failed. By default, a new
|
175
|
+
# context is successful and only changes when explicitly failed
|
176
|
+
#
|
177
|
+
# @note The {#failure?} method is the inverse of the {#success?} method
|
178
|
+
#
|
179
|
+
# @example Check if a context has failed
|
180
|
+
# context = MyInteractor::Context.new
|
181
|
+
# #=> <#MyInteractor::Context>
|
182
|
+
#
|
183
|
+
# context.failure?
|
184
|
+
# false
|
185
|
+
#
|
186
|
+
# context.fail!
|
187
|
+
# #=> ActiveInteractor::Context::Failure: <#MyInteractor::Context>
|
188
|
+
#
|
189
|
+
# context.failure?
|
190
|
+
# #=> true
|
191
|
+
#
|
192
|
+
# @return [Boolean] `false` by default or `true` if failed
|
193
|
+
def failure?
|
194
|
+
@_failed || false
|
195
|
+
end
|
196
|
+
alias fail? failure?
|
197
|
+
|
198
|
+
# All keys of properties currently defined on the instance
|
199
|
+
#
|
200
|
+
# @example An instance of Context with unknown attribute
|
201
|
+
# MyInteractor::Context.attributes = :first_name, :last_name
|
202
|
+
# #=> [:first_name, :last_name]
|
203
|
+
#
|
204
|
+
# context = MyInteractor::Context.new(first_name: 'Aaron', last_name: 'Allen', unknown: 'unknown')
|
205
|
+
# #=> <#MyInteractor::Context first_name='Aaron', last_name='Allen', unknown='unknown'>
|
206
|
+
#
|
207
|
+
# context.keys
|
208
|
+
# #=> [:first_name, :last_name, :unknown]
|
209
|
+
#
|
210
|
+
# @return [Array<Symbol>] keys defined on the instance
|
211
|
+
def keys
|
212
|
+
each_pair.map { |pair| pair[0].to_sym }
|
213
|
+
end
|
214
|
+
|
215
|
+
# Attempt to call the interactor for missing validation callback methods
|
216
|
+
# @raise [NameError] if the method is not a validation callback or method
|
217
|
+
# does not exist on the interactor instance
|
218
|
+
def method_missing(name, *args, &block)
|
219
|
+
interactor.send(name, *args, &block) if validation_callback?(name)
|
220
|
+
super
|
221
|
+
end
|
222
|
+
|
223
|
+
# Attempt to call the interactor for missing validation callback methods
|
224
|
+
# @return [Boolean] `true` if method is a validation callback and exists
|
225
|
+
# on the interactor instance
|
226
|
+
def respond_to_missing?(name, include_private)
|
227
|
+
return false unless validation_callback?(name)
|
228
|
+
|
229
|
+
interactor.respond_to?(name, include_private)
|
230
|
+
end
|
231
|
+
|
232
|
+
# Roll back an interactor context. Any interactors to which this
|
233
|
+
# context has been passed and which have been successfully called are asked
|
234
|
+
# to roll themselves back by invoking their
|
235
|
+
# {ActiveInteractor::Interactor#rollback #rollback} instance methods.
|
236
|
+
#
|
237
|
+
# @example Rollback an interactor's context
|
238
|
+
# context = MyInteractor.perform(name: 'Aaron')
|
239
|
+
# #=> <#MyInteractor::Context name='Aaron'>
|
240
|
+
#
|
241
|
+
# context.rollback!
|
242
|
+
# #=> true
|
243
|
+
#
|
244
|
+
# context
|
245
|
+
# #=> <#MyInteractor::Context name='Aaron'>
|
246
|
+
#
|
247
|
+
# @return [Boolean] `true` if rolled back successfully or `false` if already
|
248
|
+
# rolled back
|
249
|
+
def rollback!
|
250
|
+
return false if @_rolled_back
|
251
|
+
|
252
|
+
_called.reverse_each(&:execute_rollback)
|
253
|
+
@_rolled_back = true
|
254
|
+
end
|
255
|
+
|
256
|
+
# Whether the context instance is successful. By default, a new
|
257
|
+
# context is successful and only changes when explicitly failed
|
258
|
+
#
|
259
|
+
# @note the {#success?} method is the inverse of the {#failure?} method
|
260
|
+
#
|
261
|
+
# @example Check if a context has failed
|
262
|
+
# context = MyInteractor::Context.new
|
263
|
+
# #=> <#MyInteractor::Context>
|
264
|
+
#
|
265
|
+
# context.success?
|
266
|
+
# true
|
267
|
+
#
|
268
|
+
# context.fail!
|
269
|
+
# #=> ActiveInteractor::Context::Failure: <#MyInteractor::Context>
|
270
|
+
#
|
271
|
+
# context.success?
|
272
|
+
# #=> false
|
273
|
+
#
|
274
|
+
# @return [Boolean] `true` by default or `false` if failed
|
275
|
+
def success?
|
276
|
+
!failure?
|
277
|
+
end
|
278
|
+
alias successful? success?
|
279
|
+
|
280
|
+
private
|
281
|
+
|
282
|
+
attr_reader :interactor
|
283
|
+
|
284
|
+
def copy_flags!(context)
|
285
|
+
@_called = context.send(:_called) if context.respond_to?(:_called, true)
|
286
|
+
@_failed = context.failure? if context.respond_to?(:failure?)
|
287
|
+
end
|
288
|
+
|
289
|
+
def _called
|
290
|
+
@_called ||= []
|
291
|
+
end
|
292
|
+
|
293
|
+
def validation_callback?(method_name)
|
294
|
+
_validate_callbacks.map(&:filter).include?(method_name)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
Dir[File.expand_path('context/*.rb', __dir__)].each { |file| require file }
|
300
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
# Provides interactor methods to included classes
|
5
|
+
#
|
6
|
+
# @author Aaron Allen <hello@aaronmallen.me>
|
7
|
+
# @since 0.0.1
|
8
|
+
# @version 0.1
|
9
|
+
module Interactor
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
included do
|
13
|
+
extend ClassMethods
|
14
|
+
include Callbacks
|
15
|
+
include Context
|
16
|
+
include Execution
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_accessor :context
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
# Invoke an interactor. This is the primary public API method to an
|
25
|
+
# interactor.
|
26
|
+
#
|
27
|
+
# @example Run an interactor
|
28
|
+
# MyInteractor.perform(name: 'Aaron')
|
29
|
+
# #=> <#MyInteractor::Context name='Aaron'>
|
30
|
+
#
|
31
|
+
# @param context [Hash] properties to assign to the interactor context
|
32
|
+
# @return [ActiveInteractor::Context::Base] an instance of context
|
33
|
+
def perform(context = {})
|
34
|
+
new(context).execute_perform
|
35
|
+
end
|
36
|
+
|
37
|
+
# Invoke an Interactor. The {.perform!} method behaves identically to
|
38
|
+
# the {.perform} method with one notable exception. If the context is failed
|
39
|
+
# during invocation of the interactor, the {ActiveInteractor::Context::Failure}
|
40
|
+
# is raised.
|
41
|
+
#
|
42
|
+
# @example Run an interactor
|
43
|
+
# MyInteractor.perform!(name: 'Aaron')
|
44
|
+
# #=> <#MyInteractor::Context name='Aaron'>
|
45
|
+
#
|
46
|
+
# @param context [Hash] properties to assign to the interactor context
|
47
|
+
# @return [ActiveInteractor::Context::Base] an instance of context
|
48
|
+
def perform!(context = {})
|
49
|
+
new(context).execute_perform!
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Whether or not the context should fail when invalid
|
54
|
+
# this will return false if
|
55
|
+
# {Interactor::Callbacks::ClassMethods#allow_context_to_be_invalid}
|
56
|
+
# has been invoked on the class.
|
57
|
+
# @return [Boolean] `true` if the context should fail
|
58
|
+
# `false` if it should not.
|
59
|
+
def fail_on_invalid_context?
|
60
|
+
self.class.__fail_on_invalid_context
|
61
|
+
end
|
62
|
+
|
63
|
+
# Invoke an Interactor instance without any hooks, tracking, or rollback
|
64
|
+
# @abstract It is expected that the {#perform} method is overwritten
|
65
|
+
# for each interactor class.
|
66
|
+
def perform; end
|
67
|
+
|
68
|
+
# Reverse prior invocation of an Interactor instance.
|
69
|
+
# @abstract Any interactor class that requires undoing upon downstream
|
70
|
+
# failure is expected to overwrite the {#rollback} method.
|
71
|
+
def rollback; end
|
72
|
+
|
73
|
+
# Whether or not the context should be cleaned after {#perform}
|
74
|
+
# if {#skip_clean_context!} has not been invoked on the instance
|
75
|
+
# and {Interactor::Callbacks::ClassMethods#clean_context_on_completion}
|
76
|
+
# is invoked on the class this will return `true`.
|
77
|
+
#
|
78
|
+
# @return [Boolean] `true` if the context should be cleaned
|
79
|
+
# `false` if it should not be cleaned.
|
80
|
+
def should_clean_context?
|
81
|
+
@should_clean_context.nil? && self.class.__clean_after_perform
|
82
|
+
end
|
83
|
+
|
84
|
+
# Skip {ActiveInteractor::Context::Base#clean! #clean! on an interactor
|
85
|
+
# context that calls the {Callbacks.clean_context_on_completion} class method.
|
86
|
+
# This method is meant to be invoked by organizer interactors
|
87
|
+
# to ensure contexts are approriately passed between interactors.
|
88
|
+
#
|
89
|
+
# @return [Boolean] `true` if the context should be cleaned
|
90
|
+
# `false` if it should not.
|
91
|
+
def skip_clean_context!
|
92
|
+
@should_clean_context = false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
Dir[File.expand_path('interactor/*.rb', __dir__)].each { |file| require file }
|