u-case 2.0.0 → 2.1.0

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