activeinteractor 1.0.0.beta.7 → 1.0.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.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +12 -0
  3. data/CHANGELOG.md +50 -158
  4. data/README.md +13 -856
  5. data/lib/active_interactor.rb +61 -4
  6. data/lib/active_interactor/base.rb +26 -20
  7. data/lib/active_interactor/config.rb +36 -18
  8. data/lib/active_interactor/configurable.rb +17 -9
  9. data/lib/active_interactor/context/attributes.rb +73 -26
  10. data/lib/active_interactor/context/base.rb +236 -65
  11. data/lib/active_interactor/context/loader.rb +20 -15
  12. data/lib/active_interactor/context/status.rb +38 -56
  13. data/lib/active_interactor/error.rb +15 -7
  14. data/lib/active_interactor/interactor/callbacks.rb +174 -160
  15. data/lib/active_interactor/interactor/context.rb +279 -87
  16. data/lib/active_interactor/interactor/perform.rb +256 -0
  17. data/lib/active_interactor/interactor/worker.rb +19 -14
  18. data/lib/active_interactor/models.rb +65 -0
  19. data/lib/active_interactor/organizer/base.rb +18 -0
  20. data/lib/active_interactor/organizer/callbacks.rb +153 -0
  21. data/lib/active_interactor/organizer/interactor_interface.rb +38 -26
  22. data/lib/active_interactor/organizer/interactor_interface_collection.rb +35 -32
  23. data/lib/active_interactor/organizer/organize.rb +67 -0
  24. data/lib/active_interactor/organizer/perform.rb +93 -0
  25. data/lib/active_interactor/rails.rb +0 -10
  26. data/lib/active_interactor/rails/orm/active_record.rb +1 -1
  27. data/lib/active_interactor/rails/railtie.rb +8 -11
  28. data/lib/active_interactor/version.rb +2 -1
  29. data/lib/rails/generators/active_interactor.rb +1 -38
  30. data/lib/rails/generators/active_interactor/application_context_generator.rb +21 -0
  31. data/lib/rails/generators/active_interactor/application_interactor_generator.rb +5 -15
  32. data/lib/rails/generators/active_interactor/application_organizer_generator.rb +21 -0
  33. data/lib/rails/generators/active_interactor/base.rb +29 -0
  34. data/lib/rails/generators/active_interactor/generator.rb +21 -0
  35. data/lib/rails/generators/active_interactor/install_generator.rb +2 -9
  36. data/lib/rails/generators/interactor/context/rspec_generator.rb +3 -10
  37. data/lib/rails/generators/interactor/context/test_unit_generator.rb +4 -11
  38. data/lib/rails/generators/interactor/context_generator.rb +7 -10
  39. data/lib/rails/generators/interactor/generates_context.rb +28 -0
  40. data/lib/rails/generators/interactor/interactor_generator.rb +8 -10
  41. data/lib/rails/generators/interactor/organizer_generator.rb +8 -12
  42. data/lib/rails/generators/interactor/rspec_generator.rb +2 -9
  43. data/lib/rails/generators/interactor/test_unit_generator.rb +3 -10
  44. data/lib/rails/generators/{active_interactor/templates/initializer.erb → templates/active_interactor.erb} +3 -11
  45. data/lib/rails/generators/{active_interactor/templates → templates}/application_context.rb +0 -0
  46. data/lib/rails/generators/{active_interactor/templates → templates}/application_interactor.rb +0 -0
  47. data/lib/rails/generators/templates/application_organizer.rb +4 -0
  48. data/lib/rails/generators/{interactor/templates → templates}/context.erb +0 -0
  49. data/lib/rails/generators/{interactor/context/templates/rspec.erb → templates/context_spec.erb} +0 -0
  50. data/lib/rails/generators/{interactor/context/templates/test_unit.erb → templates/context_test_unit.erb} +0 -0
  51. data/lib/rails/generators/{interactor/templates → templates}/interactor.erb +0 -0
  52. data/lib/rails/generators/{interactor/templates/rspec.erb → templates/interactor_spec.erb} +0 -0
  53. data/lib/rails/generators/{interactor/templates/test_unit.erb → templates/interactor_text_unit.erb} +0 -0
  54. data/lib/rails/generators/{interactor/templates → templates}/organizer.erb +0 -0
  55. data/spec/active_interactor/base_spec.rb +3 -3
  56. data/spec/active_interactor/interactor/{perform_options_spec.rb → perform/options_spec.rb} +1 -1
  57. data/spec/active_interactor/interactor/worker_spec.rb +14 -15
  58. data/spec/active_interactor/{organizer_spec.rb → organizer/base_spec.rb} +27 -17
  59. data/spec/integration/a_basic_interactor_spec.rb +106 -0
  60. data/spec/integration/a_basic_organizer_spec.rb +97 -0
  61. data/spec/integration/a_failing_interactor_spec.rb +42 -0
  62. data/spec/integration/active_record_integration_spec.rb +9 -9
  63. data/spec/integration/an_interactor_with_after_context_validation_callbacks_spec.rb +69 -0
  64. data/spec/integration/an_interactor_with_after_perform_callbacks_spec.rb +30 -0
  65. data/spec/integration/an_interactor_with_after_rollback_callbacks_spec.rb +33 -0
  66. data/spec/integration/an_interactor_with_an_existing_context_class_spec.rb +49 -0
  67. data/spec/integration/an_interactor_with_around_perform_callbacks_spec.rb +35 -0
  68. data/spec/integration/an_interactor_with_around_rollback_callbacks_spec.rb +39 -0
  69. data/spec/integration/an_interactor_with_before_perform_callbacks_spec.rb +30 -0
  70. data/spec/integration/an_interactor_with_before_rollback_callbacks_spec.rb +33 -0
  71. data/spec/integration/an_interactor_with_validations_on_called_spec.rb +40 -0
  72. data/spec/integration/an_interactor_with_validations_on_calling_spec.rb +36 -0
  73. data/spec/integration/an_interactor_with_validations_spec.rb +93 -0
  74. data/spec/integration/an_organizer_performing_in_parallel_spec.rb +48 -0
  75. data/spec/integration/an_organizer_with_after_each_callbacks_spec.rb +34 -0
  76. data/spec/integration/an_organizer_with_around_each_callbacks_spec.rb +39 -0
  77. data/spec/integration/an_organizer_with_before_each_callbacks_spec.rb +34 -0
  78. data/spec/integration/an_organizer_with_conditionally_organized_interactors_spec.rb +314 -0
  79. data/spec/spec_helper.rb +8 -12
  80. data/spec/support/coverage.rb +4 -0
  81. data/spec/support/coverage/reporters.rb +11 -0
  82. data/spec/support/coverage/reporters/codacy.rb +39 -0
  83. data/spec/support/coverage/reporters/simple_cov.rb +54 -0
  84. data/spec/support/coverage/runner.rb +66 -0
  85. data/spec/support/helpers/factories.rb +1 -1
  86. data/spec/support/shared_examples/a_class_with_interactor_callback_methods_example.rb +8 -8
  87. data/spec/support/shared_examples/a_class_with_interactor_context_methods_example.rb +5 -5
  88. data/spec/support/shared_examples/a_class_with_interactor_methods_example.rb +2 -2
  89. data/spec/support/shared_examples/a_class_with_organizer_callback_methods_example.rb +3 -3
  90. metadata +83 -40
  91. data/lib/active_interactor/interactor.rb +0 -84
  92. data/lib/active_interactor/interactor/perform_options.rb +0 -29
  93. data/lib/active_interactor/organizer.rb +0 -269
  94. data/lib/active_interactor/rails/config.rb +0 -45
  95. data/lib/active_interactor/rails/models.rb +0 -58
  96. data/lib/rails/generators/active_interactor/templates/application_organizer.rb +0 -4
  97. data/spec/active_interactor/rails/config_spec.rb +0 -29
  98. data/spec/active_interactor/rails_spec.rb +0 -24
  99. data/spec/integration/basic_callback_integration_spec.rb +0 -355
  100. data/spec/integration/basic_context_integration_spec.rb +0 -73
  101. data/spec/integration/basic_integration_spec.rb +0 -570
  102. data/spec/integration/basic_validations_integration_spec.rb +0 -204
