decouplio 1.0.0alpha1 → 1.0.0alpha2

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -0
  3. data/README.md +26 -2
  4. data/benchmarks/Gemfile +2 -1
  5. data/benchmarks/multi_step_benchmark.rb +335 -0
  6. data/benchmarks/single_step_benchmark.rb +159 -0
  7. data/docker-compose.yml +13 -2
  8. data/docs/deny.rb +59 -0
  9. data/docs/doby.rb +1 -1
  10. data/docs/doby_deny.md +171 -0
  11. data/docs/fail.md +143 -0
  12. data/docs/fail.rb +126 -29
  13. data/docs/resq.md +2 -2
  14. data/docs/step.md +148 -0
  15. data/docs/step.rb +119 -18
  16. data/docs/step_as_a_service.md +25 -11
  17. data/docs/step_as_a_service.rb +2 -2
  18. data/docs/wrap.md +8 -0
  19. data/lib/decouplio/action.rb +22 -3
  20. data/lib/decouplio/composer.rb +78 -12
  21. data/lib/decouplio/const/reserved_methods.rb +13 -9
  22. data/lib/decouplio/const/results.rb +2 -0
  23. data/lib/decouplio/const/types.rb +11 -5
  24. data/lib/decouplio/const/validations/deny.rb +11 -0
  25. data/lib/decouplio/const/validations/fail.rb +1 -1
  26. data/lib/decouplio/errors/deny_can_not_be_first_step_error.rb +18 -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/logic_dsl.rb +14 -2
  29. data/lib/decouplio/options_validator.rb +8 -3
  30. data/lib/decouplio/steps/deny.rb +31 -0
  31. data/lib/decouplio/steps/doby.rb +5 -1
  32. data/lib/decouplio/steps/fail.rb +7 -22
  33. data/lib/decouplio/steps/inner_action_fail.rb +7 -22
  34. data/lib/decouplio/steps/inner_action_step.rb +7 -18
  35. data/lib/decouplio/steps/service_fail.rb +11 -23
  36. data/lib/decouplio/steps/service_pass.rb +4 -1
  37. data/lib/decouplio/steps/service_step.rb +11 -19
  38. data/lib/decouplio/steps/shared/fail_resolver.rb +40 -0
  39. data/lib/decouplio/steps/shared/step_resolver.rb +43 -0
  40. data/lib/decouplio/steps/step.rb +7 -18
  41. data/lib/decouplio/steps/wrap.rb +7 -18
  42. data/lib/decouplio/version.rb +1 -1
  43. metadata +12 -5
  44. data/benchmarks/benchmarks.rb +0 -527
  45. data/docs/doby.md +0 -80
data/docs/step.md CHANGED
@@ -88,6 +88,8 @@ step(step_name, **options)
88
88
  |-|-|
89
89
  |:finish_him|action stops execution if `step` method returns truthy value|
90
90
  |symbol with next step name|step with specified symbol name performs if step method returns truthy value|
91
+ |:PASS|will direct execution flow to nearest success track step. If current step is the last step when action will finish as `success`|
92
+ |:FAIL|will direct execution flow to nearest failure track step. If current step is the last step when action will finish as `failure`|
91
93
 
92
94
  ### on_success: :finish_him
93
95
 
@@ -312,11 +314,83 @@ Can be used if for some reason you need to jump to fail step
312
314
 
313
315
  ***
314
316
 
