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