u-case 2.0.0 → 2.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a64ec32f1fc225bd83e86b56a34eca857b1e1e220e84bd35b64b5324d3d74b60
4
- data.tar.gz: 23d17b47d23384fba17ace8e96b22ce07f4d42f01d676e7c29c88d99e9fc2b77
3
+ metadata.gz: 2cc6d19966687eef5e783d907074f36cb5b27492cea1e5e9bfadeaa68a799e69
4
+ data.tar.gz: cb63d747bdeaf71604fc52f7adecd80a8f2071c2087222ad9c1423fd29e381bb
5
5
  SHA512:
6
- metadata.gz: 6b3732611a933699d9b8dfa8db011e58a317565eb7f2ab13c9f965f92f2369ac10838c99cdd1c54a977ed0e2afcf11801f8b233e62615208406da323dfab2d66
7
- data.tar.gz: 5e967876fadc6093232c9c5d2d2a70b835cad1b7e670b61d6667cacd50ab0e5b9632ecb6fa231cb09e5f7826a26c4188911dc6fca3b3a4eb970b3eda22f1a50b
6
+ metadata.gz: aab4e530a1a74c9a3900cf4347a1d8f01b9dea9551565943c46b5e6367aedf3c25ba0e504fdcffd691a9276a1a902242d46489f4df4dfed02b761b46da06db1c
7
+ data.tar.gz: b409f247130404652073cd18edaf4e69746ed4a50091ee48f936e964e7993bf45e93738b8cde19aa5fee6eddcae5fb7722f5ac23639bede336dec1303491b644
data/Gemfile CHANGED
@@ -2,14 +2,14 @@ source "https://rubygems.org"
2
2
 
3
3
  git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
- activemodel_version = ENV.fetch('ACTIVEMODEL_VERSION', '6.1')
5
+ activemodel_version = ENV.fetch('ACTIVEMODEL_VERSION', '6.1.0')
6
6
 
7
7
  activemodel = case activemodel_version
8
8
  when '3.2' then '3.2.22'
9
9
  when '5.2' then '5.2.3'
10
10
  end
11
11
 
12
- if activemodel_version < '6.1'
12
+ if activemodel_version < '6.1.0'
13
13
  gem 'activemodel', activemodel, require: false
14
14
  gem 'activesupport', activemodel, require: false
15
15
  end
data/README.md CHANGED
@@ -8,36 +8,43 @@
8
8
 
9
9
  Create simple and powerful use cases as objects.
10
10
 
