u-case 3.0.0.rc4 → 3.0.0.rc5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -3
- data/README.md +201 -209
- data/README.pt-BR.md +1390 -0
- data/lib/micro/case.rb +18 -7
- data/lib/micro/case/result.rb +18 -3
- data/lib/micro/case/utils.rb +7 -0
- data/lib/micro/case/version.rb +1 -1
- data/lib/micro/cases/flow.rb +18 -24
- data/u-case.gemspec +2 -2
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 12b63a467f7516217f29a5705859141be80fc2fb2209c41e77d7c8a365745b8c
|
4
|
+
data.tar.gz: 4501314217a04cca7d56c0091a8a2c02f93f308f51c323462af36c71532be1b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85351b75b4e2e2e62e66f2dff078a607ffa29e5c0df93dc3f6ce07dbfd0444ecd3034358fc08b358698c7ca2ccd73dbc5b8ab597ce339c5d7f4970df504b8a99
|
7
|
+
data.tar.gz: 5ef5552ca31fc9d9474f9a54717c7ce9a45ad9aba46710bf6671ae5836a492981beb4bee3cbdfbb5418770bcd896a634846eb659b5eaf1c098d6fa0c9339dc7b
|
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,7 +36,7 @@ pry_version =
|
|
37
36
|
group :development, :test do
|
38
37
|
gem 'awesome_print', '~> 1.8'
|
39
38
|
|
40
|
-
gem 'byebug', '~> 10.0', '>= 10.0.2' if RUBY_VERSION =~ /\A2.
|
39
|
+
gem 'byebug', '~> 10.0', '>= 10.0.2' if RUBY_VERSION =~ /\A2.[23]/
|
41
40
|
|
42
41
|
gem 'pry', "~> #{pry_version}"
|
43
42
|
gem 'pry-byebug', "~> #{pry_byebug_version}"
|
data/README.md
CHANGED
@@ -4,30 +4,31 @@
|
|
4
4
|
[![Maintainability](https://api.codeclimate.com/v1/badges/5c3c8ad1b0b943f88efd/maintainability)](https://codeclimate.com/github/serradura/u-case/maintainability)
|
5
5
|
[![Test Coverage](https://api.codeclimate.com/v1/badges/5c3c8ad1b0b943f88efd/test_coverage)](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.rc5 | https://github.com/serradura/u-case/blob/master/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,49 +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 hook usage without a type exposes the result itself?](#why-the-hook-usage-without-a-type-exposes-the-result-itself)
|
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)
|
41
42
|
- [Using decomposition to access the result data and type](#using-decomposition-to-access-the-result-data-and-type)
|
42
43
|
- [What happens if a result hook was declared multiple times?](#what-happens-if-a-result-hook-was-declared-multiple-times)
|
43
44
|
- [How to use the `Micro::Case::Result#then` method?](#how-to-use-the-microcaseresultthen-method)
|
44
45
|
- [What does happens when a `Micro::Case::Result#then` receives a block?](#what-does-happens-when-a-microcaseresultthen-receives-a-block)
|
45
46
|
- [How to make attributes data injection using this feature?](#how-to-make-attributes-data-injection-using-this-feature)
|
46
47
|
- [`Micro::Cases::Flow` - How to compose use cases?](#microcasesflow---how-to-compose-use-cases)
|
47
|
-
- [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)
|
48
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)
|
49
50
|
- [How to understand what is happening during a flow execution?](#how-to-understand-what-is-happening-during-a-flow-execution)
|
50
51
|
- [`Micro::Case::Result#transitions` schema](#microcaseresulttransitions-schema)
|
51
52
|
- [Is it possible disable the `Micro::Case::Result#transitions`?](#is-it-possible-disable-the-microcaseresulttransitions)
|
52
|
-
- [Is it possible to declare a flow
|
53
|
+
- [Is it possible to declare a flow that includes the use case itself as a step?](#is-it-possible-to-declare-a-flow-that-includes-the-use-case-itself-as-a-step)
|
53
54
|
- [`Micro::Case::Strict` - What is a strict use case?](#microcasestrict---what-is-a-strict-use-case)
|
54
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)
|
55
56
|
- [`Micro::Cases::Safe::Flow`](#microcasessafeflow)
|
56
57
|
- [`Micro::Case::Result#on_exception`](#microcaseresulton_exception)
|
57
|
-
- [`u-case/with_activemodel_validation` - How to validate use case attributes?](#u-casewith_activemodel_validation---how-to-validate-use-case-attributes)
|
58
|
-
- [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)
|
59
60
|
- [`Kind::Validator`](#kindvalidator)
|
60
61
|
- [`Micro::Case.config`](#microcaseconfig)
|
61
62
|
- [Benchmarks](#benchmarks)
|
62
|
-
- [`Micro::Case` (
|
63
|
-
- [Best overall](#best-overall)
|
63
|
+
- [`Micro::Case` (v3.0.0)](#microcase-v300)
|
64
64
|
- [Success results](#success-results)
|
65
65
|
- [Failure results](#failure-results)
|
66
|
-
- [`Micro::
|
66
|
+
- [`Micro::Cases::Flow` (v3.0.0)](#microcasesflow-v300)
|
67
67
|
- [Comparisons](#comparisons)
|
68
68
|
- [Examples](#examples)
|
69
69
|
- [1️⃣ Rails App (API)](#1️⃣-rails-app-api)
|
70
70
|
- [2️⃣ CLI calculator](#2️⃣-cli-calculator)
|
71
71
|
- [3️⃣ Users creation](#3️⃣-users-creation)
|
72
|
-
- [4️⃣ Rescuing
|
72
|
+
- [4️⃣ Rescuing exceptions inside of the use cases](#4️⃣-rescuing-exceptions-inside-of-the-use-cases)
|
73
73
|
- [Development](#development)
|
74
74
|
- [Contributing](#contributing)
|
75
75
|
- [License](#license)
|
76
76
|
- [Code of Conduct](#code-of-conduct)
|
77
77
|
|
78
|
-
##
|
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 |
|
79
85
|
|
80
|
-
>
|
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.
|
81
87
|
|
82
88
|
## Dependencies
|
83
89
|
|
@@ -85,7 +91,7 @@ Version | Documentation
|
|
85
91
|
|
86
92
|
A simple type system (at runtime) for Ruby.
|
87
93
|
|
88
|
-
|
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).
|
89
95
|
2. [`u-attributes`](https://github.com/serradura/u-attributes) gem.
|
90
96
|
|
91
97
|
This gem allows defining read-only attributes, that is, your objects will have only getters to access their attributes data.
|
@@ -96,7 +102,7 @@ Version | Documentation
|
|
96
102
|
Add this line to your application's Gemfile:
|
97
103
|
|
98
104
|
```ruby
|
99
|
-
gem 'u-case'
|
105
|
+
gem 'u-case', '~> 3.0.0.rc5'
|
100
106
|
```
|
101
107
|
|
102
108
|
And then execute:
|
@@ -105,7 +111,7 @@ And then execute:
|
|
105
111
|
|
106
112
|
Or install it yourself as:
|
107
113
|
|
108
|
-
$ gem install u-case
|
114
|
+
$ gem install u-case --pre
|
109
115
|
|
110
116
|
## Usage
|
111
117
|
|
@@ -119,7 +125,7 @@ class Multiply < Micro::Case
|
|
119
125
|
# 2. Define the method `call!` with its business logic
|
120
126
|
def call!
|
121
127
|
|
122
|
-
# 3. Wrap the use case
|
128
|
+
# 3. Wrap the use case output using the `Success(result: *)` or `Failure(result: *)` methods
|
123
129
|
if a.is_a?(Numeric) && b.is_a?(Numeric)
|
124
130
|
Success result: { number: a * b }
|
125
131
|
else
|
@@ -128,9 +134,9 @@ class Multiply < Micro::Case
|
|
128
134
|
end
|
129
135
|
end
|
130
136
|
|
131
|
-
|
132
|
-
#
|
133
|
-
|
137
|
+
#========================#
|
138
|
+
# Performing an use case #
|
139
|
+
#========================#
|
134
140
|
|
135
141
|
# Success result
|
136
142
|
|
@@ -148,8 +154,7 @@ bad_result.data # { message: "`a` and `b` attributes must be numeric" }
|
|
148
154
|
|
149
155
|
# Note:
|
150
156
|
# ----
|
151
|
-
# The result of a Micro::Case.call
|
152
|
-
# is an instance of Micro::Case::Result
|
157
|
+
# The result of a Micro::Case.call is an instance of Micro::Case::Result
|
153
158
|
```
|
154
159
|
|
155
160
|
[⬆️ Back to Top](#table-of-contents-)
|
@@ -163,6 +168,9 @@ A `Micro::Case::Result` stores the use cases output data. These are their main m
|
|
163
168
|
- `#type` a Symbol which gives meaning for the result, this is useful to declare different types of failures or success.
|
164
169
|
- `#data` the result data itself.
|
165
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.
|
166
174
|
- `#on_success` or `#on_failure` are hook methods that help you to define the application flow.
|
167
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.
|
168
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,9 +181,9 @@ A `Micro::Case::Result` stores the use cases output data. These are their main m
|
|
173
181
|
|
174
182
|
#### What are the default result types?
|
175
183
|
|
176
|
-
Every result has a type and these are
|
184
|
+
Every result has a type, and these are their default values:
|
177
185
|
- `:ok` when success
|
178
|
-
- `:error
|
186
|
+
- `:error` or `:exception` when failures
|
179
187
|
|
180
188
|
```ruby
|
181
189
|
class Divide < Micro::Case
|
@@ -269,9 +277,9 @@ bad_result.failure? # true
|
|
269
277
|
|
270
278
|
[⬆️ Back to Top](#table-of-contents-)
|
271
279
|
|
272
|
-
#### Is it possible to define a custom
|
280
|
+
#### Is it possible to define a custom type without a result data?
|
273
281
|
|
274
|
-
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.
|
275
283
|
|
276
284
|
```ruby
|
277
285
|
class Multiply < Micro::Case
|
@@ -303,7 +311,7 @@ result.use_case.attributes # {"a"=>2, "b"=>"2"}
|
|
303
311
|
|
304
312
|
#### How to use the result hooks?
|
305
313
|
|
306
|
-
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`.
|
307
315
|
|
308
316
|
The examples below show how to use them:
|
309
317
|
|
@@ -329,7 +337,7 @@ Double
|
|
329
337
|
.on_failure(:invalid) { |result| raise TypeError, result[:msg] }
|
330
338
|
.on_failure(:lte_zero) { |result| raise ArgumentError, result[:msg] }
|
331
339
|
|
332
|
-
# The output
|
340
|
+
# The output will be:
|
333
341
|
# 6
|
334
342
|
|
335
343
|
#=============================#
|
@@ -345,18 +353,17 @@ Double
|
|
345
353
|
|
346
354
|
# The outputs will be:
|
347
355
|
#
|
348
|
-
# 1.
|
349
|
-
# 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)
|
350
358
|
|
351
359
|
# Note:
|
352
360
|
# ----
|
353
|
-
# The use case responsible for the
|
361
|
+
# The use case responsible for the result will always be accessible as the second hook argument
|
354
362
|
```
|
355
363
|
|
356
|
-
#### Why the hook usage without a type exposes the result itself?
|
364
|
+
#### Why the hook usage without a defined type exposes the result itself?
|
357
365
|
|
358
|
-
Answer: To allow you to define how to handle the program flow using some
|
359
|
-
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`.
|
360
367
|
|
361
368
|
```ruby
|
362
369
|
class Double < Micro::Case
|
@@ -380,7 +387,7 @@ Double
|
|
380
387
|
end
|
381
388
|
end
|
382
389
|
|
383
|
-
# The output will be
|
390
|
+
# The output will be an exception:
|
384
391
|
#
|
385
392
|
# ArgumentError (number `-1` must be greater than 0)
|
386
393
|
```
|
@@ -389,11 +396,11 @@ Double
|
|
389
396
|
|
390
397
|
##### Using decomposition to access the result data and type
|
391
398
|
|
392
|
-
The syntax to decompose an Array can be used in
|
399
|
+
The syntax to decompose an Array can be used in assignments and in method/block arguments.
|
393
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).
|
394
401
|
|
395
402
|
```ruby
|
396
|
-
# The object exposed in the hook is a Micro::Case::Result
|
403
|
+
# The object exposed in the hook without a type is a Micro::Case::Result and it can be decomposed. e.g:
|
397
404
|
|
398
405
|
Double
|
399
406
|
.call(number: -2)
|
@@ -450,8 +457,7 @@ result[:number] * 4 == accum # true
|
|
450
457
|
|
451
458
|
#### How to use the `Micro::Case::Result#then` method?
|
452
459
|
|
453
|
-
This method allows you to create dynamic flows, so, with it,
|
454
|
-
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:
|
455
461
|
|
456
462
|
```ruby
|
457
463
|
class ForbidNegativeNumber < Micro::Case
|
@@ -497,7 +503,7 @@ result2.success? # true
|
|
497
503
|
|
498
504
|
##### What does happens when a `Micro::Case::Result#then` receives a block?
|
499
505
|
|
500
|
-
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:
|
501
507
|
|
502
508
|
```ruby
|
503
509
|
class Add < Micro::Case
|
@@ -549,8 +555,7 @@ Todo::FindAllForUser
|
|
549
555
|
|
550
556
|
### `Micro::Cases::Flow` - How to compose use cases?
|
551
557
|
|
552
|
-
|
553
|
-
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.
|
554
559
|
|
555
560
|
```ruby
|
556
561
|
module Steps
|
@@ -605,9 +610,9 @@ result = Add2ToAllNumbers.call(numbers: %w[1 1 2 2 3 4])
|
|
605
610
|
result.success? # true
|
606
611
|
result.data # {:numbers => [3, 3, 4, 4, 5, 6]}
|
607
612
|
|
608
|
-
|
609
|
-
#
|
610
|
-
|
613
|
+
#-------------------------------#
|
614
|
+
# Creating a flow using classes #
|
615
|
+
#-------------------------------#
|
611
616
|
|
612
617
|
class DoubleAllNumbers < Micro::Case
|
613
618
|
flow Steps::ConvertTextToNumbers,
|
@@ -617,12 +622,11 @@ end
|
|
617
622
|
DoubleAllNumbers.
|
618
623
|
call(numbers: %w[1 1 b 2 3 4]).
|
619
624
|
on_failure { |result| puts result[:message] } # "numbers must contain only numeric types"
|
625
|
+
```
|
620
626
|
|
621
|
-
|
622
|
-
# ----
|
623
|
-
# When happening a failure, the use case responsible
|
624
|
-
# will be accessible in the result
|
627
|
+
When happening a failure, the use case responsible will be accessible in the result.
|
625
628
|
|
629
|
+
```ruby
|
626
630
|
result = DoubleAllNumbers.call(numbers: %w[1 1 b 2 3 4])
|
627
631
|
|
628
632
|
result.failure? # true
|
@@ -635,7 +639,7 @@ end
|
|
635
639
|
|
636
640
|
[⬆️ Back to Top](#table-of-contents-)
|
637
641
|
|
638
|
-
#### Is it possible to compose a
|
642
|
+
#### Is it possible to compose a flow with other flows?
|
639
643
|
|
640
644
|
Answer: Yes, it is possible.
|
641
645
|
|
@@ -705,13 +709,13 @@ DoubleAllNumbersAndSquareAndAdd2
|
|
705
709
|
.on_success { |result| p result[:numbers] } # [6, 6, 18, 18, 38, 66]
|
706
710
|
```
|
707
711
|
|
708
|
-
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).
|
709
713
|
|
710
714
|
[⬆️ Back to Top](#table-of-contents-)
|
711
715
|
|
712
716
|
#### Is it possible a flow accumulates its input and merges each success result to use as the argument of the next use cases?
|
713
717
|
|
714
|
-
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.
|
715
719
|
|
716
720
|
```ruby
|
717
721
|
module Users
|
@@ -755,7 +759,7 @@ Users::Authenticate
|
|
755
759
|
.on_failure(:user_not_found) { render status: 404 }
|
756
760
|
```
|
757
761
|
|
758
|
-
First,
|
762
|
+
First, let's see the attributes used by each use case:
|
759
763
|
|
760
764
|
```ruby
|
761
765
|
class Users::FindByEmail < Micro::Case
|
@@ -768,14 +772,13 @@ end
|
|
768
772
|
```
|
769
773
|
|
770
774
|
As you can see the `Users::ValidatePassword` expects a user as its input. So, how does it receives the user?
|
771
|
-
It receives the user from the `Users::FindByEmail` success result!
|
775
|
+
Answer: It receives the user from the `Users::FindByEmail` success result!
|
772
776
|
|
773
|
-
And this
|
774
|
-
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!
|
775
778
|
|
776
779
|
> input **>>** process **>>** output
|
777
780
|
|
778
|
-
> **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.
|
779
782
|
|
780
783
|
[⬆️ Back to Top](#table-of-contents-)
|
781
784
|
|
@@ -824,7 +827,7 @@ user_authenticated.transitions
|
|
824
827
|
```
|
825
828
|
|
826
829
|
The example above shows the output generated by the `Micro::Case::Result#transitions`.
|
827
|
-
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.
|
828
831
|
|
829
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).
|
830
833
|
|
@@ -841,11 +844,11 @@ And look up the `accessible_attributes` property, it shows whats attributes are
|
|
841
844
|
[success:, failure:] => { # (Output)
|
842
845
|
type: <Symbol>, # Result type. Defaults:
|
843
846
|
# Success = :ok, Failure = :error/:exception
|
844
|
-
result: <Hash> # The data returned by the use case
|
847
|
+
result: <Hash> # The data returned by the use case result
|
845
848
|
},
|
846
849
|
accessible_attributes: <Array>, # Properties that can be accessed by the use case's attributes,
|
847
|
-
#
|
848
|
-
# 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.
|
849
852
|
}
|
850
853
|
]
|
851
854
|
```
|
@@ -854,9 +857,9 @@ And look up the `accessible_attributes` property, it shows whats attributes are
|
|
854
857
|
|
855
858
|
Answer: Yes, it is! You can use the `Micro::Case.config` to do this. [Link to](#microcaseconfig) this section.
|
856
859
|
|
857
|
-
#### Is it possible to declare a flow
|
860
|
+
#### Is it possible to declare a flow that includes the use case itself as a step?
|
858
861
|
|
859
|
-
Answer: Yes, it is! You can use the `self.call!` macro. e.g:
|
862
|
+
Answer: Yes, it is! You can use `self` or the `self.call!` macro. e.g:
|
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'
|
@@ -952,25 +950,21 @@ result[:exception].is_a?(ZeroDivisionError) # true
|
|
952
950
|
result.on_failure(:exception) do |result|
|
953
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
|
|
957
|
+
```ruby
|
961
958
|
result.on_failure(:exception) do |data, use_case|
|
962
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
|
|
@@ -1073,9 +1067,9 @@ class Multiply < Micro::Case
|
|
1073
1067
|
end
|
1074
1068
|
```
|
1075
1069
|
|
1076
|
-
But if do you want an automatic way to fail your use cases on validation errors, you
|
1070
|
+
But if do you want an automatic way to fail your use cases on validation errors, you could do:
|
1077
1071
|
|
1078
|
-
1. **require 'u-case/with_activemodel_validation'**
|
1072
|
+
1. **require 'u-case/with_activemodel_validation'** in the Gemfile
|
1079
1073
|
|
1080
1074
|
```ruby
|
1081
1075
|
gem 'u-case', require: 'u-case/with_activemodel_validation'
|
@@ -1097,16 +1091,13 @@ class Multiply < Micro::Case
|
|
1097
1091
|
Success result: { number: a * b }
|
1098
1092
|
end
|
1099
1093
|
end
|
1100
|
-
|
1101
|
-
# Note:
|
1102
|
-
# ----
|
1103
|
-
# After requiring the validation mode, the
|
1104
|
-
# Micro::Case::Strict and Micro::Case::Safe classes will inherit this new behavior.
|
1105
1094
|
```
|
1106
1095
|
|
1107
|
-
|
1096
|
+
> **Note:** After requiring the validation mode, the `Micro::Case::Strict` and `Micro::Case::Safe` classes will inherit this new behavior.
|
1108
1097
|
|
1109
|
-
|
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:
|
1110
1101
|
|
1111
1102
|
```ruby
|
1112
1103
|
require 'u-case/with_activemodel_validation'
|
@@ -1125,7 +1116,7 @@ end
|
|
1125
1116
|
|
1126
1117
|
Multiply.call(a: 2, b: 'a')
|
1127
1118
|
|
1128
|
-
# The output will be
|
1119
|
+
# The output will be:
|
1129
1120
|
# TypeError (String can't be coerced into Integer)
|
1130
1121
|
```
|
1131
1122
|
|
@@ -1133,9 +1124,9 @@ Multiply.call(a: 2, b: 'a')
|
|
1133
1124
|
|
1134
1125
|
#### `Kind::Validator`
|
1135
1126
|
|
1136
|
-
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).
|
1137
1128
|
|
1138
|
-
The example below shows how to validate the attributes
|
1129
|
+
The example below shows how to validate the attributes types.
|
1139
1130
|
|
1140
1131
|
```ruby
|
1141
1132
|
class Todo::List::AddItem < Micro::Case
|
@@ -1160,17 +1151,17 @@ end
|
|
1160
1151
|
|
1161
1152
|
## `Micro::Case.config`
|
1162
1153
|
|
1163
|
-
The idea of this
|
1154
|
+
The idea of this resource is to allow the configuration of some `u-case` features/modules.
|
1164
1155
|
I recommend you use it only once in your codebase. e.g. In a Rails initializer.
|
1165
1156
|
|
1166
|
-
You can see below, which are
|
1157
|
+
You can see below, which are the available configurations with their default values:
|
1167
1158
|
|
1168
1159
|
```ruby
|
1169
1160
|
Micro::Case.config do |config|
|
1170
1161
|
# Use ActiveModel to auto-validate your use cases' attributes.
|
1171
|
-
config.
|
1162
|
+
config.enable_activemodel_validation = false
|
1172
1163
|
|
1173
|
-
# Use to enable/disable the `Micro::Case::Results#transitions
|
1164
|
+
# Use to enable/disable the `Micro::Case::Results#transitions`.
|
1174
1165
|
config.enable_transitions = true
|
1175
1166
|
end
|
1176
1167
|
```
|
@@ -1179,62 +1170,48 @@ end
|
|
1179
1170
|
|
1180
1171
|
## Benchmarks
|
1181
1172
|
|
1182
|
-
### `Micro::Case` (
|
1183
|
-
|
1184
|
-
#### Best overall
|
1185
|
-
|
1186
|
-
The table below contains the average between the [Success results](#success-results) and [Failure results](#failure-results) benchmarks.
|
1187
|
-
|
1188
|
-
| Gem / Abstraction | Iterations per second | Comparison |
|
1189
|
-
| ---------------------- | --------------------: | ----------------: |
|
1190
|
-
| **Micro::Case** | 105124.3 | _**The Fastest**_ |
|
1191
|
-
| Dry::Monads | 103290.1 | 0.02x slower |
|
1192
|
-
| Interactor | 21342.3 | 4.93x slower |
|
1193
|
-
| Trailblazer::Operation | 14652.7 | 7.17x slower |
|
1194
|
-
| Dry::Transaction | 5310.3 | 19.80x slower |
|
1195
|
-
|
1196
|
-
---
|
1173
|
+
### `Micro::Case` (v3.0.0)
|
1197
1174
|
|
1198
1175
|
#### Success results
|
1199
1176
|
|
1200
1177
|
| Gem / Abstraction | Iterations per second | Comparison |
|
1201
1178
|
| ----------------- | --------------------: | ----------------: |
|
1202
|
-
| Dry::Monads |
|
1203
|
-
| **Micro::Case** |
|
1204
|
-
| Interactor |
|
1205
|
-
| Trailblazer::Operation |
|
1206
|
-
| 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 |
|
1207
1184
|
|
1208
1185
|
<details>
|
1209
1186
|
<summary>Show the full <a href="https://github.com/evanphx/benchmark-ips">benchmark/ips</a> results.</summary>
|
1210
1187
|
|
1211
1188
|
```ruby
|
1212
1189
|
# Warming up --------------------------------------
|
1213
|
-
# Interactor
|
1214
|
-
# Trailblazer::Operation 1.
|
1215
|
-
# Dry::Monads
|
1216
|
-
# Dry::Transaction
|
1217
|
-
# Micro::Case 10.
|
1218
|
-
# Micro::Case::Strict
|
1219
|
-
# 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
|
1220
1197
|
|
1221
1198
|
# Calculating -------------------------------------
|
1222
|
-
# Interactor
|
1223
|
-
# Trailblazer::Operation 14.
|
1224
|
-
# Dry::Monads
|
1225
|
-
# Dry::Transaction 5.
|
1226
|
-
# Micro::Case
|
1227
|
-
# Micro::Case::Strict
|
1228
|
-
# 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
|
1229
1206
|
|
1230
1207
|
# Comparison:
|
1231
|
-
# Dry::Monads:
|
1232
|
-
#
|
1233
|
-
#
|
1234
|
-
# Micro::Case::Strict:
|
1235
|
-
# Interactor:
|
1236
|
-
# Trailblazer::Operation:
|
1237
|
-
# 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
|
1238
1215
|
```
|
1239
1216
|
</details>
|
1240
1217
|
|
@@ -1244,42 +1221,42 @@ https://github.com/serradura/u-case/blob/master/benchmarks/use_case/with_success
|
|
1244
1221
|
|
1245
1222
|
| Gem / Abstraction | Iterations per second | Comparison |
|
1246
1223
|
| ----------------- | --------------------: | ----------------: |
|
1247
|
-
| **Micro::Case** |
|
1248
|
-
| Dry::Monads |
|
1249
|
-
| Trailblazer::Operation |
|
1250
|
-
| Interactor |
|
1251
|
-
| 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 |
|
1252
1229
|
|
1253
1230
|
<details>
|
1254
1231
|
<summary>Show the full <a href="https://github.com/evanphx/benchmark-ips">benchmark/ips</a> results.</summary>
|
1255
1232
|
|
1256
1233
|
```ruby
|
1257
1234
|
# Warming up --------------------------------------
|
1258
|
-
# Interactor 1.
|
1259
|
-
# Trailblazer::Operation 1.
|
1260
|
-
# Dry::Monads 7.
|
1261
|
-
# Dry::Transaction
|
1262
|
-
# Micro::Case 9.
|
1263
|
-
# Micro::Case::Strict
|
1264
|
-
# 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
|
1265
1242
|
|
1266
1243
|
# Calculating -------------------------------------
|
1267
|
-
# Interactor 13.
|
1268
|
-
# Trailblazer::Operation 14.
|
1269
|
-
# Dry::Monads
|
1270
|
-
# Dry::Transaction 4.
|
1271
|
-
# Micro::Case
|
1272
|
-
# Micro::Case::Strict
|
1273
|
-
# 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
|
1274
1251
|
|
1275
1252
|
# Comparison:
|
1276
|
-
#
|
1277
|
-
#
|
1278
|
-
# Micro::Case::Strict:
|
1279
|
-
# Dry::Monads:
|
1280
|
-
# Trailblazer::Operation:
|
1281
|
-
# Interactor:
|
1282
|
-
# 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
|
1283
1260
|
```
|
1284
1261
|
</details>
|
1285
1262
|
|
@@ -1287,33 +1264,42 @@ https://github.com/serradura/u-case/blob/master/benchmarks/use_case/with_failure
|
|
1287
1264
|
|
1288
1265
|
---
|
1289
1266
|
|
1290
|
-
### `Micro::
|
1267
|
+
### `Micro::Cases::Flow` (v3.0.0)
|
1291
1268
|
|
1292
1269
|
| Gems / Abstraction | [Success results](https://github.com/serradura/u-case/blob/master/benchmarks/flow/with_success_result.rb#L40) | [Failure results](https://github.com/serradura/u-case/blob/master/benchmarks/flow/with_failure_result.rb#L40) |
|
1293
|
-
|
|
1294
|
-
| Micro::Case
|
1295
|
-
| Micro::Case
|
1296
|
-
|
|
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 |
|
1297
1276
|
|
1298
|
-
\* 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.
|
1299
1278
|
|
1300
1279
|
<details>
|
1301
1280
|
<summary><strong>Success results</strong> - Show the full benchmark/ips results.</summary>
|
1302
1281
|
|
1303
1282
|
```ruby
|
1304
1283
|
# Warming up --------------------------------------
|
1305
|
-
# Interactor::Organizer
|
1306
|
-
#
|
1307
|
-
# 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
|
+
|
1308
1290
|
# Calculating -------------------------------------
|
1309
|
-
# Interactor::Organizer
|
1310
|
-
#
|
1311
|
-
# 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
|
1312
1296
|
|
1313
1297
|
# Comparison:
|
1314
|
-
#
|
1315
|
-
# Micro::Case
|
1316
|
-
#
|
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
|
1317
1303
|
```
|
1318
1304
|
</details>
|
1319
1305
|
|
@@ -1322,19 +1308,25 @@ https://github.com/serradura/u-case/blob/master/benchmarks/use_case/with_failure
|
|
1322
1308
|
|
1323
1309
|
```ruby
|
1324
1310
|
# Warming up --------------------------------------
|
1325
|
-
# Interactor::Organizer
|
1326
|
-
#
|
1327
|
-
# 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
|
1328
1316
|
|
1329
1317
|
# Calculating -------------------------------------
|
1330
|
-
# Interactor::Organizer
|
1331
|
-
#
|
1332
|
-
# 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
|
1333
1323
|
|
1334
1324
|
# Comparison:
|
1335
|
-
# Micro::Case
|
1336
|
-
#
|
1337
|
-
#
|
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
|
1338
1330
|
```
|
1339
1331
|
</details>
|
1340
1332
|
|
@@ -1353,7 +1345,7 @@ Check it out implementations of the same use case with different gems/abstractio
|
|
1353
1345
|
|
1354
1346
|
### 1️⃣ Rails App (API)
|
1355
1347
|
|
1356
|
-
> 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.
|
1357
1349
|
>
|
1358
1350
|
> Link: https://github.com/serradura/from-fat-controllers-to-use-cases
|
1359
1351
|
|
@@ -1365,11 +1357,11 @@ Check it out implementations of the same use case with different gems/abstractio
|
|
1365
1357
|
|
1366
1358
|
### 3️⃣ Users creation
|
1367
1359
|
|
1368
|
-
> 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.
|
1369
1361
|
>
|
1370
1362
|
> Link: https://github.com/serradura/u-case/blob/master/examples/users_creation.rb
|
1371
1363
|
|
1372
|
-
### 4️⃣ Rescuing
|
1364
|
+
### 4️⃣ Rescuing exceptions inside of the use cases
|
1373
1365
|
|
1374
1366
|
> Link: https://github.com/serradura/u-case/blob/master/examples/rescuing_exceptions.rb
|
1375
1367
|
|