@@ -1,130 +1,322 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_model'
4
- require 'active_support/core_ext/string/inflections'
5
-
6
3
  module ActiveInteractor
7
4
  module Interactor
8
- # Interactor context methods included by all {Base}
5
+ # Interactor context methods. Because {Context} is a module classes should include {Context} rather than inherit
6
+ # from it.
7
+ #
9
8
  # @author Aaron Allen <hello@aaronmallen.me>
10
- # @since 0.0.1
11
- # @!attribute [rw] context
12
- # @api private
13
- # @return [Context::Base] an instance of the interactor's context class
9
+ # @since 0.1.0
14
10
  module Context
15
- # @!method context_fail!(errors = nil)
16
- # Fail the interactor's context
17
- # @since 1.0.0
18
- # @see ActiveInteractor::Context::Base#fail!
19
-
20
- # @!method context_rollback!
21
- # Rollback the interactor's context
22
- # @since 1.0.0
23
- # @see ActiveInteractor::Context::Base#rollback!
24
-
25
- # @!method context_valid?(context = nil)
26
- # Whether or not the interactor's context is valid
27
- # @since 1.0.0
28
- # @see ActiveInteractor::Context::Base#valid?
29
- def self.included(base)
30
- base.class_eval do
31
- extend ClassMethods
32
- delegate :fail!, :rollback!, to: :context, prefix: true
33
- delegate(*ActiveModel::Validations.instance_methods, to: :context, prefix: true)
34
- delegate(*ActiveModel::Validations::HelperMethods.instance_methods, to: :context, prefix: true)
35
-
36
- private
37
-
38
- attr_accessor :context
39
- end
40
- end
41
-
42
- # Interactor context class methods extended by all {Base}
11
+ # Interactor context class methods. Because {ClassMethods} is a module classes should extend {ClassMethods}
12
+ # rather than inherit from it.
13
+ #
14
+ # @author Aaron Allen <hello@aaronmallen.me>
15
+ # @since 0.1.0
43
16
  module ClassMethods