317
+ ### on_success: :PASS
318
+ It will perform like regular `step`, just move to next success track step.
319
+
320
+ ### on_success: :FAIL
321
+ <details><summary><b>EXAMPLE (CLICK ME)</b></summary>
322
+ <p>
323
+
324
+ ```ruby
325
+ require 'decouplio'
326
+
327
+ class SomeActionOnSuccessFail < Decouplio::Action
328
+ logic do
329
+ step :step_one
330
+ step :step_two, on_success: :FAIL
331
+ end
332
+
333
+ def step_one(**)
334
+ ctx[:step_one] = 'Success'
335
+ end
336
+
337
+ def step_two(step_two_param:, **)
338
+ ctx[:step_two] = step_two_param
339
+ end
340
+ end
341
+
342
+ success_action = SomeActionOnSuccessFail.call(step_two_param: true)
343
+ failure_action = SomeActionOnSuccessFail.call(step_two_param: false)
344
+
345
+ success_action # =>
346
+ # Result: failure
347
+
348
+ # Railway Flow:
349
+ # step_one -> step_two
350
+
351
+ # Context:
352
+ # :step_two_param => true
353
+ # :step_one => "Success"
354
+ # :step_two => true
355
+
356
+ # Errors:
357
+ # {}
358
+
359
+ failure_action # =>
360
+ # Result: failure
361
+
362
+ # Railway Flow:
363
+ # step_one -> step_two
364
+
365
+ # Context:
366
+ # :step_two_param => false
367
+ # :step_one => "Success"
368
+ # :step_two => false
369
+
370
+ # Errors:
371
+ # {}
372
+ ```
373
+
374
+ ```mermaid
375
+ flowchart LR
376
+ 1(start)-->2(step_one);
377
+ 2(step_one)-->|success track|3(step_two);
378
+ 3(step_two)-->|success track|4(finish_failure);
379
+ 3(step_two)-->|failure track|4(finish_failure);
380
+ ```
381
+ </p>
382
+ </details>
383
+
384
+ ***
385
+
386
+
315
387
  ### on_failure:
316
388
  |Allowed values|Description|
317
389
  |-|-|
318
390
  |:finish_him|action stops execution if `step` method returns falsy value|
319
391
  |symbol with next step name|step with specified symbol name performs if step method returns falsy value|
392
+ |:PASS|will direct execution flow to nearest success track step. If current step is the last step when action will finish as `success`|
393
+ |:FAIL|will direct execution flow to nearest failure track step. If current step is the last step when action will finish as `failure`|
320
394
 
321
395
  ### on_failure: :finish_him
322
396
 
@@ -550,6 +624,80 @@ Can be used in case if you need to come back to success track
550
624
 
551
625
  ***
552
626
 
627
+ ### on_failure: :PASS
628
+ <details><summary><b>EXAMPLE (CLICK ME)</b></summary>
629
+ <p>
630
+
631
+ ```ruby
632
+ require 'decouplio'
633
+
634
+ class SomeActionOnFailurePass < Decouplio::Action
635
+ logic do
636
+ step :step_one
637
+ step :step_two, on_failure: :PASS
638
+ end
639
+
640
+ def step_one(**)
641
+ ctx[:step_one] = true
642
+ end
643
+
644
+ def step_two(step_two_param:, **)
645
+ ctx[:step_two] = step_two_param
646
+ end
647
+ end
648
+
649
+
650
+ success_action = SomeActionOnFailurePass.call(step_two_param: true)
651
+ failure_action = SomeActionOnFailurePass.call(step_two_param: false)
652
+
653
+ success_action # =>
654
+ # Result: success
655
+
656
+ # Railway Flow:
657
+ # step_one -> step_two
658
+
659
+ # Context:
660
+ # :step_two_param => true
661
+ # :step_one => true
662
+ # :step_two => true
663
+
664
+ # Errors:
665
+ # {}
666
+
667
+ failure_action # =>
668
+ # Result: success
669
+
670
+ # Railway Flow:
671
+ # step_one -> step_two
672
+
673
+ # Context:
674
+ # :step_two_param => false
675
+ # :step_one => true
676
+ # :step_two => false
677
+
678
+ # Errors:
679
+ # {}
680
+
681
+ ```
682
+
683
+ ```mermaid
684
+ flowchart LR
685
+ 1(start)-->2(step_one);
686
+ 2(step_one)-->|success track|3(step_two);
687
+ 3(step_two)-->|success track|4(finish_success);
688
+ 3(step_two)-->|failure track|4(finish_success);
689
+ ```
690
+ </p>
691
+ </details>
692
+
693
+ ***
694
+
695
+
696
+ ### on_failure: :FAIL
697
+ It will perform like regular `step`, just move to next failure track step.
698
+
699
+ ***
700
+
553
701
  ### if: condition method name
