u-case 3.0.0.rc2 → 3.0.0.rc7
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 +274 -259
- 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 +4 -6
- data/lib/micro/case/result.rb +89 -44
- 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 +2 -2
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0102a68d35e94a3fec4c86a68289412a97d8aa5b9bf960b3dc08cc122b990f80
|
4
|
+
data.tar.gz: 7183f0447bb6b79315bf9425d3ec77037bc1b7a99384cdf8b3aa3e061cb9ea8b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c9403cbe651b87a63ac73e65c7dc72158a095f1edfd5789ed84e4024418f7b2ca34d99f3319f098459ec22c9211b0d372694fddb9242e53fbdae5cf3f59edd7
|
7
|
+
data.tar.gz: 65194dbc56bf9f6ae785c6980643b625a8d8c96be63700ea1b10343e5cac3fa317eb4aee4a0d787116a64738dcc26c6b5ed7beeda67de09a006c48868d194b1d
|
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.rc7 | 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,46 +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)
|
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.rc7 | 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 @@ Version | Documentation
|
|
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 @@ Version | Documentation
|
|
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.rc7'
|
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,7 +503,7 @@ 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
|
@@ -553,8 +555,7 @@ Todo::FindAllForUser
|
|
553
555
|
|
554
556
|
### `Micro::Cases::Flow` - How to compose use cases?
|
555
557
|
|
556
|
-
|
557
|
-
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.
|
558
559
|
|
559
560
|
```ruby
|
560
561
|
module Steps
|
@@ -609,24 +610,23 @@ result = Add2ToAllNumbers.call(numbers: %w[1 1 2 2 3 4])
|
|
609
610
|
result.success? # true
|
610
611
|
result.data # {:numbers => [3, 3, 4, 4, 5, 6]}
|
611
612
|
|
612
|
-
|
613
|
-
#
|
614
|
-
|
613
|
+
#-------------------------------#
|
614
|
+
# Creating a flow using classes #
|
615
|
+
#-------------------------------#
|
615
616
|
|
616
617
|
class DoubleAllNumbers < Micro::Case
|
617
618
|
flow Steps::ConvertTextToNumbers,
|
618
619
|
Steps::Double
|
619
620
|
end
|
620
621
|
|
621
|
-
DoubleAllNumbers
|
622
|
-
|
623
|
-
|
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
|
+
```
|
624
626
|
|
625
|
-
|
626
|
-
# ----
|
627
|
-
# When happening a failure, the use case responsible
|
628
|
-
# will be accessible in the result
|
627
|
+
When happening a failure, the use case responsible will be accessible in the result.
|
629
628
|
|
629
|
+
```ruby
|
630
630
|
result = DoubleAllNumbers.call(numbers: %w[1 1 b 2 3 4])
|
631
631
|
|
632
632
|
result.failure? # true
|
@@ -639,7 +639,7 @@ end
|
|
639
639
|
|
640
640
|
[⬆️ Back to Top](#table-of-contents-)
|
641
641
|
|
642
|
-
#### Is it possible to compose a
|
642
|
+
#### Is it possible to compose a flow with other flows?
|
643
643
|
|
644
644
|
Answer: Yes, it is possible.
|
645
645
|
|
@@ -702,20 +702,20 @@ DoubleAllNumbersAndSquareAndAdd2 =
|
|
702
702
|
|
703
703
|
SquareAllNumbersAndDouble
|
704
704
|
.call(numbers: %w[1 1 2 2 3 4])
|
705
|
-
.on_success { |
|
705
|
+
.on_success { |result| p result[:numbers] } # [6, 6, 12, 12, 22, 36]
|
706
706
|
|
707
707
|
DoubleAllNumbersAndSquareAndAdd2
|
708
708
|
.call(numbers: %w[1 1 2 2 3 4])
|
709
|
-
.on_success { |
|
709
|
+
.on_success { |result| p result[:numbers] } # [6, 6, 18, 18, 38, 66]
|
710
710
|
```
|
711
711
|
|
712
|
-
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).
|
713
713
|
|
714
714
|
[⬆️ Back to Top](#table-of-contents-)
|
715
715
|
|
716
716
|
#### Is it possible a flow accumulates its input and merges each success result to use as the argument of the next use cases?
|
717
717
|
|
718
|
-
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.
|
719
719
|
|
720
720
|
```ruby
|
721
721
|
module Users
|
@@ -759,7 +759,7 @@ Users::Authenticate
|
|
759
759
|
.on_failure(:user_not_found) { render status: 404 }
|
760
760
|
```
|
761
761
|
|
762
|
-
First,
|
762
|
+
First, let's see the attributes used by each use case:
|
763
763
|
|
764
764
|
```ruby
|
765
765
|
class Users::FindByEmail < Micro::Case
|
@@ -772,14 +772,13 @@ end
|
|
772
772
|
```
|
773
773
|
|
774
774
|
As you can see the `Users::ValidatePassword` expects a user as its input. So, how does it receives the user?
|
775
|
-
It receives the user from the `Users::FindByEmail` success result!
|
775
|
+
Answer: It receives the user from the `Users::FindByEmail` success result!
|
776
776
|
|
777
|
-
And this
|
778
|
-
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!
|
779
778
|
|
780
779
|
> input **>>** process **>>** output
|
781
780
|
|
782
|
-
> **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.
|
783
782
|
|
784
783
|
[⬆️ Back to Top](#table-of-contents-)
|
785
784
|
|
@@ -828,14 +827,12 @@ user_authenticated.transitions
|
|
828
827
|
```
|
829
828
|
|
830
829
|
The example above shows the output generated by the `Micro::Case::Result#transitions`.
|
831
|
-
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.
|
832
831
|
|
833
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).
|
834
833
|
|
835
834
|
> **Note:** The [`Micro::Case::Result#then`](#how-to-use-the-microcaseresultthen-method) increments the `Micro::Case::Result#transitions`.
|
836
835
|
|
837
|
-
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.
|
838
|
-
|
839
836
|
##### `Micro::Case::Result#transitions` schema
|
840
837
|
```ruby
|
841
838
|
[
|
@@ -847,18 +844,22 @@ PS: Use the `Micro::Case::Result.disable_transition_tracking` feature toggle to
|
|
847
844
|
[success:, failure:] => { # (Output)
|
848
845
|
type: <Symbol>, # Result type. Defaults:
|
849
846
|
# Success = :ok, Failure = :error/:exception
|
850
|
-
result: <Hash> # The data returned by the use case
|
847
|
+
result: <Hash> # The data returned by the use case result
|
851
848
|
},
|
852
849
|
accessible_attributes: <Array>, # Properties that can be accessed by the use case's attributes,
|
853
|
-
#
|
854
|
-
# 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.
|
855
852
|
}
|
856
853
|
]
|
857
854
|
```
|
858
855
|
|
859
|
-
|
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.
|
860
859
|
|
861
|
-
|
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:
|
862
863
|
|
863
864
|
```ruby
|
864
865
|
class ConvertTextToNumber < Micro::Case
|
@@ -893,16 +894,15 @@ result = Double.call(text: '4')
|
|
893
894
|
|
894
895
|
result.success? # true
|
895
896
|
result[:number] # "8"
|
896
|
-
|
897
|
-
# NOTE: This feature can be used with the Micro::Case::Safe.
|
898
|
-
# Checkout this test: https://github.com/serradura/u-case/blob/714c6b658fc6aa02617e6833ddee09eddc760f2a/test/micro/case/safe/with_inner_flow_test.rb
|
899
897
|
```
|
900
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
|
+
|
901
901
|
[⬆️ Back to Top](#table-of-contents-)
|
902
902
|
|
903
903
|
### `Micro::Case::Strict` - What is a strict use case?
|
904
904
|
|
905
|
-
Answer:
|
905
|
+
Answer: it is a kind of use case that will require all the keywords (attributes) on its initialization.
|
906
906
|
|
907
907
|
```ruby
|
908
908
|
class Double < Micro::Case::Strict
|
@@ -915,7 +915,7 @@ end
|
|
915
915
|
|
916
916
|
Double.call({})
|
917
917
|
|
918
|
-
# The output will be
|
918
|
+
# The output will be:
|
919
919
|
# ArgumentError (missing keyword: :numbers)
|
920
920
|
```
|
921
921
|
|
@@ -923,11 +923,7 @@ Double.call({})
|
|
923
923
|
|
924
924
|
### `Micro::Case::Safe` - Is there some feature to auto handle exceptions inside of a use case or flow?
|
925
925
|
|
926
|
-
|
927
|
-
|
928
|
-
**Use cases:**
|
929
|
-
|
930
|
-
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:
|
931
927
|
|
932
928
|
```ruby
|
933
929
|
require 'logger'
|
@@ -951,28 +947,24 @@ result.type == :exception # true
|
|
951
947
|
result.data # { exception: #<ZeroDivisionError...> }
|
952
948
|
result[:exception].is_a?(ZeroDivisionError) # true
|
953
949
|
|
954
|
-
result.on_failure(:exception) do |
|
955
|
-
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
|
956
952
|
end
|
953
|
+
```
|
957
954
|
|
958
|
-
|
959
|
-
# ----
|
960
|
-
# If you need to handle a specific error,
|
961
|
-
# 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:
|
962
956
|
|
963
|
-
|
964
|
-
|
957
|
+
```ruby
|
958
|
+
result.on_failure(:exception) do |data, use_case|
|
959
|
+
case exception = data[:exception]
|
965
960
|
when ZeroDivisionError then AppLogger.error(exception.message)
|
966
961
|
else AppLogger.debug("#{use_case.class.name} was the use case responsible for the exception")
|
967
962
|
end
|
968
963
|
end
|
969
|
-
|
970
|
-
# Another note:
|
971
|
-
# ------------
|
972
|
-
# It is possible to rescue an exception even when is a safe use case.
|
973
|
-
# Examples: https://github.com/serradura/u-case/blob/714c6b658fc6aa02617e6833ddee09eddc760f2a/test/micro/case/safe_test.rb#L90-L118
|
974
964
|
```
|
975
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
|
+
|
976
968
|
[⬆️ Back to Top](#table-of-contents-)
|
977
969
|
|
978
970
|
#### `Micro::Cases::Safe::Flow`
|
@@ -988,9 +980,11 @@ module Users
|
|
988
980
|
SendToCRM
|
989
981
|
])
|
990
982
|
end
|
983
|
+
```
|
991
984
|
|
992
|
-
|
985
|
+
Defining within classes:
|
993
986
|
|
987
|
+
```ruby
|
994
988
|
module Users
|
995
989
|
class Create < Micro::Case::Safe
|
996
990
|
flow ProcessParams,
|
@@ -1009,9 +1003,9 @@ In functional programming errors/exceptions are handled as regular data, the ide
|
|
1009
1003
|
|
1010
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.
|
1011
1005
|
|
1012
|
-
> **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.
|
1013
1007
|
|
1014
|
-
How does it work
|
1008
|
+
**How does it work?**
|
1015
1009
|
|
1016
1010
|
```ruby
|
1017
1011
|
class Divide < Micro::Case::Safe
|
@@ -1047,21 +1041,19 @@ Divide.
|
|
1047
1041
|
# Oh no, something went wrong!
|
1048
1042
|
```
|
1049
1043
|
|
1050
|
-
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.
|
1051
1045
|
|
1052
1046
|
[⬆️ Back to Top](#table-of-contents-)
|
1053
1047
|
|
1054
|
-
### `u-case/with_activemodel_validation` - How to validate use case attributes?
|
1048
|
+
### `u-case/with_activemodel_validation` - How to validate the use case attributes?
|
1055
1049
|
|
1056
1050
|
**Requirement:**
|
1057
1051
|
|
1058
1052
|
To do this your application must have the [activemodel >= 3.2, < 6.1.0](https://rubygems.org/gems/activemodel) as a dependency.
|
1059
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
|
+
|
1060
1056
|
```ruby
|
1061
|
-
#
|
1062
|
-
# By default, if your application has the activemodel as a dependency,
|
1063
|
-
# any kind of use case can use it to validate their attributes.
|
1064
|
-
#
|
1065
1057
|
class Multiply < Micro::Case
|
1066
1058
|
attributes :a, :b
|
1067
1059
|
|
@@ -1073,18 +1065,22 @@ class Multiply < Micro::Case
|
|
1073
1065
|
Success result: { number: a * b }
|
1074
1066
|
end
|
1075
1067
|
end
|
1068
|
+
```
|
1076
1069
|
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
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
|
1073
|
+
|
1074
|
+
```ruby
|
1075
|
+
gem 'u-case', require: 'u-case/with_activemodel_validation'
|
1076
|
+
```
|
1080
1077
|
|
1081
|
-
|
1082
|
-
require 'u-case/with_activemodel_validation' # or require 'micro/case/with_validation'
|
1078
|
+
2. Use the `Micro::Case.config` to enable it. [Link to](#microcaseconfig) this section.
|
1083
1079
|
|
1084
|
-
|
1085
|
-
gem 'u-case', require: 'u-case/with_activemodel_validation'
|
1080
|
+
Using this approach, you can rewrite the previous example with less code. e.g:
|
1086
1081
|
|
1087
|
-
|
1082
|
+
```ruby
|
1083
|
+
require 'u-case/with_activemodel_validation'
|
1088
1084
|
|
1089
1085
|
class Multiply < Micro::Case
|
1090
1086
|
attributes :a, :b
|
@@ -1095,16 +1091,13 @@ class Multiply < Micro::Case
|
|
1095
1091
|
Success result: { number: a * b }
|
1096
1092
|
end
|
1097
1093
|
end
|
1098
|
-
|
1099
|
-
# Note:
|
1100
|
-
# ----
|
1101
|
-
# After requiring the validation mode, the
|
1102
|
-
# Micro::Case::Strict and Micro::Case::Safe classes will inherit this new behavior.
|
1103
1094
|
```
|
1104
1095
|
|
1105
|
-
|
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?
|
1106
1099
|
|
1107
|
-
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:
|
1108
1101
|
|
1109
1102
|
```ruby
|
1110
1103
|
require 'u-case/with_activemodel_validation'
|
@@ -1123,7 +1116,7 @@ end
|
|
1123
1116
|
|
1124
1117
|
Multiply.call(a: 2, b: 'a')
|
1125
1118
|
|
1126
|
-
# The output will be
|
1119
|
+
# The output will be:
|
1127
1120
|
# TypeError (String can't be coerced into Integer)
|
1128
1121
|
```
|
1129
1122
|
|
@@ -1131,9 +1124,9 @@ Multiply.call(a: 2, b: 'a')
|
|
1131
1124
|
|
1132
1125
|
#### `Kind::Validator`
|
1133
1126
|
|
1134
|
-
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).
|
1135
1128
|
|
1136
|
-
The example below shows how to validate the attributes
|
1129
|
+
The example below shows how to validate the attributes types.
|
1137
1130
|
|
1138
1131
|
```ruby
|
1139
1132
|
class Todo::List::AddItem < Micro::Case
|
@@ -1154,143 +1147,159 @@ class Todo::List::AddItem < Micro::Case
|
|
1154
1147
|
end
|
1155
1148
|
```
|
1156
1149
|
|
1157
|
-
|
1150
|
+
[⬆️ Back to Top](#table-of-contents-)
|
1158
1151
|
|
1159
|
-
|
1152
|
+
## `Micro::Case.config`
|
1160
1153
|
|
1161
|
-
|
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.
|
1162
1156
|
|
1163
|
-
|
1157
|
+
You can see below, which are the available configurations with their default values:
|
1164
1158
|
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
| Interactor | 21342.3 | 4.93x slower |
|
1170
|
-
| Trailblazer::Operation | 14652.7 | 7.17x slower |
|
1171
|
-
| 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
|
1172
1163
|
|
1173
|
-
|
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)
|
1174
1174
|
|
1175
1175
|
#### Success results
|
1176
1176
|
|
1177
1177
|
| Gem / Abstraction | Iterations per second | Comparison |
|
1178
1178
|
| ----------------- | --------------------: | ----------------: |
|
1179
|
-
| Dry::Monads |
|
1180
|
-
| **Micro::Case** |
|
1181
|
-
| Interactor |
|
1182
|
-
| Trailblazer::Operation |
|
1183
|
-
| 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 |
|
1184
1184
|
|
1185
1185
|
<details>
|
1186
1186
|
<summary>Show the full <a href="https://github.com/evanphx/benchmark-ips">benchmark/ips</a> results.</summary>
|
1187
1187
|
|
1188
1188
|
```ruby
|
1189
1189
|
# Warming up --------------------------------------
|
1190
|
-
# Interactor
|
1191
|
-
# Trailblazer::Operation 1.
|
1192
|
-
# Dry::Monads
|
1193
|
-
# Dry::Transaction
|
1194
|
-
# Micro::Case 10.
|
1195
|
-
# Micro::Case::Strict
|
1196
|
-
# 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
|
1197
1197
|
|
1198
1198
|
# Calculating -------------------------------------
|
1199
|
-
# Interactor
|
1200
|
-
# Trailblazer::Operation 14.
|
1201
|
-
# Dry::Monads
|
1202
|
-
# Dry::Transaction 5.
|
1203
|
-
# Micro::Case
|
1204
|
-
# Micro::Case::Strict
|
1205
|
-
# 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
|
1206
1206
|
|
1207
1207
|
# Comparison:
|
1208
|
-
# Dry::Monads:
|
1209
|
-
#
|
1210
|
-
#
|
1211
|
-
# Micro::Case::Strict:
|
1212
|
-
# Interactor:
|
1213
|
-
# Trailblazer::Operation:
|
1214
|
-
# 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
|
1215
1215
|
```
|
1216
1216
|
</details>
|
1217
1217
|
|
1218
|
-
https://github.com/serradura/u-case/blob/
|
1218
|
+
https://github.com/serradura/u-case/blob/main/benchmarks/use_case/with_success_result.rb
|
1219
1219
|
|
1220
1220
|
#### Failure results
|
1221
1221
|
|
1222
1222
|
| Gem / Abstraction | Iterations per second | Comparison |
|
1223
1223
|
| ----------------- | --------------------: | ----------------: |
|
1224
|
-
| **Micro::Case** |
|
1225
|
-
| Dry::Monads |
|
1226
|
-
| Trailblazer::Operation |
|
1227
|
-
| Interactor |
|
1228
|
-
| 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 |
|
1229
1229
|
|
1230
1230
|
<details>
|
1231
1231
|
<summary>Show the full <a href="https://github.com/evanphx/benchmark-ips">benchmark/ips</a> results.</summary>
|
1232
1232
|
|
1233
1233
|
```ruby
|
1234
1234
|
# Warming up --------------------------------------
|
1235
|
-
# Interactor 1.
|
1236
|
-
# Trailblazer::Operation 1.
|
1237
|
-
# Dry::Monads 7.
|
1238
|
-
# Dry::Transaction
|
1239
|
-
# Micro::Case 9.
|
1240
|
-
# Micro::Case::Strict
|
1241
|
-
# 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
|
1242
1242
|
|
1243
1243
|
# Calculating -------------------------------------
|
1244
|
-
# Interactor 13.
|
1245
|
-
# Trailblazer::Operation 14.
|
1246
|
-
# Dry::Monads
|
1247
|
-
# Dry::Transaction 4.
|
1248
|
-
# Micro::Case
|
1249
|
-
# Micro::Case::Strict
|
1250
|
-
# 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
|
1251
1251
|
|
1252
1252
|
# Comparison:
|
1253
|
-
#
|
1254
|
-
#
|
1255
|
-
# Micro::Case::Strict:
|
1256
|
-
# Dry::Monads:
|
1257
|
-
# Trailblazer::Operation:
|
1258
|
-
# Interactor:
|
1259
|
-
# 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
|
1260
1260
|
```
|
1261
1261
|
</details>
|
1262
1262
|
|
1263
|
-
https://github.com/serradura/u-case/blob/
|
1263
|
+
https://github.com/serradura/u-case/blob/main/benchmarks/use_case/with_failure_result.rb
|
1264
1264
|
|
1265
1265
|
---
|
1266
1266
|
|
1267
|
-
### `Micro::
|
1267
|
+
### `Micro::Cases::Flow` (v3.0.0)
|
1268
1268
|
|
1269
|
-
| Gems / Abstraction | [Success results](https://github.com/serradura/u-case/blob/
|
1270
|
-
|
|
1271
|
-
| Micro::Case
|
1272
|
-
| Micro::Case
|
1273
|
-
|
|
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 |
|
1274
1276
|
|
1275
|
-
\* 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.
|
1276
1278
|
|
1277
1279
|
<details>
|
1278
1280
|
<summary><strong>Success results</strong> - Show the full benchmark/ips results.</summary>
|
1279
1281
|
|
1280
1282
|
```ruby
|
1281
1283
|
# Warming up --------------------------------------
|
1282
|
-
# Interactor::Organizer
|
1283
|
-
#
|
1284
|
-
# 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
|
+
|
1285
1290
|
# Calculating -------------------------------------
|
1286
|
-
# Interactor::Organizer
|
1287
|
-
#
|
1288
|
-
# 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
|
1289
1296
|
|
1290
1297
|
# Comparison:
|
1291
|
-
#
|
1292
|
-
# Micro::Case
|
1293
|
-
#
|
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
|
1294
1303
|
```
|
1295
1304
|
</details>
|
1296
1305
|
|
@@ -1299,30 +1308,36 @@ https://github.com/serradura/u-case/blob/master/benchmarks/use_case/with_failure
|
|
1299
1308
|
|
1300
1309
|
```ruby
|
1301
1310
|
# Warming up --------------------------------------
|
1302
|
-
# Interactor::Organizer
|
1303
|
-
#
|
1304
|
-
# 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
|
1305
1316
|
|
1306
1317
|
# Calculating -------------------------------------
|
1307
|
-
# Interactor::Organizer
|
1308
|
-
#
|
1309
|
-
# 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
|
1310
1323
|
|
1311
1324
|
# Comparison:
|
1312
|
-
# Micro::Case
|
1313
|
-
#
|
1314
|
-
#
|
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
|
1315
1330
|
```
|
1316
1331
|
</details>
|
1317
1332
|
|
1318
|
-
https://github.com/serradura/u-case/tree/
|
1333
|
+
https://github.com/serradura/u-case/tree/main/benchmarks/flow
|
1319
1334
|
|
1320
1335
|
### Comparisons
|
1321
1336
|
|
1322
1337
|
Check it out implementations of the same use case with different gems/abstractions.
|
1323
1338
|
|
1324
|
-
* [interactor](https://github.com/serradura/u-case/blob/
|
1325
|
-
* [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)
|
1326
1341
|
|
1327
1342
|
[⬆️ Back to Top](#table-of-contents-)
|
1328
1343
|
|
@@ -1330,7 +1345,7 @@ Check it out implementations of the same use case with different gems/abstractio
|
|
1330
1345
|
|
1331
1346
|
### 1️⃣ Rails App (API)
|
1332
1347
|
|
1333
|
-
> 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.
|
1334
1349
|
>
|
1335
1350
|
> Link: https://github.com/serradura/from-fat-controllers-to-use-cases
|
1336
1351
|
|
@@ -1338,17 +1353,17 @@ Check it out implementations of the same use case with different gems/abstractio
|
|
1338
1353
|
|
1339
1354
|
> Rake tasks to demonstrate how to handle user data, and how to use different failure types to control the program flow.
|
1340
1355
|
>
|
1341
|
-
> Link: https://github.com/serradura/u-case/tree/
|
1356
|
+
> Link: https://github.com/serradura/u-case/tree/main/examples/calculator
|
1342
1357
|
|
1343
1358
|
### 3️⃣ Users creation
|
1344
1359
|
|
1345
|
-
> 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.
|
1346
1361
|
>
|
1347
|
-
> Link: https://github.com/serradura/u-case/blob/
|
1362
|
+
> Link: https://github.com/serradura/u-case/blob/main/examples/users_creation.rb
|
1348
1363
|
|
1349
|
-
### 4️⃣ Rescuing
|
1364
|
+
### 4️⃣ Rescuing exceptions inside of the use cases
|
1350
1365
|
|
1351
|
-
> Link: https://github.com/serradura/u-case/blob/
|
1366
|
+
> Link: https://github.com/serradura/u-case/blob/main/examples/rescuing_exceptions.rb
|
1352
1367
|
|
1353
1368
|
[⬆️ Back to Top](#table-of-contents-)
|
1354
1369
|
|
@@ -1368,4 +1383,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
1368
1383
|
|
1369
1384
|
## Code of Conduct
|
1370
1385
|
|
1371
|
-
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).
|