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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6fc8ba19ece1da4ceed9d40d07e248dd77294c23c0b90b4a4012f74bce2726f9
4
- data.tar.gz: f785b96877bab8eaed1f7cc612aad65d75bfa723170221cd54d46270e820f152
3
+ metadata.gz: df118b852db85867fab87a6582604a59c89fd0a194843e569c663cb09b969abc
4
+ data.tar.gz: 281f4e6672238ccfac070bd0c17f6831f76b455c26444a49945388b85738153c
5
5
  SHA512:
6
- metadata.gz: 589bd2fe219fc23e75eb2319242de9692ff17b157c09987495ee5931ee66137415b9c9d8291a6cd1dbcd8dc3bb7a8df8c206ecfec93c01249296c49ba590ccde
7
- data.tar.gz: d699de2f8be391922a8d26ca96767ca654547760724f732a2396e367d3cf903fac8c6d6b4c6d3a704f928d94671c3beec6292747c202020507a92e96dadba8d5
6
+ metadata.gz: 19822562f299f87b863686d0449e4c66b31096696f9f73c47e2c226a62413cdd8a379150a9e5d35a7d3f5e35433300b787965111a0bf0f9a0e29bcf4628afa04
7
+ data.tar.gz: dee8cdf2ad05de200a09549fd3ad4f2c904e34da01374230d69a6a040d2ea9c0542e76cae81a80b0bbd259f81c413784934e21e8069dbfda1b16a1acc1247056
data/.yardopts ADDED
@@ -0,0 +1,12 @@
1
+ --title ActiveInteractor
2
+ --readme README.md
3
+ --markup-provider redcarpet
4
+ --markup markdown
5
+ --exclude lib/rails
6
+ --protected
7
+ --no-private
8
+ --embed-mixins
9
+ lib/**/*.rb
10
+ -
11
+ CHANGELOG.md
12
+ LICENSE
data/CHANGELOG.md CHANGED
@@ -7,197 +7,123 @@ and this project adheres to [Semantic Versioning].
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [v1.0.0-beta.7] - 2020-01-21
10
+ ## [v1.0.0] - 2020-01-26
11
11
 
12
12
  ### Added
13
13
 
