u-case 3.0.0.rc3 → 3.0.0.rc8
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/.travis.sh +8 -4
- data/Gemfile +3 -2
- data/README.md +281 -262
- data/README.pt-BR.md +1420 -0
- data/lib/micro/case.rb +58 -36
- data/lib/micro/case/config.rb +1 -1
- data/lib/micro/case/error.rb +4 -2
- data/lib/micro/case/result.rb +92 -36
- data/lib/micro/case/safe.rb +2 -2
- data/lib/micro/case/utils.rb +7 -0
- data/lib/micro/case/version.rb +1 -1
- data/lib/micro/case/with_activemodel_validation.rb +1 -1
- data/lib/micro/cases/flow.rb +24 -40
- data/lib/micro/cases/safe/flow.rb +2 -2
- data/u-case.gemspec +2 -2
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2265a60386ce542b8ee6006a58d4fb801dcfb396d7dfcf84d342910095fcbf8
|
4
|
+
data.tar.gz: 1502d1a284c616860ae99e0c03fe02a81be3e9d0adc0cc75a414825b4132f792
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab5c45e1e1e12f157a4e5f5fa596fdb778e6bd6267d5111e438094ff37e017a0452e4a1d85a39df0e63ec28876260bad8f7c4a56ca18796a95805187efe2d002
|
7
|
+
data.tar.gz: 6cefcdbbb39a4400bbd4c52b7ccf77f2c142958680049a3e53f4af37d1ef696fdc3a9019cde0f8f18463dd814e79e9753ef5a270eb446dbb2bee81e7c734ffab
|
data/.travis.sh
CHANGED
@@ -3,17 +3,21 @@
|
|
3
3
|
ruby_v=$(ruby -v)
|
4
4
|
|
5
5
|
ACTIVEMODEL_VERSION='3.2' bundle update
|
6
|
-
ACTIVEMODEL_VERSION='3.2' bundle exec rake test
|
6
|
+
ACTIVEMODEL_VERSION='3.2' ENABLE_TRANSITIONS='true' bundle exec rake test
|
7
|
+
ACTIVEMODEL_VERSION='3.2' ENABLE_TRANSITIONS='false' bundle exec rake test
|
7
8
|
|
8
9
|
if [[ ! $ruby_v =~ '2.2.0' ]]; then
|
9
10
|
ACTIVEMODEL_VERSION='5.2' bundle update
|
10
|
-
ACTIVEMODEL_VERSION='5.2' bundle exec rake test
|
11
|
+
ACTIVEMODEL_VERSION='5.2' ENABLE_TRANSITIONS='true' bundle exec rake test
|
12
|
+
ACTIVEMODEL_VERSION='5.2' ENABLE_TRANSITIONS='false' bundle exec rake test
|
11
13
|
fi
|
12
14
|
|
13
15
|
if [[ $ruby_v =~ '2.5.' ]] || [[ $ruby_v =~ '2.6.' ]] || [[ $ruby_v =~ '2.7.' ]]; then
|
14
16
|
ACTIVEMODEL_VERSION='6.0' bundle update
|
15
|
-
ACTIVEMODEL_VERSION='6.0' bundle exec rake test
|
17
|
+
ACTIVEMODEL_VERSION='6.0' ENABLE_TRANSITIONS='true' bundle exec rake test
|
18
|
+
ACTIVEMODEL_VERSION='6.0' ENABLE_TRANSITIONS='false' bundle exec rake test
|
16
19
|
fi
|
17
20
|
|
18
21
|
bundle update
|
19
|
-
bundle exec rake test
|
22
|
+
ENABLE_TRANSITIONS='true' bundle exec rake test
|
23
|
+
ENABLE_TRANSITIONS='false' bundle exec rake test
|
data/Gemfile
CHANGED
@@ -22,8 +22,7 @@ end
|
|
22
22
|
|
23
23
|
pry_byebug_version =
|
24
24
|
case RUBY_VERSION
|
25
|
-
when /\A2.
|
26
|
-
when /\A2.3/ then '3.7'
|
25
|
+
when /\A2.[23]/ then '3.6'
|
27
26
|
else '3.9'
|
28
27
|
end
|
29
28
|
|
@@ -37,6 +36,8 @@ pry_version =
|
|
37
36
|
group :development, :test do
|
38
37
|
gem 'awesome_print', '~> 1.8'
|
39
38
|
|
39
|
+
gem 'byebug', '~> 10.0', '>= 10.0.2' if RUBY_VERSION =~ /\A2.[23]/
|
40
|
+
|
40
41
|
gem 'pry', "~> #{pry_version}"
|
41
42
|
gem 'pry-byebug', "~> #{pry_byebug_version}"
|
42
43
|
end
|
data/README.md
CHANGED
@@ -1,33 +1,34 @@
|
|
1
1
|