44
- delegate(*ActiveModel::Validations::ClassMethods.instance_methods, to: :context_class, prefix: :context)
45
- delegate(*ActiveModel::Validations::HelperMethods.instance_methods, to: :context_class, prefix: :context)
46
17
  # @!method context_attributes(*attributes)
47
- # Set or get attributes defined on the interactor's context class
48
- # @since 0.0.1
49
- # @see ActiveInteractor::Context::Attributes#attributes
18
+ # Call {ActiveInteractor::Context::Attributes#attributes .attributes} on the {Base interactor} class'
19
+ # {#context_class context class}
20
+ #
21
+ # @since 0.1.0
22
+ #
23
+ # @example
24
+ # class MyInteractor < ActiveInteractor::Base
25
+ # context_attributes :first_name, :last_name
26
+ # end
27
+ #
28
+ # MyInteractor.context_class.attributes
29
+ # #=> [:first_name, :last_name]
50
30
  delegate :attributes, to: :context_class, prefix: :context
51
31
 
52
- # The context class of the interactor
53
- # @note If a context class is not defined on the interactor
54
- # with {#contextualize_with} ActiveInteractor will attempt
55
- # to find a class with the naming conventions:
56
- # `MyInteractor::Context` or `MyInteractorContext`
57
- # if no matching context class is found a context class
58
- # will be created with the naming convention: `MyInteractor::Context`.
59
- # @example With an existing class named `MyInteractor::Context`
60
- # class MyInteractor::Context < ActiveInteractor::Context::Base
61
- # end
62
- #
63
- # class MyInteractor < ActiveInteractor::Base
64
- # end
65
- #
66
- # MyInteractor.context_class
67
- # #=> MyInteractor::Context
68
- # @example With an existing class named `MyInteractorContext`
69
- # class MyInteractorContext < ActiveInteractor::Context::Base
70
- # end
71
- #
72
- # class MyInteractor < ActiveInteractor::Base
73
- # end
74
- #
75
- # MyInteractor.context_class
76
- # #=> MyInteractorContext
77
- # @example With no existing context class
78
- # class MyInteractor < ActiveInteractor::Base
79
- # end
80
- #
81
- # MyInteractor.context_class
82
- # #=> MyInteractor::Context
83
- # @return [Class] the interactor's context class
32
+ # @!method context_attribute_method?(attribute)
33
+ # Call {ActiveInteractor::Context::Base.attribute_method? .attribute_method?} on the {Base interactor} class'
34
+ # {#context_class context class}
35
+ #
36
+ # @since 0.1.0
37
+ #
38
+ # @!method context_clear_validators!(attribute)
39
+ # Call {ActiveInteractor::Context::Base.clear_validators! .clear_validators!} on the {Base interactor} class'
40
+ # {#context_class context class}
41
+ #
42
+ # @since 0.1.0
43
+ #
44
+ # @!method context_validate(*args, &block)
45
+ # Call {ActiveInteractor::Context::Base.validate .validate} on the {Base interactor} class'
46
+ # {#context_class context class}
47
+ #
48
+ # @since 0.1.0
49
+ #
50
+ # @!method context_validates(*attributes)
51
+ # Call {ActiveInteractor::Context::Base.validates .validates} on the {Base interactor} class'
52
+ # {#context_class context class}
53
+ #
54
+ # @since 0.1.0
55
+ #
56
+ # @!method context_validates!(*attributes)
57
+ # Call {ActiveInteractor::Context::Base.validates! .validates!} on the {Base interactor} class'
58
+ # {#context_class context class}
59
+ #
60
+ # @since 0.1.0
61
+ #
62
+ # @!method context_validates_each(*attr_names, &block)
63
+ # Call {ActiveInteractor::Context::Base.validates_each .validates_each} on the {Base interactor} class'
64
+ # {#context_class context class}
65
+ #
66
+ # @since 0.1.0
67
+ #
68
+ # @!method context_validates_with(*args, &block)
69
+ # Call {ActiveInteractor::Context::Base.validates_with .validates_with} on the {Base interactor} class'
70
+ # {#context_class context class}
71
+ #
72
+ # @since 0.1.0
73
+ #
74
+ # @!method context_validators
75
+ # Call {ActiveInteractor::Context::Base.validators .validators} on the {Base interactor} class'
76
+ # {#context_class context class}
77
+ #
78
+ # @since 0.1.0
79
+ #
80
+ # @!method context_validators_on(*attributes)
81
+ # Call {ActiveInteractor::Context::Base.validators_on .validators_on} on the {Base interactor} class'
82
+ # {#context_class context class}
83
+ #
84
+ # @since 0.1.0
85
+ delegate(*ActiveModel::Validations::ClassMethods.instance_methods, to: :context_class, prefix: :context)
86
+
87
+ # @!method context_validates_absence_of(*attr_names)
88
+ # Call {ActiveInteractor::Context::Base.validates_absence_of .validates_absence_of} on the {Base interactor}
89
+ # class' {#context_class context class}
90
+ #
91
+ # @since 0.1.0
92
+ #
93
+ # @!method context_validates_acceptance_of(*attr_names)
94
+ # Call {ActiveInteractor::Context::Base.validates_acceptance_of .validates_acceptance_of} on the
95
+ # {Base interactor} class' {#context_class context class}
96
+ #
97
+ # @since 0.1.0
98
+ #
99
+ # @!method context_validates_confirmation_of(*attr_names)
100
+ # Call {ActiveInteractor::Context::Base.validates_confirmation_of .validates_confirmation_of} on the
101
+ # {Base interactor} class' {#context_class context class}
102
+ #
103
+ # @since 0.1.0
104
+ #
105
+ # @!method context_validates_exclusion_of(*attr_names)
106
+ # Call {ActiveInteractor::Context::Base.validates_exclusion_of .validates_exclusion_of} on the
107
+ # {Base interactor} class' {#context_class context class}
108
+ #
109
+ # @since 0.1.0
110
+ #
111
+ # @!method context_validates_format_of(*attr_names)
112
+ # Call {ActiveInteractor::Context::Base.validates_format_of .validates_format_of} on the {Base interactor}
113
+ # class' {#context_class context class}
114
+ #
115
+ # @since 0.1.0
116
+ #
117
+ # @!method context_validates_inclusion_of(*attr_names)
118
+ # Call {ActiveInteractor::Context::Base.validates_inclusion_of .validates_inclusion_of} on the
119
+ # {Base interactor} class' {#context_class context class}
120
+ #
121
+ # @since 0.1.0
122
+ #
123
+ # @!method context_validates_length_of(*attr_names)
124
+ # Call {ActiveInteractor::Context::Base.validates_length_of .validates_length_of} on the {Base interactor}
125
+ # class' {#context_class context class}
126
+ #
127
+ # @since 0.1.0
128
+ #
129
+ # @!method context_validates_numericality_of(*attr_names)
130
+ # Call {ActiveInteractor::Context::Base.validates_numericality_of .validates_numericality_of} on the
131
+ # {Base interactor} class' {#context_class context class}
132
+ #
133
+ # @since 0.1.0
134
+ #
135
+ # @!method context_validates_presence_of(*attr_names)
136
+ # Call {ActiveInteractor::Context::Base.validates_presence_of .validates_presence_of} on the {Base interactor}
137
+ # class' {#context_class context class}
138
+ #
139
+ # @since 0.1.0
140
+ #
141
+ # @!method context_validates_size_of(*attr_names)
142
+ # Call {ActiveInteractor::Context::Base.validates_size_of .validates_size_of} on the {Base interactor} class'
143
+ # {#context_class context class}
144
+ #
145
+ # @since 0.1.0
146
+ delegate(*ActiveModel::Validations::HelperMethods.instance_methods, to: :context_class, prefix: :context)
147
+
148
+ # Get the {Base interactor} class' {ActiveInteractor::Context::Base context} class. If no class is found or no
149
+ # class is set via the {#contextualize_with} method a new class is created.
150
+ #
151
+ # @see ActiveInteractor::Context::Loader.find_or_create
152
+ #
153
+ # @return [Const] the {Base interactor} class' {ActiveInteractor::Context::Base context} class
84
154
  def context_class
