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
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'An organizer with .around_each callbacks', type: :integration do
6
+ let!(:interactor1) { build_interactor('TestInteractor1') }
7
+ let!(:interactor2) { build_interactor('TestInteractor2') }
8
+ let(:interactor_class) do
9
+ build_organizer do
10
+ around_each_perform :test_around_each_perform
11
+ organize TestInteractor1, TestInteractor2
12
+
13
+ private
14
+
15
+ def find_index
16
+ (context.around_each_step.last || 0) + 1
17
+ end
18
+
19
+ def test_around_each_perform
20
+ context.around_each_step ||= []
21
+ context.around_each_step << find_index
22
+ yield
23
+ context.around_each_step << find_index
24
+ end
25
+ end
26
+ end
27
+
28
+ include_examples 'a class with interactor methods'
29
+ include_examples 'a class with interactor callback methods'
30
+ include_examples 'a class with interactor context methods'
31
+ include_examples 'a class with organizer callback methods'
32
+
33
+ describe '.perform' do
34
+ subject { interactor_class.perform }
35
+
36
+ it { is_expected.to be_a interactor_class.context_class }
37
+ it { is_expected.to have_attributes(around_each_step: [1, 2, 3, 4]) }
38
+ end
39
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'An organizer with .before_each callbacks', type: :integration do
6
+ let!(:interactor1) { build_interactor('TestInteractor1') }
7
+ let!(:interactor2) { build_interactor('TestInteractor2') }
8
+ let(:interactor_class) do
9
+ build_organizer do
10
+ before_each_perform :test_before_each_perform
11
+ organize TestInteractor1, TestInteractor2
12
+
13
+ private
14
+
15
+ def test_before_each_perform; end
16
+ end
17
+ end
18
+
19
+ include_examples 'a class with interactor methods'
20
+ include_examples 'a class with interactor callback methods'
21
+ include_examples 'a class with interactor context methods'
22
+ include_examples 'a class with organizer callback methods'
23
+
24
+ describe '.perform' do
25
+ subject { interactor_class.perform }
26
+
27
+ it { is_expected.to be_a interactor_class.context_class }
28
+ it 'is expected to receive #test_before_each_perform twice' do
29
+ expect_any_instance_of(interactor_class).to receive(:test_before_each_perform)
30
+ .exactly(:twice)
31
+ subject
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,314 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'An organizer with conditionally organized interactors', type: :integration do
6
+ let!(:test_interactor_1) { build_interactor('TestInteractor1') }
7
+ let!(:test_interactor_2) { build_interactor('TestInteractor2') }
8
+
9
+ context 'with a condition on the first interactor { :if => -> { context.test_1 } } ' \
10
+ 'and a condition on the second interactor { :if => -> { context.test_2 } }' do
11
+ let(:interactor_class) do
12
+ build_organizer do
13
+ organize do
14
+ add :test_interactor_1, if: -> { context.test_1 }
15
+ add :test_interactor_2, if: -> { context.test_2 }
16
+ end
17
+ end
18
+ end
19
+
20
+ include_examples 'a class with interactor methods'
21
+ include_examples 'a class with interactor callback methods'
22
+ include_examples 'a class with interactor context methods'
23
+ include_examples 'a class with organizer callback methods'
24
+
25
+ describe '.perform' do
26
+ subject { interactor_class.perform(context_attributes) }
27
+
28
+ context 'with context_attributes test_1 and test_2 both eq to true' do
29
+ let(:context_attributes) { { test_1: true, test_2: true } }
30
+
31
+ it { is_expected.to be_a interactor_class.context_class }
32
+ it { is_expected.to be_successful }
33
+ it 'is expected to receive #perform on both interactors' do
34
+ expect_any_instance_of(test_interactor_1).to receive(:perform)
35
+ expect_any_instance_of(test_interactor_2).to receive(:perform)
36
+ subject
37
+ end
38
+ end
39
+
40
+ context 'with context_attributes test_1 eq to true and test_2 eq to false' do
41
+ let(:context_attributes) { { test_1: true, test_2: false } }
42
+
43
+ it { is_expected.to be_a interactor_class.context_class }
44
+ it { is_expected.to be_successful }
45
+ it 'is expected to receive #perform on the first interactor' do
46
+ expect_any_instance_of(test_interactor_1).to receive(:perform)
47
+ subject
48
+ end
49
+
50
+ it 'is expected not to receive #perform on the first interactor' do
51
+ expect_any_instance_of(test_interactor_2).not_to receive(:perform)
52
+ subject
53
+ end
54
+ end
55
+
56
+ context 'with context_attributes test_1 eq to false and test_2 eq to true' do
57
+ let(:context_attributes) { { test_1: false, test_2: true } }
58
+
59
+ it { is_expected.to be_a interactor_class.context_class }
60
+ it { is_expected.to be_successful }
61
+ it 'is expected not to receive #perform on the first interactor' do
62
+ expect_any_instance_of(test_interactor_1).not_to receive(:perform)
63
+ subject
64
+ end
65
+
66
+ it 'is expected to receive #perform on the first interactor' do
67
+ expect_any_instance_of(test_interactor_2).to receive(:perform)
68
+ subject
69
+ end
70
+ end
71
+
72
+ context 'with context_attributes test_1 and test_2 both eq to false' do
73
+ let(:context_attributes) { { test_1: false, test_2: false } }
74
+
75
+ it { is_expected.to be_a interactor_class.context_class }
76
+ it { is_expected.to be_successful }
77
+ it 'is expected not to receive #perform on the first interactor' do
78
+ expect_any_instance_of(test_interactor_1).not_to receive(:perform)
79
+ subject
80
+ end
81
+
82
+ it 'is expected not to receive #perform on the first interactor' do
83
+ expect_any_instance_of(test_interactor_2).not_to receive(:perform)
84
+ subject
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ context 'with a condition on the first interactor { :unless => -> { context.test_1 } } ' \
91
+ 'and a condition on the second interactor { :unless => -> { context.test_2 } }' do
92
+ let(:interactor_class) do
93
+ build_organizer do
94
+ organize do
95
+ add :test_interactor_1, unless: -> { context.test_1 }
96
+ add :test_interactor_2, unless: -> { context.test_2 }
97
+ end
98
+ end
99
+ end
100
+
101
+ include_examples 'a class with interactor methods'
102
+ include_examples 'a class with interactor callback methods'
103
+ include_examples 'a class with interactor context methods'
104
+ include_examples 'a class with organizer callback methods'
105
+
106
+ describe '.perform' do
107
+ subject { interactor_class.perform(context_attributes) }
108
+
109
+ context 'with context_attributes test_1 and test_2 both eq to true' do
110
+ let(:context_attributes) { { test_1: true, test_2: true } }
111
+
112
+ it { is_expected.to be_a interactor_class.context_class }
113
+ it { is_expected.to be_successful }
114
+ it 'is expected not to receive #perform on the first interactor' do
115
+ expect_any_instance_of(test_interactor_1).not_to receive(:perform)
116
+ subject
117
+ end
118
+
119
+ it 'is expected not to receive #perform on the second interactor' do
120
+ expect_any_instance_of(test_interactor_2).not_to receive(:perform)
121
+ subject
122
+ end
123
+ end
124
+
125
+ context 'with context_attributes test_1 eq to true and test_2 eq to false' do
126
+ let(:context_attributes) { { test_1: true, test_2: false } }
127
+
128
+ it { is_expected.to be_a interactor_class.context_class }
129
+ it { is_expected.to be_successful }
130
+ it 'is expected not to receive #perform on the first interactor' do
131
+ expect_any_instance_of(test_interactor_1).not_to receive(:perform)
132
+ subject
133
+ end
134
+
135
+ it 'is expected to receive #perform on the second interactor' do
136
+ expect_any_instance_of(test_interactor_2).to receive(:perform)
137
+ subject
138
+ end
139
+ end
140
+
141
+ context 'with context_attributes test_1 eq to false and test_2 eq to true' do
142
+ let(:context_attributes) { { test_1: false, test_2: true } }
143
+
144
+ it { is_expected.to be_a interactor_class.context_class }
145
+ it { is_expected.to be_successful }
146
+ it 'is expected to receive #perform on the first interactor' do
147
+ expect_any_instance_of(test_interactor_1).to receive(:perform)
148
+ subject
149
+ end
150
+
151
+ it 'is expected not to receive #perform on the second interactor' do
152
+ expect_any_instance_of(test_interactor_2).not_to receive(:perform)
153
+ subject
154
+ end
155
+ end
156
+
157
+ context 'with context_attributes test_1 and test_2 both eq to false' do
158
+ let(:context_attributes) { { test_1: false, test_2: false } }
159
+
160
+ it { is_expected.to be_a interactor_class.context_class }
161
+ it { is_expected.to be_successful }
162
+ it 'is expected to receive #perform on both interactors' do
163
+ expect_any_instance_of(test_interactor_1).to receive(:perform)
164
+ expect_any_instance_of(test_interactor_2).to receive(:perform)
165
+ subject
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ context 'with a condition on the first interactor { :if => :test_method } and :test_method returning true' do
172
+ let(:interactor_class) do
173
+ build_organizer do
174
+ organize do
175
+ add :test_interactor_1, if: :test_method
176
+ add :test_interactor_2
177
+ end
178
+
179
+ private
180
+
181
+ def test_method
182
+ true
183
+ end
184
+ end
185
+ end
186
+
187
+ include_examples 'a class with interactor methods'
188
+ include_examples 'a class with interactor callback methods'
189
+ include_examples 'a class with interactor context methods'
190
+ include_examples 'a class with organizer callback methods'
191
+
192
+ describe '.perform' do
193
+ subject { interactor_class.perform }
194
+
195
+ it { is_expected.to be_a interactor_class.context_class }
196
+ it { is_expected.to be_successful }
197
+ it 'is expected to receive #perform on both interactors' do
198
+ expect_any_instance_of(test_interactor_1).to receive(:perform)
199
+ expect_any_instance_of(test_interactor_2).to receive(:perform)
200
+ subject
201
+ end
202
+ end
203
+ end
204
+
205
+ context 'with a condition on the first interactor { :if => :test_method } and :test_method returning false' do
206
+ let(:interactor_class) do
207
+ build_organizer do
208
+ organize do
209
+ add :test_interactor_1, if: :test_method
210
+ add :test_interactor_2
211
+ end
212
+
213
+ private
214
+
215
+ def test_method
216
+ false
217
+ end
218
+ end
219
+ end
220
+
221
+ include_examples 'a class with interactor methods'
222
+ include_examples 'a class with interactor callback methods'
223
+ include_examples 'a class with interactor context methods'
224
+ include_examples 'a class with organizer callback methods'
225
+
226
+ describe '.perform' do
227
+ subject { interactor_class.perform }
228
+
229
+ it { is_expected.to be_a interactor_class.context_class }
230
+ it { is_expected.to be_successful }
231
+ it 'is expected not to receive #perform on the first interactor' do
232
+ expect_any_instance_of(test_interactor_1).not_to receive(:perform)
233
+ subject
234
+ end
235
+
236
+ it 'is expected to receive #perform on the second interactor' do
237
+ expect_any_instance_of(test_interactor_2).to receive(:perform)
238
+ subject
239
+ end
240
+ end
241
+ end
242
+
243
+ context 'with a condition on the first interactor { :unless => :test_method } and :test_method returning true' do
244
+ let(:interactor_class) do
245
+ build_organizer do
246
+ organize do
247
+ add :test_interactor_1, unless: :test_method
248
+ add :test_interactor_2
249
+ end
250
+
251
+ private
252
+
253
+ def test_method
254
+ true
255
+ end
256
+ end
257
+ end
258
+
259
+ include_examples 'a class with interactor methods'
260
+ include_examples 'a class with interactor callback methods'
261
+ include_examples 'a class with interactor context methods'
262
+ include_examples 'a class with organizer callback methods'
263
+
264
+ describe '.perform' do
265
+ subject { interactor_class.perform }
266
+
267
+ it { is_expected.to be_a interactor_class.context_class }
268
+ it { is_expected.to be_successful }
269
+ it 'is expected not to receive #perform on the first interactor' do
270
+ expect_any_instance_of(test_interactor_1).not_to receive(:perform)
271
+ subject
272
+ end
273
+
274
+ it 'is expected to receive #perform on the second interactor' do
275
+ expect_any_instance_of(test_interactor_2).to receive(:perform)
276
+ subject
277
+ end
278
+ end
279
+ end
280
+
281
+ context 'with a condition on the first interactor { :unless => :test_method } and :test_method returning false' do
282
+ let(:interactor_class) do
283
+ build_organizer do
284
+ organize do
285
+ add :test_interactor_1, unless: :test_method
286
+ add :test_interactor_2
287
+ end
288
+
289
+ private
290
+
291
+ def test_method
292
+ false
293
+ end
294
+ end
295
+ end
296
+
297
+ include_examples 'a class with interactor methods'
298
+ include_examples 'a class with interactor callback methods'
299
+ include_examples 'a class with interactor context methods'
300
+ include_examples 'a class with organizer callback methods'
301
+
302
+ describe '.perform' do
303
+ subject { interactor_class.perform }
304
+
305
+ it { is_expected.to be_a interactor_class.context_class }
306
+ it { is_expected.to be_successful }
307
+ it 'is expected to receive #perform on both interactors' do
308
+ expect_any_instance_of(test_interactor_1).to receive(:perform)
309
+ expect_any_instance_of(test_interactor_2).to receive(:perform)
310
+ subject
311
+ end
312
+ end
313
+ end
314
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,17 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- begin
4
- require 'codacy-coverage'
5
- require 'simplecov'
6
-
7
- Codacy::Reporter.start if ENV['CODACY_PROJECT_TOKEN']
8
- SimpleCov.start do
9
- track_files '/lib/**/*.rb'
10
- add_filter '/spec/'
11
- add_filter '/lib/rails/**'
12
- end
13
- rescue LoadError
14
- puts 'Not reporting coverage...'
3
+ require_relative 'support/coverage'
4
+
5
+ Spec::Coverage::Runner.start do
6
+ add_formatter :simple_cov_html
7
+ add_formatter :codacy
8
+ track '/lib/**/*.rb'
9
+ filter '/spec/'
10
+ filter '/lib/rails/**'
15
11
  end
16
12
 
17
13
  require 'bundler/setup'
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'coverage/reporters'
4
+ require_relative 'coverage/runner'
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spec
4
+ module Coverage
5
+ module Reporters
6
+ EMPTY_LOAD_RESULT = [{}, [], []].freeze
7
+ end
8
+ end
9
+ end
10
+
11
+ Dir[File.expand_path('reporters/*.rb', __dir__)].sort.each { |file| require file }
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spec
4
+ module Coverage
5
+ module Reporters
6
+ module Codacy
7
+ class << self
8
+ def load
9
+ return EMPTY_LOAD_RESULT unless load_dependencies
10
+
11
+ [formatters, initialize_steps, run_steps]
12
+ end
13
+
14
+ private
15
+
16
+ def load_dependencies
17
+ require 'codacy-coverage'
18
+ true
19
+ rescue LoadError
20
+ puts 'Skipping Codacy Coverage reporting...'
21
+ false
22
+ end
23
+
24
+ def formatters
25
+ { codacy: ::Codacy::Formatter }
26
+ end
27
+
28
+ def initialize_steps
29
+ []
30
+ end
31
+
32
+ def run_steps
33
+ []
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end