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 +4 -4
- data/Gemfile +2 -2
- data/README.md +307 -59
- data/lib/micro/case.rb +47 -12
- data/lib/micro/case/flow.rb +3 -0
- data/lib/micro/case/safe/flow.rb +4 -0
- data/lib/micro/case/version.rb +1 -1
- data/lib/micro/case/with_validation.rb +6 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2cc6d19966687eef5e783d907074f36cb5b27492cea1e5e9bfadeaa68a799e69
|
4
|
+
data.tar.gz: cb63d747bdeaf71604fc52f7adecd80a8f2071c2087222ad9c1423fd29e381bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
12
|
-
1. Be simple to use and easy to learn (input **>>** process/transform **>>** output).
|
13
|
-
2.
|
14
|
-
3. No callbacks (before, after, around
|
15
|
-
4. Solve complex business logic
|
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)](
|
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
|
25
|
-
- [What are the default
|
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
|
30
|
-
- [What happens if a result hook
|
31
|
-
- [How to compose
|
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
|
-
|
35
|
-
- [
|
36
|
-
- [
|
37
|
-
|
38
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
1021
|
+
[⬆️ Back to Top](#table-of-contents-)
|
780
1022
|
|
781
|
-
|
1023
|
+
## Examples
|
782
1024
|
|
783
|
-
https://github.com/serradura/u-case/
|
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
|
-
|
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
|
|
data/lib/micro/case.rb
CHANGED
@@ -42,14 +42,35 @@ module Micro
|
|
42
42
|
instance
|
43
43
|
end
|
44
44
|
|
45
|
-
def self.
|
46
|
-
|
45
|
+
def self.__get_flow__
|
46
|
+
@__flow
|
47
|
+
end
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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 =
|
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
|
data/lib/micro/case/flow.rb
CHANGED
@@ -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)
|
data/lib/micro/case/safe/flow.rb
CHANGED
data/lib/micro/case/version.rb
CHANGED
@@ -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.
|
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
|
+
date: 2019-12-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: u-attributes
|