85
155
  @context_class ||= ActiveInteractor::Context::Loader.find_or_create(self)
86
156
  end
87
157
 
88
- # Manual define an interactor's context class
158
+ # Set the {Base interactor} class' {#context_class context class}
159
+ #
89
160
  # @since 1.0.0
161
+ #
90
162
  # @example
91
- # class AGenericContext < ActiveInteractor::Context::Base
163
+ # class User < ActiveRecord::Base
92
164
  # end
93
165
  #
94
166
  # class MyInteractor < ActiveInteractor::Base
95
- # contextualize_with :a_generic_context
167
+ # contextualize_with :user
96
168
  # end
97
169
  #
98
170
  # MyInteractor.context_class
99
- # #=> AGenericContext
100
- # @param klass [String|Symbol|Class] the context class
101
- # @raise [Error::InvalidContextClass] if no matching class can be found
102
- # @return [Class] the context class
171
+ # #=> User
172
+ #
173
+ # @param klass [Const, Symbol, String] the class to use as context
174
+ # @raise [Error::InvalidContextClass] if the class can not be found.
175
+ # @return [Const] the {Base interactor} class' {ActiveInteractor::Context::Base context} class
103
176
  def contextualize_with(klass)
104
177
  @context_class = begin
105
178
  context_class = klass.to_s.classify.safe_constantize
