activeinteractor 1.0.0.beta.7 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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