14
- - [#127] `ActiveInteractor::Rails::Models`
15
-
16
- ### Fixed
17
-
18
- - [#127] #126 `act_as_context` not working
19
-
20
- ### Removed
21
-
22
- - [#127] `ActiveInteractor::Rails::ActiveRecord`
23
-
24
- ## [v1.0.0-beta.6] - 2020-01-21
25
-
26
- ### Added
27
-
28
- - [#124] `ActiveInteractor::Context::Status`
29
- - [#124] `ActiveInteractor::Rails::ActiveRecord`
30
-
31
- ### Changed
32
-
33
- - [#124] Abstracted status methods from `ActiveInteractor::Context::Base`
34
- into `ActiveInteractor::Context::Status`
35
-
36
- ## [v1.0.0-beta.5] - 2020-01-21
37
-
38
- ### Added
39
-
40
- - [#122] `ActiveInteractor::Rails::Railtie`
41
- - [#122] `ActiveInteractor::Rails.config`
42
- - [#122] `ActiveInteractor::Rails.configure`
43
-
44
- ### Removed
45
-
46
- - [#122] `ActiveInteractor::Railtie` use `ActiveInteractor::Rails::Railtie` instead
47
- - [#122] `ActiveInteractor::Config#rails` use `ActiveInteractor::Rails.configure` instead
48
-
49
- ## [v1.0.0-beta.4] - 2020-01-14
50
-
51
- ### Added
52
-
53
- - [#114] `ActiveInteractor::Organizer::InteractorInterface`
54
- - [#114] `ActiveInteractor::Organizer::InteractorInterfaceCollection`
55
- - [#115] `ActiveInteractor::Interactor#options`
56
- - [#115] `ActiveInteractor::Interactor#with_options`
57
- - [#115] `ActiveInteractor::Interactor::PerformOptions#skip_each_perform_callbacks`
58
-
59
- ### Changed
60
-
61
- - [#115] `ActiveInteractor::Interactor::Worker#execute_perform` and `#execute_perform!` no longer accept arguments,
62
- use `ActiveInteractor::Interactor#with_options` instead.
63
- - [#115] `ActiveInteractor::Organizer` can now skip `each_perform` callbacks with
64
- the option `skip_each_perform_callbacks`
65
-
66
- ### Removed
67
-
68
- - [#115] `ActiveInteractor::Interactor#execute_rollback`
69
- - [#115] `ActiveInteractor::Interactor::Worker#run_callbacks`
70
-
71
- ## [v1.0.0-beta.3] - 2020-01-12
72
-
73
- ### Added
74
-
75
- - [#109] `ActiveInteractor::Organizer.parallel`
76
- - [#109] `ActiveInteractor::Organizer.perform_in_parallel`
77
- - [#109] `ActiveInteractor::Context::Base#merge`
78
- - [#110] `ActiveInteractor::Interactor::PerformOptions`
79
-
80
- ### Changed
81
-
82
- - [#110] `ActiveInteractor::Interactor.perform` now takes options
83
-
84
- ## [v1.0.0-beta.2] - 2020-01-07
85
-
86
- ### Added
87
-
88
- - [#102] `ActiveInteractor::Config`
89
- - [#102] `ActiveInteractor.config`
90
- - [#103] `ActiveInteractor::Rails`
91
- - [#103] `ActiveInteractor::Rails::Config`
92
- - [#102] `ActiveInteractor::Railtie`
93
- - [#105] interactor, organizer, and context generators now accept context_attributes
94
- as arguments.
95
-
96
- ### Changed
97
-
98
- - [#102] `ActiveInteractor.logger` is now part of `ActiveInteractor.config`
99
- - [#104] Interactor generators will no longer generate separate context classes for
100
- interactors if `ActiveInteractor.config.rails.generate_context_classes` is set to `false`
101
-
102
- ### Fixed
103
-
104
- - [#103] various generator fixes
105
-
106
- ### Removed
107
-
108
- - [#102] `ActiveInteractor.logger=` use `ActiveInteractor.config.logger=` instead
109
-
110
- ## [v1.0.0-beta.1] - 2020-01-06
111
-
112
- ### Added
113
-
114
- - `ActiveInteractor.logger=`
115
- - `ActiveInteractor::Base#dup`
14
+ - `ActiveInteractor::Config`
15
+ - `ActiveInteractor::Configurable`
16
+ - `ActiveInteractor::Context::Attributes#merge!`
17
+ - `ActiveInteractor::Context::Base#merge`
116
18
  - `ActiveInteractor::Context::Loader`
19
+ - `ActiveInteractor::Context::Status`
117
20
  - `ActiveInteractor::Error::InvalidContextClass`
21
+ - `ActiveInteractor::Models`
22
+ - `ActiveInteractor::Organizer::Callbacks`
23
+ - `ActiveInteractor::Organizer::InteractorInterface`
24
+ - `ActiveInteractor::Organizer::InteractorInterfaceCollection`
25
+ - `ActiveInteractor::Organizer::Organize`
26
+ - `ActiveInteractor::Organizer::Perform`
27
+ - `ActiveInteractor::Interactor::Context.contextualize_with`
118
28
  - `ActiveInteractor::Interactor::Context#context_fail!`
119
29
  - `ActiveInteractor::Interactor::Context#context_rollback!`
120
- - `ActiveInteractor::Interactor::Context.contextualize_with`
121
30
  - `ActiveInteractor::Interactor::Context#finalize_context!`
31
+ - `ActiveInteractor::Interactor::Perform`
32
+ - `ActiveInteractor::Interactor::Perform::Options`
33
+ - `ActiveInteractor::Rails`
34
+ - `ActiveInteractor::Rails::Railtie`
122
35
 
123
36
  ### Changed
124
37
 
38
+ - `ActiveInteractor::Base` now calls an `ActiveSupport.on_load` hook with `:active_interactor` and
39
+ `ActiveInteractor::Base`
125
40
  - `ActiveInteractor::Context::Attributes.attributes` now excepts arguments for attributes
126
- - `ActiveInteractor::Generators` various improvements to rails generators
41
+ - `ActiveInteractor::Interactor.perform` now takes options
127
42
  - `ActiveInteractor::Interactor::Context.context_class` will now first attempt to find an
128
43
  existing context class, and only create a new context class if a context is not found.
129
- - `ActiveInteractor::Organizer.organize` now excepts symbols and strings as arguments.
44
+ - Moved `ActiveInteractor::Organizer` to `ActiveInteractor::Organizer::Base`
45
+ - interactor, organizer, and context generators now accept `context_attributes`
46
+ as arguments.
47
+
48
+ ### Fixed
49
+
50
+ - various rails generator fixes
130
51
 
131
52
  ### Removed
132
53
 
133
- - `ActiveInteractor::Configuration`
134
- - `ActiveInteractor::Context::Attributes.attributes=` in favor of `ActiveInteractor::Context#attributes`
54
+ - `ActiveInteractor::Configuration` use `ActiveInteractor::Config`
55
+ - `ActiveInteractor::Context::Attributes.attributes=` use `ActiveInteractor::Context#attributes`
135
56
  - `ActiveInteractor::Context::Attributes.attribute_aliases`
57
+ - `ActiveInteractor::Context::Attributes.alias_attributes`
136
58
  - `ActiveInteractor::Context::Attributes#clean!`
137
59
  - `ActiveInteractor::Context::Attributes#keys`
138
60
  - `ActiveInteractor::Interactor#fail_on_invalid_context?`
61
+ - `ActiveInteractor::Interactor#execute_rollback`
139
62
  - `ActiveInteractor::Interactor#should_clean_context?`
63
+ - `ActiveInteractor::Interactor#skip_clean_context!`
64
+ - `ActiveInteractor::Interactor::Callbacks.allow_context_to_be_invalid`
140
65
  - `ActiveInteractor::Interactor::Callbacks.clean_context_on_completion`
141
66
  - `ActiveInteractor::Interactor::Context.context_attribute_aliases`
142
67
  - `ActiveInteractor::Interactor::Execution`
68
+ - `ActiveInteractor::Interactor::Worker#run_callbacks`
143
69
 
144
70
  ## [v0.1.7] - 2019-09-10
145
71
 
146
72
  ### Fixed
147
73
 
148
- - [#61] Ensure `Organizer` accurately reports context success
74
+ - Ensure `Organizer` accurately reports context success
149
75
 
150
76
  ## [v0.1.6] - 2019-07-24
151
77
 
152
78
  ### Changed
153
79
 
154
- - [#45] Lowered method complexity and enforced single responsibility
80
+ - Lowered method complexity and enforced single responsibility
155
81
 
156
82
  ### Security
157
83
 
158
- - [#48] Update simplecov: 0.16.1 → 0.17.0 (major)
159
- - [#51] Update rake: 12.3.2 → 12.3.3 (patch)
84
+ - Update simplecov: 0.16.1 → 0.17.0 (major)
85
+ - Update rake: 12.3.2 → 12.3.3 (patch)
160
86
 
161
87
  ## [v0.1.5] - 2019-06-30
162
88
 
163
89
  ### Added
164
90
 
165
- - [#39] `ActiveInteractor::Error` module
91
+ - `ActiveInteractor::Error` module
166
92
 
167
93
  ### Deprecated
168
94
 
169
- - [#39] `ActiveInteractor::Context::Failure` in favor of `ActiveInteractor::Error::ContextFailure`
95
+ - `ActiveInteractor::Context::Failure` in favor of `ActiveInteractor::Error::ContextFailure`
170
96
 
171
97
  ### Security
172
98
 
173
- - [#33], [#37] Update rubocop: 0.67.2 → 0.72.0 (major)
174
- - [#34] Various dependency updates
175
- - [#38] Update yard: 0.9.19 → 0.9.20 (minor)
99
+ - Update rubocop: 0.67.2 → 0.72.0 (major)
100
+ - Various dependency updates
101
+ - Update yard: 0.9.19 → 0.9.20 (minor)
176
102
 
177
103
  ## [v0.1.4] - 2019-04-12
178
104
 
179
105
  ### Added
180
106
 
181
- - [#28] The ability to alias attributes on interactor contexts.
107
+ - The ability to alias attributes on interactor contexts.
182
108
 
183
109
  ## [v0.1.3] - 2019-04-01
184
110
 
185
111
  ### Added
186
112
 
187
- - [#25] Implement `each_perform` callbacks on organizers
113
+ - Implement `each_perform` callbacks on organizers
188
114
 
189
115
  ## [v0.1.2] - 2019-04-01
190
116
 
191
117
  ### Added
192
118
 
193
- - [#22] Allow the directory interactors are generated in to be configurable
119
+ - Allow the directory interactors are generated in to be configurable
194
120
 
195
121
  ## [v0.1.1] - 2019-03-30
196
122
 
197
123
  ### Fixed
198
124
 
199
- - [#15] `NameError` (uninitialized constant `ActiveInteractor::Organizer`)
200
- - [#16] `NoMethodError` (undefined method `merge` for `ActiveInteractor::Context::Base`)
125
+ - `NameError` (uninitialized constant `ActiveInteractor::Organizer`)
126
+ - `NoMethodError` (undefined method `merge` for `ActiveInteractor::Context::Base`)
201
127
 
202
128
  ## v0.1.0 - 2019-03-30
203
129
 
@@ -208,14 +134,8 @@ and this project adheres to [Semantic Versioning].
208
134
 
209
135
  <!-- versions -->
210
136
 
211
- [Unreleased]: https://github.com/aaronmallen/activeinteractor/compare/v1.0.0-beta.7...HEAD
212
- [v1.0.0-beta.7]: https://github.com/aaronmallen/activeinteractor/compare/v1.0.0-beta.6...v1.0.0-beta.7
213
- [v1.0.0-beta.6]: https://github.com/aaronmallen/activeinteractor/compare/v1.0.0-beta.5...v1.0.0-beta.6
214
- [v1.0.0-beta.5]: https://github.com/aaronmallen/activeinteractor/compare/v1.0.0-beta.4...v1.0.0-beta.5
215
- [v1.0.0-beta.4]: https://github.com/aaronmallen/activeinteractor/compare/v1.0.0-beta.3...v1.0.0-beta.4
216
- [v1.0.0-beta.3]: https://github.com/aaronmallen/activeinteractor/compare/v1.0.0-beta.2...v1.0.0-beta.3
217
- [v1.0.0-beta.2]: https://github.com/aaronmallen/activeinteractor/compare/v1.0.0-beta.1...v1.0.0-beta.2
218
- [v1.0.0-beta.1]: https://github.com/aaronmallen/activeinteractor/compare/v0.1.7...v1.0.0-beta.1
137
+ [Unreleased]: https://github.com/aaronmallen/activeinteractor/compare/v1.0.0...HEAD
138
+ [v1.0.0]: https://github.com/aaronmallen/activeinteractor/compare/v0.1.7...v1.0.0
219
139
  [v0.1.7]: https://github.com/aaronmallen/activeinteractor/compare/v0.1.6...v0.1.7
220
140
  [v0.1.6]: https://github.com/aaronmallen/activeinteractor/compare/v0.1.5...v0.1.6
221
141
  [v0.1.5]: https://github.com/aaronmallen/activeinteractor/compare/v0.1.4...v0.1.5
@@ -223,31 +143,3 @@ and this project adheres to [Semantic Versioning].
223
143
  [v0.1.3]: https://github.com/aaronmallen/activeinteractor/compare/v0.1.2...v0.1.3
224
144
  [v0.1.2]: https://github.com/aaronmallen/activeinteractor/compare/v0.1.1...v0.1.2
225
145
  [v0.1.1]: https://github.com/aaronmallen/activeinteractor/compare/v0.1.0...v0.1.1
226
-
227
- <!-- pull requests and issues -->
228
-
229
- [#15]: https://github.com/aaronmallen/activeinteractor/pull/15
230
- [#16]: https://github.com/aaronmallen/activeinteractor/pull/16
231
- [#22]: https://github.com/aaronmallen/activeinteractor/pull/22
232
- [#25]: https://github.com/aaronmallen/activeinteractor/pull/25
233
- [#28]: https://github.com/aaronmallen/activeinteractor/pull/28
234
- [#33]: https://github.com/aaronmallen/activeinteractor/pull/33
235
- [#34]: https://github.com/aaronmallen/activeinteractor/pull/34
236
- [#37]: https://github.com/aaronmallen/activeinteractor/pull/37
237
- [#38]: https://github.com/aaronmallen/activeinteractor/pull/38
238
- [#39]: https://github.com/aaronmallen/activeinteractor/pull/39
239
- [#45]: https://github.com/aaronmallen/activeinteractor/pull/45
240
- [#48]: https://github.com/aaronmallen/activeinteractor/pull/48
241
- [#51]: https://github.com/aaronmallen/activeinteractor/pull/51
242
- [#61]: https://github.com/aaronmallen/activeinteractor/pull/61
243
- [#102]: https://github.com/aaronmallen/activeinteractor/pull/102
244
- [#103]: https://github.com/aaronmallen/activeinteractor/pull/103
245
- [#104]: https://github.com/aaronmallen/activeinteractor/pull/104
246
- [#105]: https://github.com/aaronmallen/activeinteractor/pull/105
247
- [#109]: https://github.com/aaronmallen/activeinteractor/pull/109
248
- [#110]: https://github.com/aaronmallen/activeinteractor/pull/110
249
- [#114]: https://github.com/aaronmallen/activeinteractor/pull/114
250
- [#115]: https://github.com/aaronmallen/activeinteractor/pull/115
251
- [#122]: https://github.com/aaronmallen/activeinteractor/pull/122
252
- [#124]: https://github.com/aaronmallen/activeinteractor/pull/124
253
- [#127]: https://github.com/aaronmallen/activeinteractor/pull/127
data/README.md CHANGED
@@ -8,50 +8,16 @@
8
8
  [![Code Quality](https://api.codacy.com/project/badge/Grade/be92c4ecf12347da82d266f6a4368b6e)](https://www.codacy.com/manual/aaronmallen/activeinteractor?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=aaronmallen/activeinteractor&amp;utm_campaign=Badge_Grade)
9
9
  [![Test Coverage](https://api.codacy.com/project/badge/Coverage/be92c4ecf12347da82d266f6a4368b6e)](https://www.codacy.com/manual/aaronmallen/activeinteractor?utm_source=github.com&utm_medium=referral&utm_content=aaronmallen/activeinteractor&utm_campaign=Badge_Coverage)
10
10
 
11
- Ruby interactors with [ActiveModel::Validations] based on the [interactor][collective_idea_interactors] gem.
12
-
13
- **ActiveInteractor v1.0.0 is currently in beta. For documentation on the current stable version please
14
- see [v0.1.7](https://github.com/aaronmallen/activeinteractor/tree/0-1-stable)**
15
-
16
- <!-- TOC -->
17
-
18
- * [Getting Started](#getting-started)
19
- * [What is an Interactor](#what-is-an-interactor)
20
- * [Usage](#usage)
21
- * [Context](#context)
22
- * [Adding to the Context](#adding-to-the-context)
23
- * [Failing the Context](#failing-the-context)
24
- * [Dealing with Failure](#dealing-with-failure)
25
- * [Context Attributes](#context-attributes)
26
- * [Validating the Context](#validating-the-context)
27
- * [Using Interactors](#using-interactors)
28
- * [Kinds of Interactors](#kinds-of-interactors)
29
- * [Interactors](#interactors)
30
- * [Organizers](#organizers)
31
- * [Organizing Interactors Conditionally](#organizing-interactors-conditionally)
32
- * [Running Interactors In Parallel](#running-interactors-in-parallel)
33
- * [Rollback](#rollback)
34
- * [Callbacks](#callbacks)
35
- * [Validation Callbacks](#validation-callbacks)
36
- * [Perform Callbacks](#perform-callbacks)
37
- * [Rollback Callbacks](#rollback-callbacks)
38
- * [Organizer Callbacks](#organizer-callbacks)
39
- * [Working With Rails](#working-with-rails)
40
- * [Generators](#generators)
41
- * [ActiveRecord Helper Methods](#activerecord-helper-methods)
42
- * [Development](#development)
43
- * [Contributing](#contributing)
44
- * [Acknowledgements](#acknowledgements)
45
- * [License](#license)
46
-
47
- <!-- TOC -->
11
+ An implementation of the [command pattern] for Ruby with [ActiveModel::Validations] based on the
12
+ [interactor][collective_idea_interactors] gem. Rich support for attributes, callbacks, and validations,
13
+ and thread safe performance methods.
48
14
 
49
15
  ## Getting Started
50
16
 
51
17
  Add this line to your application's Gemfile:
52
18
 
53
19
  ```ruby
54
- gem 'activeinteractor', '~> 1.0.0.beta.7'
20
+ gem 'activeinteractor'
55
21
  ```
56
22
 
57
23
  And then execute:
@@ -63,824 +29,14 @@ bundle
63
29
  Or install it yourself as:
64
30
 
65
31
  ```bash
66
- gem install activeinteractor --pre
32
+ gem install activeinteractor
67
33
  ```
68
34
 
69
- ## What is an Interactor
70
-
71
- An interactor is a simple, single-purpose service object.
72
-
73
- Interactors can be used to reduce the responsibility of your controllers,
74
- workers, and models and encapsulate your application's [business logic][business_logic_wikipedia].
75
- Each interactor represents one thing that your application does.
76
-
77
35
  ## Usage
78
36
 
79
- ### Context
80
-
81
- Each interactor will have it's own immutable context and context class. All context classes should
82
- inherit from `ActiveInteractor::Context::Base`. By default an interactor will attempt to find an existing
83
- class following the naming conventions: `MyInteractor::Context` or `MyInteractorContext`. If no class
84
- is found a context class will be created using the naming convention `MyInteractor::Context` for example:
85
-
86
- ```ruby
87
- class MyInteractor < ActiveInteractor::Base; end
88
- class MyInteractor::Context < ActiveInteractor::Context::Base; end
89
-
90
- MyInteractor.context_class #=> MyInteractor::Context
91
- ```
92
-
93
- ```ruby
94
- class MyInteractorContext < ActiveInteractor::Context::Base; end
95
- class MyInteractor < ActiveInteractor::Base; end
96
-
97
- MyInteractor.context_class #=> MyInteractorContext
98
- ```
99
-
100
- ```ruby
101
- class MyInteractor < ActiveInteractor::Base; end
102
-
103
- MyInteractor.context_class #=> MyInteractor::Context
104
- ```
105
-
106
- Additionally you can manually specify a context for an interactor with the `contextualize_with`
107
- method.
108
-
109
- ```ruby
110
- class MyGenericContext < ActiveInteractor::Context::Base; end
111
-
112
- class MyInteractor
113
- contextualize_with :my_generic_context
114
- end
115
-
116
- MyInteractor.context_class #=> MyGenericContext
117
- ```
118
-
119
- An interactor's context contains everything the interactor needs to do its work. When an interactor does its single purpose,
120
- it affects its given context.
121
-
122
- #### Adding to the Context
123
-
124
- All instances of context inherit from `OpenStruct`. As an interactor runs it can add information to
125
- it's context.
126
-
127
- ```ruby
128
- class MyInteractor
129
- def perform
130
- context.user = User.create(...)
131
- end
132
- end
133
- ```
134
-
135
- #### Failing the Context
136
-
137
- When something goes wrong in your interactor, you can flag the context as failed.
138
-
139
- ```ruby
140
- context.fail!
141
- ```
142
-
143
- When given an argument of an instance of `ActiveModel::Errors`, the `#fail!` method can also update the context.
144
- The following are equivalent:
145
-
146
- ```ruby
147
- context.errors.merge!(user.errors)
148
- context.
149
- ```
150
-
151
- ```ruby
152
- context.fail!(user.errors)
153
- ```
154
-
155
- You can ask a context if it's a failure:
156
-
157
- ```ruby
158
- class MyInteractor
159
- def perform
160
- context.fail!
161
- end
162
- end
163
-
164
- result = MyInteractor.perform
165
- result.failure? #=> true
166
- ```
167
-
168
- or if it's a success:
169
-
170
- ```ruby
171
- class MyInteractor
172
- def perform
173
- context.user = User.create(...)
174
- end
175
- end
176
-
177
- result = MyInteractor.perform
178
- result.success? #=> true
179
- ```
180
-
181
- #### Dealing with Failure
182
-
183
- `context.fail!` always throws an exception of type `ActiveInteractor::Error::ContextFailure`.
184
-
185
- Normally, however, these exceptions are not seen. In the recommended usage, the consuming object invokes the interactor
186
- using the class method `perform`, then checks the `success?` method of the context.
187
-
188
- This works because the `perform` class method swallows exceptions. When unit testing an interactor, if calling custom business
189
- logic methods directly and bypassing `perform`, be aware that `fail!` will generate such exceptions.
190
-
191
- See [Using Interactors](#using-interactors), below, for the recommended usage of `perform` and `success?`.
192
-
193
- #### Context Attributes
194
-
195
- Each context instance has basic attribute assignment methods. Assigning attributes to a context is a simple way to
196
- explicitly defined what properties a context should have after an interactor has done it's work.
197
-
198
- You can see what attributes are defined on a given context with the `#attributes` method:
199
-
200
- ```ruby
201
- class MyInteractorContext < ActiveInteractor::Context::Base
202
- attributes :first_name, :last_name, :email, :user
203
- end
204
-
205
- class MyInteractor < ActiveInteractor::Base; end
206
-
207
- result = MyInteractor.perform(
208
- first_name: 'Aaron',
209
- last_name: 'Allen',
210
- email: 'hello@aaronmallen.me',
211
- occupation: 'Software Dude'
212
- )
213
- #=> <#MyInteractor::Context first_name='Aaron' last_name='Allen' email='hello@aaronmallen.me' occupation='Software Dude'>
214
-
215
- result.attributes #=> { first_name: 'Aaron', last_name: 'Allen', email: 'hello@aaronmallen.me' }
216
- result.occupation #=> 'Software Dude'
217
- ```
218
-
219
- #### Validating the Context
220
-
221
- All context instances include [ActiveModel::Validations]; additionally ActiveInteractor delegates all the validation methods
222
- provided by [ActiveModel::Validations] onto an interactor's context class from the interactor itself. All of the methods
223
- found in [ActiveModel::Validations] can be invoked directly on your interactor with the prefix `context_`.
224
- However this can be confusing and it is recommended to make all validation calls on a context class directly.
225
-
226
- ActiveInteractor provides two validation callback steps:
37
+ Be sure to read the [wiki] for detailed information on how to use ActiveInteractor.
227
38
 
228
- * `:calling` used before `#perform` is invoked on an interactor
229
- * `:called` used after `#perform` is invoked on an interactor
230
-
231
- A basic implementation might look like this:
232
-
233
- ```ruby
234
- class MyInteractorContext < ActiveInteractor::Context::Base
235
- attributes :first_name, :last_name, :email, :user
236
- # only validates presence before perform is invoked
237
- validates :first_name, presence: true, on: :calling
238
- # validates before and after perform is invoked
239
- validates :email, presence: true,
240
- format: { with: URI::MailTo::EMAIL_REGEXP }
241
- # validates after perform is invoked
242
- validates :user, presence: true, on: :called
243
- validate :user_is_a_user, on: :called
244
-
245
- private
246
-
247
- def user_is_a_user
248
- return if user.is_a?(User)
249
-
250
- errors.add(:user, :invalid)
251
- end
252
- end
253
-
254
- class MyInteractor < ActiveInteractor::Base
255
- def perform
256
- context.user = User.create_with(
257
- first_name: context.first_name,
258
- last_name: context.last_name
259
- ).find_or_create_by(email: context.email)
260
- end
261
- end
262
-
263
- result = MyInteractor.perform(last_name: 'Allen')
264
- #=> <#MyInteractor::Context last_name='Allen>
265
- result.failure? #=> true
266
- result.valid? #=> false
267
- result.errors[:first_name] #=> ['can not be blank']
268
-
269
- result = MyInterator.perform(first_name: 'Aaron', email: 'hello@aaronmallen.me')
270
- #=> <#MyInteractor::Context first_name='Aaron' email='hello@aaronmallen.me' user=<#User ...>>
271
- result.success? #=> true
272
- result.valid? #=> true
273
- result.errors.empty? #=> true
274
- ```
275
-
276
- ### Using Interactors
277
-
278
- Most of the time, your application will use its interactors from its controllers. The following controller:
279
-
280
- ```ruby
281
- class SessionsController < ApplicationController
282
- def create
283
- if user = User.authenticate(session_params[:email], session_params[:password])
284
- session[:user_token] = user.secret_token
285
- redirect_to user
286
- else
287
- flash.now[:message] = "Please try again."
288
- render :new
289
- end
290
- end
291
-
292
- private
293
-
294
- def session_params
295
- params.require(:session).permit(:email, :password)
296
- end
297
- end
298
- ```
299
-
300
- can be refactored to:
301
-
302
- ```ruby
303
- class SessionsController < ApplicationController
304
- def create
305
- result = AuthenticateUser.perform(session_params)
306
-
307
- if result.success?
308
- session[:user_token] = result.token
309
- redirect_to result.user
310
- else
311
- flash.now[:message] = t(result.errors.full_messages)
312
- render :new
313
- end
314
- end
315
-
316
- private
317
-
318
- def session_params
319
- params.require(:session).permit(:email, :password)
320
- end
321
- end
322
- ```
323
-
324
- given the basic interactor and context:
325
-
326
- ```ruby
327
- class AuthenticateUserContext < ActiveInteractor::Context::Base
328
- attributes :email, :password, :user, :token
329
- validates :email, presence: true,
330
- format: { with: URI::MailTo::EMAIL_REGEXP }
331
- validates :password, presence: true
332
- validates :user, presence: true, on: :called
333
- end
334
-
335
- class AuthenticateUser < ActiveInteractor::Base
336
- def perform
337
- context.user = User.authenticate(
338
- context.email,
339
- context.password
340
- )
341
- context.token = context.user.secret_token
342
- end
343
- end
344
- ```
345
-
346
- The `.perform` class method is the proper way to invoke an interactor. The hash argument is converted to the interactor instance's
347
- context. The `#perform` instance method is invoked along with any callbacks and validations that the interactor might define.
348
- Finally, the context (along with any changes made to it) is returned.
349
-
350
- #### Kinds of Interactors
351
-
352
- There are two kinds of interactors built into ActiveInteractor: basic interactors and organizers.
353
-
354
- ##### Interactors
355
-
356
- A basic interactor is a class that inherits from `ActiveInteractor::Base` and defines `perform`.
357
-
358
- ```ruby
359
- class AuthenticateUser < ActiveInteractor::Base
360
- def perform
361
- user = User.authenticate(context.email, context.password)
362
- if user
363
- context.user = user
364
- context.token = user.secret_token
365
- else
366
- context.fail!
367
- end
368
- end
369
- end
370
- ```
371
-
372
- Basic interactors are the building blocks. They are your application's single-purpose units of work.
373
-
374
- ##### Organizers
375
-
376
- An organizer is an important variation on the basic interactor. Its single purpose is to run other interactors.
377
-
378
- ```ruby
379
- class CreateOrder < ActiveInteractor::Base
380
- def perform
381
- ...
382
- end
383
- end
384
-
385
- class ChargeCard < ActiveInteractor::Base
386
- def perform
387
- ...
388
- end
389
- end
390
-
391
- class SendThankYou < ActiveInteractor::Base
392
- def perform
393
- ...
394
- end
395
- end
396
-
397
- class PlaceOrder < ActiveInteractor::Organizer
398
-
399
- organize :create_order, :charge_card, :send_thank_you
400
- end
401
- ```
402
-
403
- In the controller, you can run the `PlaceOrder` organizer just like you would any other interactor:
404
-
405
- ```ruby
406
- class OrdersController < ApplicationController
407
- def create
408
- result = PlaceOrder.perform(order_params: order_params)
409
-
410
- if result.success?
411
- redirect_to result.order
412
- else
413
- @order = result.order
414
- render :new
415
- end
416
- end
417
-
418
- private
419
-
420
- def order_params
421
- params.require(:order).permit!
422
- end
423
- end
424
- ```
425
-
426
- The organizer passes its context to the interactors that it organizes, one at a time and in order. Each interactor may
427
- change that context before it's passed along to the next interactor.
428
-
429
- ###### Organizing Interactors Conditionally
430
-
431
- We can also add conditional statements to our organizer by passing a block to the `.organize` method:
432
-
433
- ```ruby
434
- class PlaceOrder < ActiveInteractor::Organizer
435
- organize do
436
- add :create_order, if :user_registered?
437
- add :charge_card, if: -> { context.order }
438
- add :send_thank_you, if: -> { context.order }
439
- end
440
-
441
- private
442
-
443
- def user_registered?
444
- context.user&.registered?
445
- end
446
- end
447
- ```
448
-
449
- ###### Running Interactors In Parallel
450
-
451
- Organizers can be told to run their interactors in parallel with the `.perform_in_parallel` class method. This
452
- will run each interactor in parallel with one and other only passing the original context to each interactor.
453
- This means each interactor must be able to perform without dependencies on prior interactor invokations.
454
-
455
- ```ruby
456
- class CreateNewUser < ActiveInteractor::Base
457
- def perform
458
- context.user = User.create(
459
- first_name: context.first_name,
460
- last_name: context.last_name
461
- )
462
- end
463
- end
464
-
465
- class LogNewUserCreation < ActiveInteractor::Base
466
- def perform
467
- context.log = Log.create(
468
- event: 'new user created',
469
- first_name: context.first_name,
470
- last_name: context.last_name
471
- )
472
- end
473
- end
474
-
475
- class CreateUser < ActiveInteractor::Organizer
476
- perform_in_parallel
477
- organize :create_new_user, :log_new_user_creation
478
- end
479
-
480
- CreateUser.perform(first_name: 'Aaron', last_name: 'Allen')
481
- #=> <#CreateUser::Context first_name='Aaron' last_name='Allen' user=>#<User ...> log=<#Log ...>>
482
- ```
483
-
484
- #### Rollback
485
-
486
- If any one of the organized interactors fails its context, the organizer stops. If the `ChargeCard` interactor fails,
487
- `SendThankYou` is never called.
488
-
489
- In addition, any interactors that had already run are given the chance to undo themselves, in reverse order.
490
- Simply define the rollback method on your interactors:
491
-
492
- ```ruby
493
- class CreateOrder < ActiveInteractor::Base
494
- def perform
495
- order = Order.create(order_params)
496
-
497
- if order.persisted?
498
- context.order = order
499
- else
500
- context.fail!
501
- end
502
- end
503
-
504
- def rollback
505
- context.order.destroy
506
- end
507
- end
508
- ```
509
-
510
- #### Callbacks
511
-
512
- ActiveInteractor uses [ActiveModel::Callbacks] and [ActiveModel::Validations::Callbacks] on context validation, perform,
513
- and rollback. Callbacks can be defined with a `block`, `Proc`, or `Symbol` method name and take the same conditional arguments
514
- outlined in those two modules.
515
-
516
- ##### Validation Callbacks
517
-
518
- We can do work before an interactor's context is validated with the `.before_context_validation` method:
519
-
520
- ```ruby
521
- class MyInteractorContext < ActiveInteractor::Context::Base
522
- attributes :first_name, :last_name, :email
523
- validates :last_name, presence: true
524
- end
525
-
526
- class MyInteractor < ActiveInteractor::Base
527
- before_context_validation { context.last_name ||= 'Unknown' }
528
- end
529
-
530
- result = MyInteractor.perform(first_name: 'Aaron', email: 'hello@aaronmallen.me')
531
- result.valid? #=> true
532
- result.last_name #=> 'Unknown'
533
- ```
534
-
535
- We can do work after an interactor's context is validated with the `.after_context_validation` method:
536
-
537
- ```ruby
538
- class MyInteractorContext < ActiveInteractor::Context::Base
539
- attributes :first_name, :last_name, :email
540
- validates :email, presence: true,
541
- format: { with: URI::MailTo::EMAIL_REGEXP }
542
- end
543
-
544
- class MyInteractor < ActiveInteractor::Base
545
- after_context_validation { context.email&.downcase! }
546
- end
547
-
548
- result = MyInteractor.perform(first_name: 'Aaron', last_name: 'Allen', email: 'HELLO@AARONMALLEN.ME')
549
- result.valid? #=> true
550
- result.email #=> 'hello@aaronmallen.me'
551
- ```
552
-
553
- ##### Perform Callbacks
554
-
555
- We can do work before `#perform` is invoked with the `.before_perform` method:
556
-
557
- ```ruby
558
- class MyInteractor < ActiveInteractor::Base
559
- before_perform :print_start
560
-
561
- def perform
562
- puts 'Performing'
563
- end
564
-
565
- private
566
-
567
- def print_start
568
- puts 'Start'
569
- end
570
- end
571
-
572
- MyInteractor.perform
573
- "Start"
574
- "Performing"
575
- #=> <#MyInteractor::Context...>
576
- ```
577
-
578
- We can do work around `#perform` invokation with the `.around_perform` method:
579
-
580
- ```ruby
581
- class MyInteractor < ActiveInteractor::Base
582
- around_perform :track_time
583
-
584
- def perform
585
- sleep(1)
586
- end
587
-
588
- private
589
-
590
- def track_time
591
- context.start_time = Time.now.utc
592
- yield
593
- context.end_time = Time.now.utc
594
- end
595
- end
596
-
597
- result = MyInteractor.perform
598
- result.start_time #=> 2019-01-01 00:00:00 UTC
599
- result.end_time #=> 2019-01-01 00:00:01 UTC
600
- ```
601
-
602
- We can do work after `#perform` is invoked with the `.after_perform` method:
603
-
604
- ```ruby
605
- class MyInteractor < ActiveInteractor::Base
606
- after_perform :print_done
607
-
608
- def perform
609
- puts 'Performing'
610
- end
611
-
612
- private
613
-
614
- def print_done
615
- puts 'Done'
616
- end
617
- end
618
-
619
- MyInteractor.perform
620
- "Performing"
621
- "Done"
622
- #=> <#MyInteractor::Context...>
623
- ```
624
-
625
- ##### Rollback Callbacks
626
-
627
- We can do work before `#rollback` is invoked with the `.before_rollback` method:
628
-
629
- ```ruby
630
- class MyInteractor < ActiveInteractor::Base
631
- before_rollback :print_start
632
-
633
- def perform
634
- context.fail!
635
- end
636
-
637
- def rollback
638
- puts 'Rolling Back'
639
- end
640
-
641
- private
642
-
643
- def print_start
644
- puts 'Start'
645
- end
646
- end
647
-
648
- MyInteractor.perform
649
- "Start"
650
- "Rolling Back"
651
- #=> <#MyInteractor::Context...>
652
- ```
653
-
654
- We can do work around `#rollback` invokation with the `.around_rollback` method:
655
-
656
- ```ruby
657
- class MyInteractor < ActiveInteractor::Base
658
- around_rollback :track_time
659
-
660
- def perform
661
- context.fail!
662
- end
663
-
664
- def rollback
665
- sleep(1)
666
- end
667
-
668
- private
669
-
670
- def track_time
671
- context.start_time = Time.now.utc
672
- yield
673
- context.end_time = Time.now.utc
674
- end
675
- end
676
-
677
- result = MyInteractor.perform
678
- result.start_time #=> 2019-01-01 00:00:00 UTC
679
- result.end_time #=> 2019-01-01 00:00:01 UTC
680
- ```
681
-
682
- We can do work after `#rollback` is invoked with the `.after_rollback` method:
683
-
684
- ```ruby
685
- class MyInteractor < ActiveInteractor::Base
686
- after_rollback :print_done
687
-
688
- def perform
689
- context.fail!
690
- end
691
-
692
- def rollback
693
- puts 'Rolling Back'
694
- end
695
-
696
- private
697
-
698
- def print_done
699
- puts 'Done'
700
- end
701
- end
702
-
703
- MyInteractor.perform
704
- "Rolling Back"
705
- "Done"
706
- #=> <#MyInteractor::Context...>
707
- ```
708
-
709
- ##### Organizer Callbacks
710
-
711
- We can do worker before `#perform` is invoked on each interactor in an [Organizer](#organizers) with the
712
- `.before_each_perform` method:
713
-
714
- ```ruby
715
- class MyInteractor1 < ActiveInteractor::Base
716
- def perform
717
- puts 'MyInteractor1'
718
- end
719
- end
720
-
721
- class MyInteractor2 < ActiveInteractor::Base
722
- def perform
723
- puts 'MyInteractor2'
724
- end
725
- end
726
-
727
- class MyOrganizer < ActiveInteractor::Organizer
728
- before_each_perform :print_start
729
-
730
- organized MyInteractor1, MyInteractor2
731
-
732
- private
733
-
734
- def print_start
735
- puts "Start"
736
- end
737
- end
738
-
739
- MyOrganizer.perform
740
- "Start"
741
- "MyInteractor1"
742
- "Start"
743
- "MyInteractor2"
744
- #=> <MyOrganizer::Context...>
745
- ```
746
-
747
- We can do worker around `#perform` invokation on each interactor in an [Organizer](#organizers) with the
748
- `.around_each_perform` method:
749
-
750
- ```ruby
751
- class MyInteractor1 < ActiveInteractor::Base
752
- def perform
753
- puts 'MyInteractor1'
754
- sleep(1)
755
- end
756
- end
757
-
758
- class MyInteractor2 < ActiveInteractor::Base
759
- def perform
760
- puts 'MyInteractor2'
761
- sleep(1)
762
- end
763
- end
764
-
765
- class MyOrganizer < ActiveInteractor::Organizer
766
- around_each_perform :print_time
767
-
768
- organized MyInteractor1, MyInteractor2
769
-
770
- private
771
-
772
- def print_time
773
- puts Time.now.utc
774
- yield
775
- puts Time.now.utc
776
- end
777
- end
778
-
779
- MyOrganizer.perform
780
- "2019-01-01 00:00:00 UTC"
781
- "MyInteractor1"
782
- "2019-01-01 00:00:01 UTC"
783
- "2019-01-01 00:00:01 UTC"
784
- "MyInteractor2"
785
- "2019-01-01 00:00:02 UTC"
786
- #=> <MyOrganizer::Context...>
787
- ```
788
-
789
- We can do worker after `#perform` is invoked on each interactor in an [Organizer](#organizers) with the
790
- `.after_each_perform` method:
791
-
792
- ```ruby
793
- class MyInteractor1 < ActiveInteractor::Base
794
- def perform
795
- puts 'MyInteractor1'
796
- end
797
- end
798
-
799
- class MyInteractor2 < ActiveInteractor::Base
800
- def perform
801
- puts 'MyInteractor2'
802
- end
803
- end
804
-
805
- class MyOrganizer < ActiveInteractor::Organizer
806
- after_each_perform :print_done
807
-
808
- organized MyInteractor1, MyInteractor2
809
-
810
- private
811
-
812
- def print_done
813
- puts "Done"
814
- end
815
- end
816
-
817
- MyOrganizer.perform
818
- "MyInteractor1"
819
- "Done"
820
- "MyInteractor2"
821
- "Done"
822
- #=> <MyOrganizer::Context...>
823
- ```
824
-
825
- ## Working With Rails
826
-
827
- If you're working with a rails project ActiveInteractor comes bundled with some useful generators
828
- to help speed up development. You should first run the install generator with:
829
-
830
- ```bash
831
- rails generate active_interactor:install [directory]
832
- ```
833
-
834
- The `directory` option allows you to customize what directory interactors will live in within your
835
- application (defaults to 'interactors').
836
-
837
- This will create an initializer a some new classes `ApplicationInteractor`, `ApplicationOrganizer` and
838
- `ApplicationContext` in the `app/<directory>` directory.
839
-
840
- ### Generators
841
-
842
- ActiveInteractor comes bundled with some rails generators to automatically generate interactors,
843
- organizers, and contexts with:
844
-
845
- ```bash
846
- rails generate interactor MyInteractor
847
- ```
848
-
849
- ```bash
850
- rails generate interactor:organizer MyInteractor1 MyInteractor2
851
- ```
852
-
853
- ```bash
854
- rails generate interactor:context MyContext
855
- ```
856
-
857
- These generators will automatically create the approriate classes and matching spec or test files.
858
-
859
- ### ActiveRecord Helper Methods
860
-
861
- In some instances you may want to use an `ActiveRecord` model as a context for an interactor. You can
862
- do this by calling the `acts_as_context` method on any `ActiveRecord` model, and then simply call the
863
- `contextualize_with` method on your interactor or organizer to point it to the approriate class.
864
-
865
- ```ruby
866
- # app/models/user
867
- class User < ApplicationRecord
868
- acts_as_context
869
- end
870
-
871
- # app/interactors/create_user
872
- class CreateUser < ApplicationInteractor
873
- contextualize_with :user
874
-
875
- def perform
876
- context.email&.downcase!
877
- context.save
878
- end
879
- end
880
-
881
- CreateUser.perform(email: 'HELLO@AARONMALLEN.ME')
882
- #=> <#User id=1 email='hello@aaronmallen.me'>
883
- ```
39
+ For technical documentation please see the gem's [ruby docs].
884
40
 
885
41
  ## Development
886
42
 
@@ -904,13 +60,14 @@ Read our guidelines for [Contributing](CONTRIBUTING.md).
904
60
 
905
61
  The gem is available as open source under the terms of the [MIT License][mit_license].
906
62
 
907
- [ActiveModel::Callbacks]: https://api.rubyonrails.org/classes/ActiveModel/Callbacks.html
908
- [ActiveModel::Validations]: https://api.rubyonrails.org/classes/ActiveModel/Validations.html
909
- [ActiveModel::Validations::Callbacks]: https://api.rubyonrails.org/classes/ActiveModel/Validations/Callbacks.html
910
- [collective_idea_interactors]: https://github.com/collectiveidea/interactor
911
- [business_logic_wikipedia]: https://en.wikipedia.org/wiki/Business_logic
912
63
  [@collectiveidea]: https://github.com/collectiveidea
913
64
  [@rails]: https://github.com/rails
914
65
  [active_model_git]: https://github.com/rails/rails/tree/master/activemodel
915
66
  [active_support_git]: https://github.com/rails/rails/tree/master/activesupport
67
+ [ActiveModel::Validations]: https://api.rubyonrails.org/classes/ActiveModel/Validations.html
68
+ [business_logic_wikipedia]: https://en.wikipedia.org/wiki/Business_logic
69
+ [collective_idea_interactors]: https://github.com/collectiveidea/interactor
70
+ [command pattern]: https://en.wikipedia.org/wiki/Command_pattern
916
71
  [mit_license]: https://opensource.org/licenses/MIT
72
+ [ruby docs]: https://www.rubydoc.info/gems/activeinteractor
73
+ [wiki]: https://github.com/aaronmallen/activeinteractor/wiki