554
702
  Can be used in case if for some reason step shouldn't be executed
555
703
 
data/docs/step.rb CHANGED
@@ -26,7 +26,7 @@ end
26
26
  success_action = SomeAction.call(param_for_step_one: true)
27
27
  failure_action = SomeAction.call(param_for_step_one: false)
28
28
 
29
- puts success_action # =>
29
+ success_action # =>
30
30
  # Result: success
31
31
 
32
32
  # Railway Flow:
@@ -38,7 +38,7 @@ puts success_action # =>
38
38
  # Errors:
39
39
  # {}
40
40
 
41
- puts failure_action # =>
41
+ failure_action # =>
42
42
  # Result: failure
43
43
 
44
44
  # Railway Flow:
@@ -76,7 +76,7 @@ end
76
76
  success_action = SomeActionOnSuccessFinishHim.call(param_for_step_one: true)
77
77
  failure_action = SomeActionOnSuccessFinishHim.call(param_for_step_one: false)
78
78
 
79
- puts success_action # =>
79
+ success_action # =>
80
80
  # Result: success
81
81
 
82
82
  # Railway Flow:
@@ -88,7 +88,7 @@ puts success_action # =>
88
88
  # Errors:
89
89
  # {}
90
90
 
91
- puts failure_action # =>
91
+ failure_action # =>
92
92
  # Result: failure
93
93
 
94
94
  # Railway Flow:
@@ -130,7 +130,7 @@ end
130
130
 
131
131
  success_action = SomeActionOnSuccessToSuccessTrack.call(param_for_step_one: true)
132
132
  failure_action = SomeActionOnSuccessToSuccessTrack.call(param_for_step_one: false)
133
- puts success_action # =>
133
+ success_action # =>
134
134
  # Result: success
135
135
 
136
136
  # Railway Flow:
@@ -143,7 +143,7 @@ puts success_action # =>
143
143
  # {}
144
144
 
145
145
 
146
- puts failure_action # =>
146
+ failure_action # =>
147
147
  # Result: failure
148
148
 
149
149
  # Railway Flow:
@@ -190,7 +190,7 @@ end
190
190
 
191
191
  success_action = SomeActionOnSuccessToFailureTrack.call(param_for_step_one: true)
192
192
  failure_action = SomeActionOnSuccessToFailureTrack.call(param_for_step_one: false)
193
- puts success_action # =>
193
+ success_action # =>
194
194
  # Result: failure
195
195
 
196
196
  # Railway Flow:
@@ -202,7 +202,7 @@ puts success_action # =>
202
202
  # Errors:
203
203
  # {}
204
204
 
205
- puts failure_action # =>
205
+ failure_action # =>
206
206
  # Result: failure
207
207
 
208
208
  # Railway Flow:
@@ -216,6 +216,56 @@ puts failure_action # =>
216
216
 
217
217
 
218
218
 
219
+ # on_success: :FAIL
220
+
221
+ class SomeActionOnSuccessFail < Decouplio::Action
222
+ logic do
223
+ step :step_one
224
+ step :step_two, on_success: :FAIL
225
+ end
226
+
227
+ def step_one(**)
228
+ ctx[:step_one] = 'Success'
229
+ end
230
+
231
+ def step_two(step_two_param:, **)
232
+ ctx[:step_two] = step_two_param
233
+ end
234
+ end
235
+
236
+ success_action = SomeActionOnSuccessFail.call(step_two_param: true)
237
+ failure_action = SomeActionOnSuccessFail.call(step_two_param: false)
238
+
239
+ success_action # =>
240
+ # Result: failure
241
+
242
+ # Railway Flow:
243
+ # step_one -> step_two
244
+
245
+ # Context:
246
+ # :step_two_param => true
247
+ # :step_one => "Success"
248
+ # :step_two => true
249
+
250
+ # Errors:
251
+ # {}
252
+
253
+ failure_action # =>
254
+ # Result: failure
255
+
256
+ # Railway Flow:
257
+ # step_one -> step_two
258
+
259
+ # Context:
260
+ # :step_two_param => false
261
+ # :step_one => "Success"
262
+ # :step_two => false
263
+
264
+ # Errors:
265
+ # {}
266
+
267
+
268
+
219
269
  # on_failure: :finish_him
