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,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'A basic interactor', type: :integration do
6
+ let(:interactor_class) do
7
+ build_interactor do
8
+ def perform
9
+ context.test_field = 'test'
10
+ end
11
+ end
12
+ end
13
+
14
+ include_examples 'a class with interactor methods'
15
+ include_examples 'a class with interactor callback methods'
16
+ include_examples 'a class with interactor context methods'
17
+
18
+ describe '.context_class' do
19
+ subject { interactor_class.context_class }
20
+
21
+ it { is_expected.to eq TestInteractor::Context }
22
+ it { is_expected.to be < ActiveInteractor::Context::Base }
23
+ end
24
+
25
+ describe '.perform' do
26
+ subject { interactor_class.perform }
27
+
28
+ it { is_expected.to be_a interactor_class.context_class }
29
+ it { is_expected.to be_successful }
30
+ it { is_expected.to have_attributes(test_field: 'test') }
31
+ end
32
+
33
+ describe '.perform!' do
34
+ subject { interactor_class.perform! }
35
+
36
+ it { expect { subject }.not_to raise_error }
37
+ it { is_expected.to be_a interactor_class.context_class }
38
+ it { is_expected.to be_successful }
39
+ it { is_expected.to have_attributes(test_field: 'test') }
40
+ end
41
+
42
+ context 'having #context_attributes :test_field' do
43
+ let(:interactor_class) do
44
+ build_interactor do
45
+ context_attributes :test_field
46
+
47
+ def perform
48
+ context.test_field = 'test'
49
+ context.some_other_field = 'test 2'
50
+ end
51
+ end
52
+ end
53
+
54
+ include_examples 'a class with interactor methods'
55
+ include_examples 'a class with interactor callback methods'
56
+ include_examples 'a class with interactor context methods'
57
+
58
+ describe '.perform' do
59
+ subject(:result) { interactor_class.perform }
60
+
61
+ it { is_expected.to be_a interactor_class.context_class }
62
+ it { is_expected.to be_successful }
63
+ it { is_expected.to have_attributes(test_field: 'test', some_other_field: 'test 2') }
64
+
65
+ describe '.attributes' do
66
+ subject { result.attributes }
67
+
68
+ it { is_expected.to eq(test_field: 'test') }
69
+ end
70
+ end
71
+ end
72
+
73
+ context 'having a .name "AnInteractor"' do
74
+ let(:interactor_class) { build_interactor('AnInteractor') }
75
+
76
+ context 'having a class defined named "AnInteractorContext"' do
77
+ let!(:context_class) { build_context('AnInteractorContext') }
78
+
79
+ describe '.context_class' do
80
+ subject { interactor_class.context_class }
81
+
82
+ it { is_expected.to eq AnInteractorContext }
83
+ it { is_expected.to be < ActiveInteractor::Context::Base }
84
+ end
85
+ end
86
+ end
87
+
88
+ context 'with a context class named "ATestContext"' do
89
+ let!(:context_class) { build_context('ATestContext') }
90
+
91
+ context 'with .contextualize_with :a_test_context' do
92
+ let(:interactor_class) do
93
+ build_interactor do
94
+ contextualize_with :a_test_context
95
+ end
96
+ end
97
+
98
+ describe '.context_class' do
99
+ subject { interactor_class.context_class }
100
+
101
+ it { is_expected.to eq ATestContext }
102
+ it { is_expected.to be < ActiveInteractor::Context::Base }
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'A basic organizer', type: :integration do
6
+ let!(:test_interactor_1) do
7
+ build_interactor('TestInteractor1') do
8
+ def perform
9
+ context.test_field_1 = 'test 1'
10
+ end
11
+ end
12
+ end
13
+
14
+ let!(:test_interactor_2) do
15
+ build_interactor('TestInteractor2') do
16
+ def perform
17
+ context.test_field_2 = 'test 2'
18
+ end
19
+ end
20
+ end
21
+
22
+ let(:interactor_class) do
23
+ build_organizer do
24
+ organize TestInteractor1, TestInteractor2
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 '.context_class' do
34
+ subject { interactor_class.context_class }
35
+
36
+ it { is_expected.to eq TestOrganizer::Context }
37
+ it { is_expected.to be < ActiveInteractor::Context::Base }
38
+ end
39
+
40
+ describe '.perform' do
41
+ subject { interactor_class.perform }
42
+
43
+ it { is_expected.to be_a interactor_class.context_class }
44
+ it { is_expected.to be_successful }
45
+ it { is_expected.to have_attributes(test_field_1: 'test 1', test_field_2: 'test 2') }
46
+ end
47
+
48
+ context 'when the first interactor fails' do
49
+ let!(:test_interactor_1) do
50
+ build_interactor('TestInteractor1') do
51
+ def perform
52
+ context.fail!
53
+ end
54
+ end
55
+ end
56
+
57
+ describe '.perform' do
58
+ subject { interactor_class.perform }
59
+
60
+ it { expect { subject }.not_to raise_error }
61
+ it { is_expected.to be_a interactor_class.context_class }
62
+ it { is_expected.to be_failure }
63
+ it 'is expected to receive #rollback on the first interactor' do
64
+ expect_any_instance_of(test_interactor_1).to receive(:rollback)
65
+ .and_call_original
66
+ subject
67
+ end
68
+ it 'is expected not to receive #perform! on the second interactor' do
69
+ expect_any_instance_of(test_interactor_2).not_to receive(:perform!)
70
+ subject
71
+ end
72
+ end
73
+ end
74
+
75
+ context 'when the second interactor fails' do
76
+ let!(:test_interactor_2) do
77
+ build_interactor('TestInteractor2') do
78
+ def perform
79
+ context.fail!
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '.perform' do
85
+ subject { interactor_class.perform }
86
+
87
+ it { expect { subject }.not_to raise_error }
88
+ it { is_expected.to be_a interactor_class.context_class }
89
+ it { is_expected.to be_failure }
90
+ it 'is expected to receive #rollback on all interactors' do
91
+ expect_any_instance_of(test_interactor_2).to receive(:rollback)
92
+ expect_any_instance_of(test_interactor_1).to receive(:rollback)
93
+ subject
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'A failing interactor', type: :integration do
6
+ let(:interactor_class) do
7
+ build_interactor do
8
+ def perform
9
+ context.fail!
10
+ end
11
+ end
12
+ end
13
+
14
+ include_examples 'a class with interactor methods'
15
+ include_examples 'a class with interactor callback methods'
16
+ include_examples 'a class with interactor context methods'
17
+
18
+ describe '.context_class' do
19
+ subject { interactor_class.context_class }
20
+
21
+ it { is_expected.to eq TestInteractor::Context }
22
+ it { is_expected.to be < ActiveInteractor::Context::Base }
23
+ end
24
+
25
+ describe '.perform' do
26
+ subject { interactor_class.perform }
27
+
28
+ it { expect { subject }.not_to raise_error }
29
+ it { is_expected.to be_a interactor_class.context_class }
30
+ it { is_expected.to be_failure }
31
+ it 'is expected to receive #rollback' do
32
+ expect_any_instance_of(interactor_class).to receive(:rollback)
33
+ subject
34
+ end
35
+ end
36
+
37
+ describe '.perform!' do
38
+ subject { interactor_class.perform! }
39
+
40
+ it { expect { subject }.to raise_error(ActiveInteractor::Error::ContextFailure) }
41
+ end
42
+ end
@@ -5,7 +5,7 @@ begin
5
5
  require 'active_interactor/rails'
