decouplio 1.0.0alpha1 → 1.0.0alpha4

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +11 -1
  3. data/.rubocop.yml +7 -0
  4. data/README.md +12 -3
  5. data/benchmarks/Gemfile +2 -1
  6. data/benchmarks/multi_step_benchmark.rb +336 -0
  7. data/benchmarks/single_step_benchmark.rb +159 -0
  8. data/decouplio.gemspec +4 -4
  9. data/docker-compose.yml +13 -2
  10. data/lib/decouplio/action.rb +34 -3
  11. data/lib/decouplio/composer.rb +111 -22
  12. data/lib/decouplio/const/doby_aide_options.rb +15 -0
  13. data/lib/decouplio/const/error_messages.rb +9 -0
  14. data/lib/decouplio/const/reserved_methods.rb +13 -9
  15. data/lib/decouplio/const/results.rb +2 -0
  16. data/lib/decouplio/const/types.rb +19 -5
  17. data/lib/decouplio/const/validations/aide.rb +38 -0
  18. data/lib/decouplio/const/validations/doby.rb +36 -0
  19. data/lib/decouplio/const/validations/fail.rb +5 -1
  20. data/lib/decouplio/const/validations/octo.rb +2 -1
  21. data/lib/decouplio/errors/aide_can_not_be_first_step_error.rb +18 -0
  22. data/lib/decouplio/errors/aide_controversial_keys_error.rb +26 -0
  23. data/lib/decouplio/errors/aide_finish_him_error.rb +26 -0
  24. data/lib/decouplio/errors/doby_controversial_keys_error.rb +26 -0
  25. data/lib/decouplio/errors/doby_finish_him_error.rb +26 -0
  26. data/lib/decouplio/errors/execution_error.rb +20 -0
  27. data/lib/decouplio/errors/{fail_is_first_step_error.rb → fail_can_not_be_first_step_error.rb} +1 -1
  28. data/lib/decouplio/errors/step_is_not_defined_for_aide_error.rb +26 -0
  29. data/lib/decouplio/errors/step_is_not_defined_for_doby_error.rb +27 -0
  30. data/lib/decouplio/errors/step_is_not_defined_for_pass_error.rb +27 -0
  31. data/lib/decouplio/logic_dsl.rb +30 -3
  32. data/lib/decouplio/options_validator.rb +162 -13
  33. data/lib/decouplio/steps/aide.rb +37 -0
  34. data/lib/decouplio/steps/base_resq.rb +13 -3
  35. data/lib/decouplio/steps/doby.rb +14 -9
  36. data/lib/decouplio/steps/fail.rb +7 -22
  37. data/lib/decouplio/steps/inner_action_fail.rb +7 -22
  38. data/lib/decouplio/steps/inner_action_step.rb +7 -18
  39. data/lib/decouplio/steps/octo.rb +7 -2
  40. data/lib/decouplio/steps/service_fail.rb +11 -23
  41. data/lib/decouplio/steps/service_pass.rb +4 -1
  42. data/lib/decouplio/steps/service_step.rb +11 -19
  43. data/lib/decouplio/steps/shared/fail_resolver.rb +40 -0
  44. data/lib/decouplio/steps/shared/step_resolver.rb +43 -0
  45. data/lib/decouplio/steps/step.rb +7 -18
  46. data/lib/decouplio/steps/wrap.rb +7 -18
  47. data/lib/decouplio/validators/condition.rb +10 -0
  48. data/lib/decouplio/version.rb +1 -1
  49. metadata +30 -41
  50. data/benchmarks/benchmarks.rb +0 -527
  51. data/docs/_config.yml +0 -1
  52. data/docs/benchmarks.md +0 -1
  53. data/docs/context.md +0 -74
  54. data/docs/context.rb +0 -62
  55. data/docs/doby.md +0 -80
  56. data/docs/doby.rb +0 -38
  57. data/docs/error_store.md +0 -347
  58. data/docs/error_store.rb +0 -202
  59. data/docs/fail.md +0 -1016
  60. data/docs/fail.rb +0 -762
  61. data/docs/index.md +0 -25
  62. data/docs/inner_action.md +0 -63
  63. data/docs/inner_action.rb +0 -43
  64. data/docs/logic_block.md +0 -25
  65. data/docs/octo.md +0 -269
  66. data/docs/octo.rb +0 -164
  67. data/docs/pass.md +0 -309
  68. data/docs/pass.rb +0 -213
  69. data/docs/quick_start.md +0 -71
  70. data/docs/quick_start.rb +0 -38
  71. data/docs/resq.md +0 -263
  72. data/docs/resq.rb +0 -176
  73. data/docs/step.md +0 -737
  74. data/docs/step.rb +0 -526
  75. data/docs/step_as_a_service.md +0 -109
  76. data/docs/step_as_a_service.rb +0 -77
  77. data/docs/wrap.md +0 -232
  78. data/docs/wrap.rb +0 -137