11
- The main goals of this project are:
12
- 1. Be simple to use and easy to learn (input **>>** process/transform **>>** output).
13
- 2. Promove referential transparency (transforming instead of modifying) and data integrity.
14
- 3. No callbacks (before, after, around...).
15
- 4. Solve complex business logic using a composition of use cases.
11
+ The main project goals are:
12
+ 1. Be simple to use and easy to learn (input **>>** process / transform **>>** output).
13
+ 2. Promote referential transparency (transforming instead of modifying) and data integrity.
14
+ 3. No callbacks (e.g: before, after, around).
15
+ 4. Solve complex business logic, by allowing the composition of use cases.
16
+ 5. Be fast and optimized (Check out the [benchmarks](#benchmarks) section).
16
17
 
17
18
  ## Table of Contents <!-- omit in toc -->
18
- - [μ-case (Micro::Case)](#%ce%bc-case-microcase)
19
+ - [μ-case (Micro::Case)](#μ-case-microcase)
19
20
  - [Required Ruby version](#required-ruby-version)
20
21
  - [Dependencies](#dependencies)
21
22
  - [Installation](#installation)
22
23
  - [Usage](#usage)
23
- - [How to define a use case?](#how-to-define-a-use-case)
24
- - [What is a `Micro::Case::Result`?](#what-is-a-microcaseresult)
25
- - [What are the default `Micro::Case::Result` types?](#what-are-the-default-microcaseresult-types)
24
+ - [Micro::Case - How to define a use case?](#microcase---how-to-define-a-use-case)
25
+ - [Micro::Case::Result - What is a use case result?](#microcaseresult---what-is-a-use-case-result)
26
+ - [What are the default result types?](#what-are-the-default-result-types)
26
27
  - [How to define custom result types?](#how-to-define-custom-result-types)
27
28
  - [Is it possible to define a custom result type without a block?](#is-it-possible-to-define-a-custom-result-type-without-a-block)
28
29
  - [How to use the result hooks?](#how-to-use-the-result-hooks)
29
- - [Why the on_failure result hook exposes a different kind of data?](#why-the-onfailure-result-hook-exposes-a-different-kind-of-data)
30
- - [What happens if a result hook is declared multiple times?](#what-happens-if-a-result-hook-is-declared-multiple-times)
31
- - [How to compose uses cases to represents complex ones?](#how-to-compose-uses-cases-to-represents-complex-ones)
30
+ - [Why the failure hook (without a type) exposes a different kind of data?](#why-the-failure-hook-without-a-type-exposes-a-different-kind-of-data)
31
+ - [What happens if a result hook was declared multiple times?](#what-happens-if-a-result-hook-was-declared-multiple-times)
32
+ - [Micro::Case::Flow - How to compose use cases?](#microcaseflow---how-to-compose-use-cases)
32
33
  - [Is it possible to compose a use case flow with other ones?](#is-it-possible-to-compose-a-use-case-flow-with-other-ones)
33
34
  - [Is it possible a flow accumulates its input and merges each success result to use as the argument of their use cases?](#is-it-possible-a-flow-accumulates-its-input-and-merges-each-success-result-to-use-as-the-argument-of-their-use-cases)
34
- - [What is a strict use case?](#what-is-a-strict-use-case)
35
- - [Is there some feature to auto handle exceptions inside of a use case or flow?](#is-there-some-feature-to-auto-handle-exceptions-inside-of-a-use-case-or-flow)
36
- - [How to validate use case attributes?](#how-to-validate-use-case-attributes)
37
- - [If I enable the auto validation, is it possible to disable it only in some specific use case classes?](#if-i-enable-the-auto-validation-is-it-possible-to-disable-it-only-in-some-specific-use-case-classes)
38
- - [Examples](#examples)
39
- - [Comparisons](#comparisons)
35
+ - [Is it possible to declare a flow using the use case itself?](#is-it-possible-to-declare-a-flow-using-the-use-case-itself)
36
+ - [Micro::Case::Strict - What is a strict use case?](#microcasestrict---what-is-a-strict-use-case)
37
+ - [Micro::Case::Safe - Is there some feature to auto handle exceptions inside of a use case or flow?](#microcasesafe---is-there-some-feature-to-auto-handle-exceptions-inside-of-a-use-case-or-flow)
38
+ - [u-case/with_validation - How to validate use case attributes?](#u-casewith_validation---how-to-validate-use-case-attributes)
39
+ - [If I enabled the auto validation, is it possible to disable it only in specific use case classes?](#if-i-enabled-the-auto-validation-is-it-possible-to-disable-it-only-in-specific-use-case-classes)
40
40
  - [Benchmarks](#benchmarks)
41
+ - [Micro::Case](#microcase)
42
+ - [Best overall](#best-overall)
43
+ - [Success results](#success-results)
44
+ - [Failure results](#failure-results)
45
+ - [Micro::Case::Flow](#microcaseflow)
46
+ - [Comparisons](#comparisons)
47
+ - [Examples](#examples)
41
48
  - [Development](#development)
42
49
  - [Contributing](#contributing)
43
50
  - [License](#license)
@@ -70,7 +77,7 @@ Or install it yourself as:
70
77
 
71
78
  ## Usage
72
79
 
73
- ### How to define a use case?
80
+ ### `Micro::Case` - How to define a use case?
74
81
 
75
82
  ```ruby
76
83
  class Multiply < Micro::Case
@@ -123,7 +130,7 @@ result.value # 6
123
130
 
124
131
  [⬆️ Back to Top](#table-of-contents-)
125
132
 
126
- ### What is a `Micro::Case::Result`?
133
+ ### `Micro::Case::Result` - What is a use case result?
127
134
 
128
135
  A `Micro::Case::Result` stores the use cases output data. These are their main methods:
129
136
  - `#success?` returns true if is a successful result.
@@ -135,7 +142,7 @@ A `Micro::Case::Result` stores the use cases output data. These are their main m
135
142
 
136
143
  [⬆️ Back to Top](#table-of-contents-)
137
144
 
138
- #### What are the default `Micro::Case::Result` types?
145
+ #### What are the default result types?
139
146
 
140
147
  Every result has a type and these are the defaults:
141
148
  - `:ok` when success
@@ -309,7 +316,7 @@ Double
309
316
  # The use case responsible for the failure will be accessible as the second hook argument
310
317
  ```
311
318
 
312
- #### Why the on_failure result hook exposes a different kind of data?
319
+ #### Why the failure hook (without a type) exposes a different kind of data?
313
320
 
314
321
  Answer: To allow you to define how to handle the program flow using some
315
322
  conditional statement (like an `if`, `case/when`).
@@ -371,7 +378,7 @@ Double
371
378
 
372
379
  [⬆️ Back to Top](#table-of-contents-)
373
380
 
374
- #### What happens if a result hook is declared multiple times?
381
+ #### What happens if a result hook was declared multiple times?
375
382
 
376
383
  Answer: The hook always will be triggered if it matches the result type.
377
384
 
@@ -404,7 +411,7 @@ result.value * 4 == accum # true
404
411
 
405
412
  [⬆️ Back to Top](#table-of-contents-)
406
413
 
407
- ### How to compose uses cases to represents complex ones?
414
+ ### `Micro::Case::Flow` - How to compose use cases?
408
415
 
409
416
  In this case, this will be a **flow** (`Micro::Case::Flow`).
410
417
  The main idea of this feature is to use/reuse use cases as steps of a new use case.
@@ -466,16 +473,31 @@ p result.value # {:numbers => [3, 3, 4, 4, 5, 6]}
466
473
  # An alternative way to create a flow using classes #
467
474
  #---------------------------------------------------#
468
475
 
469
- class DoubleAllNumbers
470
- include Micro::Case::Flow
471
-
472
- flow Steps::ConvertTextToNumbers, Steps::Double
476
+ class DoubleAllNumbers < Micro::Case
477
+ flow Steps::ConvertTextToNumbers,
478
+ Steps::Double
473
479
  end
474
480
 
475
481
  DoubleAllNumbers
476
482
  .call(numbers: %w[1 1 b 2 3 4])
477
483
  .on_failure { |message| p message } # "numbers must contain only numeric types"
478
484
 
485
+ # !------------------------------------ ! #
486
+ # ! Deprecated: Micro::Case::Flow mixin ! #
487
+ # !-------------------------------------! #
488
+
489
+ # The code below still works, but it will output a warning message:
490
+ # Deprecation: Micro::Case::Flow mixin is being deprecated, please use `Micro::Case` inheritance instead.
491
+
492
+ class DoubleAllNumbers
493
+ include Micro::Case::Flow
494
+
495
+ flow Steps::ConvertTextToNumbers,
496
+ Steps::Double
497
+ end
498
+
499
+ # Note: This feature will be removed in the next major release (3.0)
500
+
479
501
  #-------------------------------------------------------------#
480
502
  # Another way to create a flow using the composition operator #
481
503
  #-------------------------------------------------------------#
@@ -574,7 +596,54 @@ Note: You can blend any of the [available syntaxes/approaches](#how-to-create-a-
574
596
 
575
597
  Answer: Yes, it is! Check out these test examples [Micro::Case::Flow](https://github.com/serradura/u-case/blob/e0066d8a6e3a9404069dfcb9bf049b854f08a33c/test/micro/case/flow/reducer_test.rb) and [Micro::Case::Safe::Flow](https://github.com/serradura/u-case/blob/e0066d8a6e3a9404069dfcb9bf049b854f08a33c/test/micro/case/safe/flow/reducer_test.rb) to see different use cases sharing their own data.
576
598
 
577
- ### What is a strict use case?
599
+ [⬆️ Back to Top](#table-of-contents-)
600
+
601
+ #### Is it possible to declare a flow using the use case itself?
602
+
603
+ Answer: Yes, it is! You can use the `self.call!` macro. e.g:
604
+
605
+ ```ruby
606
+ class ConvertTextToNumber < Micro::Case
607
+ attribute :text
608
+
609
+ def call!
610
+ Success { { number: text.to_i } }
611
+ end
612
+ end
613
+
614
+ class ConvertNumberToText < Micro::Case
615
+ attribute :number
616
+
617
+ def call!
618
+ Success { { text: number.to_s } }
619
+ end
620
+ end
621
+
622
+ class Double < Micro::Case
623
+ attribute :number
624
+
625
+ def call!
626
+ Success { { number: number * 2 } }
627
+ end
628
+
629
+ # NOTE: You need to declare the flow after the definition of the attributes.
630
+ flow ConvertTextToNumber,
631
+ self.call!,
632
+ ConvertNumberToText
633
+ end
634
+
635
+ result = Double.call(text: '4')
636
+
637
+ result.success? # true
638
+ result.value # "8"
639
+
640
+ # NOTE: This feature can be used with the Micro::Case::Safe.
641
+ # Checkout the test: test/micro/case/safe/flow/with_classes/using_itself_test.rb
642
+ ```
643
+
644
+ [⬆️ Back to Top](#table-of-contents-)
645
+
646
+ ### `Micro::Case::Strict` - What is a strict use case?
578
647
 
579
648
  Answer: Is a use case which will require all the keywords (attributes) on its initialization.
580
649
 
@@ -595,7 +664,7 @@ Double.call({})
595
664
 
596
665
  [⬆️ Back to Top](#table-of-contents-)
597
666
 
598
- ### Is there some feature to auto handle exceptions inside of a use case or flow?
667
+ ### `Micro::Case::Safe` - Is there some feature to auto handle exceptions inside of a use case or flow?
599
668
 
600
669
  Answer: Yes, there is!
601
670
 
@@ -655,17 +724,7 @@ end
655
724
  # Note:
656
725
  # The ampersand is based on the safe navigation operator. https://ruby-doc.org/core-2.6/doc/syntax/calling_methods_rdoc.html#label-Safe+navigation+operator
657
726
 
658
- # The alternatives are:
659
-
660
- module Users
661
- class Create
662
- include Micro::Case::Safe::Flow
663
-
664
- flow ProcessParams, ValidateParams, Persist, SendToCRM
665
- end
666
- end
667
-
668
- # or
727
+ # The alternatives to declare a safe flow are:
669
728
 
670
729
  module Users
671
730
  Create = Micro::Case::Safe::Flow([
@@ -675,11 +734,40 @@ module Users
675
734
  SendToCRM
676
735
  ])
677
736
  end
737
+
738
+ # or within classes
739
+
740
+ module Users
741
+ class Create < Micro::Case::Safe
742
+ flow ProcessParams,
743
+ ValidateParams,
744
+ Persist,
745
+ SendToCRM
746
+ end
747
+ end
748
+
749
+
750
+ # !------------------------------------------ ! #
751
+ # ! Deprecated: Micro::Case::Safe::Flow mixin ! #
752
+ # !-------------------------------------------! #
753
+
754
+ # The code below still works, but it will output a warning message:
755
+ # Deprecation: Micro::Case::Flow mixin is being deprecated, please use `Micro::Case` inheritance instead.
756
+
757
+ module Users
758
+ class Create
759
+ include Micro::Case::Safe::Flow
760
+
761
+ flow ProcessParams, ValidateParams, Persist, SendToCRM
762
+ end
763
+ end
764
+
765
+ # Note: This feature will be removed in the next major release (3.0)
678
766
  ```
679
767
 
680
768
  [⬆️ Back to Top](#table-of-contents-)
681
769
 
682
- ### How to validate use case attributes?
770
+ ### `u-case/with_validation` - How to validate use case attributes?
683
771
 
684
772
  **Requirement:**
685
773
 
@@ -730,7 +818,7 @@ end
730
818
  # Micro::Case::Strict and Micro::Case::Safe classes will inherit this new behavior.
731
819
  ```
732
820
 
733
- #### If I enable the auto validation, is it possible to disable it only in some specific use case classes?
821
+ #### If I enabled the auto validation, is it possible to disable it only in specific use case classes?
734
822
 
735
823
  Answer: Yes, it is. To do this, you only need to use the `disable_auto_validation` macro. e.g:
736
824
 
@@ -757,32 +845,192 @@ Multiply.call(a: 2, b: 'a')
757
845
 
758
846
  [⬆️ Back to Top](#table-of-contents-)
759
847
 
760
- ### Examples
761
-
762
- 1. [Rescuing an exception inside of use cases](https://github.com/serradura/u-case/blob/master/examples/rescuing_exceptions.rb)
763
- 2. [Users creation](https://github.com/serradura/u-case/blob/master/examples/users_creation.rb)
764
-
765
- An example of flow in how to define steps to sanitize, validate, and persist some input data.
766
- 3. [CLI calculator](https://github.com/serradura/u-case/tree/master/examples/calculator)
767
-
768
- A more complex example which use rake tasks to demonstrate how to handle user data, and how to use different failures type to control the program flow.
769
-
770
- [⬆️ Back to Top](#table-of-contents-)
848
+ ## Benchmarks
771
849
 
772
- ## Comparisons
850
+ ### `Micro::Case`
851
+
852
+ #### Best overall
853
+
854
+ The table below contains the average between the [Success results](#success-results) and [Failure results](#failure-results) benchmarks.
855
+
856
+ | Gem / Abstraction | Iterations per second | Comparison |
857
+ | ---------------------- | --------------------: | ---------------: |
858
+ | **Micro::Case** | 116629.7 | _**The Faster**_ |
859
+ | Dry::Monads | 101796.3 | 1.14x slower |
860
+ | Interactor | 21230.5 | 5.49x slower |
861
+ | Trailblazer::Operation | 16466.6 | 7.08x slower |
862
+ | Dry::Transaction | 5069.5 | 23.00x slower |
863
+
864
+ ---
865
+
866
+ #### Success results
867
+
868
+ | Gem / Abstraction | Iterations per second | Comparison |
869
+ | ----------------- | --------------------: | ---------------: |
870
+ | Dry::Monads | 139352.5 | _**The Faster**_ |
871
+ | **Micro::Case** | 124749.4 | 1.12x slower |
872
+ | Interactor | 28974.4 | 4.81x slower |
873
+ | Trailblazer::Operation | 17275.6 | 8.07x slower |
874
+ | Dry::Transaction | 5571.7 | 25.01x slower |
875
+
876
+ <details>
877
+ <summary>Show the full <a href="https://github.com/evanphx/benchmark-ips">benchmark/ips</a> results.</summary>
878
+
879
+ ```ruby
880
+ # Warming up --------------------------------------
881
+ # Interactor 2.865k i/100ms
882
+ # Trailblazer::Operation
883
+ # 1.686k i/100ms
884
+ # Dry::Monads 13.389k i/100ms
885
+ # Dry::Transaction 551.000 i/100ms
886
+ # Micro::Case 11.984k i/100ms
887
+ # Micro::Case::Strict 9.102k i/100ms
888
+ # Micro::Case::Safe 11.747k i/100ms
889
+ # Calculating -------------------------------------
890
+ # Interactor 28.974k (± 2.7%) i/s - 146.115k in 5.046703s
891
+ # Trailblazer::Operation
892
+ # 17.276k (± 1.8%) i/s - 87.672k in 5.076609s
893
+ # Dry::Monads 139.353k (± 2.5%) i/s - 709.617k in 5.095599s
894
+ # Dry::Transaction 5.572k (± 3.6%) i/s - 28.101k in 5.050376s
895
+ # Micro::Case 124.749k (± 1.9%) i/s - 635.152k in 5.093310s
896
+ # Micro::Case::Strict 93.417k (± 4.8%) i/s - 473.304k in 5.081341s
897
+ # Micro::Case::Safe 120.607k (± 3.2%) i/s - 610.844k in 5.070394s
898
+
899
+ # Comparison:
900
+ # Dry::Monads: 139352.5 i/s
901
+ # Micro::Case: 124749.4 i/s - 1.12x slower
902
+ # Micro::Case::Safe: 120607.3 i/s - 1.16x slower
903
+ # Micro::Case::Strict: 93417.3 i/s - 1.49x slower
904
+ # Interactor: 28974.4 i/s - 4.81x slower
905
+ # Trailblazer::Operation: 17275.6 i/s - 8.07x slower
906
+ # Dry::Transaction: 5571.7 i/s - 25.01x slower
907
+ ```
908
+ </details>
909
+
910
+ https://github.com/serradura/u-case/blob/master/benchmarks/use_case/with_success_result.rb
911
+
912
+ #### Failure results
913
+
914
+ | Gem / Abstraction | Iterations per second | Comparison |
915
+ | ----------------- | --------------------: | ---------------: |
916
+ | **Micro::Case** | 108510.0 | _**The Faster**_ |
917
+ | Dry::Monads | 64240.1 | 1.69x slower |
918
+ | Trailblazer::Operation | 15657.7 | 6.93x slower |
919
+ | Interactor | 13486.7 | 8.05x slower |
920
+ | Dry::Transaction | 4567.3 | 23.76x slower |
921
+
922
+ <details>
923
+ <summary>Show the full <a href="https://github.com/evanphx/benchmark-ips">benchmark/ips</a> results.</summary>
924
+
925
+ ```ruby
926
+ # Warming up --------------------------------------
927
+ # Interactor 1.331k i/100ms
928
+ # Trailblazer::Operation
929
+ # 1.544k i/100ms
930
+ # Dry::Monads 6.343k i/100ms
931
+ # Dry::Transaction 456.000 i/100ms
932
+ # Micro::Case 10.429k i/100ms
933
+ # Micro::Case::Strict 8.109k i/100ms
934
+ # Micro::Case::Safe 10.280k i/100ms
935
+ # Calculating -------------------------------------
936
+ # Interactor 13.487k (± 1.9%) i/s - 67.881k in 5.035059s
937
+ # Trailblazer::Operation
938
+ # 15.658k (± 1.6%) i/s - 78.744k in 5.030427s
939
+ # Dry::Monads 64.240k (± 1.8%) i/s - 323.493k in 5.037461s
940
+ # Dry::Transaction 4.567k (± 1.3%) i/s - 23.256k in 5.092699s
941
+ # Micro::Case 108.510k (± 2.3%) i/s - 542.308k in 5.000605s
942
+ # Micro::Case::Strict 83.527k (± 1.4%) i/s - 421.668k in 5.049245s
943
+ # Micro::Case::Safe 105.641k (± 3.7%) i/s - 534.560k in 5.067836s
944
+
945
+ # Comparison:
946
+ # Micro::Case: 108510.0 i/s
947
+ # Micro::Case::Safe: 105640.6 i/s - same-ish: difference falls within error
948
+ # Micro::Case::Strict: 83526.8 i/s - 1.30x slower
949
+ # Dry::Monads: 64240.1 i/s - 1.69x slower
950
+ # Trailblazer::Operation: 15657.7 i/s - 6.93x slower
951
+ # Interactor: 13486.7 i/s - 8.05x slower
952
+ # Dry::Transaction: 4567.3 i/s - 23.76x slower
953
+ ```
954
+ </details>
955
+
956
+ https://github.com/serradura/u-case/blob/master/benchmarks/use_case/with_failure_result.rb
957
+
958
+ ---
959
+
960
+ ### `Micro::Case::Flow`
961
+
962
+ | Gems / Abstraction | [Success results](https://github.com/serradura/u-case/blob/master/benchmarks/flow/with_success_result.rb#L40) | [Failure results](https://github.com/serradura/u-case/blob/master/benchmarks/flow/with_failure_result.rb#L40) |
963
+ | ------------------ | ---------------: | ---------------: |
964
+ | Micro::Case::Flow | _**The Faster**_ | _**The Faster**_ |
965
+ | Micro::Case::Safe::Flow | 0x slower | 0x slower |
966
+ | Interactor::Organizer | 1.47x slower | 5.51x slower |
967
+
968
+ \* The `Dry::Monads`, `Dry::Transaction`, `Trailblazer::Operation` are out of this analysis because all of them doesn't have this kind of feature.
969
+
970
+ <details>
971
+ <summary><strong>Success results</strong> - Show the full benchmark/ips results.</summary>
972
+
973
+ ```ruby
974
+ # Warming up --------------------------------------
975
+ # Interactor::Organizer 4.880k i/100ms
976
+ # Micro::Case::Flow 7.035k i/100ms
977
+ # Micro::Case::Safe::Flow 7.059k i/100ms
978
+
979
+ # Calculating -------------------------------------
980
+ # Interactor::Organizer 50.208k (± 1.3%) i/s - 253.760k in 5.055099s
981
+ # Micro::Case::Flow 73.791k (± 0.9%) i/s - 372.855k in 5.053311s
982
+ # Micro::Case::Safe::Flow 73.314k (± 1.1%) i/s - 367.068k in 5.007473s
983
+
984
+ # Comparison:
985
+ # Micro::Case::Flow: 73790.7 i/s
986
+ # Micro::Case::Safe::Flow: 73313.7 i/s - same-ish: difference falls within error
987
+ # Interactor::Organizer: 50207.7 i/s - 1.47x slower
988
+ ```
989
+ </details>
990
+
991
+ <details>
992
+ <summary><strong>Failure results</strong> - Show the full benchmark/ips results.</summary>
993
+
994
+ ```ruby
995
+ # Warming up --------------------------------------
996
+ # Interactor::Organizer 2.372k i/100ms
997
+ # Micro::Case::Flow 12.802k i/100ms
998
+ # Micro::Case::Safe::Flow 12.673k i/100ms
999
+
1000
+ # Calculating -------------------------------------
1001
+ # Interactor::Organizer 24.522k (± 2.0%) i/s - 123.344k in 5.032159s
1002
+ # Micro::Case::Flow 135.122k (± 1.7%) i/s - 678.506k in 5.022903s
1003
+ # Micro::Case::Safe::Flow 133.980k (± 1.4%) i/s - 671.669k in 5.014181s
1004
+
1005
+ # Comparison:
1006
+ # Micro::Case::Flow: 135122.0 i/s
1007
+ # Micro::Case::Safe::Flow: 133979.8 i/s - same-ish: difference falls within error
1008
+ # Interactor::Organizer: 24521.8 i/s - 5.51x slower
1009
+ ```
1010
+ </details>
1011
+
1012
+ https://github.com/serradura/u-case/tree/master/benchmarks/flow
1013
+
1014
+ ### Comparisons
773
1015
 
774
1016
  Check it out implementations of the same use case with different gems/abstractions.
775
1017
 
776
1018
  * [interactor](https://github.com/serradura/u-case/blob/master/comparisons/interactor.rb)
777
1019
  * [u-case](https://github.com/serradura/u-case/blob/master/comparisons/u-case.rb)
778
1020
 
779
- ## Benchmarks
1021
+ [⬆️ Back to Top](#table-of-contents-)
780
1022
 
781
- **[interactor](https://github.com/collectiveidea/interactor)** VS **[u-case](https://github.com/serradura/u-case)**
1023
+ ## Examples
782
1024
 
783
- https://github.com/serradura/u-case/tree/master/benchmarks/interactor
1025
+ 1. [Rescuing an exception inside of use cases](https://github.com/serradura/u-case/blob/master/examples/rescuing_exceptions.rb)
1026
+ 2. [Users creation](https://github.com/serradura/u-case/blob/master/examples/users_creation.rb)
784
1027
 
785
- ![interactor VS u-case](https://github.com/serradura/u-case/blob/master/assets/u-case_benchmarks.png?raw=true)
1028
+ An example of flow in how to define steps to sanitize, validate, and persist some input data.
1029
+ 3. [CLI calculator](https://github.com/serradura/u-case/tree/master/examples/calculator)
1030
+
1031
+ A more complex example which use rake tasks to demonstrate how to handle user data, and how to use different failures type to control the program flow.
1032
+
1033
+ [⬆️ Back to Top](#table-of-contents-)
786
1034
 
787
1035
  ## Development
788
1036
 
@@ -42,14 +42,35 @@ module Micro
42
42
  instance
43
43
  end
44
44
 
45
- def self.__failure_type(arg, type)
46
- return type if type != :error
45
+ def self.__get_flow__
46
+ @__flow
47
+ end
47
48
 
48
- case arg
49
- when Exception then :exception
50
- when Symbol then arg
51
- else type
52
- end
49
+ private_class_method def self.__set_flow__(reducer, args)
50
+ def self.use_cases; __get_flow__.use_cases; end
51
+
52
+ self.class_eval('def use_cases; self.class.use_cases; end')
53
+
54
+ reducer.build(args)
55
+ end
56
+
57
+ def self.flow(*args)
58
+ @__flow ||= __set_flow__(Flow::Reducer, args)
59
+ end
60
+
61
+ def self.call!
62
+ return const_get(:Flow_Step) if const_defined?(:Flow_Step)
63
+
64
+ const_set(:Flow_Step, Class.new(self) do
65
+ private def __call
66
+ __call_use_case
67
+ end
68
+ end)
69
+ end
70
+
71
+ def initialize(input)
72
+ @__input = input
73
+ self.attributes = input
53
74
  end
54
75
 
55
76
  def call!
@@ -70,6 +91,12 @@ module Micro
70
91
  private
71
92
 
72
93
  def __call
94
+ return self.class.__get_flow__.call(@__input) if self.class.__get_flow__
95
+
96
+ __call_use_case
97
+ end
98
+
99
+ def __call_use_case
73
100
  result = call!
74
101
 
75
102
  return result if result.is_a?(Result)
@@ -77,10 +104,6 @@ module Micro
77
104
  raise Error::UnexpectedResult.new(self.class)
78
105
  end
79
106
 
80
- def __get_result__
81
- @__result ||= Result.new
82
- end
83
-
84
107
  def Success(arg = :ok)
85
108
  value, type = block_given? ? [yield, arg] : [arg, :ok]
86
109
 
@@ -89,9 +112,21 @@ module Micro
89
112
 
90
113
  def Failure(arg = :error)
91
114
  value = block_given? ? yield : arg
92
- type = self.class.__failure_type(value, block_given? ? arg : :error)
115
+ type = __map_failure_type(value, block_given? ? arg : :error)
93
116
 
94
117
  __get_result__.__set__(false, value, type, self)
95
118
  end
119
+
120
+ def __get_result__
121
+ @__result ||= Result.new
122
+ end
123
+
124
+ def __map_failure_type(arg, type)
125
+ return type if type != :error
126
+ return arg if arg.is_a?(Symbol)
127
+ return :exception if arg.is_a?(Exception)
128
+
129
+ type
130
+ end
96
131
  end
97
132
  end
@@ -29,7 +29,10 @@ module Micro
29
29
 
30
30
  private_constant :ClassMethods, :CONSTRUCTOR
31
31
 
32
+ # Deprecated: Classes with flows are now defined via `Micro::Case` inheritance
32
33
  def self.included(base)
34
+ warn 'Deprecation: Micro::Case::Flow mixin is being deprecated, please use `Micro::Case` inheritance instead.'
35
+
33
36
  def base.flow_reducer; Reducer; end
34
37
 
35
38
  base.extend(ClassMethods)
@@ -35,6 +35,10 @@ module Micro
35
35
  def self.Flow(args)
36
36
  Flow::Reducer.build(Array(args))
37
37
  end
38
+
39
+ def self.flow(*args)
40
+ @__flow ||= __set_flow__(Flow::Reducer, args)
41
+ end
38
42
  end
39
43
  end
40
44
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Micro
4
4
  class Case
5
- VERSION = '2.0.0'.freeze
5
+ VERSION = '2.1.0'.freeze
6
6
  end
7
7
  end
@@ -14,6 +14,12 @@ module Micro
14
14
  @disable_auto_validation = true
15
15
  end
16
16
 
17
+ def initialize(input)
18
+ @__input = input
19
+ self.attributes = input
20
+ run_validations! if respond_to?(:run_validations!, true)
21
+ end
22
+
17
23
  def call
18
24
  return failure_by_validation_error(self) if !self.class.auto_validation_disabled? && invalid?
19
25
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: u-case
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-19 00:00:00.000000000 Z
11
+ date: 2019-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: u-attributes