u-case 3.0.0.rc1 → 3.0.0.rc6
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 +3 -2
- data/README.md +283 -266
- data/README.pt-BR.md +1390 -0
- data/lib/micro/case.rb +60 -33
- data/lib/micro/case/config.rb +23 -0
- data/lib/micro/case/error.rb +18 -17
- data/lib/micro/case/result.rb +96 -50
- 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 +3 -1
- data/lib/micro/cases/flow.rb +24 -40
- data/lib/micro/cases/safe/flow.rb +2 -2
- data/lib/u-case/with_activemodel_validation.rb +0 -2
- data/u-case.gemspec +3 -3
- metadata +14 -7
- data/lib/u-case/with_validation.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a1e5c36bfc65522732f2e00f8b57a2fe84bbe59a730cb85287f56de7850dea0
|
4
|
+
data.tar.gz: 22bf46677b24e2f8fb6f3b060ba6bc348a7a8025556d1f3cf6ddf81d0e204279
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6276a81c7e006d07922b2b30de0a066e1a9a2bc0856586287a35263e21f9c353d571fbf666a08348988c6f42ea893730008e77ea42f65867c801aa24bf19299
|
7
|
+
data.tar.gz: 47e3482b444c3b9730a3cc5e37ca78bc26556454c1155045663e9b1c597203fc11584dbc7e6716fefaf093cfb8dbcd5f7db63b84b04d8bd5598e94b00c2dfb7c
|
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
|
-
Version
|
24
|
-
|
25
|
-
|
26
|
-
2.6.0
|
27
|
-
1.1.0
|
22
|
+
Version | Documentation
|
23
|
+
--------- | -------------
|
24
|
+
3.0.0.rc5 | https://github.com/serradura/u-case/blob/main/README.md
|
25
|
+
2.6.0 | https://github.com/serradura/u-case/blob/v2.x/README.md
|
26
|
+
1.1.0 | https://github.com/serradura/u-case/blob/v1.x/README.md
|
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).
|
28
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,46 +36,54 @@ Unreleased | https://github.com/serradura/u-case/blob/master/README.md
|
|
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)
|
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)
|
51
54
|
- [`Micro::Case::Strict` - What is a strict use case?](#microcasestrict---what-is-a-strict-use-case)
|
52
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)
|
53
56
|
- [`Micro::Cases::Safe::Flow`](#microcasessafeflow)
|
54
57
|
- [`Micro::Case::Result#on_exception`](#microcaseresulton_exception)
|
55
|
-
- [`u-case/with_activemodel_validation` - How to validate use case attributes?](#u-casewith_activemodel_validation---how-to-validate-use-case-attributes)
|
56
|
-
- [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)
|
57
60
|
- [`Kind::Validator`](#kindvalidator)
|
61
|
+
- [`Micro::Case.config`](#microcaseconfig)
|
58
62
|
- [Benchmarks](#benchmarks)
|
59
|
-
- [`Micro::Case` (
|
60
|
-
- [Best overall](#best-overall)
|
63
|
+
- [`Micro::Case` (v3.0.0)](#microcase-v300)
|
61
64
|
- [Success results](#success-results)
|
62
65
|
- [Failure results](#failure-results)
|
63
|
-
- [`Micro::
|
66
|
+
- [`Micro::Cases::Flow` (v3.0.0)](#microcasesflow-v300)
|
64
67
|
- [Comparisons](#comparisons)
|
65
68
|
- [Examples](#examples)
|
66
69
|
- [1️⃣ Rails App (API)](#1️⃣-rails-app-api)
|
67
70
|
- [2️⃣ CLI calculator](#2️⃣-cli-calculator)
|
68
71
|
- [3️⃣ Users creation](#3️⃣-users-creation)
|
69
|
-
- [4️⃣ Rescuing
|
72
|
+
- [4️⃣ Rescuing exceptions inside of the use cases](#4️⃣-rescuing-exceptions-inside-of-the-use-cases)
|
70
73
|
- [Development](#development)
|
71
74
|
- [Contributing](#contributing)
|
72
75
|
- [License](#license)
|
73
76
|
- [Code of Conduct](#code-of-conduct)
|
74
77
|
|
75
|
-
##
|
78
|
+
## Compatibility
|
79
|
+
|
80
|
+
| u-case | branch | ruby | activemodel |
|
81
|
+
| -------------- | ------- | -------- | ------------- |
|
82
|
+
| 3.0.0.rc5 | 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 |
|
76
85
|
|
77
|
-
>
|
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.
|
78
87
|
|
79
88
|
## Dependencies
|
80
89
|
|
@@ -82,7 +91,7 @@ Unreleased | https://github.com/serradura/u-case/blob/master/README.md
|
|
82
91
|
|
83
92
|
A simple type system (at runtime) for Ruby.
|
84
93
|
|
85
|
-
|
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).
|
86
95
|
2. [`u-attributes`](https://github.com/serradura/u-attributes) gem.
|
87
96
|
|
88
97
|
This gem allows defining read-only attributes, that is, your objects will have only getters to access their attributes data.
|
@@ -93,7 +102,7 @@ Unreleased | https://github.com/serradura/u-case/blob/master/README.md
|
|
93
102
|
Add this line to your application's Gemfile:
|
94
103
|
|
95
104
|
```ruby
|
96
|
-
gem 'u-case'
|
105
|
+
gem 'u-case', '~> 3.0.0.rc5'
|
97
106
|
```
|
98
107
|
|
99
108
|
And then execute:
|
@@ -102,7 +111,7 @@ And then execute:
|
|
102
111
|
|
103
112
|
Or install it yourself as:
|
104
113
|
|
105
|
-
$ gem install u-case
|
114
|
+
$ gem install u-case --pre
|
106
115
|
|
107
116
|
## Usage
|
108
117
|
|
@@ -116,7 +125,7 @@ class Multiply < Micro::Case
|
|
116
125
|
# 2. Define the method `call!` with its business logic
|
117
126
|
def call!
|
118
127
|
|
119
|
-
# 3. Wrap the use case
|
128
|
+
# 3. Wrap the use case output using the `Success(result: *)` or `Failure(result: *)` methods
|
120
129
|
if a.is_a?(Numeric) && b.is_a?(Numeric)
|
121
130
|
Success result: { number: a * b }
|
122
131
|
else
|
@@ -125,9 +134,9 @@ class Multiply < Micro::Case
|
|
125
134
|
end
|
126
135
|
end
|
127
136
|
|
128
|
-
|
129
|
-
#
|
130
|
-
|
137
|
+
#========================#
|
138
|
+
# Performing an use case #
|
139
|
+
#========================#
|
131
140
|
|
132
141
|
# Success result
|
133
142
|
|
@@ -143,18 +152,9 @@ bad_result = Multiply.call(a: 2, b: '2')
|
|
143
152
|
bad_result.failure? # true
|
144
153
|
bad_result.data # { message: "`a` and `b` attributes must be numeric" }
|
145
154
|
|
146
|
-
#-----------------------------#
|
147
|
-
# Calling a use case instance #
|
148
|
-
#-----------------------------#
|
149
|
-
|
150
|
-
result = Multiply.new(a: 2, b: 3).call
|
151
|
-
|
152
|
-
result.value # { number: 6 }
|
153
|
-
|
154
155
|
# Note:
|
155
156
|
# ----
|
156
|
-
# The result of a Micro::Case.call
|
157
|
-
# is an instance of Micro::Case::Result
|
157
|
+
# The result of a Micro::Case.call is an instance of Micro::Case::Result
|
158
158
|
```
|
159
159
|
|
160
160
|
[⬆️ Back to Top](#table-of-contents-)
|
@@ -164,12 +164,16 @@ result.value # { number: 6 }
|
|
164
164
|
A `Micro::Case::Result` stores the use cases output data. These are their main methods:
|
165
165
|
- `#success?` returns true if is a successful result.
|
166
166
|
- `#failure?` returns true if is an unsuccessful result.
|
167
|
-
- `#
|
167
|
+
- `#use_case` returns the use case responsible for it. This feature is handy to handle a flow failure (this topic will be covered ahead).
|
168
168
|
- `#type` a Symbol which gives meaning for the result, this is useful to declare different types of failures or success.
|
169
|
+
- `#data` the result data itself.
|
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.
|
169
174
|
- `#on_success` or `#on_failure` are hook methods that help you to define the application flow.
|
170
|
-
- `#use_case` if is a failure result, the use case responsible for it will be accessible through this method. This feature is handy to handle a flow failure (this topic will be covered ahead).
|
171
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.
|
172
|
-
- `#
|
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).
|
173
177
|
|
174
178
|
> **Note:** for backward compatibility, you could use the `#value` method as an alias of `#data` method.
|
175
179
|
|
@@ -177,9 +181,9 @@ A `Micro::Case::Result` stores the use cases output data. These are their main m
|
|
177
181
|
|
178
182
|
#### What are the default result types?
|
179
183
|
|
180
|
-
Every result has a type and these are
|
184
|
+
Every result has a type, and these are their default values:
|
181
185
|
- `:ok` when success
|
182
|
-
- `:error
|
186
|
+
- `:error` or `:exception` when failures
|
183
187
|
|
184
188
|
```ruby
|
185
189
|
class Divide < Micro::Case
|
@@ -207,7 +211,7 @@ result = Divide.call(a: 2, b: 2)
|
|
207
211
|
result.type # :ok
|
208
212
|
result.data # { number: 1 }
|
209
213
|
result.success? # true
|
210
|
-
result.use_case #
|
214
|
+
result.use_case # #<Divide:0x0000 @__attributes={"a"=>2, "b"=>2}, @a=2, @b=2, @__result=...>
|
211
215
|
|
212
216
|
# Failure result (type == :error)
|
213
217
|
|
@@ -216,7 +220,7 @@ bad_result = Divide.call(a: 2, b: '2')
|
|
216
220
|
bad_result.type # :error
|
217
221
|
bad_result.data # { invalid_attributes: { "b"=>"2" } }
|
218
222
|
bad_result.failure? # true
|
219
|
-
bad_result.use_case # #<Divide:0x0000 @__attributes={"a"=>2, "b"=>"2"}, @a=2, @b="2", @__result
|
223
|
+
bad_result.use_case # #<Divide:0x0000 @__attributes={"a"=>2, "b"=>"2"}, @a=2, @b="2", @__result=...>
|
220
224
|
|
221
225
|
# Failure result (type == :exception)
|
222
226
|
|
@@ -273,9 +277,9 @@ bad_result.failure? # true
|
|
273
277
|
|
274
278
|
[⬆️ Back to Top](#table-of-contents-)
|
275
279
|
|
276
|
-
#### Is it possible to define a custom
|
280
|
+
#### Is it possible to define a custom type without a result data?
|
277
281
|
|
278
|
-
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.
|
279
283
|
|
280
284
|
```ruby
|
281
285
|
class Multiply < Micro::Case
|
@@ -307,7 +311,7 @@ result.use_case.attributes # {"a"=>2, "b"=>"2"}
|
|
307
311
|
|
308
312
|
#### How to use the result hooks?
|
309
313
|
|
310
|
-
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`.
|
311
315
|
|
312
316
|
The examples below show how to use them:
|
313
317
|
|
@@ -333,7 +337,7 @@ Double
|
|
333
337
|
.on_failure(:invalid) { |result| raise TypeError, result[:msg] }
|
334
338
|
.on_failure(:lte_zero) { |result| raise ArgumentError, result[:msg] }
|
335
339
|
|
336
|
-
# The output
|
340
|
+
# The output will be:
|
337
341
|
# 6
|
338
342
|
|
339
343
|
#=============================#
|
@@ -349,18 +353,17 @@ Double
|
|
349
353
|
|
350
354
|
# The outputs will be:
|
351
355
|
#
|
352
|
-
# 1.
|
353
|
-
# 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)
|
354
358
|
|
355
359
|
# Note:
|
356
360
|
# ----
|
357
|
-
# The use case responsible for the
|
361
|
+
# The use case responsible for the result will always be accessible as the second hook argument
|
358
362
|
```
|
359
363
|
|
360
|
-
#### Why the
|
364
|
+
#### Why the hook usage without a defined type exposes the result itself?
|
361
365
|
|
362
|
-
Answer: To allow you to define how to handle the program flow using some
|
363
|
-
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`.
|
364
367
|
|
365
368
|
```ruby
|
366
369
|
class Double < Micro::Case
|
@@ -374,12 +377,8 @@ class Double < Micro::Case
|
|
374
377
|
end
|
375
378
|
end
|
376
379
|
|
377
|
-
#=================================#
|
378
|
-
# Using the result type and value #
|
379
|
-
#=================================#
|
380
|
-
|
381
380
|
Double
|
382
|
-
.call(-1)
|
381
|
+
.call(number: -1)
|
383
382
|
.on_failure do |result, use_case|
|
384
383
|
case result.type
|
385
384
|
when :invalid then raise TypeError, "number must be a numeric value"
|
@@ -388,22 +387,23 @@ Double
|
|
388
387
|
end
|
389
388
|
end
|
390
389
|
|
391
|
-
# The output will be
|
390
|
+
# The output will be an exception:
|
392
391
|
#
|
393
392
|
# ArgumentError (number `-1` must be greater than 0)
|
393
|
+
```
|
394
394
|
|
395
|
-
|
396
|
-
# Using decomposition to access the result data and type #
|
397
|
-
#=========================================================#
|
395
|
+
> **Note:** The same that was did in the previous examples could be done with `#on_success` hook!
|
398
396
|
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
#
|
403
|
-
|
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:
|
404
404
|
|
405
405
|
Double
|
406
|
-
.call(-2)
|
406
|
+
.call(number: -2)
|
407
407
|
.on_failure do |(data, type), use_case|
|
408
408
|
case type
|
409
409
|
when :invalid then raise TypeError, 'number must be a numeric value'
|
@@ -417,6 +417,8 @@ Double
|
|
417
417
|
# ArgumentError (the number `-2` must be greater than 0)
|
418
418
|
```
|
419
419
|
|
420
|
+
> **Note:** The same that was did in the previous examples could be done with `#on_success` hook!
|
421
|
+
|
420
422
|
[⬆️ Back to Top](#table-of-contents-)
|
421
423
|
|
422
424
|
#### What happens if a result hook was declared multiple times?
|
@@ -442,10 +444,11 @@ result[:number] * 4 # 24
|
|
442
444
|
|
443
445
|
accum = 0
|
444
446
|
|
445
|
-
result
|
446
|
-
|
447
|
-
|
448
|
-
|
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] }
|
449
452
|
|
450
453
|
accum # 24
|
451
454
|
|
@@ -454,8 +457,7 @@ result[:number] * 4 == accum # true
|
|
454
457
|
|
455
458
|
#### How to use the `Micro::Case::Result#then` method?
|
456
459
|
|
457
|
-
This method allows you to create dynamic flows, so, with it,
|
458
|
-
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:
|
459
461
|
|
460
462
|
```ruby
|
461
463
|
class ForbidNegativeNumber < Micro::Case
|
@@ -501,16 +503,18 @@ result2.success? # true
|
|
501
503
|
|
502
504
|
##### What does happens when a `Micro::Case::Result#then` receives a block?
|
503
505
|
|
504
|
-
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:
|
505
507
|
|
506
508
|
```ruby
|
507
509
|
class Add < Micro::Case
|
508
510
|
attributes :a, :b
|
509
511
|
|
510
512
|
def call!
|
511
|
-
|
512
|
-
|
513
|
-
|
513
|
+
if Kind.of?(Numeric, a, b)
|
514
|
+
Success result: { sum: a + b }
|
515
|
+
else
|
516
|
+
Failure(:attributes_arent_numbers)
|
517
|
+
end
|
514
518
|
end
|
515
519
|
end
|
516
520
|
|
@@ -551,8 +555,7 @@ Todo::FindAllForUser
|
|
551
555
|
|
552
556
|
### `Micro::Cases::Flow` - How to compose use cases?
|
553
557
|
|
554
|
-
|
555
|
-
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.
|
556
559
|
|
557
560
|
```ruby
|
558
561
|
module Steps
|
@@ -607,24 +610,23 @@ result = Add2ToAllNumbers.call(numbers: %w[1 1 2 2 3 4])
|
|
607
610
|
result.success? # true
|
608
611
|
result.data # {:numbers => [3, 3, 4, 4, 5, 6]}
|
609
612
|
|
610
|
-
|
611
|
-
#
|
612
|
-
|
613
|
+
#-------------------------------#
|
614
|
+
# Creating a flow using classes #
|
615
|
+
#-------------------------------#
|
613
616
|
|
614
617
|
class DoubleAllNumbers < Micro::Case
|
615
618
|
flow Steps::ConvertTextToNumbers,
|
616
619
|
Steps::Double
|
617
620
|
end
|
618
621
|
|
619
|
-
DoubleAllNumbers
|
620
|
-
|
621
|
-
|
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
|
+
```
|
622
626
|
|
623
|
-
|
624
|
-
# ----
|
625
|
-
# When happening a failure, the use case responsible
|
626
|
-
# will be accessible in the result
|
627
|
+
When happening a failure, the use case responsible will be accessible in the result.
|
627
628
|
|
629
|
+
```ruby
|
628
630
|
result = DoubleAllNumbers.call(numbers: %w[1 1 b 2 3 4])
|
629
631
|
|
630
632
|
result.failure? # true
|
@@ -637,7 +639,7 @@ end
|
|
637
639
|
|
638
640
|
[⬆️ Back to Top](#table-of-contents-)
|
639
641
|
|
640
|
-
#### Is it possible to compose a
|
642
|
+
#### Is it possible to compose a flow with other flows?
|
641
643
|
|
642
644
|
Answer: Yes, it is possible.
|
643
645
|
|
@@ -700,20 +702,20 @@ DoubleAllNumbersAndSquareAndAdd2 =
|
|
700
702
|
|
701
703
|
SquareAllNumbersAndDouble
|
702
704
|
.call(numbers: %w[1 1 2 2 3 4])
|
703
|
-
.on_success { |
|
705
|
+
.on_success { |result| p result[:numbers] } # [6, 6, 12, 12, 22, 36]
|
704
706
|
|
705
707
|
DoubleAllNumbersAndSquareAndAdd2
|
706
708
|
.call(numbers: %w[1 1 2 2 3 4])
|
707
|
-
.on_success { |
|
709
|
+
.on_success { |result| p result[:numbers] } # [6, 6, 18, 18, 38, 66]
|
708
710
|
```
|
709
711
|
|
710
|
-
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).
|
711
713
|
|
712
714
|
[⬆️ Back to Top](#table-of-contents-)
|
713
715
|
|
714
716
|
#### Is it possible a flow accumulates its input and merges each success result to use as the argument of the next use cases?
|
715
717
|
|
716
|
-
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.
|
717
719
|
|
718
720
|
```ruby
|
719
721
|
module Users
|
@@ -757,7 +759,7 @@ Users::Authenticate
|
|
757
759
|
.on_failure(:user_not_found) { render status: 404 }
|
758
760
|
```
|
759
761
|
|
760
|
-
First,
|
762
|
+
First, let's see the attributes used by each use case:
|
761
763
|
|
762
764
|
```ruby
|
763
765
|
class Users::FindByEmail < Micro::Case
|
@@ -770,14 +772,13 @@ end
|
|
770
772
|
```
|
771
773
|
|
772
774
|
As you can see the `Users::ValidatePassword` expects a user as its input. So, how does it receives the user?
|
773
|
-
It receives the user from the `Users::FindByEmail` success result!
|
775
|
+
Answer: It receives the user from the `Users::FindByEmail` success result!
|
774
776
|
|
775
|
-
And this
|
776
|
-
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!
|
777
778
|
|
778
779
|
> input **>>** process **>>** output
|
779
780
|
|
780
|
-
> **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.
|
781
782
|
|
782
783
|
[⬆️ Back to Top](#table-of-contents-)
|
783
784
|
|
@@ -826,14 +827,12 @@ user_authenticated.transitions
|
|
826
827
|
```
|
827
828
|
|
828
829
|
The example above shows the output generated by the `Micro::Case::Result#transitions`.
|
829
|
-
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.
|
830
831
|
|
831
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).
|
832
833
|
|
833
834
|
> **Note:** The [`Micro::Case::Result#then`](#how-to-use-the-microcaseresultthen-method) increments the `Micro::Case::Result#transitions`.
|
834
835
|
|
835
|
-
PS: Use the `Micro::Case::Result.disable_transition_tracking` feature toggle to disable this feature (use once, because it is global) and increase the use cases' performance.
|
836
|
-
|
837
836
|
##### `Micro::Case::Result#transitions` schema
|
838
837
|
```ruby
|
839
838
|
[
|
@@ -845,18 +844,22 @@ PS: Use the `Micro::Case::Result.disable_transition_tracking` feature toggle to
|
|
845
844
|
[success:, failure:] => { # (Output)
|
846
845
|
type: <Symbol>, # Result type. Defaults:
|
847
846
|
# Success = :ok, Failure = :error/:exception
|
848
|
-
result: <Hash> # The data returned by the use case
|
847
|
+
result: <Hash> # The data returned by the use case result
|
849
848
|
},
|
850
849
|
accessible_attributes: <Array>, # Properties that can be accessed by the use case's attributes,
|
851
|
-
#
|
852
|
-
# 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.
|
853
852
|
}
|
854
853
|
]
|
855
854
|
```
|
856
855
|
|
857
|
-
|
856
|
+
##### Is it possible disable the `Micro::Case::Result#transitions`?
|
857
|
+
|
858
|
+
Answer: Yes, it is! You can use the `Micro::Case.config` to do this. [Link to](#microcaseconfig) this section.
|
858
859
|
|
859
|
-
|
860
|
+
#### Is it possible to declare a flow that includes the use case itself as a step?
|
861
|
+
|
862
|
+
Answer: Yes, it is! You can use `self` or the `self.call!` macro. e.g:
|
860
863
|
|
861
864
|
```ruby
|
862
865
|
class ConvertTextToNumber < Micro::Case
|
@@ -891,16 +894,15 @@ result = Double.call(text: '4')
|
|
891
894
|
|
892
895
|
result.success? # true
|
893
896
|
result[:number] # "8"
|
894
|
-
|
895
|
-
# NOTE: This feature can be used with the Micro::Case::Safe.
|
896
|
-
# Checkout this test: https://github.com/serradura/u-case/blob/714c6b658fc6aa02617e6833ddee09eddc760f2a/test/micro/case/safe/with_inner_flow_test.rb
|
897
897
|
```
|
898
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
|
+
|
899
901
|
[⬆️ Back to Top](#table-of-contents-)
|
900
902
|
|
901
903
|
### `Micro::Case::Strict` - What is a strict use case?
|
902
904
|
|
903
|
-
Answer:
|
905
|
+
Answer: it is a kind of use case that will require all the keywords (attributes) on its initialization.
|
904
906
|
|
905
907
|
```ruby
|
906
908
|
class Double < Micro::Case::Strict
|
@@ -913,7 +915,7 @@ end
|
|
913
915
|
|
914
916
|
Double.call({})
|
915
917
|
|
916
|
-
# The output will be
|
918
|
+
# The output will be:
|
917
919
|
# ArgumentError (missing keyword: :numbers)
|
918
920
|
```
|
919
921
|
|
@@ -921,11 +923,7 @@ Double.call({})
|
|
921
923
|
|
922
924
|
### `Micro::Case::Safe` - Is there some feature to auto handle exceptions inside of a use case or flow?
|
923
925
|
|
924
|
-
|
925
|
-
|
926
|
-
**Use cases:**
|
927
|
-
|
928
|
-
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:
|
929
927
|
|
930
928
|
```ruby
|
931
929
|
require 'logger'
|
@@ -949,28 +947,24 @@ result.type == :exception # true
|
|
949
947
|
result.data # { exception: #<ZeroDivisionError...> }
|
950
948
|
result[:exception].is_a?(ZeroDivisionError) # true
|
951
949
|
|
952
|
-
result.on_failure(:exception) do |
|
953
|
-
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
|
954
952
|
end
|
953
|
+
```
|
955
954
|
|
956
|
-
|
957
|
-
# ----
|
958
|
-
# If you need to handle a specific error,
|
959
|
-
# 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:
|
960
956
|
|
961
|
-
|
962
|
-
|
957
|
+
```ruby
|
958
|
+
result.on_failure(:exception) do |data, use_case|
|
959
|
+
case exception = data[:exception]
|
963
960
|
when ZeroDivisionError then AppLogger.error(exception.message)
|
964
961
|
else AppLogger.debug("#{use_case.class.name} was the use case responsible for the exception")
|
965
962
|
end
|
966
963
|
end
|
967
|
-
|
968
|
-
# Another note:
|
969
|
-
# ------------
|
970
|
-
# It is possible to rescue an exception even when is a safe use case.
|
971
|
-
# Examples: https://github.com/serradura/u-case/blob/714c6b658fc6aa02617e6833ddee09eddc760f2a/test/micro/case/safe_test.rb#L90-L118
|
972
964
|
```
|
973
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
|
+
|
974
968
|
[⬆️ Back to Top](#table-of-contents-)
|
975
969
|
|
976
970
|
#### `Micro::Cases::Safe::Flow`
|
@@ -986,9 +980,11 @@ module Users
|
|
986
980
|
SendToCRM
|
987
981
|
])
|
988
982
|
end
|
983
|
+
```
|
989
984
|
|
990
|
-
|
985
|
+
Defining within classes:
|
991
986
|
|
987
|
+
```ruby
|
992
988
|
module Users
|
993
989
|
class Create < Micro::Case::Safe
|
994
990
|
flow ProcessParams,
|
@@ -1007,9 +1003,9 @@ In functional programming errors/exceptions are handled as regular data, the ide
|
|
1007
1003
|
|
1008
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.
|
1009
1005
|
|
1010
|
-
> **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.
|
1011
1007
|
|
1012
|
-
How does it work
|
1008
|
+
**How does it work?**
|
1013
1009
|
|
1014
1010
|
```ruby
|
1015
1011
|
class Divide < Micro::Case::Safe
|
@@ -1045,21 +1041,19 @@ Divide.
|
|
1045
1041
|
# Oh no, something went wrong!
|
1046
1042
|
```
|
1047
1043
|
|
1048
|
-
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.
|
1049
1045
|
|
1050
1046
|
[⬆️ Back to Top](#table-of-contents-)
|
1051
1047
|
|
1052
|
-
### `u-case/with_activemodel_validation` - How to validate use case attributes?
|
1048
|
+
### `u-case/with_activemodel_validation` - How to validate the use case attributes?
|
1053
1049
|
|
1054
1050
|
**Requirement:**
|
1055
1051
|
|
1056
1052
|
To do this your application must have the [activemodel >= 3.2, < 6.1.0](https://rubygems.org/gems/activemodel) as a dependency.
|
1057
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
|
+
|
1058
1056
|
```ruby
|
1059
|
-
#
|
1060
|
-
# By default, if your application has the activemodel as a dependency,
|
1061
|
-
# any kind of use case can use it to validate their attributes.
|
1062
|
-
#
|
1063
1057
|
class Multiply < Micro::Case
|
1064
1058
|
attributes :a, :b
|
1065
1059
|
|
@@ -1071,18 +1065,22 @@ class Multiply < Micro::Case
|
|
1071
1065
|
Success result: { number: a * b }
|
1072
1066
|
end
|
1073
1067
|
end
|
1068
|
+
```
|
1074
1069
|
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1070
|
+
But if do you want an automatic way to fail your use cases on validation errors, you could do:
|
1071
|
+
|
1072
|
+
1. **require 'u-case/with_activemodel_validation'** in the Gemfile
|
1078
1073
|
|
1079
|
-
|
1080
|
-
|
1074
|
+
```ruby
|
1075
|
+
gem 'u-case', require: 'u-case/with_activemodel_validation'
|
1076
|
+
```
|
1081
1077
|
|
1082
|
-
|
1083
|
-
gem 'u-case', require: 'u-case/with_activemodel_validation'
|
1078
|
+
2. Use the `Micro::Case.config` to enable it. [Link to](#microcaseconfig) this section.
|
1084
1079
|
|
1085
|
-
|
1080
|
+
Using this approach, you can rewrite the previous example with less code. e.g:
|
1081
|
+
|
1082
|
+
```ruby
|
1083
|
+
require 'u-case/with_activemodel_validation'
|
1086
1084
|
|
1087
1085
|
class Multiply < Micro::Case
|
1088
1086
|
attributes :a, :b
|
@@ -1093,16 +1091,13 @@ class Multiply < Micro::Case
|
|
1093
1091
|
Success result: { number: a * b }
|
1094
1092
|
end
|
1095
1093
|
end
|
1096
|
-
|
1097
|
-
# Note:
|
1098
|
-
# ----
|
1099
|
-
# After requiring the validation mode, the
|
1100
|
-
# Micro::Case::Strict and Micro::Case::Safe classes will inherit this new behavior.
|
1101
1094
|
```
|
1102
1095
|
|
1103
|
-
|
1096
|
+
> **Note:** After requiring the validation mode, the `Micro::Case::Strict` and `Micro::Case::Safe` classes will inherit this new behavior.
|
1104
1097
|
|
1105
|
-
|
1098
|
+
#### If I enabled the auto validation, is it possible to disable it only in specific use cases?
|
1099
|
+
|
1100
|
+
Answer: Yes, it is possible. To do this, you will need to use the `disable_auto_validation` macro. e.g:
|
1106
1101
|
|
1107
1102
|
```ruby
|
1108
1103
|
require 'u-case/with_activemodel_validation'
|
@@ -1121,7 +1116,7 @@ end
|
|
1121
1116
|
|
1122
1117
|
Multiply.call(a: 2, b: 'a')
|
1123
1118
|
|
1124
|
-
# The output will be
|
1119
|
+
# The output will be:
|
1125
1120
|
# TypeError (String can't be coerced into Integer)
|
1126
1121
|
```
|
1127
1122
|
|
@@ -1129,9 +1124,9 @@ Multiply.call(a: 2, b: 'a')
|
|
1129
1124
|
|
1130
1125
|
#### `Kind::Validator`
|
1131
1126
|
|
1132
|
-
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).
|
1133
1128
|
|
1134
|
-
The example below shows how to validate the attributes
|
1129
|
+
The example below shows how to validate the attributes types.
|
1135
1130
|
|
1136
1131
|
```ruby
|
1137
1132
|
class Todo::List::AddItem < Micro::Case
|
@@ -1152,143 +1147,159 @@ class Todo::List::AddItem < Micro::Case
|
|
1152
1147
|
end
|
1153
1148
|
```
|
1154
1149
|
|
1155
|
-
|
1150
|
+
[⬆️ Back to Top](#table-of-contents-)
|
1156
1151
|
|
1157
|
-
|
1152
|
+
## `Micro::Case.config`
|
1158
1153
|
|
1159
|
-
|
1154
|
+
The idea of this resource is to allow the configuration of some `u-case` features/modules.
|
1155
|
+
I recommend you use it only once in your codebase. e.g. In a Rails initializer.
|
1160
1156
|
|
1161
|
-
|
1157
|
+
You can see below, which are the available configurations with their default values:
|
1162
1158
|
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
| Interactor | 21342.3 | 4.93x slower |
|
1168
|
-
| Trailblazer::Operation | 14652.7 | 7.17x slower |
|
1169
|
-
| Dry::Transaction | 5310.3 | 19.80x slower |
|
1159
|
+
```ruby
|
1160
|
+
Micro::Case.config do |config|
|
1161
|
+
# Use ActiveModel to auto-validate your use cases' attributes.
|
1162
|
+
config.enable_activemodel_validation = false
|
1170
1163
|
|
1171
|
-
|
1164
|
+
# Use to enable/disable the `Micro::Case::Results#transitions`.
|
1165
|
+
config.enable_transitions = true
|
1166
|
+
end
|
1167
|
+
```
|
1168
|
+
|
1169
|
+
[⬆️ Back to Top](#table-of-contents-)
|
1170
|
+
|
1171
|
+
## Benchmarks
|
1172
|
+
|
1173
|
+
### `Micro::Case` (v3.0.0)
|
1172
1174
|
|
1173
1175
|
#### Success results
|
1174
1176
|
|
1175
1177
|
| Gem / Abstraction | Iterations per second | Comparison |
|
1176
1178
|
| ----------------- | --------------------: | ----------------: |
|
1177
|
-
| Dry::Monads |
|
1178
|
-
| **Micro::Case** |
|
1179
|
-
| Interactor |
|
1180
|
-
| Trailblazer::Operation |
|
1181
|
-
| Dry::Transaction |
|
1179
|
+
| Dry::Monads | 139037.7 | _**The Fastest**_ |
|
1180
|
+
| **Micro::Case** | 101497.3 | 1.37x slower |
|
1181
|
+
| Interactor | 30694.2 | 4.53x slower |
|
1182
|
+
| Trailblazer::Operation | 14580.8 | 9.54x slower |
|
1183
|
+
| Dry::Transaction | 5728.0 | 24.27x slower |
|
1182
1184
|
|
1183
1185
|
<details>
|
1184
1186
|
<summary>Show the full <a href="https://github.com/evanphx/benchmark-ips">benchmark/ips</a> results.</summary>
|
1185
1187
|
|
1186
1188
|
```ruby
|
1187
1189
|
# Warming up --------------------------------------
|
1188
|
-
# Interactor
|
1189
|
-
# Trailblazer::Operation 1.
|
1190
|
-
# Dry::Monads
|
1191
|
-
# Dry::Transaction
|
1192
|
-
# Micro::Case 10.
|
1193
|
-
# Micro::Case::Strict
|
1194
|
-
# Micro::Case::Safe 10.
|
1190
|
+
# Interactor 3.056k i/100ms
|
1191
|
+
# Trailblazer::Operation 1.480k i/100ms
|
1192
|
+
# Dry::Monads 14.316k i/100ms
|
1193
|
+
# Dry::Transaction 576.000 i/100ms
|
1194
|
+
# Micro::Case 10.388k i/100ms
|
1195
|
+
# Micro::Case::Strict 8.223k i/100ms
|
1196
|
+
# Micro::Case::Safe 10.057k i/100ms
|
1195
1197
|
|
1196
1198
|
# Calculating -------------------------------------
|
1197
|
-
# Interactor
|
1198
|
-
# Trailblazer::Operation 14.
|
1199
|
-
# Dry::Monads
|
1200
|
-
# Dry::Transaction 5.
|
1201
|
-
# Micro::Case
|
1202
|
-
# Micro::Case::Strict
|
1203
|
-
# Micro::Case::Safe
|
1199
|
+
# Interactor 30.694k (± 2.3%) i/s - 155.856k in 5.080475s
|
1200
|
+
# Trailblazer::Operation 14.581k (± 3.9%) i/s - 74.000k in 5.083091s
|
1201
|
+
# Dry::Monads 139.038k (± 3.0%) i/s - 701.484k in 5.049921s
|
1202
|
+
# Dry::Transaction 5.728k (± 3.6%) i/s - 28.800k in 5.034599s
|
1203
|
+
# Micro::Case 100.712k (± 3.4%) i/s - 509.012k in 5.060139s
|
1204
|
+
# Micro::Case::Strict 81.513k (± 3.4%) i/s - 411.150k in 5.049962s
|
1205
|
+
# Micro::Case::Safe 101.497k (± 3.1%) i/s - 512.907k in 5.058463s
|
1204
1206
|
|
1205
1207
|
# Comparison:
|
1206
|
-
# Dry::Monads:
|
1207
|
-
#
|
1208
|
-
#
|
1209
|
-
# Micro::Case::Strict:
|
1210
|
-
# Interactor:
|
1211
|
-
# Trailblazer::Operation:
|
1212
|
-
# Dry::Transaction:
|
1208
|
+
# Dry::Monads: 139037.7 i/s
|
1209
|
+
# Micro::Case::Safe: 101497.3 i/s - 1.37x (± 0.00) slower
|
1210
|
+
# Micro::Case: 100711.6 i/s - 1.38x (± 0.00) slower
|
1211
|
+
# Micro::Case::Strict: 81512.9 i/s - 1.71x (± 0.00) slower
|
1212
|
+
# Interactor: 30694.2 i/s - 4.53x (± 0.00) slower
|
1213
|
+
# Trailblazer::Operation: 14580.8 i/s - 9.54x (± 0.00) slower
|
1214
|
+
# Dry::Transaction: 5728.0 i/s - 24.27x (± 0.00) slower
|
1213
1215
|
```
|
1214
1216
|
</details>
|
1215
1217
|
|
1216
|
-
https://github.com/serradura/u-case/blob/
|
1218
|
+
https://github.com/serradura/u-case/blob/main/benchmarks/use_case/with_success_result.rb
|
1217
1219
|
|
1218
1220
|
#### Failure results
|
1219
1221
|
|
1220
1222
|
| Gem / Abstraction | Iterations per second | Comparison |
|
1221
1223
|
| ----------------- | --------------------: | ----------------: |
|
1222
|
-
| **Micro::Case** |
|
1223
|
-
| Dry::Monads |
|
1224
|
-
| Trailblazer::Operation |
|
1225
|
-
| Interactor |
|
1226
|
-
| Dry::Transaction |
|
1224
|
+
| **Micro::Case** | 94619.6 | _**The Fastest**_ |
|
1225
|
+
| Dry::Monads | 70250.6 | 1.35x slower |
|
1226
|
+
| Trailblazer::Operation | 14786.1 | 6.40x slower |
|
1227
|
+
| Interactor | 13770.0 | 6.87x slower |
|
1228
|
+
| Dry::Transaction | 4994.4 | 18.95x slower |
|
1227
1229
|
|
1228
1230
|
<details>
|
1229
1231
|
<summary>Show the full <a href="https://github.com/evanphx/benchmark-ips">benchmark/ips</a> results.</summary>
|
1230
1232
|
|
1231
1233
|
```ruby
|
1232
1234
|
# Warming up --------------------------------------
|
1233
|
-
# Interactor 1.
|
1234
|
-
# Trailblazer::Operation 1.
|
1235
|
-
# Dry::Monads 7.
|
1236
|
-
# Dry::Transaction
|
1237
|
-
# Micro::Case 9.
|
1238
|
-
# Micro::Case::Strict
|
1239
|
-
# Micro::Case::Safe 9.
|
1235
|
+
# Interactor 1.408k i/100ms
|
1236
|
+
# Trailblazer::Operation 1.492k i/100ms
|
1237
|
+
# Dry::Monads 7.224k i/100ms
|
1238
|
+
# Dry::Transaction 501.000 i/100ms
|
1239
|
+
# Micro::Case 9.664k i/100ms
|
1240
|
+
# Micro::Case::Strict 7.823k i/100ms
|
1241
|
+
# Micro::Case::Safe 9.464k i/100ms
|
1240
1242
|
|
1241
1243
|
# Calculating -------------------------------------
|
1242
|
-
# Interactor 13.
|
1243
|
-
# Trailblazer::Operation 14.
|
1244
|
-
# Dry::Monads
|
1245
|
-
# Dry::Transaction 4.
|
1246
|
-
# Micro::Case
|
1247
|
-
# Micro::Case::Strict
|
1248
|
-
# Micro::Case::Safe
|
1244
|
+
# Interactor 13.770k (± 4.3%) i/s - 68.992k in 5.020330s
|
1245
|
+
# Trailblazer::Operation 14.786k (± 5.3%) i/s - 74.600k in 5.064700s
|
1246
|
+
# Dry::Monads 70.251k (± 6.7%) i/s - 353.976k in 5.063010s
|
1247
|
+
# Dry::Transaction 4.994k (± 4.0%) i/s - 25.050k in 5.023997s
|
1248
|
+
# Micro::Case 94.620k (± 3.8%) i/s - 473.536k in 5.012483s
|
1249
|
+
# Micro::Case::Strict 76.059k (± 3.0%) i/s - 383.327k in 5.044482s
|
1250
|
+
# Micro::Case::Safe 91.719k (± 5.6%) i/s - 463.736k in 5.072552s
|
1249
1251
|
|
1250
1252
|
# Comparison:
|
1251
|
-
#
|
1252
|
-
#
|
1253
|
-
# Micro::Case::Strict:
|
1254
|
-
# Dry::Monads:
|
1255
|
-
# Trailblazer::Operation:
|
1256
|
-
# Interactor:
|
1257
|
-
# Dry::Transaction:
|
1253
|
+
# Micro::Case: 94619.6 i/s
|
1254
|
+
# Micro::Case::Safe: 91719.4 i/s - same-ish: difference falls within error
|
1255
|
+
# Micro::Case::Strict: 76058.7 i/s - 1.24x (± 0.00) slower
|
1256
|
+
# Dry::Monads: 70250.6 i/s - 1.35x (± 0.00) slower
|
1257
|
+
# Trailblazer::Operation: 14786.1 i/s - 6.40x (± 0.00) slower
|
1258
|
+
# Interactor: 13770.0 i/s - 6.87x (± 0.00) slower
|
1259
|
+
# Dry::Transaction: 4994.4 i/s - 18.95x (± 0.00) slower
|
1258
1260
|
```
|
1259
1261
|
</details>
|
1260
1262
|
|
1261
|
-
https://github.com/serradura/u-case/blob/
|
1263
|
+
https://github.com/serradura/u-case/blob/main/benchmarks/use_case/with_failure_result.rb
|
1262
1264
|
|
1263
1265
|
---
|
1264
1266
|
|
1265
|
-
### `Micro::
|
1267
|
+
### `Micro::Cases::Flow` (v3.0.0)
|
1266
1268
|
|
1267
|
-
| Gems / Abstraction | [Success results](https://github.com/serradura/u-case/blob/
|
1268
|
-
|
|
1269
|
-
| Micro::Case
|
1270
|
-
| Micro::Case
|
1271
|
-
|
|
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 `then` method | 1.48x slower | 0x slower |
|
1273
|
+
| Micro::Cases.flow | 1.62x slower | 1.16x slower |
|
1274
|
+
| Micro::Cases.safe_flow | 1.64x slower | 1.16x slower |
|
1275
|
+
| Interactor::Organizer | 1.95x slower | 6.17x slower |
|
1272
1276
|
|
1273
|
-
\* The `Dry::Monads`, `Dry::Transaction`, `Trailblazer::Operation` are out of this analysis because all of them doesn't have this kind of feature.
|
1277
|
+
\* The `Dry::Monads`, `Dry::Transaction`, `Trailblazer::Operation` gems are out of this analysis because all of them doesn't have this kind of feature.
|
1274
1278
|
|
1275
1279
|
<details>
|
1276
1280
|
<summary><strong>Success results</strong> - Show the full benchmark/ips results.</summary>
|
1277
1281
|
|
1278
1282
|
```ruby
|
1279
1283
|
# Warming up --------------------------------------
|
1280
|
-
# Interactor::Organizer
|
1281
|
-
#
|
1282
|
-
# Micro::
|
1284
|
+
# Interactor::Organizer 5.219k i/100ms
|
1285
|
+
# Micro::Cases.flow([]) 6.451k i/100ms
|
1286
|
+
# Micro::Cases::safe_flow([]) 6.421k i/100ms
|
1287
|
+
# Micro::Case flow using `then` method 7.139k i/100ms
|
1288
|
+
# Micro::Case flow using private methods 10.355k i/100ms
|
1289
|
+
|
1283
1290
|
# Calculating -------------------------------------
|
1284
|
-
# Interactor::Organizer
|
1285
|
-
#
|
1286
|
-
# Micro::
|
1291
|
+
# Interactor::Organizer 52.959k (± 1.7%) i/s - 266.169k in 5.027332s
|
1292
|
+
# Micro::Cases.flow([]) 63.947k (± 1.7%) i/s - 322.550k in 5.045597s
|
1293
|
+
# Micro::Cases::safe_flow([]) 63.047k (± 3.1%) i/s - 321.050k in 5.097228s
|
1294
|
+
# Micro::Case flow using `then` method 69.644k (± 4.0%) i/s - 349.811k in 5.031120s
|
1295
|
+
# Micro::Case flow using private methods 103.297k (± 1.4%) i/s - 517.750k in 5.013254s
|
1287
1296
|
|
1288
1297
|
# Comparison:
|
1289
|
-
#
|
1290
|
-
# Micro::Case
|
1291
|
-
#
|
1298
|
+
# Micro::Case flow using private methods: 103297.4 i/s
|
1299
|
+
# Micro::Case flow using `then` method: 69644.0 i/s - 1.48x (± 0.00) slower
|
1300
|
+
# Micro::Cases.flow([]): 63946.7 i/s - 1.62x (± 0.00) slower
|
1301
|
+
# Micro::Cases::safe_flow([]): 63047.2 i/s - 1.64x (± 0.00) slower
|
1302
|
+
# Interactor::Organizer: 52958.9 i/s - 1.95x (± 0.00) slower
|
1292
1303
|
```
|
1293
1304
|
</details>
|
1294
1305
|
|
@@ -1297,30 +1308,36 @@ https://github.com/serradura/u-case/blob/master/benchmarks/use_case/with_failure
|
|
1297
1308
|
|
1298
1309
|
```ruby
|
1299
1310
|
# Warming up --------------------------------------
|
1300
|
-
# Interactor::Organizer
|
1301
|
-
#
|
1302
|
-
# Micro::
|
1311
|
+
# Interactor::Organizer 2.381k i/100ms
|
1312
|
+
# Micro::Cases.flow([]) 12.003k i/100ms
|
1313
|
+
# Micro::Cases::safe_flow([]) 12.771k i/100ms
|
1314
|
+
# Micro::Case flow using `then` method 15.085k i/100ms
|
1315
|
+
# Micro::Case flow using private methods 14.254k i/100ms
|
1303
1316
|
|
1304
1317
|
# Calculating -------------------------------------
|
1305
|
-
# Interactor::Organizer
|
1306
|
-
#
|
1307
|
-
# Micro::
|
1318
|
+
# Interactor::Organizer 23.579k (± 3.2%) i/s - 119.050k in 5.054410s
|
1319
|
+
# Micro::Cases.flow([]) 124.072k (± 3.4%) i/s - 624.156k in 5.036618s
|
1320
|
+
# Micro::Cases::safe_flow([]) 124.894k (± 3.6%) i/s - 625.779k in 5.017494s
|
1321
|
+
# Micro::Case flow using `then` method 145.370k (± 4.8%) i/s - 739.165k in 5.096972s
|
1322
|
+
# Micro::Case flow using private methods 139.753k (± 5.6%) i/s - 698.446k in 5.015207s
|
1308
1323
|
|
1309
1324
|
# Comparison:
|
1310
|
-
# Micro::Case
|
1311
|
-
#
|
1312
|
-
#
|
1325
|
+
# Micro::Case flow using `then` method: 145369.7 i/s
|
1326
|
+
# Micro::Case flow using private methods: 139753.4 i/s - same-ish: difference falls within error
|
1327
|
+
# Micro::Cases::safe_flow([]): 124893.7 i/s - 1.16x (± 0.00) slower
|
1328
|
+
# Micro::Cases.flow([]): 124071.8 i/s - 1.17x (± 0.00) slower
|
1329
|
+
# Interactor::Organizer: 23578.7 i/s - 6.17x (± 0.00) slower
|
1313
1330
|
```
|
1314
1331
|
</details>
|
1315
1332
|
|
1316
|
-
https://github.com/serradura/u-case/tree/
|
1333
|
+
https://github.com/serradura/u-case/tree/main/benchmarks/flow
|
1317
1334
|
|
1318
1335
|
### Comparisons
|
1319
1336
|
|
1320
1337
|
Check it out implementations of the same use case with different gems/abstractions.
|
1321
1338
|
|
1322
|
-
* [interactor](https://github.com/serradura/u-case/blob/
|
1323
|
-
* [u-case](https://github.com/serradura/u-case/blob/
|
1339
|
+
* [interactor](https://github.com/serradura/u-case/blob/main/comparisons/interactor.rb)
|
1340
|
+
* [u-case](https://github.com/serradura/u-case/blob/main/comparisons/u-case.rb)
|
1324
1341
|
|
1325
1342
|
[⬆️ Back to Top](#table-of-contents-)
|
1326
1343
|
|
@@ -1328,7 +1345,7 @@ Check it out implementations of the same use case with different gems/abstractio
|
|
1328
1345
|
|
1329
1346
|
### 1️⃣ Rails App (API)
|
1330
1347
|
|
1331
|
-
> 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.
|
1348
|
+
> 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.
|
1332
1349
|
>
|
1333
1350
|
> Link: https://github.com/serradura/from-fat-controllers-to-use-cases
|
1334
1351
|
|
@@ -1336,17 +1353,17 @@ Check it out implementations of the same use case with different gems/abstractio
|
|
1336
1353
|
|
1337
1354
|
> Rake tasks to demonstrate how to handle user data, and how to use different failure types to control the program flow.
|
1338
1355
|
>
|
1339
|
-
> Link: https://github.com/serradura/u-case/tree/
|
1356
|
+
> Link: https://github.com/serradura/u-case/tree/main/examples/calculator
|
1340
1357
|
|
1341
1358
|
### 3️⃣ Users creation
|
1342
1359
|
|
1343
|
-
> An example of a use case flow that
|
1360
|
+
> An example of a use case flow that defines steps to sanitize, validate, and persist its input data.
|
1344
1361
|
>
|
1345
|
-
> Link: https://github.com/serradura/u-case/blob/
|
1362
|
+
> Link: https://github.com/serradura/u-case/blob/main/examples/users_creation.rb
|
1346
1363
|
|
1347
|
-
### 4️⃣ Rescuing
|
1364
|
+
### 4️⃣ Rescuing exceptions inside of the use cases
|
1348
1365
|
|
1349
|
-
> Link: https://github.com/serradura/u-case/blob/
|
1366
|
+
> Link: https://github.com/serradura/u-case/blob/main/examples/rescuing_exceptions.rb
|
1350
1367
|
|
1351
1368
|
[⬆️ Back to Top](#table-of-contents-)
|
1352
1369
|
|
@@ -1366,4 +1383,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
1366
1383
|
|
1367
1384
|
## Code of Conduct
|
1368
1385
|
|
1369
|
-
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/
|
1386
|
+
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).
|