6
6
  require 'active_interactor/rails/orm/active_record'
7
7
 
8
- RSpec.describe 'ActiveRecord Integration', type: :integration do
8
+ RSpec.describe 'ActiveRecord integration', type: :integration do
9
9
  let!(:active_record_base_mock) do
10
10
  build_class('ActiveRecordBaseMock') do
11
11
  def self.attr_accessor(*attributes)
@@ -43,7 +43,7 @@ begin
43
43
  end
44
44
  end
45
45
 
46
- context 'after ActiveSupport.run_load_hooks has been invoked with :active_record' do
46
+ context 'after ActiveSupport.run_load_hooks has been received with :active_record' do
47
47
  before { ActiveSupport.run_load_hooks(:active_record, active_record_base_mock) }
48
48
 
49
49
  describe 'an ActiveRecord model class with .acts_as_context' do
@@ -81,7 +81,7 @@ begin
81
81
  subject
82
82
  end
83
83
 
84
- it 'is expected to invoke super on parent class with nil attributes' do
84
+ it 'is expected to receive super on parent class with nil attributes' do
85
85
  expect(active_record_base_mock).to receive(:new).with(nil)
86
86
  subject
87
87
  end
@@ -101,7 +101,7 @@ begin
101
101
  subject
102
102
  end
103
103
 
104
- it 'is expected to invoke super on parent class with { :foo => "foo" }' do
104
+ it 'is expected to receive super on parent class with { :foo => "foo" }' do
105
105
  expect(active_record_base_mock).to receive(:new).with(foo: 'foo')
106
106
  subject
107
107
  end
@@ -122,7 +122,7 @@ begin
122
122
  context 'with _rolled_back equal to true on previous instance' do
123
123
  before { previous_instance.instance_variable_set('@_rolled_back', true) }
124
124
 
125
- it 'is expected to have instance_variable @_rolled_back eq to true' do
125
+ it 'is expected to have instance variable @_rolled_back eq to true' do
126
126
  expect(subject.instance_variable_get('@_rolled_back')).to eq true
127
127
  end
128
128
  end
@@ -130,7 +130,7 @@ begin
130
130
  context 'with _called eq to ["foo"] on previous instance' do
131
131
  before { previous_instance.instance_variable_set('@_called', %w[foo]) }
132
132
 