106
- raise(Error::InvalidContextClass, klass) unless context_class
179
+ raise(ActiveInteractor::Error::InvalidContextClass, klass) unless context_class
107
180
 
108
181
  context_class
109
182
  end
110
183
  end
111
184
  end
112
185
 
113
- # @api private
114
- # @param context [Context::Base|Hash] attributes to assign to the context
115
- # @return [Base] a new instane of {Base}
186
+ # @!method context_fail!(errors = nil)
187
+ # Call {ActiveInteractor::Context::Status#fail! #fail!} on the {Base interactor} instance's
188
+ # {ActiveInteractor::Context::Base context} instance
189
+ #
190
+ # @since 1.0.0
191
+ #
192
+ # @!method context_rollback!
193
+ # Call {ActiveInteractor::Context::Status#rollback! #rollback!} on the {Base interactor} instance's
194
+ # {ActiveInteractor::Context::Base context} instance
195
+ #
196
+ # @since 1.0.0
197
+ delegate :fail!, :rollback!, to: :context, prefix: true
198
+
199
+ # @!method context_errors
200
+ # Call {ActiveInteractor::Context::Base#errors #errors} on the {Base interactor} instance's
201
+ # {ActiveInteractor::Context::Base context} instance
202
+ #
203
+ # @since 0.1.0
204
+ #
205
+ # @!method context_invalid?(context = nil)
206
+ # Call {ActiveInteractor::Context::Base#invalid? #invalid?} on the {Base interactor} instance's
207
+ # {ActiveInteractor::Context::Base context} instance
208
+ #
209
+ # @since 0.1.0
210
+ #
211
+ # @!method context_valid?(context = nil)
212
+ # Call {ActiveInteractor::Context::Base#valid? #valid?} on the {Base interactor} instance's
213
+ # {ActiveInteractor::Context::Base context} instance
214
+ #
215
+ # @since 0.1.0
216
+ #
217
+ # @!method context_validate(context = nil)
218
+ # Call {ActiveInteractor::Context::Base#validate #validate} on the {Base interactor} instance's
219
+ # {ActiveInteractor::Context::Base context} instance
220
+ #
221
+ # @since 0.1.0
222
+ #
223
+ # @!method context_validate!(context = nil)
224
+ # Call {ActiveInteractor::Context::Base#validate! #validate!} on the {Base interactor} instance's
225
+ # {ActiveInteractor::Context::Base context} instance
226
+ #
227
+ # @since 0.1.0
228
+ #
229
+ # @!method context_validates_with(*args, &block)
230
+ # Call {ActiveInteractor::Context::Base#validates_with #validates_with} on the {Base interactor} instance's
231
+ # {ActiveInteractor::Context::Base context} instance
232
+ #
233
+ # @since 0.1.0
234
+ delegate(*ActiveModel::Validations.instance_methods, to: :context, prefix: true)
235
+
236
+ # @!method context_validates_absence_of(*attr_names)
237
+ # Call {ActiveInteractor::Context::Base#validates_absence_of #validates_absence_of} on the {Base interactor}
238
+ # instance's {ActiveInteractor::Context::Base context} instance
239
+ #
240
+ # @since 0.1.0
241
+ #
242
+ # @!method context_validates_acceptance_of(*attr_names)
243
+ # Call {ActiveInteractor::Context::Base#validates_acceptance_of #validates_acceptance_of} on the
244
+ # {Base interactor} instance's {ActiveInteractor::Context::Base context} instance
245
+ #
246
+ # @since 0.1.0
247
+ #
248
+ # @!method context_validates_confirmation_of(*attr_names)
249
+ # Call {ActiveInteractor::Context::Base#validates_confirmation_of #validates_confirmation_of} on the
250
+ # {Base interactor} instance's {ActiveInteractor::Context::Base context} instance
251
+ #
252
+ # @since 0.1.0
253
+ #
254
+ # @!method context_validates_exclusion_of(*attr_names)
255
+ # Call {ActiveInteractor::Context::Base#validates_exclusion_of #validates_exclusion_of} on the {Base interactor}
256
+ # instance's {ActiveInteractor::Context::Base context} instance
257
+ #
258
+ # @since 0.1.0
259
+ #
260
+ # @!method context_validates_format_of(*attr_names)
261
+ # Call {ActiveInteractor::Context::Base#validates_format_of #validates_format_of} on the {Base interactor}
262
+ # instance's {ActiveInteractor::Context::Base context} instance
263
+ #
264
+ # @since 0.1.0
265
+ #
266
+ # @!method context_validates_inclusion_of(*attr_names)
267
+ # Call {ActiveInteractor::Context::Base#validates_inclusion_of #validates_inclusion_of} on the
268
+ # {Base interactor} instance's {ActiveInteractor::Context::Base context} instance
269
+ #
270
+ # @since 0.1.0
271
+ #
272
+ # @!method context_validates_length_of(*attr_names)
273
+ # Call {ActiveInteractor::Context::Base#validates_length_of #validates_length_of} on the {Base interactor}
274
+ # instance's {ActiveInteractor::Context::Base context} instance
275
+ #
276
+ # @since 0.1.0
277
+ #
278
+ # @!method context_validates_numericality_of(*attr_names)
279
+ # Call {ActiveInteractor::Context::Base#validates_numericality_of #validates_numericality_of} on the
280
+ # {Base interactor} instance's {ActiveInteractor::Context::Base context} instance
281
+ #
282
+ # @since 0.1.0
283
+ #
284
+ # @!method context_validates_presence_of(*attr_names)
285
+ # Call {ActiveInteractor::Context::Base#validates_presence_of #validates_presence_of} on the {Base interactor}
286
+ # instance's {ActiveInteractor::Context::Base context} instance
287
+ #
288
+ # @since 0.1.0
289
+ #
290
+ # @!method context_validates_size_of(*attr_names)
291
+ # Call {ActiveInteractor::Context::Base#validates_size_of #validates_size_of} on the {Base interactor}
292
+ # instance's {ActiveInteractor::Context::Base context} instance
293
+ #
294
+ # @since 0.1.0
295
+ delegate(*ActiveModel::Validations::HelperMethods.instance_methods, to: :context, prefix: true)
296
+
297
+ # Initialize a new instance of {Base}
298
+ #
299
+ # @param context [Hash, Class] attributes to assign to the {Base interactor} instance's
300
+ # {ActiveInteractor::Context::Base context} instance
301
+ # @return [Base] a new instance of {Base}
116
302
  def initialize(context = {})
