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 +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
|