220
270
  class SomeActionOnFailureFinishHim < Decouplio::Action
221
271
  logic do
@@ -244,7 +294,7 @@ end
244
294
 
245
295
  success_action = SomeActionOnFailureFinishHim.call(param_for_step_one: true)
246
296
  failure_action = SomeActionOnFailureFinishHim.call(param_for_step_one: false)
247
- puts success_action # =>
297
+ success_action # =>
248
298
  # Result: success
249
299
 
250
300
  # Railway Flow:
@@ -256,7 +306,7 @@ puts success_action # =>
256
306
  # Errors:
257
307
  # {}
258
308
 
259
- puts failure_action # =>
309
+ failure_action # =>
260
310
  # Result: failure
261
311
 
262
312
  # Railway Flow:
@@ -303,7 +353,7 @@ end
303
353
 
304
354
  success_action = SomeActionOnFailureToSuccessTrack.call(param_for_step_one: true)
305
355
  failure_action = SomeActionOnFailureToSuccessTrack.call(param_for_step_one: false)
306
- puts success_action # =>
356
+ success_action # =>
307
357
  # Result: success
308
358
 
309
359
  # Railway Flow:
@@ -316,7 +366,7 @@ puts success_action # =>
316
366
  # {}
317
367
 
318
368
 
319
- puts failure_action # =>
369
+ failure_action # =>
320
370
  # Result: success
321
371
 
322
372
  # Railway Flow:
@@ -363,7 +413,7 @@ end
363
413
 
364
414
  success_action = SomeActionOnFailureToFailureTrack.call(param_for_step_one: true)
365
415
  failure_action = SomeActionOnFailureToFailureTrack.call(param_for_step_one: false)
366
- puts success_action # =>
416
+ success_action # =>
367
417
  # Result: success
368
418
 
369
419
  # Railway Flow:
@@ -375,7 +425,7 @@ puts success_action # =>
375
425
  # Errors:
376
426
  # {}
377
427
 
378
- puts failure_action # =>
428
+ failure_action # =>
379
429
  # Result: failure
380
430
 
381
431
  # Railway Flow:
@@ -389,6 +439,57 @@ puts failure_action # =>
389
439
 
390
440
 
391
441
 
442
+ # on_failure: :PASS
443
+ class SomeActionOnFailurePass < Decouplio::Action
444
+ logic do
445
+ step :step_one
446
+ step :step_two, on_failure: :PASS
447
+ end
448
+
449
+ def step_one(**)
450
+ ctx[:step_one] = true
451
+ end
452
+
453
+ def step_two(step_two_param:, **)
454
+ ctx[:step_two] = step_two_param
455
+ end
456
+ end
457
+
458
+
459
+ success_action = SomeActionOnFailurePass.call(step_two_param: true)
460
+ failure_action = SomeActionOnFailurePass.call(step_two_param: false)
461
+
462
+ success_action # =>
463
+ # Result: success
464
+
465
+ # Railway Flow:
466
+ # step_one -> step_two
467
+
468
+ # Context:
469
+ # :step_two_param => true
470
+ # :step_one => true
471
+ # :step_two => true
472
+
473
+ # Errors:
474
+ # {}
475
+
476
+ failure_action # =>
477
+ # Result: success
478
+
479
+ # Railway Flow:
480
+ # step_one -> step_two
481
+
482
+ # Context:
483
+ # :step_two_param => false
484
+ # :step_one => true
485
+ # :step_two => false
486
+
487
+ # Errors:
488
+ # {}
489
+
490
+
491
+
492
+
392
493
  # if: condition method name
393
494
  class SomeActionOnIfCondition < Decouplio::Action
394
495
  logic do