117
303
  @context = self.class.context_class.new(context)
118
304
  end
119
305
 
120
- # @api private
121
- # Mark the interactor's context as called and return the context
306
+ # Mark the {Base interactor} instance as called on the instance's {ActiveInteractor::Context::Base context}
307
+ # instance and return the {ActiveInteractor::Context::Base context} instance.
308
+ #
122
309
  # @since 1.0.0
123
- # @return [Context::Base] an instance of the interactor's context class
310
+ #
311
+ # @return [Class] the {ActiveInteractor::Context::Base context} instance
124
312
  def finalize_context!
125
313
  context.called!(self)
126
314
  context
127
315
  end
316
+
317
+ private
318
+
319
+ attr_accessor :context
128
320
  end
129
321
  end
130
322
  end
@@ -0,0 +1,256 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveInteractor
4
+ module Interactor
5
+ # Interactor perform methods. Because {Perform} is a module classes should include {Perform} rather than inherit
6
+ # from it.
7
+ #
8
+ # @author Aaron Allen <hello@aaronmallen.me>
9
+ # @since 1.0.0
10
+ module Perform
11
+ # Interactor perform class methods. Because {ClassMethods} is a module classes should extend {ClassMethods}
12
+ # rather than inherit from it.
13
+ #
14
+ # @author Aaron Allen <hello@aaronmallen.me>
15
+ # @since 1.0.0
16
+ module ClassMethods
17
+ # Initialize a new {Base interactor} instance and call its {Interactor::Perform#perform #perform} method. This
18
+ # is the default api to interact with all {Base interactors}.
19
+ #
20
+ # @since 0.1.0
21
+ #
22
+ # @example
23
+ # class MyInteractor < ActiveInteractor::Base
24
+ # def perform
25
+ # puts "I performed"
26
+ # end
27
+ # end
28
+ #
29
+ # MyInteractor.perform
30
+ # "I performed"
31
+ # #=> <#MyInteractor::Context>
32
+ #
33
+ # @param context [Hash, Class] attributes to assign to the {Base interactor} instance's
34
+ # {ActiveInteractor::Context::Base context} instance
35
+ # @param options [Hash, Perform::Options] options to use for the {.perform}. See {Perform::Options}
36
+ # @return [Class] an instance of the {Base interactor} class' {ActiveInteractor::Context::Base context}
37
+ # instance
38
+ def perform(context = {}, options = {})
39
+ new(context).with_options(options).execute_perform
40
+ end
41
+
42
+ # Initialize a new {Base interactor} instance and call its {Interactor::Perform#perform #perform} method without
43
+ # rescuing {ActiveInteractor::Error::ContextFailure}.
44
+ #
45
+ # @since 0.1.0
46
+ #
47
+ # @example Calling {Perform::ClassMethods#perform! .perform!} without failure
48
+ # class MyInteractor < ActiveInteractor::Base
49
+ # def perform
50
+ # puts "I performed"
51
+ # end
52
+ # end
53
+ #
54
+ # MyInteractor.perform!
55
+ # "I performed"
56
+ # #=> <#MyInteractor::Context>
57
+ #
58
+ # @example Calling {Perform::ClassMethods#perform! .perform!} with failure
59
+ # class MyInteractor < ActiveInteractor::Base
60
+ # def perform
61
+ # context.fail!
62
+ # end
63
+ # end
64
+ #
65
+ # MyInteractor.perform!
66
+ # ActiveInteractor::Error::ContextFailure "<#MyInteractor::Context>"
67
+ #
68
+ # @param context [Hash, Class] attributes to assign to the {Base interactor} instance's
69
+ # {ActiveInteractor::Context::Base context} instance
70
+ # @param options [Hash, Perform::Options] options to use for the {.perform}. See {Perform::Options}
71
+ # @raise [Error::ContextFailure] if the {ActiveInteractor::Context::Base context} instance
72
+ # fails.
73
+ # @return [Class] an instance of the {Base interactor} class' {ActiveInteractor::Context::Base context}
74
+ # instance
75
+ def perform!(context = {}, options = {})
76
+ new(context).with_options(options).execute_perform!
77
+ end
78
+ end
79
+
80
+ # Interactor {Interactor::Perform#perform #perform} options
81
+ #
82
+ # @author Aaron Allen <hello@aaronmallen.me>
83
+ # @since 1.0.0
84
+ #
85
+ # @!attribute [rw] skip_each_perform_callbacks
86
+ # if `true` an {Organizer::Base organizer} will be instructed to skip
87
+ # {Organizer::Callbacks::ClassMethods each_perform} callbacks.
88
+ #
89
+ # @since 1.0.0
90
+ #
91
+ # @return [Boolean] whether or not to skip {Organizer::Callbacks::ClassMethods each_perform} callbacks
92
+ #
93
+ # @!attribute [rw] skip_perform_callbacks
94
+ # if `true` an {Base interactor} will be instructed to skip {Interactor::Callbacks::ClassMethods perform}
95
+ # callbacks.
96
+ #
97
+ # @since 1.0.0
98
+ #
99
+ # @return [Boolean] whether or not to skip {Interactor::Callbacks::ClassMethods perform} callbacks.
100
+ #
101
+ # @!attribute [rw] skip_rollback
102
+ # if `true` an {Base interactor} will be instructed to skip {Interactor::Perform#rollback #rollback} on
103
+ # {Context::Base context} {ActiveInteractor::Context::Status#fail! failure}.
104
+ #
105
+ # @since 1.0.0
106
+ #
107
+ # @return [Boolean] whether or not to skip {Interactor::Perform#rollback #rollback}
108
+ #
109
+ # @!attribute [rw] skip_rollback_callbacks
110
+ # if `true` an {Base interactor} will be instructed to skip {Interactor::Callbacks::ClassMethods rollback}
111
+ # callbacks on {Context::Base context} {ActiveInteractor::Context::Status#fail! failure}.
112
+ #
113
+ # @since 1.0.0
114
+ #
115
+ # @return [Boolean] whether or not to skip {Interactor::Callbacks::ClassMethods rollback} callbacks.
116
+ #
117
+ # @!attribute [rw] validate
118
+ # if `false` an {Base interactor} will not run validations.
119
+ #
120
+ # @since 1.0.0
121
+ #
122
+ # @return [Boolean] whether or to run validations.
123
+ #
124
+ # @!attribute [rw] validate_on_calling
125
+ # if `false` an {Base interactor} will not run validations with the validation context `:calling`.
126
+ #
127
+ # @since 1.0.0
128
+ #
129
+ # @return [Boolean] whether or to run validations with the validation context `:calling`
130
+ #
131
+ # @!attribute [rw] validate_on_called
132
+ # if `false` an {Base interactor} will not run validations with the validation context `:called`.
133
+ #
134
+ # @since 1.0.0
135
+ #
136
+ # @return [Boolean] whether or to run validation with the validation context `:called`.
137
+ #
138
+ # @!method initialize(options = {})
139
+ # Initialize a new instance of {Options}
140
+ #
141
+ # @since 1.0.0
142
+ #
143
+ # @param options [Hash{Symbol=>*}] the attributes to assign to {Options}
144
+ # @option options [Boolean] :skip_each_perform_callbacks (false) the {Options#skip_each_perform_callbacks}
145
+ # attribute
146
+ # @option options [Boolean] :skip_perform_callbacks (false) the {Options#skip_perform_callbacks} attribute
147
+ # @option options [Boolean] :skip_rollback (false) the {Options#skip_rollback} attribute
148
+ # @option options [Boolean] :skip_rollback_callbacks (false) the {Options#skip_rollback_callbacks} attribute
149
+ # @option options [Boolean] :validate (true) the {Options#validate} attribute
150
+ # @option options [Boolean] :validate_on_calling (true) the {Options#validate_on_calling} attribute
151
+ # @option options [Boolean] :validate_on_called (true) the {Options#validate_on_called} attribute
152
+ # @return [Options] a new instance of {Options}
153
+ class Options
154
+ include ActiveInteractor::Configurable
155
+ defaults skip_each_perform_callbacks: false, skip_perform_callbacks: false, skip_rollback: false,
156
+ skip_rollback_callbacks: false, validate: true, validate_on_calling: true, validate_on_called: true
157
+ end
158
+
159
+ # @!method execute_perform
160
+ # Run the {Base interactor} instance's {#perform} with callbacks and validation.
161
+ #
162
+ # @api private
163
+ # @since 0.1.0
164
+ #
165
+ # @!method execute_perform!
166
+ # Run the {Base interactor} instance's {#perform} with callbacks and validation without rescuing
167
+ # {Error::ContextFailure}.
168
+ #
169
+ # @api private
170
+ # @since 0.1.0
171
+ delegate :execute_perform, :execute_perform!, to: :worker
172
+
173
+ # Duplicate an {Base interactor} instance as well as it's {#options} and {ActiveInteractor::Context::Base context}
174
+ # instances.
175
+ #
176
+ # @return [Base] a duplicated {Base interactor} instance
177
+ def deep_dup
178
+ self.class.new(context.dup).with_options(options.dup)
179
+ end
180
+
181
+ # Options for the {Base interactor} {#perform}
182
+ #
183
+ # @return [Options] an instance of {Options}
184
+ def options
185
+ @options ||= ActiveInteractor::Interactor::Perform::Options.new
186
+ end
187
+
188
+ # The steps to run when an {Base interactor} is called. An {Base interactor's} {#perform} method should never be
189
+ # called directly on an {Base interactor} instance and should instead be invoked by the {Base interactor's} class
190
+ # method {Perform::ClassMethods#perform .perform}.
191
+ #
192
+ # @since 0.1.0
193
+ #
194
+ # @abstract {Base interactors} should override {#perform} with the appropriate steps and
195
+ # {ActiveInteractor::Context::Base context} mutations required for the {Base interactor} to do its work.
196
+ #
197
+ # @example
198
+ # class MyInteractor < ActiveInteractor::Base
199
+ # def perform
200
+ # context.first_name = 'Aaron'
201
+ # end
202
+ # end
203
+ #
204
+ # MyInteractor.perform
205
+ # #=> <#MyInteractor::Context first_name='Aaron'>
206
+ def perform; end
207
+
208
+ # The steps to run when an {Base interactor} {ActiveInteractor::Context::Status#fail! fails}. An
209
+ # {Base interactor's} {#rollback} method should never be called directly and is instead called by the
210
+ # {Base interactor's} {ActiveInteractor::Context::Base context} when
211
+ # {ActiveInteractor::Context::Status#fail! #fail} is called.
212
+ #
213
+ # @since 0.1.0
214
+ #
215
+ # @abstract {Base interactors} should override {#rollback} with the appropriate steps and
216
+ # {ActiveInteractor::Context::Base context} mutations required for the {Base interactor} to roll back its work.
217
+ #
218
+ # @example
219
+ # class MyInteractor < ActiveInteractor::Base
220
+ # def perform
221
+ # context.first_name = 'Aaron'
222
+ # context.fail!
223
+ # end
224
+ #
225
+ # def rollback
226
+ # context.first_name = 'Bob'
227
+ # end
228
+ # end
229
+ #
230
+ # MyInteractor.perform
231
+ # #=> <#MyInteractor::Context first_name='Bob'>
232
+ def rollback; end
233
+
234
+ # Set {Options options} for an {Base interactor's} {#perform}
235
+ #
236
+ # @api private
237
+ #
238
+ # @param options [Hash, Perform::Options] options to use for the perform call. See {Perform::Options}
239
+ # @return [self] the {Base interactor} instance
240
+ def with_options(options)
241
+ @options = if options.is_a?(ActiveInteractor::Interactor::Perform::Options)
242
+ options
243
+ else
244
+ ActiveInteractor::Interactor::Perform::Options.new(options)
245
+ end
246
+ self
247
+ end
248
+
249
+ private
250
+
251
+ def worker
252
+ ActiveInteractor::Interactor::Worker.new(self)
253
+ end
254
+ end
255
+ end
256
+ end