data/docs/doby.md DELETED
@@ -1,80 +0,0 @@
1
- # Doby
2
-
3
- It's a step type to make configurable manipulations with action context.
4
-
5
-
6
- ## Signature
7
-
8
- ```ruby
9
- doby(class_constant, **options)
10
- ```
11
-
12
- ## How to use?
13
-
14
- Create the ruby class which has `.call` class method.
15
-
16
- `.call` method signature:
17
- ```ruby
18
- # :ctx - is the required kwarg
19
- # ** - kwargs passed from action
20
- def self.call(ctx:, **)
21
- end
22
- ```
23
-
24
- ```ruby
25
- class AssignDoby
26
- def self.call(ctx:, to:, from: nil, value: nil)
27
- raise 'from/value is empty' unless from || value
28
-
29
- ctx[to] = value || ctx[from]
30
- end
31
- end
32
- ```
33
-
34
- Now you can use `AssignDoby` class inside action
35
- ```ruby
36
- require 'decouplio'
37
-
38
- class AssignDoby
39
- def self.call(ctx:, to:, from: nil, value: nil)
40
- raise 'from/value is empty' unless from || value
41
-
42
- ctx[to] = value || ctx[from]
43
- end
44
- end
45
-
46
- class SomeAction < Decouplio::Action
47
- logic do
48
- step :user
49
- step AssignDoby, to: :current_user, from: :user
50
- end
51
-
52
- def user(id:, **)
53
- ctx[:user] = "User with id: #{id}"
54
- end
55
- end
56
-
57
- action = SomeAction.call(id: 1)
58
-
59
- action[:user] # => "User with id: 1"
60
-
61
- action[:current_user] # => "User with id: 1"
62
-
63
- action # =>
64
- # Result: success
65
-
66
- # Railway Flow:
67
- # user -> AssignDoby
68
-
69
- # Context:
70
- # {:id=>1, :user=>"User with id: 1", :current_user=>"User with id: 1"}
71
-
72
- # Errors:
73
- # {}
74
- ```
75
-
76
- ## Behavior
77
-
78
- - `doby` behaves similar to `step`, depending on `.call` method returning value(truthy or falsy) the execution will be moved to `success or failure` track accordingly.
79
- - `doby` doesn't have `on_success, on_failure, if, unless, finish_him` options.
80
- - All options passed after class constant will be passed as kwargs for `.call` method.
data/docs/doby.rb DELETED
@@ -1,38 +0,0 @@
1
- require_relative '../lib/decouplio'
2
-
3
- class AssignDoby
4
- def self.call(ctx:, to:, from: nil, value: nil)
5
- raise 'from/value is empty' unless from || value
6
-
7
- ctx[to] = value || ctx[from]
8
- end
9
- end
10
-
11
- class SomeAction < Decouplio::Action
12
- logic do
13
- step :user
14
- doby AssignDoby, to: :current_user, from: :user
15
- end
16
-
17
- def user(id:, **)
18
- ctx[:user] = "User with id: #{id}"
19
- end
20
- end
21
-
22
- action = SomeAction.call(id: 1)
23
-
24
- puts action[:user] # => "User with id: 1"
25
-
26
- puts action[:current_user] # => "User with id: 1"
27
-
28
- puts action # =>
29
- # Result: success
30
-
31
- # Railway Flow:
32
- # user -> AssignDoby
33
-
34
- # Context:
35
- # {:id=>1, :user=>"User with id: 1", :current_user=>"User with id: 1"}
36
-
37
- # Errors:
38
- # {}
data/docs/error_store.md DELETED
@@ -1,347 +0,0 @@
1
- # Error store
2
-
3
- It's an object to store errors. By default `Decouplio::DefaultErrorHandler` is used for `Decouplio::Action`
4
-
5
- ```ruby
6
- module Decouplio
7
- class DefaultErrorHandler
8
- attr_reader :errors
9
-
10
- def initialize
11
- @errors = {}
12
- end
13
-
14
- def add_error(key, message)
15
- @errors.store(
16
- key,
17
- (@errors[key] || []) + [message].flatten
18
- )
19
- end
20
-
21
- def merge(error_store)
22
- @errors = @errors.merge(error_store.errors) do |_key, this_val, other_val|
23
- this_val + other_val
24
- end
25
- end
26
- end
27
- end
28
- ```
29
-
30
- ## How to use
31
- Inside step method you can call `#add_error` method
32
-
33
- ```ruby
34
- add_error(key, message)
35
- ```
36
-
37
- ```ruby
38
- require 'decouplio'
39
-
40
- class SomeAction < Decouplio::Action
41
- logic do
42
- step :step_one
43
- fail :fail_one
44
- end
45
-
46
- def step_one(**)
47
- false
48
- end
49
-
50
- def fail_one(**)
51
- add_error(:something_went_wrong, 'Something went wrong')
52
- end
53
- end
54
-
55
- action = SomeAction.call
56
-
57
- action # =>
58
- # Result: failure
59
-
60
- # Railway Flow:
61
- # step_one -> fail_one
62
-
63
- # Context:
64
- # {}
65
-
66
- # Errors:
67
- # {:something_went_wrong=>["Something went wrong"]}
68
-
69
- action.errors # =>
70
- # {:something_went_wrong=>["Something went wrong"]}
71
- ```
72
-
73
- ## Behavior
74
-
75
- - If error was added, it doesn't mean that action result is `failure`, action can be `success` and have errors, so basically `error store` is just a container for errors. Such behavior was implemented to provide more freedom.
76
- - Error store for parent an inner action should be the same. It's because different error stores may have different `add_error` method signature and error hash structure.
77
-
78
- ## Custom error store
79
-
80
- If you want to use your own custom error store then you can do it in this way:
81
-
82
- - Define our own class with two public methods
83
- - `#add_error(<signature you want>)` - method which adds error to `error_store`
84
- - `#merge(error_store_to_merge)` - will be used by `Decouplio` to merge errors from inner actions to parent action.
85
- - should have `attr_reader :errors`
86
-
87
- <details><summary><b>EXAMPLE (CLICK ME)</b></summary>
88
- <p>
89
-
90
- ```ruby
91
- require 'decouplio'
92
-
93
- class CustomErrorStore
94
- attr_reader :errors
95
-
96
- def initialize
97
- @errors = {}
98
- end
99
-
100
- def add_error(key:, message:, namespace: :root)
101
- @errors[namespace] ||= {}
102
- @errors[namespace].store(
103
- key,
104
- (@errors[namespace][key] || []) + [message].flatten
105
- )
106
- end
107
-
108
- def merge(error_store)
109
- @errors = deep_merge(@errors, error_store.errors)
110
- end
111
-
112
- private
113
-
114
- def deep_merge(this_hash, other_hash)
115
- this_hash.merge(other_hash) do |_key, this_val, other_val|
116
- if this_val.is_a?(Hash) && other_val.is_a?(Hash)
117
- deep_merge(this_val, other_val)
118
- else
119
- this_val + other_val
120
- end
121
- end
122
- end
123
- end
124
-
125
- class SomeActionWithCustomErrorStore < Decouplio::Action
126
- error_store_class CustomErrorStore
127
-
128
- logic do
129
- step :step_one
130
- step :step_two
131
- end
132
-
133
- def step_one(**)
134
- add_error(
135
- key: :under_root,
136
- message: 'Error Message One'
137
- )
138
- end
139
-
140
- def step_two(**)
141
- add_error(
142
- namespace: :step_two,
143
- key: :error_happened,
144
- message: 'Error Message Two'
145
- )
146
- end
147
- end
148
-
149
- action = SomeActionWithCustomErrorStore.call
150
-
151
- action # =>
152
- # Result: success
153
-
154
- # Railway Flow:
155
- # step_one -> step_two
156
-
157
- # Context:
158
- # {}
159
-
160
- # Errors:
161
- # {:root=>{:under_root=>["Error Message One"]}, :step_two=>{:error_happened=>["Error Message Two"]}}
162
-
163
- action.errors # =>
164
- # {:root=>{:under_root=>["Error Message One"]}, :step_two=>{:error_happened=>["Error Message Two"]}}
165
- ```
166
-
167
- </p>
168
- </details>
169
-
170
- ***
171
-
172
- ## Custom error store and inner action
173
-
174
- ### When error store is the same for parent and inner action
175
- <details><summary><b>EXAMPLE (CLICK ME)</b></summary>
176
- <p>
177
-
178
- ```ruby
179
- require 'decouplio'
180
-
181
- class CustomErrorStore
182
- attr_reader :errors
183
-
184
- def initialize
185
- @errors = {}
186
- end
187
-
188
- def add_error(key:, message:, namespace: :root)
189
- @errors[namespace] ||= {}
190
- @errors[namespace].store(
191
- key,
192
- (@errors[namespace][key] || []) + [message].flatten
193
- )
194
- end
195
-
196
- def merge(error_store)
197
- @errors = deep_merge(@errors, error_store.errors)
198
- end
199
-
200
- private
201
-
202
- def deep_merge(this_hash, other_hash)
203
- this_hash.merge(other_hash) do |_key, this_val, other_val|
204
- if this_val.is_a?(Hash) && other_val.is_a?(Hash)
205
- deep_merge(this_val, other_val)
206
- else
207
- this_val + other_val
208
- end
209
- end
210
- end
211
- end
212
-
213
- class InnerActionWithCustomErrorStore < Decouplio::Action
214
- error_store_class CustomErrorStore
215
-
216
- logic do
217
- step :inner_step
218
- end
219
-
220
- def inner_step(**)
221
- add_error(
222
- namespace: :inner,
223
- key: :inner_key,
224
- message: 'Somebody was told me...'
225
- )
226
- end
227
- end
228
-
229
- class ParentActionWithCustomErrorStore < Decouplio::Action
230
- error_store_class CustomErrorStore
231
-
232
- logic do
233
- step :step_one, action: InnerActionWithCustomErrorStore
234
- step :step_two
235
- end
236
-
237
- def step_two(**)
238
- add_error(
239
- namespace: :parent,
240
- key: :error_happened,
241
- message: 'Message'
242
- )
243
- end
244
- end
245
-
246
- action = ParentActionWithCustomErrorStore.call
247
-
248
- puts action # =>
249
- # Result: success
250
-
251
- # Railway Flow:
252
- # step_one -> inner_step -> step_two
253
-
254
- # Context:
255
- # {}
256
-
257
- # Errors:
258
- # {:inner=>{:inner_key=>["Somebody was told me..."]}, :parent=>{:error_happened=>["Message"]}}
259
-
260
- puts action.errors # =>
261
- # {:inner=>{:inner_key=>["Somebody was told me..."]}, :parent=>{:error_happened=>["Message"]}}
262
-
263
- ```
264
-
265
- </p>
266
- </details>
267
-
268
- ***
269
-
270
- ### When error store is different for parent and inner action
271
- If inner action error store is different from parent action error store then error will be raised.
272
-
273
- <details><summary><b>EXAMPLE (CLICK ME)</b></summary>
274
- <p>
275
-
276
- ```ruby
277
- require 'decouplio'
278
-
279
- class CustomErrorStore
280
- attr_reader :errors
281
-
282
- def initialize
283
- @errors = {}
284
- end
285
-
286
- def add_error(key:, message:, namespace: :root)
287
- @errors[namespace] ||= {}
288
- @errors[namespace].store(
289
- key,
290
- (@errors[namespace][key] || []) + [message].flatten
291
- )
292
- end
293
-
294
- def merge(error_store)
295
- @errors = deep_merge(@errors, error_store.errors)
296
- end
297
-
298
- private
299
-
300
- def deep_merge(this_hash, other_hash)
301
- this_hash.merge(other_hash) do |_key, this_val, other_val|
302
- if this_val.is_a?(Hash) && other_val.is_a?(Hash)
303
- deep_merge(this_val, other_val)
304
- else
305
- this_val + other_val
306
- end
307
- end
308
- end
309
- end
310
-
311
- class InnerActionWithDefaultErrorStore < Decouplio::Action
312
- logic do
313
- step :inner_step
314
- end
315
-
316
- def inner_step(**)
317
- add_error(
318
- key: :inner_key,
319
- message: 'Somebody was told me...'
320
- )
321
- end
322
- end
323
-
324
- class ParentActionForInnerActionDefaultErrorStore < Decouplio::Action
325
- error_store_class CustomErrorStore
326
-
327
- logic do
328
- step :step_one, action: InnerActionWithDefaultErrorStore
329
- step :step_two
330
- end
331
-
332
- def step_two(**)
333
- add_error(
334
- namespace: :parent,
335
- key: :error_happened,
336
- message: 'Message'
337
- )
338
- end
339
- end # =>
340
- # Error store for action and inner action should be the same. (Decouplio::Errors::ErrorStoreError)
341
-
342
- ```
343
-
344
- </p>
345
- </details>
346
-
347
- ***
data/docs/error_store.rb DELETED
@@ -1,202 +0,0 @@
1
- require_relative '../lib/decouplio'
2
-
3
- # How to use
4
- class SomeAction < Decouplio::Action
5
- logic do
6
- step :step_one
7
- fail :fail_one
8
- end
9
-
10
- def step_one(**)
11
- false
12
- end
13
-
14
- def fail_one(**)
15
- add_error(:something_went_wrong, 'Something went wrong')
16
- end
17
- end
18
-
19
- action = SomeAction.call
20
-
21
- puts action # =>
22
- # Result: failure
23
-
24
- # Railway Flow:
25
- # step_one -> fail_one
26
-
27
- # Context:
28
- # {}
29
-
30
- # Errors:
31
- # {:something_went_wrong=>["Something went wrong"]}
32
-
33
- puts action.errors # =>
34
- # {:something_went_wrong=>["Something went wrong"]}
35
-
36
-
37
-
38
-
39
- # Custom error store
40
- class CustomErrorStore
41
- attr_reader :errors
42
-
43
- def initialize
44
- @errors = {}
45
- end
46
-
47
- def add_error(key:, message:, namespace: :root)
48
- @errors[namespace] ||= {}
49
- @errors[namespace].store(
50
- key,
51
- (@errors[namespace][key] || []) + [message].flatten
52
- )
53
- end
54
-
55
- def merge(error_store)
56
- @errors = deep_merge(@errors, error_store.errors)
57
- end
58
-
59
- private
60
-
61
- def deep_merge(this_hash, other_hash)
62
- this_hash.merge(other_hash) do |_key, this_val, other_val|
63
- if this_val.is_a?(Hash) && other_val.is_a?(Hash)
64
- deep_merge(this_val, other_val)
65
- else
66
- this_val + other_val
67
- end
68
- end
69
- end
70
- end
71
-
72
- class SomeActionWithCustomErrorStore < Decouplio::Action
73
- error_store_class CustomErrorStore
74
-
75
- logic do
76
- step :step_one
77
- step :step_two
78
- end
79
-
80
- def step_one(**)
81
- add_error(
82
- key: :under_root,
83
- message: 'Error Message One'
84
- )
85
- end
86
-
87
- def step_two(**)
88
- add_error(
89
- namespace: :step_two,
90
- key: :error_happened,
91
- message: 'Error Message Two'
92
- )
93
- end
94
- end
95
-
96
- action = SomeActionWithCustomErrorStore.call
97
-
98
- puts action # =>
99
- # Result: success
100
-
101
- # Railway Flow:
102
- # step_one -> step_two
103
-
104
- # Context:
105
- # {}
106
-
107
- # Errors:
108
- # {:root=>{:under_root=>["Error Message One"]}, :step_two=>{:error_happened=>["Error Message Two"]}}
109
-
110
- puts action.errors # =>
111
- # {:root=>{:under_root=>["Error Message One"]}, :step_two=>{:error_happened=>["Error Message Two"]}}
112
-
113
-
114
-
115
-
116
- # When error store is the same for parent and inner action
117
-
118
- class InnerActionWithCustomErrorStore < Decouplio::Action
119
- error_store_class CustomErrorStore
120
-
121
- logic do
122
- step :inner_step
123
- end
124
-
125
- def inner_step(**)
126
- add_error(
127
- namespace: :inner,
128
- key: :inner_key,
129
- message: 'Somebody was told me...'
130
- )
131
- end
132
- end
133
-
134
- class ParentActionWithCustomErrorStore < Decouplio::Action
135
- error_store_class CustomErrorStore
136
-
137
- logic do
138
- step :step_one, action: InnerActionWithCustomErrorStore
139
- step :step_two
140
- end
141
-
142
- def step_two(**)
143
- add_error(
144
- namespace: :parent,
145
- key: :error_happened,
146
- message: 'Message'
147
- )
148
- end
149
- end
150
-
151
- action = ParentActionWithCustomErrorStore.call
152
-
153
- puts action # =>
154
- # Result: success
155
-
156
- # Railway Flow:
157
- # step_one -> inner_step -> step_two
158
-
159
- # Context:
160
- # {}
161
-
162
- # Errors:
163
- # {:inner=>{:inner_key=>["Somebody was told me..."]}, :parent=>{:error_happened=>["Message"]}}
164
-
165
- puts action.errors # =>
166
- # {:inner=>{:inner_key=>["Somebody was told me..."]}, :parent=>{:error_happened=>["Message"]}}
167
-
168
-
169
-
170
-
171
- # When error store is different for parent and inner action
172
-
173
- class InnerActionWithDefaultErrorStore < Decouplio::Action
174
- logic do
175
- step :inner_step
176
- end
177
-
178
- def inner_step(**)
179
- add_error(
180
- key: :inner_key,
181
- message: 'Somebody was told me...'
182
- )
183
- end
184
- end
185
-
186
- class ParentActionForInnerActionDefaultErrorStore < Decouplio::Action
187
- error_store_class CustomErrorStore
188
-
189
- logic do
190
- step :step_one, action: InnerActionWithDefaultErrorStore
191
- step :step_two
192
- end
193
-
194
- def step_two(**)
195
- add_error(
196
- namespace: :parent,
197
- key: :error_happened,
198
- message: 'Message'
199
- )
200
- end
201
- end # =>
202
- # Error store for action and inner action should be the same. (Decouplio::Errors::ErrorStoreError)