@@ -432,7 +533,7 @@ condition_negative = SomeActionOnIfCondition.call(
432
533
  param_for_step_one: true,
433
534
  step_condition_param: false
434
535
  )
435
- puts condition_positive # =>
536
+ condition_positive # =>
436
537
  # Result: success
437
538
 
438
539
  # Railway Flow:
@@ -444,7 +545,7 @@ puts condition_positive # =>
444
545
  # Errors:
445
546
  # {}
446
547
 
447
- puts condition_negative # =>
548
+ condition_negative # =>
448
549
  # Result: success
449
550
 
450
551
  # Railway Flow:
@@ -501,7 +602,7 @@ condition_negative = SomeActionOnUnlessCondition.call(
501
602
  param_for_step_one: true,
502
603
  step_condition_param: false
503
604
  )
504
- puts condition_positive # =>
605
+ condition_positive # =>
505
606
  # Result: success
506
607
 
507
608
  # Railway Flow:
@@ -513,7 +614,7 @@ puts condition_positive # =>
513
614
  # Errors:
514
615
  # {}
515
616
 
516
- puts condition_negative # =>
617
+ condition_negative # =>
517
618
  # Result: success
518
619
 
519
620
  # Railway Flow:
@@ -8,14 +8,23 @@ It's similar to [Inner action](https://github.com/differencialx/decouplio/blob/m
8
8
  (step|fail|pass)(service_class, **options)
9
9
  ```
10
10
 
11
+ ## Behavior
12
+
13
+ - service class should implement `.call` class method
14
+ - service class can be used as `step` or `fail` or `pass`
15
+ - all options of `step|fail|pass` can be used as for [Inner action](https://github.com/differencialx/decouplio/blob/master/docs/inner_action.md)
16
+ - depending on returning value of `.call` method(truthy ot falsy) the execution will be moved to `success or failure` track accordingly.
17
+
11
18
  ## How to use?
12
19
 
13
20
  Create a PORO class with `.call` class method.
14
21
 
15
22
  ```ruby
16
- # :ctx - is the required kwarg
23
+ # :ctx - it's a ctx from Decouplio::Action
24
+ # :error_store - it's an error_store from Decouplio::Action,
25
+ # you can call #add_error on it
17
26
  class Concat
18
- def self.call(ctx:)
27
+ def self.call(ctx:, **)
19
28
  new(ctx: ctx).call
20
29
  end
21
30
 
@@ -30,12 +39,24 @@ end
30
39
 
31
40
  # OR
32
41
 
33
- # :ctx - is the required kwarg
42
+ # :ctx - it's a ctx from Decouplio::Action
43
+ # :error_store - it's an error_store from Decouplio::Action,
44
+ # you can call #add_error on it
34
45
  class Subtract
35
- def self.call(ctx:)
46
+ def self.call(ctx:, **)
36
47
  ctx[:result] = ctx[:one] - ctx[:two]
37
48
  end
38
49
  end
50
+
51
+ # OR
52
+
53
+ class MakeRequest
54
+ def self.call(ctx:, error_store:)
55
+ ctx[:client].get(ctx[:url])
56
+ rescue Net::OpenTimeout => error
57
+ error_store.add_error(:connection_error, error.message)
58
+ end
59
+ end
39
60
  ```
40
61
 
41
62
  Now you can use these classes as a `step|fail|pass` step
@@ -100,10 +121,3 @@ puts action # =>
100
121
  # {}
101
122
 
102
123
  ```