|
2
2
|
[](https://rubygems.org/gems/u-case)
|
3
|
-
[](https://travis-ci.com/serradura/u-case)
|
4
4
|
[](https://codeclimate.com/github/serradura/u-case/maintainability)
|
5
5
|
[](https://codeclimate.com/github/serradura/u-case/test_coverage)
|
6
6
|
|
7
|
-
|
8
|
-
====================
|
7
|
+
<img src="./assets/ucase_logo_v1.png" alt="u-case - Create simple and powerful use cases as Ruby objects.">
|
9
8
|
|
10
|
-
Create simple and powerful use cases as objects.
|
9
|
+
Create simple and powerful use cases as Ruby objects.
|
11
10
|
|
12
11
|
The main project goals are:
|
13
12
|
1. Easy to use and easy to learn (input **>>** process **>>** output).
|
14
|
-
2. Promote
|
15
|
-
3. No callbacks (
|
16
|
-
4. Solve complex business logic, by allowing the composition of use cases.
|
13
|
+
2. Promote immutability (transforming data instead of modifying it) and data integrity.
|
14
|
+
3. No callbacks (ex: before, after, around) to avoid code indirections that could compromise the state and understanding of application flows.
|
15
|
+
4. Solve complex business logic, by allowing the composition of use cases (flow creation).
|
17
16
|
5. Be fast and optimized (Check out the [benchmarks](#benchmarks) section).
|
18
17
|
|
19
|
-
> Note
|
18
|
+
> **Note:** Check out the repo https://github.com/serradura/from-fat-controllers-to-use-cases to see a Rails application that uses this gem to handle its business logic.
|
20
19
|
|
21
20
|
## Documentation <!-- omit in toc -->
|
22
21
|
|
23
22
|
Version | Documentation
|
24
23
|
--------- | -------------
|
25
|
-
3.0.0.
|
24
|
+
3.0.0.rc8 | https://github.com/serradura/u-case/blob/main/README.md
|
26
25
|
2.6.0 | https://github.com/serradura/u-case/blob/v2.x/README.md
|
27
26
|
1.1.0 | https://github.com/serradura/u-case/blob/v1.x/README.md
|
28
27
|
|
28
|
+
> **Note:** Você entende português? 🇧🇷🇵🇹 Verifique o [README traduzido em pt-BR](https://github.com/serradura/u-case/blob/main/README.pt-BR.md).
|
29
|
+
|
29
30
|
## Table of Contents <!-- omit in toc -->
|
30
|
-
- [
|
31
|
+
- [Compatibility](#compatibility)
|
31
32
|
- [Dependencies](#dependencies)
|
32
33
|
- [Installation](#installation)
|
33
34
|
- [Usage](#usage)
|
@@ -35,48 +36,54 @@ Version | Documentation
|
|
35
36
|
- [`Micro::Case::Result` - What is a use case result?](#microcaseresult---what-is-a-use-case-result)
|
36
37
|
- [What are the default result types?](#what-are-the-default-result-types)
|
37
38
|
- [How to define custom result types?](#how-to-define-custom-result-types)
|
38
|
-
- [Is it possible to define a custom
|
39
|
+
- [Is it possible to define a custom type without a result data?](#is-it-possible-to-define-a-custom-type-without-a-result-data)
|
39
40
|
- [How to use the result hooks?](#how-to-use-the-result-hooks)
|
40
|
-
- [Why the
|
41
|
+
- [Why the hook usage without a defined type exposes the result itself?](#why-the-hook-usage-without-a-defined-type-exposes-the-result-itself)
|
42
|
+
- [Using decomposition to access the result data and type](#using-decomposition-to-access-the-result-data-and-type)
|
41
43
|
- [What happens if a result hook was declared multiple times?](#what-happens-if-a-result-hook-was-declared-multiple-times)
|
42
44
|
- [How to use the `Micro::Case::Result#then` method?](#how-to-use-the-microcaseresultthen-method)
|
43
45
|
- [What does happens when a `Micro::Case::Result#then` receives a block?](#what-does-happens-when-a-microcaseresultthen-receives-a-block)
|
44
46
|
- [How to make attributes data injection using this feature?](#how-to-make-attributes-data-injection-using-this-feature)
|
45
47
|
- [`Micro::Cases::Flow` - How to compose use cases?](#microcasesflow---how-to-compose-use-cases)
|
46
|
-
- [Is it possible to compose a
|
48
|
+
- [Is it possible to compose a flow with other flows?](#is-it-possible-to-compose-a-flow-with-other-flows)
|
47
49
|
- [Is it possible a flow accumulates its input and merges each success result to use as the argument of the next use cases?](#is-it-possible-a-flow-accumulates-its-input-and-merges-each-success-result-to-use-as-the-argument-of-the-next-use-cases)
|
48
50
|
- [How to understand what is happening during a flow execution?](#how-to-understand-what-is-happening-during-a-flow-execution)
|
49
51
|
- [`Micro::Case::Result#transitions` schema](#microcaseresulttransitions-schema)
|
50
52
|
- [Is it possible disable the `Micro::Case::Result#transitions`?](#is-it-possible-disable-the-microcaseresulttransitions)
|
51
|
-
- [Is it possible to declare a flow
|
53
|
+
- [Is it possible to declare a flow that includes the use case itself as a step?](#is-it-possible-to-declare-a-flow-that-includes-the-use-case-itself-as-a-step)
|
52
54
|
- [`Micro::Case::Strict` - What is a strict use case?](#microcasestrict---what-is-a-strict-use-case)
|
53
55
|
- [`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)
|
54
56
|
- [`Micro::Cases::Safe::Flow`](#microcasessafeflow)
|
55
57
|
- [`Micro::Case::Result#on_exception`](#microcaseresulton_exception)
|
56
|
-
- [`u-case/with_activemodel_validation` - How to validate use case attributes?](#u-casewith_activemodel_validation---how-to-validate-use-case-attributes)
|
57
|
-
- [If I enabled the auto validation, is it possible to disable it only in specific use
|
58
|
+
- [`u-case/with_activemodel_validation` - How to validate the use case attributes?](#u-casewith_activemodel_validation---how-to-validate-the-use-case-attributes)
|
59
|
+
- [If I enabled the auto validation, is it possible to disable it only in specific use cases?](#if-i-enabled-the-auto-validation-is-it-possible-to-disable-it-only-in-specific-use-cases)
|
58
60
|
- [`Kind::Validator`](#kindvalidator)
|
59
61
|
- [`Micro::Case.config`](#microcaseconfig)
|
60
62
|
- [Benchmarks](#benchmarks)
|
61
|
-
- [`Micro::Case` (
|
62
|
-
- [Best overall](#best-overall)
|
63
|
+
- [`Micro::Case` (v3.0.0)](#microcase-v300)
|
63
64
|
- [Success results](#success-results)
|
64
65
|
- [Failure results](#failure-results)
|
65
|
-
- [`Micro::
|
66
|
+
- [`Micro::Cases::Flow` (v3.0.0)](#microcasesflow-v300)
|
66
67
|
- [Comparisons](#comparisons)
|
67
68
|
- [Examples](#examples)
|
68
|
-
- [1️⃣
|
69
|
-
- [2️⃣
|
70
|
-
- [3️⃣
|
71
|
-
- [4️⃣ Rescuing
|
69
|
+
- [1️⃣ Users creation](#1️⃣-users-creation)
|
70
|
+
- [2️⃣ Rails App (API)](#2️⃣-rails-app-api)
|
71
|
+
- [3️⃣ CLI calculator](#3️⃣-cli-calculator)
|
72
|
+
- [4️⃣ Rescuing exceptions inside of the use cases](#4️⃣-rescuing-exceptions-inside-of-the-use-cases)
|
72
73
|
- [Development](#development)
|
73
74
|
- [Contributing](#contributing)
|
74
75
|
- [License](#license)
|
75
76
|
- [Code of Conduct](#code-of-conduct)
|
76
77
|
|
77
|
-
##
|
78
|
+
## Compatibility
|
79
|
+
|
80
|
+
| u-case | branch | ruby | activemodel |
|
81
|
+
| -------------- | ------- | -------- | ------------- |
|
82
|
+
| 3.0.0.rc8 | main | >= 2.2.0 | >= 3.2, < 6.1 |
|
83
|
+
| 2.6.0 | v2.x | >= 2.2.0 | >= 3.2, < 6.1 |
|
84
|
+
| 1.1.0 | v1.x | >= 2.2.0 | >= 3.2, < 6.1 |
|
78
85
|
|
79
|
-
>
|
86
|
+
> Note: The activemodel is an optional dependency, this module [can be enabled](#u-casewith_activemodel_validation---how-to-validate-use-case-attributes) to validate the use cases' attributes.
|
80
87
|
|
81
88
|
## Dependencies
|
82
89
|
|
@@ -84,7 +91,7 @@ Version | Documentation
|
|
84
91
|
|
85
92
|
A simple type system (at runtime) for Ruby.
|
86
93
|
|
87
|
-
|
94
|
+
It is used to validate some internal u-case's methods input. This gem also exposes an [`ActiveModel validator`](https://github.com/serradura/kind#kindvalidator-activemodelvalidations) when requiring the [`u-case/with_activemodel_validation`](#u-casewith_activemodel_validation---how-to-validate-use-case-attributes) module, or when the [`Micro::Case.config`](#microcaseconfig) was used to enable it. Lastly, two type checkers are available through it: [`Kind::Of::Micro::Case`, `Kind::Of::Micro::Case::Result`](https://github.com/serradura/kind#registering-new-custom-type-checker).
|
88
95
|
2. [`u-attributes`](https://github.com/serradura/u-attributes) gem.
|
89
96
|
|
90
97
|
This gem allows defining read-only attributes, that is, your objects will have only getters to access their attributes data.
|
@@ -95,7 +102,7 @@ Version | Documentation
|
|
95
102
|
Add this line to your application's Gemfile:
|
96
103
|
|
97
104
|
```ruby
|
98
|
-
gem 'u-case'
|
105
|
+
gem 'u-case', '~> 3.0.0.rc8'
|
99
106
|
```
|
100
107
|
|
101
108
|
And then execute:
|
@@ -104,7 +111,7 @@ And then execute:
|
|
104
111
|
|
105
112
|
Or install it yourself as:
|
106
113
|
|
107
|
-
$ gem install u-case
|
114
|
+
$ gem install u-case --pre
|
108
115
|
|
109
116
|
## Usage
|
110
117
|
|
@@ -118,7 +125,7 @@ class Multiply < Micro::Case
|
|
118
125
|
# 2. Define the method `call!` with its business logic
|
119
126
|
def call!
|
120
127
|
|
121
|
-
# 3. Wrap the use case
|
128
|
+
# 3. Wrap the use case output using the `Success(result: *)` or `Failure(result: *)` methods
|
122
129
|
if a.is_a?(Numeric) && b.is_a?(Numeric)
|
123
130
|
Success result: { number: a * b }
|
124
131
|
else
|
@@ -127,9 +134,9 @@ class Multiply < Micro::Case
|
|
127
134
|
end
|
128
135
|
end
|
129
136
|
|
130
|
-
|
131
|
-
#
|
132
|
-
|
137
|
+
#========================#
|
138
|
+
# Performing an use case #
|
139
|
+
#========================#
|
133
140
|
|
134
141
|
# Success result
|
135
142
|
|
@@ -145,18 +152,9 @@ bad_result = Multiply.call(a: 2, b: '2')
|
|
145
152
|
bad_result.failure? # true
|
146
153
|
bad_result.data # { message: "`a` and `b` attributes must be numeric" }
|
147
154
|
|
148
|
-
#-----------------------------#
|
149
|
-
# Calling a use case instance #
|
150
|
-
#-----------------------------#
|
151
|
-
|
152
|
-
result = Multiply.new(a: 2, b: 3).call
|
153
|
-
|
154
|
-
result.value # { number: 6 }
|
155
|
-
|
156
155
|
# Note:
|
157
156
|
# ----
|
158
|
-
# The result of a Micro::Case.call
|
159
|
-
# is an instance of Micro::Case::Result
|
157
|
+
# The result of a Micro::Case.call is an instance of Micro::Case::Result
|
160
158
|
```
|
161
159
|
|
162
160
|
[⬆️ Back to Top](#table-of-contents-)
|
@@ -170,6 +168,9 @@ A `Micro::Case::Result` stores the use cases output data. These are their main m
|
|
170
168
|
- `#type` a Symbol which gives meaning for the result, this is useful to declare different types of failures or success.
|
171
169
|
- `#data` the result data itself.
|
172
170
|
- `#[]` and `#values_at` are shortcuts to access the `#data` values.
|
171
|
+
- `#key?` returns `true` if the key is present in `#data`.
|
172
|
+
- `#value?` returns `true` if the given value is present in `#data`.
|
173
|
+
- `#slice` returns a new hash that includes only the given keys. If the given keys don't exist, an empty hash is returned.
|
173
174
|
- `#on_success` or `#on_failure` are hook methods that help you to define the application flow.
|
174
175
|
- `#then` this method will allow applying a new use case if the current result was a success. The idea of this feature is to allow the creation of dynamic flows.
|
175
176
|
- `#transitions` returns an array with all of transformations wich a result [has during a flow](#how-to-understand-what-is-happening-during-a-flow-execution).
|
@@ -180,9 +181,9 @@ A `Micro::Case::Result` stores the use cases output data. These are their main m
|
|
180
181
|
|
181
182
|
#### What are the default result types?
|
182
183
|
|
183
|
-
Every result has a type and these are
|
184
|
+
Every result has a type, and these are their default values:
|
184
185
|
- `:ok` when success
|
185
|
-
- `:error
|
186
|
+
- `:error` or `:exception` when failures
|
186
187
|
|
187
188
|
```ruby
|
188
189
|
class Divide < Micro::Case
|
@@ -276,9 +277,9 @@ bad_result.failure? # true
|
|
276
277
|
|
277
278
|
[⬆️ Back to Top](#table-of-contents-)
|
278
279
|
|
279
|
-
#### Is it possible to define a custom
|
280
|
+
#### Is it possible to define a custom type without a result data?
|
280
281
|
|
281
|
-
Answer: Yes, it is possible. But this will have special behavior because the result data will be a hash with the given type as the key and true as its value.
|
282
|
+
Answer: Yes, it is possible. But this will have special behavior because the result data will be a hash with the given type as the key and `true` as its value.
|
282
283
|
|
283
284
|
```ruby
|
284
285
|
class Multiply < Micro::Case
|
@@ -310,7 +311,7 @@ result.use_case.attributes # {"a"=>2, "b"=>"2"}
|
|
310
311
|
|
311
312
|
#### How to use the result hooks?
|
312
313
|
|
313
|
-
As mentioned earlier, the `Micro::Case::Result` has two methods to improve the flow control. They are: `#on_success`, `on_failure`.
|
314
|
+
As [mentioned earlier](#microcaseresult---what-is-a-use-case-result), the `Micro::Case::Result` has two methods to improve the application flow control. They are: `#on_success`, `on_failure`.
|
314
315
|
|
315
316
|
The examples below show how to use them:
|
316
317
|
|
@@ -336,7 +337,7 @@ Double
|
|
336
337
|
.on_failure(:invalid) { |result| raise TypeError, result[:msg] }
|
337
338
|
.on_failure(:lte_zero) { |result| raise ArgumentError, result[:msg] }
|
338
339
|
|
339
|
-
# The output
|
340
|
+
# The output will be:
|
340
341
|
# 6
|
341
342
|
|
342
343
|
#=============================#
|
@@ -352,18 +353,17 @@ Double
|
|
352
353
|
|
353
354
|
# The outputs will be:
|
354
355
|
#
|
355
|
-
# 1.
|
356
|
-
# 2.
|
356
|
+
# 1. It will print the message: Double was the use case responsible for the failure
|
357
|
+
# 2. It will raise the exception: ArgumentError (the number must be greater than 0)
|
357
358
|
|
358
359
|
# Note:
|
359
360
|
# ----
|
360
|
-
# The use case responsible for the
|
361
|
+
# The use case responsible for the result will always be accessible as the second hook argument
|
361
362
|
```
|
362
363
|
|
363
|
-
#### Why the
|
364
|
+
#### Why the hook usage without a defined type exposes the result itself?
|
364
365
|
|
365
|
-
Answer: To allow you to define how to handle the program flow using some
|
366
|
-
conditional statement (like an `if`, `case/when`).
|
366
|
+
Answer: To allow you to define how to handle the program flow using some conditional statement like an `if` or `case when`.
|
367
367
|
|
368
368
|
```ruby
|
369
369
|
class Double < Micro::Case
|
@@ -377,12 +377,8 @@ class Double < Micro::Case
|
|
377
377
|
end
|
378
378
|
end
|
379
379
|
|
380
|
-
#=================================#
|
381
|
-
# Using the result type and value #
|
382
|
-
#=================================#
|
383
|
-
|
384
380
|
Double
|
385
|
-
.call(-1)
|
381
|
+
.call(number: -1)
|
386
382
|
.on_failure do |result, use_case|
|
387
383
|
case result.type
|
388
384
|
when :invalid then raise TypeError, "number must be a numeric value"
|
@@ -391,22 +387,23 @@ Double
|
|
391
387
|
end
|
392
388
|
end
|
393
389
|
|
394
|
-
# The output will be
|
390
|
+
# The output will be an exception:
|
395
391
|
#
|
396
392
|
# ArgumentError (number `-1` must be greater than 0)
|
393
|
+
```
|
397
394
|
|
398
|
-
|
399
|
-
# Using decomposition to access the result data and type #
|
400
|
-
#=========================================================#
|
395
|
+
> **Note:** The same that was did in the previous examples could be done with `#on_success` hook!
|
401
396
|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
#
|
406
|
-
|
397
|
+
##### Using decomposition to access the result data and type
|
398
|
+
|
399
|
+
The syntax to decompose an Array can be used in assignments and in method/block arguments.
|
400
|
+
If you doesn't know it, check out the [Ruby doc](https://ruby-doc.org/core-2.2.0/doc/syntax/assignment_rdoc.html#label-Array+Decomposition).
|
401
|
+
|
402
|
+
```ruby
|
403
|
+
# The object exposed in the hook without a type is a Micro::Case::Result and it can be decomposed. e.g:
|
407
404
|
|
408
405
|
Double
|
409
|
-
.call(-2)
|
406
|
+
.call(number: -2)
|
410
407
|
.on_failure do |(data, type), use_case|
|
411
408
|
case type
|
412
409
|
when :invalid then raise TypeError, 'number must be a numeric value'
|
@@ -420,6 +417,8 @@ Double
|
|
420
417
|
# ArgumentError (the number `-2` must be greater than 0)
|
421
418
|
```
|
422
419
|
|
420
|
+
> **Note:** The same that was did in the previous examples could be done with `#on_success` hook!
|
421
|
+
|
423
422
|
[⬆️ Back to Top](#table-of-contents-)
|
424
423
|
|
425
424
|
#### What happens if a result hook was declared multiple times?
|
@@ -445,10 +444,11 @@ result[:number] * 4 # 24
|
|
445
444
|
|
446
445
|
accum = 0
|
447
446
|
|
448
|
-
result
|
449
|
-
|
450
|
-
|
451
|
-
|
447
|
+
result
|
448
|
+
.on_success { |result| accum += result[:number] }
|
449
|
+
.on_success { |result| accum += result[:number] }
|
450
|
+
.on_success(:computed) { |result| accum += result[:number] }
|
451
|
+
.on_success(:computed) { |result| accum += result[:number] }
|
452
452
|
|
453
453
|
accum # 24
|
454
454
|
|
@@ -457,8 +457,7 @@ result[:number] * 4 == accum # true
|
|
457
457
|
|
458
458
|
#### How to use the `Micro::Case::Result#then` method?
|
459
459
|
|
460
|
-
This method allows you to create dynamic flows, so, with it,
|
461
|
-
you can add new use cases or flows to continue the result transformation. e.g:
|
460
|
+
This method allows you to create dynamic flows, so, with it, you can add new use cases or flows to continue the result transformation. e.g:
|
462
461
|
|
463
462
|
```ruby
|
464
463
|
class ForbidNegativeNumber < Micro::Case
|
@@ -504,7 +503,7 @@ result2.success? # true
|
|
504
503
|
|
505
504
|
##### What does happens when a `Micro::Case::Result#then` receives a block?
|
506
505
|
|
507
|
-
It will yields self (a `Micro::Case::Result instance
|
506
|
+
It will yields self (a `Micro::Case::Result` instance) to the block, and will return the output of the block instead of itself. e.g:
|
508
507
|
|
509
508
|
```ruby
|
510
509
|
class Add < Micro::Case
|
@@ -556,8 +555,7 @@ Todo::FindAllForUser
|
|
556
555
|
|
557
556
|
### `Micro::Cases::Flow` - How to compose use cases?
|
558
557
|
|
559
|
-
|
560
|
-
The main idea of this feature is to use/reuse use cases as steps of a new use case.
|
558
|
+
We call as **flow** a composition of use cases. The main idea of this feature is to use/reuse use cases as steps of a new use case. e.g.
|
561
559
|
|
562
560
|
```ruby
|
563
561
|
module Steps
|
@@ -612,24 +610,23 @@ result = Add2ToAllNumbers.call(numbers: %w[1 1 2 2 3 4])
|
|
612
610
|
result.success? # true
|
613
611
|
result.data # {:numbers => [3, 3, 4, 4, 5, 6]}
|
614
612
|
|
615
|
-
|
616
|
-
#
|
617
|
-
|
613
|
+
#-------------------------------#
|
614
|
+
# Creating a flow using classes #
|
615
|
+
#-------------------------------#
|
618
616
|
|
619
617
|
class DoubleAllNumbers < Micro::Case
|
620
618
|
flow Steps::ConvertTextToNumbers,
|
621
619
|
Steps::Double
|
622
620
|
end
|
623
621
|
|
624
|
-
DoubleAllNumbers
|
625
|
-
|
626
|
-
|
622
|
+
DoubleAllNumbers.
|
623
|
+
call(numbers: %w[1 1 b 2 3 4]).
|
624
|
+
on_failure { |result| puts result[:message] } # "numbers must contain only numeric types"
|
625
|
+
```
|
627
626
|
|
628
|
-
|
629
|
-
# ----
|
630
|
-
# When happening a failure, the use case responsible
|
631
|
-
# will be accessible in the result
|
627
|
+
When happening a failure, the use case responsible will be accessible in the result.
|
632
628
|
|
629
|
+
```ruby
|
633
630
|
result = DoubleAllNumbers.call(numbers: %w[1 1 b 2 3 4])
|
634
631
|
|
635
632
|
result.failure? # true
|
@@ -642,7 +639,7 @@ end
|
|
642
639
|
|
643
640
|
[⬆️ Back to Top](#table-of-contents-)
|
644
641
|
|
645
|
-
#### Is it possible to compose a
|
642
|
+
#### Is it possible to compose a flow with other flows?
|
646
643
|
|
647
644
|
Answer: Yes, it is possible.
|
648
645
|
|
@@ -705,20 +702,20 @@ DoubleAllNumbersAndSquareAndAdd2 =
|
|
705
702
|
|
706
703
|
SquareAllNumbersAndDouble
|
707
704
|
.call(numbers: %w[1 1 2 2 3 4])
|
708
|
-
.on_success { |
|
705
|
+
.on_success { |result| p result[:numbers] } # [6, 6, 12, 12, 22, 36]
|
709
706
|
|
710
707
|
DoubleAllNumbersAndSquareAndAdd2
|
711
708
|
.call(numbers: %w[1 1 2 2 3 4])
|
712
|
-
.on_success { |
|
709
|
+
.on_success { |result| p result[:numbers] } # [6, 6, 18, 18, 38, 66]
|
713
710
|
```
|
714
711
|
|
715
|
-
Note
|
712
|
+
> **Note:** You can blend any [approach](#microcasesflow---how-to-compose-use-cases) to create use case flows - [examples](https://github.com/serradura/u-case/blob/714c6b658fc6aa02617e6833ddee09eddc760f2a/test/micro/cases/flow/blend_test.rb#L5-L35).
|
716
713
|
|
717
714
|
[⬆️ Back to Top](#table-of-contents-)
|
718
715
|
|
719
716
|
#### Is it possible a flow accumulates its input and merges each success result to use as the argument of the next use cases?
|
720
717
|
|
721
|
-
Answer: Yes, it is possible! Look at the example below to understand how the data accumulation works inside of
|
718
|
+
Answer: Yes, it is possible! Look at the example below to understand how the data accumulation works inside of a flow execution.
|
722
719
|
|
723
720
|
```ruby
|
724
721
|
module Users
|
@@ -762,7 +759,7 @@ Users::Authenticate
|
|
762
759
|
.on_failure(:user_not_found) { render status: 404 }
|
763
760
|
```
|
764
761
|
|
765
|
-
First,
|
762
|
+
First, let's see the attributes used by each use case:
|
766
763
|
|
767
764
|
```ruby
|
768
765
|
class Users::FindByEmail < Micro::Case
|
@@ -775,14 +772,13 @@ end
|
|
775
772
|
```
|
776
773
|
|
777
774
|
As you can see the `Users::ValidatePassword` expects a user as its input. So, how does it receives the user?
|
778
|
-
It receives the user from the `Users::FindByEmail` success result!
|
775
|
+
Answer: It receives the user from the `Users::FindByEmail` success result!
|
779
776
|
|
780
|
-
And this
|
781
|
-
of one step will compose the input of the next use case in the flow!
|
777
|
+
And this is the power of use cases composition because the output of one step will compose the input of the next use case in the flow!
|
782
778
|
|
783
779
|
> input **>>** process **>>** output
|
784
780
|
|
785
|
-
> **Note:** Check out these test examples [Micro::Cases::Flow](https://github.com/serradura/u-case/blob/c96a3650469da40dc9f83ff678204055b7015d01/test/micro/cases/flow/result_transitions_test.rb) and [Micro::Cases::Safe::Flow](https://github.com/serradura/u-case/blob/c96a3650469da40dc9f83ff678204055b7015d01/test/micro/cases/safe/flow/result_transitions_test.rb) to see different use cases
|
781
|
+
> **Note:** Check out these test examples [Micro::Cases::Flow](https://github.com/serradura/u-case/blob/c96a3650469da40dc9f83ff678204055b7015d01/test/micro/cases/flow/result_transitions_test.rb) and [Micro::Cases::Safe::Flow](https://github.com/serradura/u-case/blob/c96a3650469da40dc9f83ff678204055b7015d01/test/micro/cases/safe/flow/result_transitions_test.rb) to see different use cases having access to the data in a flow.
|
786
782
|
|
787
783
|
[⬆️ Back to Top](#table-of-contents-)
|
788
784
|
|
@@ -831,7 +827,7 @@ user_authenticated.transitions
|
|
831
827
|
```
|
832
828
|
|
833
829
|
The example above shows the output generated by the `Micro::Case::Result#transitions`.
|
834
|
-
With it is possible to analyze the use cases execution order and what were the given `inputs` (`[:attributes]`) and `outputs` (`[:success][:result]`) in the entire execution.
|
830
|
+
With it is possible to analyze the use cases' execution order and what were the given `inputs` (`[:attributes]`) and `outputs` (`[:success][:result]`) in the entire execution.
|
835
831
|
|
836
832
|
And look up the `accessible_attributes` property, it shows whats attributes are accessible in that flow step. For example, in the last step, you can see that the `accessible_attributes` increased because of the [data flow accumulation](#is-it-possible-a-flow-accumulates-its-input-and-merges-each-success-result-to-use-as-the-argument-of-the-next-use-cases).
|
837
833
|
|
@@ -848,11 +844,11 @@ And look up the `accessible_attributes` property, it shows whats attributes are
|
|
848
844
|
[success:, failure:] => { # (Output)
|
849
845
|
type: <Symbol>, # Result type. Defaults:
|
850
846
|
# Success = :ok, Failure = :error/:exception
|
851
|
-
result: <Hash> # The data returned by the use case
|
847
|
+
result: <Hash> # The data returned by the use case result
|
852
848
|
},
|
853
849
|
accessible_attributes: <Array>, # Properties that can be accessed by the use case's attributes,
|
854
|
-
#
|
855
|
-
# with
|
850
|
+
# it starts with Hash used to invoke it and that will be incremented
|
851
|
+
# with the result values of each use case in the flow.
|
856
852
|
}
|
857
853
|
]
|
858
854
|
```
|
@@ -861,9 +857,9 @@ And look up the `accessible_attributes` property, it shows whats attributes are
|
|
861
857
|
|
862
858
|
Answer: Yes, it is! You can use the `Micro::Case.config` to do this. [Link to](#microcaseconfig) this section.
|
863
859
|
|
864
|
-
#### Is it possible to declare a flow
|
860
|
+
#### Is it possible to declare a flow that includes the use case itself as a step?
|
865
861
|
|
866
|
-
Answer: Yes, it is! You can use the `self.call!` macro. e.g:
|
862
|
+
Answer: Yes, it is! You can use `self` or the `self.call!` macro. e.g:
|
867
863
|
|
868
864
|
```ruby
|
869
865
|
class ConvertTextToNumber < Micro::Case
|
@@ -898,16 +894,15 @@ result = Double.call(text: '4')
|
|
898
894
|
|
899
895
|
result.success? # true
|
900
896
|
result[:number] # "8"
|
901
|
-
|
902
|
-
# NOTE: This feature can be used with the Micro::Case::Safe.
|
903
|
-
# Checkout this test: https://github.com/serradura/u-case/blob/714c6b658fc6aa02617e6833ddee09eddc760f2a/test/micro/case/safe/with_inner_flow_test.rb
|
904
897
|
```
|
905
898
|
|
899
|
+
> **Note:** This feature can be used with the Micro::Case::Safe. Checkout this test to see an example: https://github.com/serradura/u-case/blob/714c6b658fc6aa02617e6833ddee09eddc760f2a/test/micro/case/safe/with_inner_flow_test.rb
|
900
|
+
|
906
901
|
[⬆️ Back to Top](#table-of-contents-)
|
907
902
|
|
908
903
|
### `Micro::Case::Strict` - What is a strict use case?
|
909
904
|
|
910
|
-
Answer:
|
905
|
+
Answer: it is a kind of use case that will require all the keywords (attributes) on its initialization.
|
911
906
|
|
912
907
|
```ruby
|
913
908
|
class Double < Micro::Case::Strict
|
@@ -920,7 +915,7 @@ end
|
|
920
915
|
|
921
916
|
Double.call({})
|
922
917
|
|
923
|
-
# The output will be
|
918
|
+
# The output will be:
|
924
919
|
# ArgumentError (missing keyword: :numbers)
|
925
920
|
```
|
926
921
|
|
@@ -928,11 +923,7 @@ Double.call({})
|
|
928
923
|
|
929
924
|
### `Micro::Case::Safe` - Is there some feature to auto handle exceptions inside of a use case or flow?
|
930
925
|
|
931
|
-
|
932
|
-
|
933
|
-
**Use cases:**
|
934
|
-
|
935
|
-
Like `Micro::Case::Strict` the `Micro::Case::Safe` is another kind of use case. It has the ability to auto intercept any exception as a failure result. e.g:
|
926
|
+
Yes, there is one! Like `Micro::Case::Strict` the `Micro::Case::Safe` is another kind of use case. It has the ability to auto intercept any exception as a failure result. e.g:
|
936
927
|
|
937
928
|
```ruby
|
938
929
|
require 'logger'
|
@@ -956,28 +947,24 @@ result.type == :exception # true
|
|
956
947
|
result.data # { exception: #<ZeroDivisionError...> }
|
957
948
|
result[:exception].is_a?(ZeroDivisionError) # true
|
958
949
|
|
959
|
-
result.on_failure(:exception) do |
|
960
|
-
AppLogger.error(exception.message) # E, [2019-08-21T00:05:44.195506 #9532] ERROR -- : divided by 0
|
950
|
+
result.on_failure(:exception) do |result|
|
951
|
+
AppLogger.error(result[:exception].message) # E, [2019-08-21T00:05:44.195506 #9532] ERROR -- : divided by 0
|
961
952
|
end
|
953
|
+
```
|
962
954
|
|
963
|
-
|
964
|
-
# ----
|
965
|
-
# If you need to handle a specific error,
|
966
|
-
# I recommend the usage of a case statement. e,g:
|
955
|
+
If you need to handle a specific error, I recommend the usage of a case statement. e,g:
|
967
956
|
|
968
|
-
|
969
|
-
|
957
|
+
```ruby
|
958
|
+
result.on_failure(:exception) do |data, use_case|
|
959
|
+
case exception = data[:exception]
|
970
960
|
when ZeroDivisionError then AppLogger.error(exception.message)
|
971
961
|
else AppLogger.debug("#{use_case.class.name} was the use case responsible for the exception")
|
972
962
|
end
|
973
963
|
end
|
974
|
-
|
975
|
-
# Another note:
|
976
|
-
# ------------
|
977
|
-
# It is possible to rescue an exception even when is a safe use case.
|
978
|
-
# Examples: https://github.com/serradura/u-case/blob/714c6b658fc6aa02617e6833ddee09eddc760f2a/test/micro/case/safe_test.rb#L90-L118
|
979
964
|
```
|
980
965
|
|
966
|
+
> **Note:** It is possible to rescue an exception even when is a safe use case. Examples: https://github.com/serradura/u-case/blob/714c6b658fc6aa02617e6833ddee09eddc760f2a/test/micro/case/safe_test.rb#L90-L118
|
967
|
+
|
981
968
|
[⬆️ Back to Top](#table-of-contents-)
|
982
969
|
|
983
970
|
#### `Micro::Cases::Safe::Flow`
|
@@ -993,9 +980,11 @@ module Users
|
|
993
980
|
SendToCRM
|
994
981
|
])
|
995
982
|
end
|
983
|
+
```
|
996
984
|
|
997
|
-
|
985
|
+
Defining within classes:
|
998
986
|
|
987
|
+
```ruby
|
999
988
|
module Users
|
1000
989
|
class Create < Micro::Case::Safe
|
1001
990
|
flow ProcessParams,
|
@@ -1014,9 +1003,9 @@ In functional programming errors/exceptions are handled as regular data, the ide
|
|
1014
1003
|
|
1015
1004
|
To address this the `Micro::Case::Result` has a special hook `#on_exception` to helping you to handle the control flow in the case of exceptions.
|
1016
1005
|
|
1017
|
-
> **Note**: this feature will work better if you use it with a `Micro::Case::Safe` use case
|
1006
|
+
> **Note**: this feature will work better if you use it with a `Micro::Case::Safe` flow or use case.
|
1018
1007
|
|
1019
|
-
How does it work
|
1008
|
+
**How does it work?**
|
1020
1009
|
|
1021
1010
|
```ruby
|
1022
1011
|
class Divide < Micro::Case::Safe
|
@@ -1052,21 +1041,19 @@ Divide.
|
|
1052
1041
|
# Oh no, something went wrong!
|
1053
1042
|
```
|
1054
1043
|
|
1055
|
-
As you can see, this hook has the same behavior of `result.on_failure(:exception)`, but, the
|
1044
|
+
As you can see, this hook has the same behavior of `result.on_failure(:exception)`, but, the idea here is to have a better communication in the code, making an explicit reference when some failure happened because of an exception.
|
1056
1045
|
|
1057
1046
|
[⬆️ Back to Top](#table-of-contents-)
|
1058
1047
|
|
1059
|
-
### `u-case/with_activemodel_validation` - How to validate use case attributes?
|
1048
|
+
### `u-case/with_activemodel_validation` - How to validate the use case attributes?
|
1060
1049
|
|
1061
1050
|
**Requirement:**
|
1062
1051
|
|
1063
1052
|
To do this your application must have the [activemodel >= 3.2, < 6.1.0](https://rubygems.org/gems/activemodel) as a dependency.
|
1064
1053
|
|
1054
|
+
By default, if your application has ActiveModel as a dependency, any kind of use case can make use of it to validate its attributes.
|
1055
|
+
|
1065
1056
|
```ruby
|
1066
|
-
#
|
1067
|
-
# By default, if your application has the activemodel as a dependency,
|
1068
|
-
# any kind of use case can use it to validate their attributes.
|
1069
|
-
#
|
1070
1057
|
class Multiply < Micro::Case
|
1071
1058
|
attributes :a, :b
|
1072
1059
|
|
@@ -1080,9 +1067,9 @@ class Multiply < Micro::Case
|
|
1080
1067
|
end
|
1081
1068
|
```
|
1082
1069
|
|
1083
|
-
But if do you want an automatic way to fail your use cases on validation errors, you
|
1070
|
+
But if do you want an automatic way to fail your use cases on validation errors, you could do:
|
1084
1071
|
|
1085
|
-
1. **require 'u-case/with_activemodel_validation'**
|
1072
|
+
1. **require 'u-case/with_activemodel_validation'** in the Gemfile
|
1086
1073
|
|
1087
1074
|
```ruby
|
1088
1075
|
gem 'u-case', require: 'u-case/with_activemodel_validation'
|
@@ -1104,16 +1091,13 @@ class Multiply < Micro::Case
|
|
1104
1091
|
Success result: { number: a * b }
|
1105
1092
|
end
|
1106
1093
|
end
|
1107
|
-
|
1108
|
-
# Note:
|
1109
|
-
# ----
|
1110
|
-
# After requiring the validation mode, the
|
1111
|
-
# Micro::Case::Strict and Micro::Case::Safe classes will inherit this new behavior.
|
1112
1094
|
```
|
1113
1095
|
|
1114
|
-
|
1096
|
+
> **Note:** After requiring the validation mode, the `Micro::Case::Strict` and `Micro::Case::Safe` classes will inherit this new behavior.
|
1097
|
+
|
1098
|
+
#### If I enabled the auto validation, is it possible to disable it only in specific use cases?
|
1115
1099
|
|
1116
|
-
Answer: Yes, it is possible. To do this, you
|
1100
|
+
Answer: Yes, it is possible. To do this, you will need to use the `disable_auto_validation` macro. e.g:
|
1117
1101
|
|
1118
1102
|
```ruby
|
1119
1103
|
require 'u-case/with_activemodel_validation'
|
@@ -1132,7 +1116,7 @@ end
|
|
1132
1116
|
|
1133
1117
|
Multiply.call(a: 2, b: 'a')
|
1134
1118
|
|
1135
|
-
# The output will be
|
1119
|
+
# The output will be:
|
1136
1120
|
# TypeError (String can't be coerced into Integer)
|
1137
1121
|
```
|
1138
1122
|
|
@@ -1140,9 +1124,9 @@ Multiply.call(a: 2, b: 'a')
|
|
1140
1124
|
|
1141
1125
|
#### `Kind::Validator`
|
1142
1126
|
|
1143
|
-
The [kind gem](https://github.com/serradura/kind) has a module to enable the validation of data type through [`ActiveModel validations`](https://guides.rubyonrails.org/active_model_basics.html#validations). So, when you require the `'u-case/with_activemodel_validation'`, this module will require the [`Kind::Validator`](https://github.com/serradura/kind#kindvalidator-activemodelvalidations).
|
1127
|
+
The [kind gem](https://github.com/serradura/kind) has a module to enable the validation of data type through [`ActiveModel validations`](https://guides.rubyonrails.org/active_model_basics.html#validations). So, when you require the `'u-case/with_activemodel_validation'`, this module will also require the [`Kind::Validator`](https://github.com/serradura/kind#kindvalidator-activemodelvalidations).
|
1144
1128
|
|
1145
|
-
The example below shows how to validate the attributes
|
1129
|
+
The example below shows how to validate the attributes types.
|
1146
1130
|
|
1147
1131
|
```ruby
|
1148
1132
|
class Todo::List::AddItem < Micro::Case
|
@@ -1163,160 +1147,175 @@ class Todo::List::AddItem < Micro::Case
|
|
1163
1147
|
end
|
1164
1148
|
```
|
1165
1149
|
|
1150
|
+
[⬆️ Back to Top](#table-of-contents-)
|
1151
|
+
|
1166
1152
|
## `Micro::Case.config`
|
1167
1153
|
|
1168
|
-
The idea of this
|
1154
|
+
The idea of this resource is to allow the configuration of some `u-case` features/modules.
|
1169
1155
|
I recommend you use it only once in your codebase. e.g. In a Rails initializer.
|
1170
1156
|
|
1171
|
-
You can see below, which are
|
1157
|
+
You can see below, which are the available configurations with their default values:
|
1172
1158
|
|
1173
1159
|
```ruby
|
1174
1160
|
Micro::Case.config do |config|
|
1175
1161
|
# Use ActiveModel to auto-validate your use cases' attributes.
|
1176
|
-
config.
|
1162
|
+
config.enable_activemodel_validation = false
|
1177
1163
|
|
1178
|
-
# Use to enable/disable the `Micro::Case::Results#transitions
|
1164
|
+
# Use to enable/disable the `Micro::Case::Results#transitions`.
|
1179
1165
|
config.enable_transitions = true
|
1180
1166
|
end
|
1181
1167
|
```
|
1182
1168
|
|
1183
|
-
|
1184
|
-
|
1185
|
-
### `Micro::Case` (v2.6.0)
|
1186
|
-
|
1187
|
-
#### Best overall
|
1188
|
-
|
1189
|
-
The table below contains the average between the [Success results](#success-results) and [Failure results](#failure-results) benchmarks.
|
1169
|
+
[⬆️ Back to Top](#table-of-contents-)
|
1190
1170
|
|
1191
|
-
|
1192
|
-
| ---------------------- | --------------------: | ----------------: |
|
1193
|
-
| **Micro::Case** | 105124.3 | _**The Fastest**_ |
|
1194
|
-
| Dry::Monads | 103290.1 | 0.02x slower |
|
1195
|
-
| Interactor | 21342.3 | 4.93x slower |
|
1196
|
-
| Trailblazer::Operation | 14652.7 | 7.17x slower |
|
1197
|
-
| Dry::Transaction | 5310.3 | 19.80x slower |
|
1171
|
+
## Benchmarks
|
1198
1172
|
|
1199
|
-
|
1173
|
+
### `Micro::Case` (v3.0.0)
|
1200
1174
|
|
1201
1175
|
#### Success results
|
1202
1176
|
|
1203
1177
|
| Gem / Abstraction | Iterations per second | Comparison |
|
1204
1178
|
| ----------------- | --------------------: | ----------------: |
|
1205
|
-
| Dry::Monads |
|
1206
|
-
| **Micro::Case** |
|
1207
|
-
| Interactor |
|
1208
|
-
| Trailblazer::Operation |
|
1209
|
-
| Dry::Transaction |
|
1179
|
+
| Dry::Monads | 141730.1 | _**The Fastest**_ |
|
1180
|
+
| **Micro::Case** | 103541.3 | 1.37x slower |
|
1181
|
+
| Interactor | 29100.8 | 4.87x slower |
|
1182
|
+
| Trailblazer::Operation | 15031.4 | 9.43x slower |
|
1183
|
+
| Dry::Transaction | 5674.0 | 24.98x slower |
|
1210
1184
|
|
1211
1185
|
<details>
|
1212
1186
|
<summary>Show the full <a href="https://github.com/evanphx/benchmark-ips">benchmark/ips</a> results.</summary>
|
1213
1187
|
|
1214
1188
|
```ruby
|
1215
1189
|
# Warming up --------------------------------------
|
1216
|
-
# Interactor 2.
|
1217
|
-
# Trailblazer::Operation 1.
|
1218
|
-
# Dry::Monads
|
1219
|
-
# Dry::Transaction
|
1220
|
-
# Micro::Case 10.
|
1221
|
-
# Micro::Case::Strict
|
1222
|
-
# Micro::Case::Safe 10.
|
1190
|
+
# Interactor 2.915k i/100ms
|
1191
|
+
# Trailblazer::Operation 1.543k i/100ms
|
1192
|
+
# Dry::Monads 14.288k i/100ms
|
1193
|
+
# Dry::Transaction 571.000 i/100ms
|
1194
|
+
# Micro::Case 10.418k i/100ms
|
1195
|
+
# Micro::Case::Strict 8.296k i/100ms
|
1196
|
+
# Micro::Case::Safe 10.254k i/100ms
|
1223
1197
|
|
1224
1198
|
# Calculating -------------------------------------
|
1225
|
-
# Interactor 29.
|
1226
|
-
# Trailblazer::Operation
|
1227
|
-
# Dry::Monads
|
1228
|
-
# Dry::Transaction 5.
|
1229
|
-
# Micro::Case
|
1230
|
-
# Micro::Case::Strict
|
1231
|
-
# Micro::Case::Safe
|
1199
|
+
# Interactor 29.101k (± 2.1%) i/s - 145.750k in 5.010660s
|
1200
|
+
# Trailblazer::Operation 15.031k (± 2.0%) i/s - 75.607k in 5.032071s
|
1201
|
+
# Dry::Monads 141.730k (± 3.1%) i/s - 714.400k in 5.045546s
|
1202
|
+
# Dry::Transaction 5.674k (± 1.9%) i/s - 28.550k in 5.033564s
|
1203
|
+
# Micro::Case 103.541k (± 1.6%) i/s - 520.900k in 5.032077s
|
1204
|
+
# Micro::Case::Strict 83.045k (± 2.4%) i/s - 423.096k in 5.098031s
|
1205
|
+
# Micro::Case::Safe 101.662k (± 1.5%) i/s - 512.700k in 5.044386s
|
1232
1206
|
|
1233
1207
|
# Comparison:
|
1234
|
-
# Dry::Monads:
|
1235
|
-
# Micro::Case:
|
1236
|
-
# Micro::Case::Safe:
|
1237
|
-
# Micro::Case::Strict:
|
1238
|
-
# Interactor:
|
1239
|
-
# Trailblazer::Operation:
|
1240
|
-
# Dry::Transaction:
|
1208
|
+
# Dry::Monads: 141730.1 i/s
|
1209
|
+
# Micro::Case: 103541.3 i/s - 1.37x (± 0.00) slower
|
1210
|
+
# Micro::Case::Safe: 101662.2 i/s - 1.39x (± 0.00) slower
|
1211
|
+
# Micro::Case::Strict: 83044.6 i/s - 1.71x (± 0.00) slower
|
1212
|
+
# Interactor: 29100.8 i/s - 4.87x (± 0.00) slower
|
1213
|
+
# Trailblazer::Operation: 15031.4 i/s - 9.43x (± 0.00) slower
|
1214
|
+
# Dry::Transaction: 5674.0 i/s - 24.98x (± 0.00) slower
|
1241
1215
|
```
|
1242
1216
|
</details>
|
1243
1217
|
|
1244
|
-
https://github.com/serradura/u-case/blob/
|
1218
|
+
https://github.com/serradura/u-case/blob/main/benchmarks/use_case/with_success_result.rb
|
1245
1219
|
|
1246
1220
|
#### Failure results
|
1247
1221
|
|
1248
1222
|
| Gem / Abstraction | Iterations per second | Comparison |
|
1249
1223
|
| ----------------- | --------------------: | ----------------: |
|
1250
|
-
| **Micro::Case** |
|
1251
|
-
| Dry::Monads |
|
1252
|
-
| Trailblazer::Operation |
|
1253
|
-
| Interactor |
|
1254
|
-
| Dry::Transaction |
|
1224
|
+
| **Micro::Case** | 98820.8 | _**The Fastest**_ |
|
1225
|
+
| Dry::Monads | 71329.7 | 1.39x slower |
|
1226
|
+
| Trailblazer::Operation | 15034.9 | 6.57x slower |
|
1227
|
+
| Interactor | 13958.7 | 7.08x slower |
|
1228
|
+
| Dry::Transaction | 5067.5 | 19.50x slower |
|
1255
1229
|
|
1256
1230
|
<details>
|
1257
1231
|
<summary>Show the full <a href="https://github.com/evanphx/benchmark-ips">benchmark/ips</a> results.</summary>
|
1258
1232
|
|
1259
1233
|
```ruby
|
1260
1234
|
# Warming up --------------------------------------
|
1261
|
-
# Interactor 1.
|
1262
|
-
# Trailblazer::Operation 1.
|
1263
|
-
# Dry::Monads 7.
|
1264
|
-
# Dry::Transaction
|
1265
|
-
# Micro::Case 9.
|
1266
|
-
# Micro::Case::Strict
|
1267
|
-
# Micro::Case::Safe 9.
|
1235
|
+
# Interactor 1.324k i/100ms
|
1236
|
+
# Trailblazer::Operation 1.525k i/100ms
|
1237
|
+
# Dry::Monads 7.126k i/100ms
|
1238
|
+
# Dry::Transaction 499.000 i/100ms
|
1239
|
+
# Micro::Case 9.919k i/100ms
|
1240
|
+
# Micro::Case::Strict 7.837k i/100ms
|
1241
|
+
# Micro::Case::Safe 9.762k i/100ms
|
1268
1242
|
|
1269
1243
|
# Calculating -------------------------------------
|
1270
|
-
# Interactor 13.
|
1271
|
-
# Trailblazer::Operation
|
1272
|
-
# Dry::Monads 71.
|
1273
|
-
# Dry::Transaction
|
1274
|
-
# Micro::Case
|
1275
|
-
# Micro::Case::Strict
|
1276
|
-
# Micro::Case::Safe
|
1244
|
+
# Interactor 13.959k (± 2.5%) i/s - 70.172k in 5.030240s
|
1245
|
+
# Trailblazer::Operation 15.035k (± 2.2%) i/s - 76.250k in 5.074108s
|
1246
|
+
# Dry::Monads 71.330k (± 2.4%) i/s - 363.426k in 5.097993s
|
1247
|
+
# Dry::Transaction 5.068k (± 1.9%) i/s - 25.449k in 5.023922s
|
1248
|
+
# Micro::Case 98.821k (± 2.9%) i/s - 495.950k in 5.023421s
|
1249
|
+
# Micro::Case::Strict 79.936k (± 3.1%) i/s - 399.687k in 5.005435s
|
1250
|
+
# Micro::Case::Safe 98.695k (± 1.9%) i/s - 497.862k in 5.046246s
|
1277
1251
|
|
1278
1252
|
# Comparison:
|
1279
|
-
#
|
1280
|
-
#
|
1281
|
-
# Micro::Case::Strict:
|
1282
|
-
# Dry::Monads:
|
1283
|
-
# Trailblazer::Operation:
|
1284
|
-
# Interactor:
|
1285
|
-
# Dry::Transaction:
|
1253
|
+
# Micro::Case: 98820.8 i/s
|
1254
|
+
# Micro::Case::Safe: 98695.0 i/s - same-ish: difference falls within error
|
1255
|
+
# Micro::Case::Strict: 79935.9 i/s - 1.24x (± 0.00) slower
|
1256
|
+
# Dry::Monads: 71329.7 i/s - 1.39x (± 0.00) slower
|
1257
|
+
# Trailblazer::Operation: 15034.9 i/s - 6.57x (± 0.00) slower
|
1258
|
+
# Interactor: 13958.7 i/s - 7.08x (± 0.00) slower
|
1259
|
+
# Dry::Transaction: 5067.5 i/s - 19.50x (± 0.00) slower
|
1286
1260
|
```
|
1287
1261
|
</details>
|
1288
1262
|
|
1289
|
-
https://github.com/serradura/u-case/blob/
|
1263
|
+
https://github.com/serradura/u-case/blob/main/benchmarks/use_case/with_failure_result.rb
|
1290
1264
|
|
1291
1265
|
---
|
1292
1266
|
|
1293
|
-
### `Micro::
|
1267
|
+
### `Micro::Cases::Flow` (v3.0.0)
|
1294
1268
|
|
1295
|
-
| Gems / Abstraction | [Success results](https://github.com/serradura/u-case/blob/
|
1296
|
-
|
|
1297
|
-
| Micro::Case
|
1298
|
-
| Micro::Case
|
1299
|
-
|
|
1269
|
+
| Gems / Abstraction | [Success results](https://github.com/serradura/u-case/blob/main/benchmarks/flow/with_success_result.rb#L40) | [Failure results](https://github.com/serradura/u-case/blob/main/benchmarks/flow/with_failure_result.rb#L40) |
|
1270
|
+
| ------------------------------------------- | ----------------: | ----------------: |
|
1271
|
+
| Micro::Case internal flow (private methods) | _**The Fastest**_ | _**The Fastest**_ |
|
1272
|
+
| Micro::Case internal flow (through lambdas) | 1.03x slower | 1.04x slower |
|
1273
|
+
| Micro::Case `then` method | 1.49x slower | 0x slower |
|
1274
|
+
| Micro::Cases.flow | 1.53x slower | 1.04x slower |
|
1275
|
+
| Micro::Cases.safe_flow | 1.54x slower | 1.04x slower |
|
1276
|
+
| Interactor::Organizer | 2.05x slower | 6.27x slower |
|
1300
1277
|
|
1301
|
-
\* The `Dry::Monads`, `Dry::Transaction`, `Trailblazer::Operation` are out of this analysis because all of them doesn't have this kind of feature.
|
1278
|
+
\* The `Dry::Monads`, `Dry::Transaction`, `Trailblazer::Operation` gems are out of this analysis because all of them doesn't have this kind of feature.
|
1302
1279
|
|
1303
1280
|
<details>
|
1304
1281
|
<summary><strong>Success results</strong> - Show the full benchmark/ips results.</summary>
|
1305
1282
|
|
1306
1283
|
```ruby
|
1307
1284
|
# Warming up --------------------------------------
|
1308
|
-
# Interactor::Organizer
|
1309
|
-
#
|
1310
|
-
# Micro::
|
1285
|
+
# Interactor::Organizer
|
1286
|
+
# 4.837k i/100ms
|
1287
|
+
# Micro::Cases.flow([])
|
1288
|
+
# 6.755k i/100ms
|
1289
|
+
# Micro::Cases::safe_flow([])
|
1290
|
+
# 6.809k i/100ms
|
1291
|
+
# Micro::Case flow using `then` method
|
1292
|
+
# 6.968k i/100ms
|
1293
|
+
# Micro::Case flow using private methods
|
1294
|
+
# 10.362k i/100ms
|
1295
|
+
# Micro::Case flow using private methods through lambdas
|
1296
|
+
# 10.258k i/100ms
|
1297
|
+
|
1311
1298
|
# Calculating -------------------------------------
|
1312
|
-
# Interactor::Organizer
|
1313
|
-
#
|
1314
|
-
# Micro::
|
1299
|
+
# Interactor::Organizer
|
1300
|
+
# 50.731k (± 1.6%) i/s - 256.361k in 5.054694s
|
1301
|
+
# Micro::Cases.flow([])
|
1302
|
+
# 67.757k (± 1.6%) i/s - 344.505k in 5.085681s
|
1303
|
+
# Micro::Cases::safe_flow([])
|
1304
|
+
# 67.613k (± 1.6%) i/s - 340.450k in 5.036562s
|
1305
|
+
# Micro::Case flow using `then` method
|
1306
|
+
# 69.483k (± 1.5%) i/s - 348.400k in 5.015351s
|
1307
|
+
# Micro::Case flow using private methods
|
1308
|
+
# 103.788k (± 1.0%) i/s - 528.462k in 5.092240s
|
1309
|
+
# Micro::Case flow using private methods through lambdas
|
1310
|
+
# 101.081k (± 1.2%) i/s - 512.900k in 5.074904s
|
1315
1311
|
|
1316
1312
|
# Comparison:
|
1317
|
-
#
|
1318
|
-
# Micro::Case
|
1319
|
-
#
|
1313
|
+
# Micro::Case flow using private methods: 103787.5 i/s
|
1314
|
+
# Micro::Case flow using private methods through lambdas: 101080.6 i/s - 1.03x (± 0.00) slower
|
1315
|
+
# Micro::Case flow using `then` method: 69483.3 i/s - 1.49x (± 0.00) slower
|
1316
|
+
# Micro::Cases.flow([]): 67757.2 i/s - 1.53x (± 0.00) slower
|
1317
|
+
# Micro::Cases::safe_flow([]): 67613.3 i/s - 1.54x (± 0.00) slower
|
1318
|
+
# Interactor::Organizer: 50730.8 i/s - 2.05x (± 0.00) slower
|
1320
1319
|
```
|
1321
1320
|
</details>
|
1322
1321
|
|
@@ -1325,56 +1324,76 @@ https://github.com/serradura/u-case/blob/master/benchmarks/use_case/with_failure
|
|
1325
1324
|
|
1326
1325
|
```ruby
|
1327
1326
|
# Warming up --------------------------------------
|
1328
|
-
# Interactor::Organizer
|
1329
|
-
#
|
1330
|
-
# Micro::
|
1331
|
-
|
1327
|
+
# Interactor::Organizer
|
1328
|
+
# 2.299k i/100ms
|
1329
|
+
# Micro::Cases.flow([])
|
1330
|
+
# 14.187k i/100ms
|
1331
|
+
# Micro::Cases::safe_flow([])
|
1332
|
+
# 13.609k i/100ms
|
1333
|
+
# Micro::Case flow using `then` method
|
1334
|
+
# 14.578k i/100ms
|
1335
|
+
# Micro::Case flow using private methods
|
1336
|
+
# 14.101k i/100ms
|
1337
|
+
# Micro::Case flow using private methods through lambdas
|
1338
|
+
# 13.670k i/100ms
|
1332
1339
|
# Calculating -------------------------------------
|
1333
|
-
# Interactor::Organizer
|
1334
|
-
#
|
1335
|
-
# Micro::
|
1340
|
+
# Interactor::Organizer
|
1341
|
+
# 23.306k (± 2.1%) i/s - 117.249k in 5.033171s
|
1342
|
+
# Micro::Cases.flow([])
|
1343
|
+
# 140.111k (± 1.6%) i/s - 709.350k in 5.064041s
|
1344
|
+
# Micro::Cases::safe_flow([])
|
1345
|
+
# 139.927k (± 1.7%) i/s - 707.668k in 5.058971s
|
1346
|
+
# Micro::Case flow using `then` method
|
1347
|
+
# 146.073k (± 2.0%) i/s - 743.478k in 5.091741s
|
1348
|
+
# Micro::Case flow using private methods
|
1349
|
+
# 142.092k (± 1.5%) i/s - 719.151k in 5.062298s
|
1350
|
+
# Micro::Case flow using private methods through lambdas
|
1351
|
+
# 140.791k (± 1.2%) i/s - 710.840k in 5.049584s
|
1336
1352
|
|
1337
1353
|
# Comparison:
|
1338
|
-
# Micro::Case
|
1339
|
-
#
|
1340
|
-
#
|
1354
|
+
# Micro::Case flow using `then` method: 146073.0 i/s
|
1355
|
+
# Micro::Case flow using private methods: 142091.7 i/s - same-ish: difference falls within error
|
1356
|
+
# Micro::Case flow using private methods through lambdas: 140791.1 i/s - 1.04x (± 0.00) slower
|
1357
|
+
# Micro::Cases.flow([]): 140110.8 i/s - 1.04x (± 0.00) slower
|
1358
|
+
# Micro::Cases::safe_flow([]): 139926.6 i/s - 1.04x (± 0.00) slower
|
1359
|
+
# Interactor::Organizer: 23305.9 i/s - 6.27x (± 0.00) slower
|
1341
1360
|
```
|
1342
1361
|
</details>
|
1343
1362
|
|
1344
|
-
https://github.com/serradura/u-case/tree/
|
1363
|
+
https://github.com/serradura/u-case/tree/main/benchmarks/flow
|
1345
1364
|
|
1346
1365
|
### Comparisons
|
1347
1366
|
|
1348
1367
|
Check it out implementations of the same use case with different gems/abstractions.
|
1349
1368
|
|
1350
|
-
* [interactor](https://github.com/serradura/u-case/blob/
|
1351
|
-
* [u-case](https://github.com/serradura/u-case/blob/
|
1369
|
+
* [interactor](https://github.com/serradura/u-case/blob/main/comparisons/interactor.rb)
|
1370
|
+
* [u-case](https://github.com/serradura/u-case/blob/main/comparisons/u-case.rb)
|
1352
1371
|
|
1353
1372
|
[⬆️ Back to Top](#table-of-contents-)
|
1354
1373
|
|
1355
1374
|
## Examples
|
1356
1375
|
|
1357
|
-
### 1️⃣
|
1376
|
+
### 1️⃣ Users creation
|
1358
1377
|
|
1359
|
-
>
|
1378
|
+
> An example of a flow that defines steps to sanitize, validate, and persist its input data. It has all possible approaches to represent use cases using the `u-case` gem.
|
1360
1379
|
>
|
1361
|
-
> Link: https://github.com/serradura/
|
1380
|
+
> Link: https://github.com/serradura/u-case/blob/main/examples/users_creation
|
1362
1381
|
|
1363
|
-
### 2️⃣
|
1382
|
+
### 2️⃣ Rails App (API)
|
1364
1383
|
|
1365
|
-
>
|
1384
|
+
> This project shows different kinds of architecture (one per commit), and in the last one, how to use the `Micro::Case` gem to handle the application business logic.
|
1366
1385
|
>
|
1367
|
-
> Link: https://github.com/serradura/
|
1386
|
+
> Link: https://github.com/serradura/from-fat-controllers-to-use-cases
|
1368
1387
|
|
1369
|
-
### 3️⃣
|
1388
|
+
### 3️⃣ CLI calculator
|
1370
1389
|
|
1371
|
-
>
|
1390
|
+
> Rake tasks to demonstrate how to handle user data, and how to use different failure types to control the program flow.
|
1372
1391
|
>
|
1373
|
-
> Link: https://github.com/serradura/u-case/
|
1392
|
+
> Link: https://github.com/serradura/u-case/tree/main/examples/calculator
|
1374
1393
|
|
1375
|
-
### 4️⃣ Rescuing
|
1394
|
+
### 4️⃣ Rescuing exceptions inside of the use cases
|
1376
1395
|
|
1377
|
-
> Link: https://github.com/serradura/u-case/blob/
|
1396
|
+
> Link: https://github.com/serradura/u-case/blob/main/examples/rescuing_exceptions.rb
|
1378
1397
|
|
1379
1398
|
[⬆️ Back to Top](#table-of-contents-)
|
1380
1399
|
|
@@ -1394,4 +1413,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
1394
1413
|
|
1395
1414
|
## Code of Conduct
|
1396
1415
|
|
1397
|
-
Everyone interacting in the Micro::Case project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/serradura/u-case/blob/
|
1416
|
+
Everyone interacting in the Micro::Case project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/serradura/u-case/blob/main/CODE_OF_CONDUCT.md).
|