133
- it 'is expected to have instance_variable @_called eq to ["foo"]' do
133
+ it 'is expected to have instance variable @_called eq to ["foo"]' do
134
134
  expect(subject.instance_variable_get('@_called')).to eq %w[foo]
135
135
  end
136
136
  end
@@ -156,7 +156,7 @@ begin
156
156
  context 'with _rolled_back equal to true on merging instance' do
157
157
  before { merge_instance.instance_variable_set('@_rolled_back', true) }
158
158
 
159
- it 'is expected to have instance_variable @_rolled_back eq to true' do
159
+ it 'is expected to have instance variable @_rolled_back eq to true' do
160
160
  expect(subject.instance_variable_get('@_rolled_back')).to eq true
161
161
  end
162
162
  end
@@ -180,7 +180,7 @@ begin
180
180
  context 'with _rolled_back equal to true on merging instance' do
181
181
  before { merge_instance.instance_variable_set('@_rolled_back', true) }
182
182
 
183
- it 'is expected to have instance_variable @_rolled_back eq to true' do
183
+ it 'is expected to have instance variable @_rolled_back eq to true' do
184
184
  expect(subject.instance_variable_get('@_rolled_back')).to eq true
185
185
  end
186
186
  end
@@ -253,7 +253,7 @@ begin
253
253
  it { expect { subject }.not_to raise_error }
254
254
  it { is_expected.to be_a model_mock }
255
255
  it { is_expected.to be_failure }
256
- it 'is expected to #rollback' do
256
+ it 'is expected to receive #rollback' do
257
257
  expect_any_instance_of(interactor_class).to receive(:rollback)
258
258
  subject
259
259
  end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'An interactor with .after_context_validation callbacks', type: :integration do
6
+ let(:interactor_class) do
7
+ build_interactor do
8
+ context_validates :test_field, presence: true
9
+ after_context_validation :downcase_test_field
10
+
11
+ private
12
+
13
+ def downcase_test_field
14
+ context.test_field = context.test_field.downcase
15
+ 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
+
23
+ describe '.perform' do
24
+ subject { interactor_class.perform(context_attributes) }
25
+
26
+ context 'with valid context attributes' do
27
+ let(:context_attributes) { { test_field: 'TEST' } }
28
+
29
+ it { is_expected.to be_a interactor_class.context_class }
30
+ it { is_expected.to be_successful }
31
+ it { is_expected.to have_attributes(test_field: 'test') }
32
+ end
33
+ end
34
+
35
+ context 'having a condition on #after_context_valdation' do
36
+ let(:interactor_class) do
37
+ build_interactor do
38
+ context_validates :test_field, presence: true
39
+ after_context_validation :downcase_test_field, if: -> { context.should_downcase }
40
+
41
+ private
42
+
43
+ def downcase_test_field
44
+ context.test_field = context.test_field.downcase
45
+ end
46
+ end
47
+ end
48
+
49
+ describe '.perform' do
50
+ subject { interactor_class.perform(context_attributes) }
51
+
52
+ context 'with :test_field "TEST" and :should_downcase true' do
53
+ let(:context_attributes) { { test_field: 'TEST', should_downcase: true } }
54
+
55
+ it { is_expected.to be_a interactor_class.context_class }
56
+ it { is_expected.to be_successful }
57
+ it { is_expected.to have_attributes(test_field: 'test') }
58
+ end
59
+
60
+ context 'with :test_field "TEST" and :should_downcase false' do
61
+ let(:context_attributes) { { test_field: 'TEST', should_downcase: false } }
62
+
63
+ it { is_expected.to be_a interactor_class.context_class }
64
+ it { is_expected.to be_successful }
65
+ it { is_expected.to have_attributes(test_field: 'TEST') }
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'An interactor with .after_perform callbacks', type: :integration do
6
+ let(:interactor_class) do
7
+ build_interactor do
8
+ after_perform :test_after_perform
9
+
10
+ private
11
+
12
+ def test_after_perform; end
13
+ end
14
+ end
15
+
16
+ include_examples 'a class with interactor methods'
17
+ include_examples 'a class with interactor callback methods'
18
+ include_examples 'a class with interactor context methods'
19
+
20
+ describe '.perform' do
21
+ subject { interactor_class.perform }
22
+
23
+ it { is_expected.to be_a interactor_class.context_class }
24
+ it { is_expected.to be_successful }
25
+ it 'is expected to receive #test_after_perform' do
26
+ expect_any_instance_of(interactor_class).to receive(:test_after_perform)
27
+ subject
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'An interactor with .after_rollback callbacks', type: :integration do
6
+ let(:interactor_class) do
7
+ build_interactor do
8
+ after_rollback :test_after_rollback
9
+
10
+ def perform
11
+ context.fail!
12
+ end
13
+
14
+ private
15
+
16
+ def test_after_rollback; 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
+
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_after_rollback' do
29
+ expect_any_instance_of(interactor_class).to receive(:test_after_rollback)
30
+ subject
31
+ end
32
+ end
33
+ end