103
-
104
- ## Behavior
105
-
106
- - service class should implement `.call` class method
107
- - service class can be used as `step` or `fail` or `pass`
108
- - all options of `step|fail|pass` can be used as [Inner action](https://github.com/differencialx/decouplio/blob/master/docs/inner_action.md)
109
- - depending on returning value of `.call` method(truthy ot falsy) the execution will be moved to `success or failure` track accordingly.
@@ -1,7 +1,7 @@
1
1
  require_relative '../lib/decouplio'
2
2
 
3
3
  class Concat
4
- def self.call(ctx:)
4
+ def self.call(ctx:, **)
5
5
  new(ctx: ctx).call
6
6
  end
7
7
 
@@ -15,7 +15,7 @@ class Concat
15
15
  end
16
16
 
17
17
  class Subtract
18
- def self.call(ctx:)
18
+ def self.call(ctx:, **)
19
19
  ctx[:result] = ctx[:one] - ctx[:two]
20
20
  end
21
21
  end
data/docs/wrap.md CHANGED
@@ -214,6 +214,10 @@ The same as for [step](https://github.com/differencialx/decouplio/blob/master/do
214
214
  The same as for [step](https://github.com/differencialx/decouplio/blob/master/docs/step.md)
215
215
  ### on_success: next failure track step
216
216
  The same as for [step](https://github.com/differencialx/decouplio/blob/master/docs/step.md)
217
+ ### on_success: :PASS
218
+ The same as for [step](https://github.com/differencialx/decouplio/blob/master/docs/step.md)
219
+ ### on_success: :FAIL
220
+ The same as for [step](https://github.com/differencialx/decouplio/blob/master/docs/step.md)
217
221
  ### on_failure:
218
222
  The same as for [step](https://github.com/differencialx/decouplio/blob/master/docs/step.md)
219
223
  ### on_failure: :finish_him
@@ -222,6 +226,10 @@ The same as for [step](https://github.com/differencialx/decouplio/blob/master/do
222
226
  The same as for [step](https://github.com/differencialx/decouplio/blob/master/docs/step.md)
223
227
  ### on_failure: next failure track step
224
228
  The same as for [step](https://github.com/differencialx/decouplio/blob/master/docs/step.md)
229
+ ### on_failure: :PASS
230
+ The same as for [step](https://github.com/differencialx/decouplio/blob/master/docs/step.md)
231
+ ### on_failure: :FAIL
232
+ The same as for [step](https://github.com/differencialx/decouplio/blob/master/docs/step.md)
225
233
  ### if: condition method name
226
234
  The same as for [step](https://github.com/differencialx/decouplio/blob/master/docs/step.md)
227
235
  ### unless: condition method name
@@ -7,15 +7,18 @@ require_relative 'default_error_handler'
7
7
  require_relative 'errors/logic_redefinition_error'
8
8
  require_relative 'errors/logic_is_not_defined_error'
9
9
  require_relative 'errors/error_store_error'
10
+ require_relative 'const/results'
10
11
 
11
12
  module Decouplio
12
13
  class Action
14
+ PREVIOUS_STEP_INDEX = -2
15
+
13
16
  extend Forwardable
14
17
  def_delegators :@error_store, :errors, :add_error
15
18
  attr_reader :railway_flow, :ctx, :error_store
16
19
 
17
20
  def initialize(
18
- parent_railway_flow: nil, parent_ctx: nil, wrapper: false, error_store:, **params
21
+ parent_railway_flow: nil, parent_ctx: nil, error_store:, **params
19
22
  )
20
23
  @error_store = error_store
21
24
  @ctx = parent_ctx || params
@@ -43,6 +46,10 @@ module Decouplio
43
46
  @failure = false
44
47
  end
45
48
 
49
+ def previous_step
50
+ railway_flow[PREVIOUS_STEP_INDEX]
51
+ end
52
+
46
53
  def append_railway_flow(stp)
47
54
  railway_flow << stp
48
55
  end
@@ -55,10 +62,22 @@ module Decouplio
55
62
  #{railway_flow.join(' -> ')}
56
63
 
57
64
  Context:
58
- #{ctx}
65
+ #{ctx.map { |k, v| "#{k.inspect} => #{v.inspect}" }.join("\n ")}
59
66
 
60
67
  Errors:
61
- #{errors}
68
+ #{
69
+ if errors.is_a?(Hash)
70
+ if errors.empty?
71
+ 'None'
72
+ else
73
+ errors.map do |k, v|
74
+ "#{k.inspect} => #{v.inspect}"
75
+ end.join("\n ")
76
+ end
77
+ else
78
+ errors
79
+ end
80
+ }
62
81
  INSPECT
63
82
  end
64
83