u-case 2.4.0 → 3.0.0.rc3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.sh +3 -2
- data/Gemfile +8 -0
- data/README.md +427 -314
- data/lib/micro/case.rb +67 -64
- data/lib/micro/case/config.rb +23 -0
- data/lib/micro/case/error.rb +20 -16
- data/lib/micro/case/result.rb +80 -55
- data/lib/micro/case/safe.rb +6 -2
- data/lib/micro/case/utils.rb +2 -2
- data/lib/micro/case/version.rb +1 -1
- data/lib/micro/case/with_activemodel_validation.rb +3 -1
- data/lib/micro/cases.rb +16 -0
- data/lib/micro/cases/flow.rb +96 -0
- data/lib/micro/cases/safe/flow.rb +18 -0
- data/lib/u-case/with_activemodel_validation.rb +0 -2
- data/u-case.gemspec +1 -1
- metadata +16 -10
- data/lib/micro/case/flow.rb +0 -48
- data/lib/micro/case/flow/reducer.rb +0 -104
- data/lib/micro/case/safe/flow.rb +0 -44
- 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: de27799d2fbf95d3399af2caa3053038fd9817c6f800747213eb8ee3722604b5
|
4
|
+
data.tar.gz: dc2f1b40964d646978d28ffb44c049a58ddca614440e296ff5fa7f35e347def1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 90af10df71576124850933260f703e7a2f9c6a67df5e5add06d7d657de34a291879e279eec59795bfd548c0e0f898dc3795657de1de524e3aac16dddf25b24cf
|
7
|
+
data.tar.gz: e5b374929b87a7a5a0f604afa5742195910809b9198c11d01110bb8cf9f2540de73aa80fedb4432357ee86b96fe5e81b91a003fd410da989ac5b22b9ad40aa30
|
data/.travis.sh
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
#!/bin/bash
|
2
2
|
|
3
|
-
bundle exec rake test
|
4
|
-
|
5
3
|
ruby_v=$(ruby -v)
|
6
4
|
|
7
5
|
ACTIVEMODEL_VERSION='3.2' bundle update
|
@@ -16,3 +14,6 @@ if [[ $ruby_v =~ '2.5.' ]] || [[ $ruby_v =~ '2.6.' ]] || [[ $ruby_v =~ '2.7.' ]]
|
|
16
14
|
ACTIVEMODEL_VERSION='6.0' bundle update
|
17
15
|
ACTIVEMODEL_VERSION='6.0' bundle exec rake test
|
18
16
|
fi
|
17
|
+
|
18
|
+
bundle update
|
19
|
+
bundle exec rake test
|
data/Gemfile
CHANGED
@@ -27,9 +27,17 @@ pry_byebug_version =
|
|
27
27
|
else '3.9'
|
28
28
|
end
|
29
29
|
|
30
|
+
pry_version =
|
31
|
+
case RUBY_VERSION
|
32
|
+
when /\A2.2/ then '0.12.2'
|
33
|
+
when /\A2.3/ then '0.12.2'
|
34
|
+
else '0.13.1'
|
35
|
+
end
|
36
|
+
|
30
37
|
group :development, :test do
|
31
38
|
gem 'awesome_print', '~> 1.8'
|
32
39
|
|
40
|
+
gem 'pry', "~> #{pry_version}"
|
33
41
|
gem 'pry-byebug', "~> #{pry_byebug_version}"
|
34
42
|
end
|
35
43
|
|
data/README.md
CHANGED
@@ -18,6 +18,14 @@ The main project goals are:
|
|
18
18
|
|
19
19
|
> 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
20
|
|
21
|
+
## Documentation <!-- omit in toc -->
|
22
|
+
|
23
|
+
Version | Documentation
|
24
|
+
--------- | -------------
|
25
|
+
3.0.0.rc3 | https://github.com/serradura/u-case/blob/master/README.md
|
26
|
+
2.6.0 | https://github.com/serradura/u-case/blob/v2.x/README.md
|
27
|
+
1.1.0 | https://github.com/serradura/u-case/blob/v1.x/README.md
|
28
|
+
|
21
29
|
## Table of Contents <!-- omit in toc -->
|
22
30
|
- [Required Ruby version](#required-ruby-version)
|
23
31
|
- [Dependencies](#dependencies)
|
@@ -29,26 +37,32 @@ The main project goals are:
|
|
29
37
|
- [How to define custom result types?](#how-to-define-custom-result-types)
|
30
38
|
- [Is it possible to define a custom result type without a block?](#is-it-possible-to-define-a-custom-result-type-without-a-block)
|
31
39
|
- [How to use the result hooks?](#how-to-use-the-result-hooks)
|
32
|
-
- [Why the failure hook (without a type) exposes
|
40
|
+
- [Why the failure hook (without a type) exposes result itself?](#why-the-failure-hook-without-a-type-exposes-result-itself)
|
33
41
|
- [What happens if a result hook was declared multiple times?](#what-happens-if-a-result-hook-was-declared-multiple-times)
|
34
42
|
- [How to use the `Micro::Case::Result#then` method?](#how-to-use-the-microcaseresultthen-method)
|
35
|
-
|
43
|
+
- [What does happens when a `Micro::Case::Result#then` receives a block?](#what-does-happens-when-a-microcaseresultthen-receives-a-block)
|
44
|
+
- [How to make attributes data injection using this feature?](#how-to-make-attributes-data-injection-using-this-feature)
|
45
|
+
- [`Micro::Cases::Flow` - How to compose use cases?](#microcasesflow---how-to-compose-use-cases)
|
36
46
|
- [Is it possible to compose a use case flow with other ones?](#is-it-possible-to-compose-a-use-case-flow-with-other-ones)
|
37
47
|
- [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)
|
38
48
|
- [How to understand what is happening during a flow execution?](#how-to-understand-what-is-happening-during-a-flow-execution)
|
39
|
-
- [Micro::Case::Result#transitions schema
|
49
|
+
- [`Micro::Case::Result#transitions` schema](#microcaseresulttransitions-schema)
|
50
|
+
- [Is it possible disable the `Micro::Case::Result#transitions`?](#is-it-possible-disable-the-microcaseresulttransitions)
|
40
51
|
- [Is it possible to declare a flow which includes the use case itself?](#is-it-possible-to-declare-a-flow-which-includes-the-use-case-itself)
|
41
52
|
- [`Micro::Case::Strict` - What is a strict use case?](#microcasestrict---what-is-a-strict-use-case)
|
42
53
|
- [`Micro::Case::Safe` - Is there some feature to auto handle exceptions inside of a use case or flow?](#microcasesafe---is-there-some-feature-to-auto-handle-exceptions-inside-of-a-use-case-or-flow)
|
54
|
+
- [`Micro::Cases::Safe::Flow`](#microcasessafeflow)
|
55
|
+
- [`Micro::Case::Result#on_exception`](#microcaseresulton_exception)
|
43
56
|
- [`u-case/with_activemodel_validation` - How to validate use case attributes?](#u-casewith_activemodel_validation---how-to-validate-use-case-attributes)
|
44
57
|
- [If I enabled the auto validation, is it possible to disable it only in specific use case classes?](#if-i-enabled-the-auto-validation-is-it-possible-to-disable-it-only-in-specific-use-case-classes)
|
45
|
-
- [Kind::Validator](#kindvalidator)
|
58
|
+
- [`Kind::Validator`](#kindvalidator)
|
59
|
+
- [`Micro::Case.config`](#microcaseconfig)
|
46
60
|
- [Benchmarks](#benchmarks)
|
47
|
-
- [`Micro::Case`](#microcase)
|
61
|
+
- [`Micro::Case` (v2.6.0)](#microcase-v260)
|
48
62
|
- [Best overall](#best-overall)
|
49
63
|
- [Success results](#success-results)
|
50
64
|
- [Failure results](#failure-results)
|
51
|
-
- [`Micro::Case::Flow`](#microcaseflow)
|
65
|
+
- [`Micro::Case::Flow` (v2.6.0)](#microcaseflow-v260)
|
52
66
|
- [Comparisons](#comparisons)
|
53
67
|
- [Examples](#examples)
|
54
68
|
- [1️⃣ Rails App (API)](#1️⃣-rails-app-api)
|
@@ -70,7 +84,7 @@ The main project goals are:
|
|
70
84
|
|
71
85
|
A simple type system (at runtime) for Ruby.
|
72
86
|
|
73
|
-
Used to validate method inputs
|
87
|
+
Used to validate method inputs using its [`activemodel validation`](https://github.com/serradura/kind#kindvalidator-activemodelvalidations) module is auto required by [`u-case/with_activemodel_validation`](#u-casewith_activemodel_validation---how-to-validate-use-case-attributes) mode, and expose `Kind::Of::Micro::Case`, `Kind::Of::Micro::Case::Result` type checkers.
|
74
88
|
2. [`u-attributes`](https://github.com/serradura/u-attributes) gem.
|
75
89
|
|
76
90
|
This gem allows defining read-only attributes, that is, your objects will have only getters to access their attributes data.
|
@@ -104,11 +118,11 @@ class Multiply < Micro::Case
|
|
104
118
|
# 2. Define the method `call!` with its business logic
|
105
119
|
def call!
|
106
120
|
|
107
|
-
# 3. Wrap the use case result/output using the `Success()` or `Failure()` methods
|
121
|
+
# 3. Wrap the use case result/output using the `Success(result: *)` or `Failure(result: *)` methods
|
108
122
|
if a.is_a?(Numeric) && b.is_a?(Numeric)
|
109
|
-
Success
|
123
|
+
Success result: { number: a * b }
|
110
124
|
else
|
111
|
-
Failure { '`a` and `b` attributes must be numeric' }
|
125
|
+
Failure result: { message: '`a` and `b` attributes must be numeric' }
|
112
126
|
end
|
113
127
|
end
|
114
128
|
end
|
@@ -122,14 +136,14 @@ end
|
|
122
136
|
result = Multiply.call(a: 2, b: 2)
|
123
137
|
|
124
138
|
result.success? # true
|
125
|
-
result.
|
139
|
+
result.data # { number: 4 }
|
126
140
|
|
127
141
|
# Failure result
|
128
142
|
|
129
143
|
bad_result = Multiply.call(a: 2, b: '2')
|
130
144
|
|
131
145
|
bad_result.failure? # true
|
132
|
-
bad_result.
|
146
|
+
bad_result.data # { message: "`a` and `b` attributes must be numeric" }
|
133
147
|
|
134
148
|
#-----------------------------#
|
135
149
|
# Calling a use case instance #
|
@@ -137,7 +151,7 @@ bad_result.value # "`a` and `b` attributes must be numeric"
|
|
137
151
|
|
138
152
|
result = Multiply.new(a: 2, b: 3).call
|
139
153
|
|
140
|
-
result.value # 6
|
154
|
+
result.value # { number: 6 }
|
141
155
|
|
142
156
|
# Note:
|
143
157
|
# ----
|
@@ -152,11 +166,15 @@ result.value # 6
|
|
152
166
|
A `Micro::Case::Result` stores the use cases output data. These are their main methods:
|
153
167
|
- `#success?` returns true if is a successful result.
|
154
168
|
- `#failure?` returns true if is an unsuccessful result.
|
155
|
-
- `#
|
169
|
+
- `#use_case` returns the use case responsible for it. This feature is handy to handle a flow failure (this topic will be covered ahead).
|
156
170
|
- `#type` a Symbol which gives meaning for the result, this is useful to declare different types of failures or success.
|
157
|
-
- `#
|
158
|
-
- `#
|
159
|
-
- `#
|
171
|
+
- `#data` the result data itself.
|
172
|
+
- `#[]` and `#values_at` are shortcuts to access the `#data` values.
|
173
|
+
- `#on_success` or `#on_failure` are hook methods that help you to define the application flow.
|
174
|
+
- `#then` this method will allow applying a new use case if the current result was a success. The idea of this feature is to allow the creation of dynamic flows.
|
175
|
+
- `#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).
|
176
|
+
|
177
|
+
> **Note:** for backward compatibility, you could use the `#value` method as an alias of `#data` method.
|
160
178
|
|
161
179
|
[⬆️ Back to Top](#table-of-contents-)
|
162
180
|
|
@@ -171,9 +189,13 @@ class Divide < Micro::Case
|
|
171
189
|
attributes :a, :b
|
172
190
|
|
173
191
|
def call!
|
174
|
-
invalid_attributes.empty?
|
175
|
-
|
176
|
-
|
192
|
+
if invalid_attributes.empty?
|
193
|
+
Success result: { number: a / b }
|
194
|
+
else
|
195
|
+
Failure result: { invalid_attributes: invalid_attributes }
|
196
|
+
end
|
197
|
+
rescue => exception
|
198
|
+
Failure result: exception
|
177
199
|
end
|
178
200
|
|
179
201
|
private def invalid_attributes
|
@@ -186,49 +208,51 @@ end
|
|
186
208
|
result = Divide.call(a: 2, b: 2)
|
187
209
|
|
188
210
|
result.type # :ok
|
189
|
-
result.
|
211
|
+
result.data # { number: 1 }
|
190
212
|
result.success? # true
|
191
|
-
result.use_case #
|
213
|
+
result.use_case # #<Divide:0x0000 @__attributes={"a"=>2, "b"=>2}, @a=2, @b=2, @__result=...>
|
192
214
|
|
193
215
|
# Failure result (type == :error)
|
194
216
|
|
195
217
|
bad_result = Divide.call(a: 2, b: '2')
|
196
218
|
|
197
219
|
bad_result.type # :error
|
198
|
-
bad_result.
|
220
|
+
bad_result.data # { invalid_attributes: { "b"=>"2" } }
|
199
221
|
bad_result.failure? # true
|
200
|
-
bad_result.use_case # #<Divide:0x0000 @__attributes={"a"=>2, "b"=>"2"}, @a=2, @b="2", @__result
|
222
|
+
bad_result.use_case # #<Divide:0x0000 @__attributes={"a"=>2, "b"=>"2"}, @a=2, @b="2", @__result=...>
|
201
223
|
|
202
224
|
# Failure result (type == :exception)
|
203
225
|
|
204
226
|
err_result = Divide.call(a: 2, b: 0)
|
205
227
|
|
206
228
|
err_result.type # :exception
|
207
|
-
err_result.
|
229
|
+
err_result.data # { exception: <ZeroDivisionError: divided by 0> }
|
208
230
|
err_result.failure? # true
|
209
|
-
err_result.use_case # #<Divide:0x0000 @__attributes={"a"=>2, "b"=>0}, @a=2, @b=0, @__result=#<Micro::Case::Result:0x0000 @use_case=#<Divide:0x0000 ...>, @type=:exception, @value=#<ZeroDivisionError: divided by 0>, @success=false
|
231
|
+
err_result.use_case # #<Divide:0x0000 @__attributes={"a"=>2, "b"=>0}, @a=2, @b=0, @__result=#<Micro::Case::Result:0x0000 @use_case=#<Divide:0x0000 ...>, @type=:exception, @value=#<ZeroDivisionError: divided by 0>, @success=false>
|
210
232
|
|
211
233
|
# Note:
|
212
234
|
# ----
|
213
235
|
# Any Exception instance which is wrapped by
|
214
|
-
# the Failure() method will receive `:exception` instead of the `:error` type.
|
236
|
+
# the Failure(result: *) method will receive `:exception` instead of the `:error` type.
|
215
237
|
```
|
216
238
|
|
217
239
|
[⬆️ Back to Top](#table-of-contents-)
|
218
240
|
|
219
241
|
#### How to define custom result types?
|
220
242
|
|
221
|
-
Answer: Use a symbol as the argument of `Success()`, `Failure()` methods and declare
|
243
|
+
Answer: Use a symbol as the argument of `Success()`, `Failure()` methods and declare the `result:` keyword to set the result data.
|
222
244
|
|
223
245
|
```ruby
|
224
246
|
class Multiply < Micro::Case
|
225
247
|
attributes :a, :b
|
226
248
|
|
227
249
|
def call!
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
250
|
+
if a.is_a?(Numeric) && b.is_a?(Numeric)
|
251
|
+
Success result: { number: a * b }
|
252
|
+
else
|
253
|
+
Failure :invalid_data, result: {
|
254
|
+
attributes: attributes.reject { |_, input| input.is_a?(Numeric) }
|
255
|
+
}
|
232
256
|
end
|
233
257
|
end
|
234
258
|
end
|
@@ -238,7 +262,7 @@ end
|
|
238
262
|
result = Multiply.call(a: 3, b: 2)
|
239
263
|
|
240
264
|
result.type # :ok
|
241
|
-
result.
|
265
|
+
result.data # { number: 6 }
|
242
266
|
result.success? # true
|
243
267
|
|
244
268
|
# Failure result
|
@@ -246,7 +270,7 @@ result.success? # true
|
|
246
270
|
bad_result = Multiply.call(a: 3, b: '2')
|
247
271
|
|
248
272
|
bad_result.type # :invalid_data
|
249
|
-
bad_result.
|
273
|
+
bad_result.data # { attributes: {"b"=>"2"} }
|
250
274
|
bad_result.failure? # true
|
251
275
|
```
|
252
276
|
|
@@ -254,23 +278,25 @@ bad_result.failure? # true
|
|
254
278
|
|
255
279
|
#### Is it possible to define a custom result type without a block?
|
256
280
|
|
257
|
-
Answer: Yes, it is. But
|
281
|
+
Answer: Yes, it is possible. But this will have special behavior because the result data will be a hash with the given type as the key and true as its value.
|
258
282
|
|
259
283
|
```ruby
|
260
284
|
class Multiply < Micro::Case
|
261
285
|
attributes :a, :b
|
262
286
|
|
263
287
|
def call!
|
264
|
-
|
265
|
-
|
266
|
-
|
288
|
+
if a.is_a?(Numeric) && b.is_a?(Numeric)
|
289
|
+
Success result: { number: a * b }
|
290
|
+
else
|
291
|
+
Failure(:invalid_data)
|
292
|
+
end
|
267
293
|
end
|
268
294
|
end
|
269
295
|
|
270
296
|
result = Multiply.call(a: 2, b: '2')
|
271
297
|
|
272
298
|
result.failure? # true
|
273
|
-
result.
|
299
|
+
result.data # { :invalid_data => true }
|
274
300
|
result.type # :invalid_data
|
275
301
|
result.use_case.attributes # {"a"=>2, "b"=>"2"}
|
276
302
|
|
@@ -293,10 +319,10 @@ class Double < Micro::Case
|
|
293
319
|
attribute :number
|
294
320
|
|
295
321
|
def call!
|
296
|
-
return Failure
|
297
|
-
return Failure
|
322
|
+
return Failure :invalid, result: { msg: 'number must be a numeric value' } unless number.is_a?(Numeric)
|
323
|
+
return Failure :lte_zero, result: { msg: 'number must be greater than 0' } if number <= 0
|
298
324
|
|
299
|
-
Success
|
325
|
+
Success result: { number: number * 2 }
|
300
326
|
end
|
301
327
|
end
|
302
328
|
|
@@ -306,9 +332,9 @@ end
|
|
306
332
|
|
307
333
|
Double
|
308
334
|
.call(number: 3)
|
309
|
-
.on_success { |
|
310
|
-
.on_failure(:invalid) { |
|
311
|
-
.on_failure(:lte_zero) { |
|
335
|
+
.on_success { |result| p result[:number] }
|
336
|
+
.on_failure(:invalid) { |result| raise TypeError, result[:msg] }
|
337
|
+
.on_failure(:lte_zero) { |result| raise ArgumentError, result[:msg] }
|
312
338
|
|
313
339
|
# The output because it is a success:
|
314
340
|
# 6
|
@@ -319,10 +345,10 @@ Double
|
|
319
345
|
|
320
346
|
Double
|
321
347
|
.call(number: -1)
|
322
|
-
.on_success { |
|
348
|
+
.on_success { |result| p result[:number] }
|
323
349
|
.on_failure { |_result, use_case| puts "#{use_case.class.name} was the use case responsible for the failure" }
|
324
|
-
.on_failure(:invalid) { |
|
325
|
-
.on_failure(:lte_zero) { |
|
350
|
+
.on_failure(:invalid) { |result| raise TypeError, result[:msg] }
|
351
|
+
.on_failure(:lte_zero) { |result| raise ArgumentError, result[:msg] }
|
326
352
|
|
327
353
|
# The outputs will be:
|
328
354
|
#
|
@@ -334,7 +360,7 @@ Double
|
|
334
360
|
# The use case responsible for the failure will be accessible as the second hook argument
|
335
361
|
```
|
336
362
|
|
337
|
-
#### Why the failure hook (without a type) exposes
|
363
|
+
#### Why the failure hook (without a type) exposes result itself?
|
338
364
|
|
339
365
|
Answer: To allow you to define how to handle the program flow using some
|
340
366
|
conditional statement (like an `if`, `case/when`).
|
@@ -345,9 +371,9 @@ class Double < Micro::Case
|
|
345
371
|
|
346
372
|
def call!
|
347
373
|
return Failure(:invalid) unless number.is_a?(Numeric)
|
348
|
-
return Failure
|
374
|
+
return Failure :lte_zero, result: attributes(:number) if number <= 0
|
349
375
|
|
350
|
-
Success
|
376
|
+
Success result: { number: number * 2 }
|
351
377
|
end
|
352
378
|
end
|
353
379
|
|
@@ -359,32 +385,32 @@ Double
|
|
359
385
|
.call(-1)
|
360
386
|
.on_failure do |result, use_case|
|
361
387
|
case result.type
|
362
|
-
when :invalid then raise TypeError,
|
363
|
-
when :lte_zero then raise ArgumentError, "
|
388
|
+
when :invalid then raise TypeError, "number must be a numeric value"
|
389
|
+
when :lte_zero then raise ArgumentError, "number `#{result[:number]}` must be greater than 0"
|
364
390
|
else raise NotImplementedError
|
365
391
|
end
|
366
392
|
end
|
367
393
|
|
368
394
|
# The output will be the exception:
|
369
395
|
#
|
370
|
-
# ArgumentError (
|
396
|
+
# ArgumentError (number `-1` must be greater than 0)
|
371
397
|
|
372
|
-
|
373
|
-
# Using decomposition to access result
|
374
|
-
|
398
|
+
#=========================================================#
|
399
|
+
# Using decomposition to access the result data and type #
|
400
|
+
#=========================================================#
|
375
401
|
|
376
402
|
# The syntax to decompose an Array can be used in methods, blocks and assigments.
|
377
|
-
# If you doesn't know
|
403
|
+
# If you doesn't know it, check out the Ruby doc:
|
378
404
|
# https://ruby-doc.org/core-2.2.0/doc/syntax/assignment_rdoc.html#label-Array+Decomposition
|
379
405
|
#
|
380
|
-
#
|
406
|
+
# The object exposed in the hook failure is a Micro::Case::Result, and it can be decomposed using this syntax. e.g:
|
381
407
|
|
382
408
|
Double
|
383
409
|
.call(-2)
|
384
|
-
.on_failure do |(
|
410
|
+
.on_failure do |(data, type), use_case|
|
385
411
|
case type
|
386
|
-
when :invalid then raise TypeError, '
|
387
|
-
when :lte_zero then raise ArgumentError, "
|
412
|
+
when :invalid then raise TypeError, 'number must be a numeric value'
|
413
|
+
when :lte_zero then raise ArgumentError, "number `#{data[:number]}` must be greater than 0"
|
388
414
|
else raise NotImplementedError
|
389
415
|
end
|
390
416
|
end
|
@@ -405,38 +431,43 @@ class Double < Micro::Case
|
|
405
431
|
attributes :number
|
406
432
|
|
407
433
|
def call!
|
408
|
-
|
409
|
-
|
410
|
-
|
434
|
+
if number.is_a?(Numeric)
|
435
|
+
Success :computed, result: { number: number * 2 }
|
436
|
+
else
|
437
|
+
Failure :invalid, result: { msg: 'number must be a numeric value' }
|
438
|
+
end
|
411
439
|
end
|
412
440
|
end
|
413
441
|
|
414
442
|
result = Double.call(number: 3)
|
415
|
-
result.
|
416
|
-
result
|
443
|
+
result.data # { number: 6 }
|
444
|
+
result[:number] * 4 # 24
|
417
445
|
|
418
446
|
accum = 0
|
419
447
|
|
420
|
-
result.on_success { |
|
421
|
-
.on_success { |
|
422
|
-
.on_success(:computed) { |
|
423
|
-
.on_success(:computed) { |
|
448
|
+
result.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] }
|
424
452
|
|
425
453
|
accum # 24
|
426
454
|
|
427
|
-
result
|
455
|
+
result[:number] * 4 == accum # true
|
428
456
|
```
|
429
457
|
|
430
458
|
#### How to use the `Micro::Case::Result#then` method?
|
431
459
|
|
460
|
+
This method allows you to create dynamic flows, so, with it,
|
461
|
+
you can add new use cases or flows to continue the result transformation. e.g:
|
462
|
+
|
432
463
|
```ruby
|
433
464
|
class ForbidNegativeNumber < Micro::Case
|
434
465
|
attribute :number
|
435
466
|
|
436
467
|
def call!
|
437
|
-
return Success
|
468
|
+
return Success result: attributes if number >= 0
|
438
469
|
|
439
|
-
Failure
|
470
|
+
Failure result: attributes
|
440
471
|
end
|
441
472
|
end
|
442
473
|
|
@@ -444,7 +475,7 @@ class Add3 < Micro::Case
|
|
444
475
|
attribute :number
|
445
476
|
|
446
477
|
def call!
|
447
|
-
Success
|
478
|
+
Success result: { number: number + 3 }
|
448
479
|
end
|
449
480
|
end
|
450
481
|
|
@@ -453,8 +484,7 @@ result1 =
|
|
453
484
|
.call(number: -1)
|
454
485
|
.then(Add3)
|
455
486
|
|
456
|
-
result1.
|
457
|
-
result1.value # {'number' => -1}
|
487
|
+
result1.data # {'number' => -1}
|
458
488
|
result1.failure? # true
|
459
489
|
|
460
490
|
# ---
|
@@ -464,16 +494,69 @@ result2 =
|
|
464
494
|
.call(number: 1)
|
465
495
|
.then(Add3)
|
466
496
|
|
467
|
-
result2.
|
468
|
-
result2.value # {'number' => 4}
|
497
|
+
result2.data # {'number' => 4}
|
469
498
|
result2.success? # true
|
470
499
|
```
|
471
500
|
|
501
|
+
> **Note:** this method changes the [`Micro::Case::Result#transitions`](#how-to-understand-what-is-happening-during-a-flow-execution).
|
502
|
+
|
472
503
|
[⬆️ Back to Top](#table-of-contents-)
|
473
504
|
|
474
|
-
|
505
|
+
##### What does happens when a `Micro::Case::Result#then` receives a block?
|
506
|
+
|
507
|
+
It will yields self (a `Micro::Case::Result instance`) to the block and return the result of the block. e.g:
|
508
|
+
|
509
|
+
```ruby
|
510
|
+
class Add < Micro::Case
|
511
|
+
attributes :a, :b
|
512
|
+
|
513
|
+
def call!
|
514
|
+
if Kind.of?(Numeric, a, b)
|
515
|
+
Success result: { sum: a + b }
|
516
|
+
else
|
517
|
+
Failure(:attributes_arent_numbers)
|
518
|
+
end
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
# --
|
523
|
+
|
524
|
+
success_result =
|
525
|
+
Add
|
526
|
+
.call(a: 2, b: 2)
|
527
|
+
.then { |result| result.success? ? result[:sum] : 0 }
|
475
528
|
|
476
|
-
|
529
|
+
puts success_result # 4
|
530
|
+
|
531
|
+
# --
|
532
|
+
|
533
|
+
failure_result =
|
534
|
+
Add
|
535
|
+
.call(a: 2, b: '2')
|
536
|
+
.then { |result| result.success? ? result[:sum] : 0 }
|
537
|
+
|
538
|
+
puts failure_result # 0
|
539
|
+
```
|
540
|
+
|
541
|
+
[⬆️ Back to Top](#table-of-contents-)
|
542
|
+
|
543
|
+
##### How to make attributes data injection using this feature?
|
544
|
+
|
545
|
+
Pass a Hash as the second argument of the `Micro::Case::Result#then` method.
|
546
|
+
|
547
|
+
```ruby
|
548
|
+
Todo::FindAllForUser
|
549
|
+
.call(user: current_user, params: params)
|
550
|
+
.then(Paginate)
|
551
|
+
.then(Serialize::PaginatedRelationAsJson, serializer: Todo::Serializer)
|
552
|
+
.on_success { |result| render_json(200, data: result[:todos]) }
|
553
|
+
```
|
554
|
+
|
555
|
+
[⬆️ Back to Top](#table-of-contents-)
|
556
|
+
|
557
|
+
### `Micro::Cases::Flow` - How to compose use cases?
|
558
|
+
|
559
|
+
In this case, this will be a **flow** (`Micro::Cases::Flow`).
|
477
560
|
The main idea of this feature is to use/reuse use cases as steps of a new use case.
|
478
561
|
|
479
562
|
```ruby
|
@@ -483,9 +566,9 @@ module Steps
|
|
483
566
|
|
484
567
|
def call!
|
485
568
|
if numbers.all? { |value| String(value) =~ /\d+/ }
|
486
|
-
Success
|
569
|
+
Success result: { numbers: numbers.map(&:to_i) }
|
487
570
|
else
|
488
|
-
Failure
|
571
|
+
Failure result: { message: 'numbers must contain only numeric types' }
|
489
572
|
end
|
490
573
|
end
|
491
574
|
end
|
@@ -494,7 +577,7 @@ module Steps
|
|
494
577
|
attribute :numbers
|
495
578
|
|
496
579
|
def call!
|
497
|
-
Success
|
580
|
+
Success result: { numbers: numbers.map { |number| number + 2 } }
|
498
581
|
end
|
499
582
|
end
|
500
583
|
|
@@ -502,7 +585,7 @@ module Steps
|
|
502
585
|
attribute :numbers
|
503
586
|
|
504
587
|
def call!
|
505
|
-
Success
|
588
|
+
Success result: { numbers: numbers.map { |number| number * 2 } }
|
506
589
|
end
|
507
590
|
end
|
508
591
|
|
@@ -510,24 +593,24 @@ module Steps
|
|
510
593
|
attribute :numbers
|
511
594
|
|
512
595
|
def call!
|
513
|
-
Success
|
596
|
+
Success result: { numbers: numbers.map { |number| number * number } }
|
514
597
|
end
|
515
598
|
end
|
516
599
|
end
|
517
600
|
|
518
|
-
|
519
|
-
# Creating a flow using
|
520
|
-
|
601
|
+
#-------------------------------------------#
|
602
|
+
# Creating a flow using Micro::Cases.flow() #
|
603
|
+
#-------------------------------------------#
|
521
604
|
|
522
|
-
Add2ToAllNumbers = Micro::
|
605
|
+
Add2ToAllNumbers = Micro::Cases.flow([
|
523
606
|
Steps::ConvertTextToNumbers,
|
524
607
|
Steps::Add2
|
525
608
|
])
|
526
609
|
|
527
610
|
result = Add2ToAllNumbers.call(numbers: %w[1 1 2 2 3 4])
|
528
611
|
|
529
|
-
|
530
|
-
|
612
|
+
result.success? # true
|
613
|
+
result.data # {:numbers => [3, 3, 4, 4, 5, 6]}
|
531
614
|
|
532
615
|
#---------------------------------------------------#
|
533
616
|
# An alternative way to create a flow using classes #
|
@@ -542,41 +625,14 @@ DoubleAllNumbers
|
|
542
625
|
.call(numbers: %w[1 1 b 2 3 4])
|
543
626
|
.on_failure { |message| p message } # "numbers must contain only numeric types"
|
544
627
|
|
545
|
-
# !------------------------------------ ! #
|
546
|
-
# ! Deprecated: Micro::Case::Flow mixin ! #
|
547
|
-
# !-------------------------------------! #
|
548
|
-
|
549
|
-
# The code below still works, but it will output a warning message:
|
550
|
-
# Deprecation: Micro::Case::Flow mixin is being deprecated, please use `Micro::Case` inheritance instead.
|
551
|
-
|
552
|
-
class DoubleAllNumbers
|
553
|
-
include Micro::Case::Flow
|
554
|
-
|
555
|
-
flow Steps::ConvertTextToNumbers,
|
556
|
-
Steps::Double
|
557
|
-
end
|
558
|
-
|
559
|
-
# Note: This feature will be removed in the next major release (3.0)
|
560
|
-
|
561
|
-
#-------------------------------------------------------------#
|
562
|
-
# Another way to create a flow using the composition operator #
|
563
|
-
#-------------------------------------------------------------#
|
564
|
-
|
565
|
-
SquareAllNumbers =
|
566
|
-
Steps::ConvertTextToNumbers >> Steps::Square
|
567
|
-
|
568
|
-
SquareAllNumbers
|
569
|
-
.call(numbers: %w[1 1 2 2 3 4])
|
570
|
-
.on_success { |value| p value[:numbers] } # [1, 1, 4, 4, 9, 16]
|
571
|
-
|
572
628
|
# Note:
|
573
629
|
# ----
|
574
630
|
# When happening a failure, the use case responsible
|
575
631
|
# will be accessible in the result
|
576
632
|
|
577
|
-
result =
|
633
|
+
result = DoubleAllNumbers.call(numbers: %w[1 1 b 2 3 4])
|
578
634
|
|
579
|
-
result.failure?
|
635
|
+
result.failure? # true
|
580
636
|
result.use_case.is_a?(Steps::ConvertTextToNumbers) # true
|
581
637
|
|
582
638
|
result.on_failure do |_message, use_case|
|
@@ -588,7 +644,7 @@ end
|
|
588
644
|
|
589
645
|
#### Is it possible to compose a use case flow with other ones?
|
590
646
|
|
591
|
-
Answer: Yes, it is.
|
647
|
+
Answer: Yes, it is possible.
|
592
648
|
|
593
649
|
```ruby
|
594
650
|
module Steps
|
@@ -597,9 +653,9 @@ module Steps
|
|
597
653
|
|
598
654
|
def call!
|
599
655
|
if numbers.all? { |value| String(value) =~ /\d+/ }
|
600
|
-
Success
|
656
|
+
Success result: { numbers: numbers.map(&:to_i) }
|
601
657
|
else
|
602
|
-
Failure
|
658
|
+
Failure result: { message: 'numbers must contain only numeric types' }
|
603
659
|
end
|
604
660
|
end
|
605
661
|
end
|
@@ -608,7 +664,7 @@ module Steps
|
|
608
664
|
attribute :numbers
|
609
665
|
|
610
666
|
def call!
|
611
|
-
Success
|
667
|
+
Success result: { numbers: numbers.map { |number| number + 2 } }
|
612
668
|
end
|
613
669
|
end
|
614
670
|
|
@@ -616,7 +672,7 @@ module Steps
|
|
616
672
|
attribute :numbers
|
617
673
|
|
618
674
|
def call!
|
619
|
-
Success
|
675
|
+
Success result: { numbers: numbers.map { |number| number * 2 } }
|
620
676
|
end
|
621
677
|
end
|
622
678
|
|
@@ -624,20 +680,28 @@ module Steps
|
|
624
680
|
attribute :numbers
|
625
681
|
|
626
682
|
def call!
|
627
|
-
Success
|
683
|
+
Success result: { numbers: numbers.map { |number| number * number } }
|
628
684
|
end
|
629
685
|
end
|
630
686
|
end
|
631
687
|
|
632
|
-
|
633
|
-
|
634
|
-
SquareAllNumbers = Steps::ConvertTextToNumbers >> Steps::Square
|
688
|
+
DoubleAllNumbers =
|
689
|
+
Micro::Cases.flow([Steps::ConvertTextToNumbers, Steps::Double])
|
635
690
|
|
636
|
-
|
637
|
-
|
691
|
+
SquareAllNumbers =
|
692
|
+
Micro::Cases.flow([Steps::ConvertTextToNumbers, Steps::Square])
|
693
|
+
|
694
|
+
DoubleAllNumbersAndAdd2 =
|
695
|
+
Micro::Cases.flow([DoubleAllNumbers, Steps::Add2])
|
696
|
+
|
697
|
+
SquareAllNumbersAndAdd2 =
|
698
|
+
Micro::Cases.flow([SquareAllNumbers, Steps::Add2])
|
699
|
+
|
700
|
+
SquareAllNumbersAndDouble =
|
701
|
+
Micro::Cases.flow([SquareAllNumbersAndAdd2, DoubleAllNumbers])
|
638
702
|
|
639
|
-
|
640
|
-
|
703
|
+
DoubleAllNumbersAndSquareAndAdd2 =
|
704
|
+
Micro::Cases.flow([DoubleAllNumbers, SquareAllNumbersAndAdd2])
|
641
705
|
|
642
706
|
SquareAllNumbersAndDouble
|
643
707
|
.call(numbers: %w[1 1 2 2 3 4])
|
@@ -648,23 +712,23 @@ DoubleAllNumbersAndSquareAndAdd2
|
|
648
712
|
.on_success { |value| p value[:numbers] } # [6, 6, 18, 18, 38, 66]
|
649
713
|
```
|
650
714
|
|
651
|
-
Note: You can blend any of the [available syntaxes/approaches](#how-to-create-a-flow-which-has-reusable-steps-to-define-a-complex-use-case) to create use case flows - [examples](https://github.com/serradura/u-case/blob/
|
715
|
+
Note: You can blend any of the [available syntaxes/approaches](#how-to-create-a-flow-which-has-reusable-steps-to-define-a-complex-use-case) to create use case flows - [examples](https://github.com/serradura/u-case/blob/714c6b658fc6aa02617e6833ddee09eddc760f2a/test/micro/cases/flow/blend_test.rb#L5-L35).
|
652
716
|
|
653
717
|
[⬆️ Back to Top](#table-of-contents-)
|
654
718
|
|
655
719
|
#### Is it possible a flow accumulates its input and merges each success result to use as the argument of the next use cases?
|
656
720
|
|
657
|
-
Answer: Yes, it is! Look at the example below to understand how the data accumulation works inside of the flow execution.
|
721
|
+
Answer: Yes, it is possible! Look at the example below to understand how the data accumulation works inside of the flow execution.
|
658
722
|
|
659
723
|
```ruby
|
660
724
|
module Users
|
661
|
-
class
|
725
|
+
class FindByEmail < Micro::Case
|
662
726
|
attribute :email
|
663
727
|
|
664
728
|
def call!
|
665
729
|
user = User.find_by(email: email)
|
666
730
|
|
667
|
-
return Success
|
731
|
+
return Success result: { user: user } if user
|
668
732
|
|
669
733
|
Failure(:user_not_found)
|
670
734
|
end
|
@@ -679,14 +743,14 @@ module Users
|
|
679
743
|
return Failure(:user_must_be_persisted) if user.new_record?
|
680
744
|
return Failure(:wrong_password) if user.wrong_password?(password)
|
681
745
|
|
682
|
-
return Success
|
746
|
+
return Success result: attributes(:user)
|
683
747
|
end
|
684
748
|
end
|
685
749
|
end
|
686
750
|
|
687
751
|
module Users
|
688
|
-
Authenticate = Micro::
|
689
|
-
|
752
|
+
Authenticate = Micro::Cases.flow([
|
753
|
+
FindByEmail,
|
690
754
|
ValidatePassword
|
691
755
|
])
|
692
756
|
end
|
@@ -694,14 +758,14 @@ end
|
|
694
758
|
Users::Authenticate
|
695
759
|
.call(email: 'somebody@test.com', password: 'password')
|
696
760
|
.on_success { |result| sign_in(result[:user]) }
|
697
|
-
.on_failure(:wrong_password) {
|
698
|
-
.on_failure(:user_not_found) {
|
761
|
+
.on_failure(:wrong_password) { render status: 401 }
|
762
|
+
.on_failure(:user_not_found) { render status: 404 }
|
699
763
|
```
|
700
764
|
|
701
|
-
First, lets see the
|
765
|
+
First, lets see the attributes used by each use case:
|
702
766
|
|
703
767
|
```ruby
|
704
|
-
class Users::
|
768
|
+
class Users::FindByEmail < Micro::Case
|
705
769
|
attribute :email
|
706
770
|
end
|
707
771
|
|
@@ -710,15 +774,15 @@ class Users::ValidatePassword < Micro::Case
|
|
710
774
|
end
|
711
775
|
```
|
712
776
|
|
713
|
-
As you can see the `Users::ValidatePassword` expects a user as its input. So, how it receives the user?
|
714
|
-
It receives the user from the `Users::
|
777
|
+
As you can see the `Users::ValidatePassword` expects a user as its input. So, how does it receives the user?
|
778
|
+
It receives the user from the `Users::FindByEmail` success result!
|
715
779
|
|
716
780
|
And this, is the power of use cases composition because the output
|
717
|
-
of one
|
781
|
+
of one step will compose the input of the next use case in the flow!
|
718
782
|
|
719
783
|
> input **>>** process **>>** output
|
720
784
|
|
721
|
-
> **Note:** Check out these test examples [Micro::
|
785
|
+
> **Note:** Check out these test examples [Micro::Cases::Flow](https://github.com/serradura/u-case/blob/c96a3650469da40dc9f83ff678204055b7015d01/test/micro/cases/flow/result_transitions_test.rb) and [Micro::Cases::Safe::Flow](https://github.com/serradura/u-case/blob/c96a3650469da40dc9f83ff678204055b7015d01/test/micro/cases/safe/flow/result_transitions_test.rb) to see different use cases sharing their own data.
|
722
786
|
|
723
787
|
[⬆️ Back to Top](#table-of-contents-)
|
724
788
|
|
@@ -736,12 +800,12 @@ user_authenticated.transitions
|
|
736
800
|
[
|
737
801
|
{
|
738
802
|
:use_case => {
|
739
|
-
:class => Users::
|
803
|
+
:class => Users::FindByEmail,
|
740
804
|
:attributes => { :email => "rodrigo@test.com" }
|
741
805
|
},
|
742
806
|
:success => {
|
743
807
|
:type => :ok,
|
744
|
-
:
|
808
|
+
:result => {
|
745
809
|
:user => #<User:0x00007fb57b1c5f88 @email="rodrigo@test.com" ...>
|
746
810
|
}
|
747
811
|
},
|
@@ -757,7 +821,7 @@ user_authenticated.transitions
|
|
757
821
|
},
|
758
822
|
:success => {
|
759
823
|
:type => :ok,
|
760
|
-
:
|
824
|
+
:result => {
|
761
825
|
:user => #<User:0x00007fb57b1c5f88 @email="rodrigo@test.com" ...>
|
762
826
|
}
|
763
827
|
},
|
@@ -767,13 +831,13 @@ user_authenticated.transitions
|
|
767
831
|
```
|
768
832
|
|
769
833
|
The example above shows the output generated by the `Micro::Case::Result#transitions`.
|
770
|
-
With it is possible to analyze the use cases execution order and what were the given `inputs` (attributes) and `outputs` (`success
|
834
|
+
With it is possible to analyze the use cases execution order and what were the given `inputs` (`[:attributes]`) and `outputs` (`[:success][:result]`) in the entire execution.
|
771
835
|
|
772
|
-
And look up the `accessible_attributes` property,
|
836
|
+
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).
|
773
837
|
|
774
838
|
> **Note:** The [`Micro::Case::Result#then`](#how-to-use-the-microcaseresultthen-method) increments the `Micro::Case::Result#transitions`.
|
775
839
|
|
776
|
-
##### Micro::Case::Result#transitions schema
|
840
|
+
##### `Micro::Case::Result#transitions` schema
|
777
841
|
```ruby
|
778
842
|
[
|
779
843
|
{
|
@@ -784,7 +848,7 @@ And look up the `accessible_attributes` property, because it shows whats attribu
|
|
784
848
|
[success:, failure:] => { # (Output)
|
785
849
|
type: <Symbol>, # Result type. Defaults:
|
786
850
|
# Success = :ok, Failure = :error/:exception
|
787
|
-
|
851
|
+
result: <Hash> # The data returned by the use case
|
788
852
|
},
|
789
853
|
accessible_attributes: <Array>, # Properties that can be accessed by the use case's attributes,
|
790
854
|
# starting with Hash used to invoke it and which are incremented
|
@@ -793,6 +857,10 @@ And look up the `accessible_attributes` property, because it shows whats attribu
|
|
793
857
|
]
|
794
858
|
```
|
795
859
|
|
860
|
+
##### Is it possible disable the `Micro::Case::Result#transitions`?
|
861
|
+
|
862
|
+
Answer: Yes, it is! You can use the `Micro::Case.config` to do this. [Link to](#microcaseconfig) this section.
|
863
|
+
|
796
864
|
#### Is it possible to declare a flow which includes the use case itself?
|
797
865
|
|
798
866
|
Answer: Yes, it is! You can use the `self.call!` macro. e.g:
|
@@ -802,7 +870,7 @@ class ConvertTextToNumber < Micro::Case
|
|
802
870
|
attribute :text
|
803
871
|
|
804
872
|
def call!
|
805
|
-
Success
|
873
|
+
Success result: { number: text.to_i }
|
806
874
|
end
|
807
875
|
end
|
808
876
|
|
@@ -810,7 +878,7 @@ class ConvertNumberToText < Micro::Case
|
|
810
878
|
attribute :number
|
811
879
|
|
812
880
|
def call!
|
813
|
-
Success
|
881
|
+
Success result: { text: number.to_s }
|
814
882
|
end
|
815
883
|
end
|
816
884
|
|
@@ -822,17 +890,17 @@ class Double < Micro::Case
|
|
822
890
|
attribute :number
|
823
891
|
|
824
892
|
def call!
|
825
|
-
Success
|
893
|
+
Success result: { number: number * 2 }
|
826
894
|
end
|
827
895
|
end
|
828
896
|
|
829
897
|
result = Double.call(text: '4')
|
830
898
|
|
831
899
|
result.success? # true
|
832
|
-
result
|
900
|
+
result[:number] # "8"
|
833
901
|
|
834
902
|
# NOTE: This feature can be used with the Micro::Case::Safe.
|
835
|
-
# Checkout
|
903
|
+
# Checkout this test: https://github.com/serradura/u-case/blob/714c6b658fc6aa02617e6833ddee09eddc760f2a/test/micro/case/safe/with_inner_flow_test.rb
|
836
904
|
```
|
837
905
|
|
838
906
|
[⬆️ Back to Top](#table-of-contents-)
|
@@ -846,7 +914,7 @@ class Double < Micro::Case::Strict
|
|
846
914
|
attribute :numbers
|
847
915
|
|
848
916
|
def call!
|
849
|
-
Success
|
917
|
+
Success result: { numbers: numbers.map { |number| number * 2 } }
|
850
918
|
end
|
851
919
|
end
|
852
920
|
|
@@ -860,7 +928,7 @@ Double.call({})
|
|
860
928
|
|
861
929
|
### `Micro::Case::Safe` - Is there some feature to auto handle exceptions inside of a use case or flow?
|
862
930
|
|
863
|
-
Answer: Yes, there is!
|
931
|
+
Answer: Yes, there is one!
|
864
932
|
|
865
933
|
**Use cases:**
|
866
934
|
|
@@ -875,14 +943,18 @@ class Divide < Micro::Case::Safe
|
|
875
943
|
attributes :a, :b
|
876
944
|
|
877
945
|
def call!
|
878
|
-
|
879
|
-
|
946
|
+
if a.is_a?(Integer) && b.is_a?(Integer)
|
947
|
+
Success result: { number: a / b}
|
948
|
+
else
|
949
|
+
Failure(:not_an_integer)
|
950
|
+
end
|
880
951
|
end
|
881
952
|
end
|
882
953
|
|
883
954
|
result = Divide.call(a: 2, b: 0)
|
884
|
-
result.type == :exception
|
885
|
-
result.
|
955
|
+
result.type == :exception # true
|
956
|
+
result.data # { exception: #<ZeroDivisionError...> }
|
957
|
+
result[:exception].is_a?(ZeroDivisionError) # true
|
886
958
|
|
887
959
|
result.on_failure(:exception) do |exception|
|
888
960
|
AppLogger.error(exception.message) # E, [2019-08-21T00:05:44.195506 #9532] ERROR -- : divided by 0
|
@@ -903,25 +975,18 @@ end
|
|
903
975
|
# Another note:
|
904
976
|
# ------------
|
905
977
|
# It is possible to rescue an exception even when is a safe use case.
|
906
|
-
# Examples: https://github.com/serradura/u-case/blob/
|
978
|
+
# Examples: https://github.com/serradura/u-case/blob/714c6b658fc6aa02617e6833ddee09eddc760f2a/test/micro/case/safe_test.rb#L90-L118
|
907
979
|
```
|
908
980
|
|
909
|
-
|
981
|
+
[⬆️ Back to Top](#table-of-contents-)
|
982
|
+
|
983
|
+
#### `Micro::Cases::Safe::Flow`
|
910
984
|
|
911
985
|
As the safe use cases, safe flows can intercept an exception in any of its steps. These are the ways to define one:
|
912
986
|
|
913
987
|
```ruby
|
914
988
|
module Users
|
915
|
-
Create =
|
916
|
-
end
|
917
|
-
|
918
|
-
# Note:
|
919
|
-
# The ampersand is based on the safe navigation operator. https://ruby-doc.org/core-2.6/doc/syntax/calling_methods_rdoc.html#label-Safe+navigation+operator
|
920
|
-
|
921
|
-
# The alternatives to declare a safe flow are:
|
922
|
-
|
923
|
-
module Users
|
924
|
-
Create = Micro::Case::Safe::Flow([
|
989
|
+
Create = Micro::Cases.safe_flow([
|
925
990
|
ProcessParams,
|
926
991
|
ValidateParams,
|
927
992
|
Persist,
|
@@ -939,26 +1004,56 @@ module Users
|
|
939
1004
|
SendToCRM
|
940
1005
|
end
|
941
1006
|
end
|
1007
|
+
```
|
942
1008
|
|
1009
|
+
[⬆️ Back to Top](#table-of-contents-)
|
943
1010
|
|
944
|
-
|
945
|
-
# ! Deprecated: Micro::Case::Safe::Flow mixin ! #
|
946
|
-
# !-------------------------------------------! #
|
1011
|
+
#### `Micro::Case::Result#on_exception`
|
947
1012
|
|
948
|
-
|
949
|
-
# Deprecation: Micro::Case::Flow mixin is being deprecated, please use `Micro::Case` inheritance instead.
|
1013
|
+
In functional programming errors/exceptions are handled as regular data, the idea is to transform the output even when it happens an unexpected behavior. For many, [exceptions are very similar to the GOTO statement](https://softwareengineering.stackexchange.com/questions/189222/are-exceptions-as-control-flow-considered-a-serious-antipattern-if-so-why), jumping the application flow to paths which could be difficult to figure out how things work in a system.
|
950
1014
|
|
951
|
-
|
952
|
-
class Create
|
953
|
-
include Micro::Case::Safe::Flow
|
1015
|
+
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.
|
954
1016
|
|
955
|
-
|
1017
|
+
> **Note**: this feature will work better if you use it with a `Micro::Case::Safe` use case/flow.
|
1018
|
+
|
1019
|
+
How does it work?
|
1020
|
+
|
1021
|
+
```ruby
|
1022
|
+
class Divide < Micro::Case::Safe
|
1023
|
+
attributes :a, :b
|
1024
|
+
|
1025
|
+
def call!
|
1026
|
+
Success result: { division: a / b }
|
956
1027
|
end
|
957
1028
|
end
|
958
1029
|
|
959
|
-
|
1030
|
+
Divide
|
1031
|
+
.call(a: 2, b: 0)
|
1032
|
+
.on_success { |result| puts result[:division] }
|
1033
|
+
.on_exception(TypeError) { puts 'Please, use only numeric attributes.' }
|
1034
|
+
.on_exception(ZeroDivisionError) { |_error| puts "Can't divide a number by 0." }
|
1035
|
+
.on_exception { |_error, _use_case| puts 'Oh no, something went wrong!' }
|
1036
|
+
|
1037
|
+
# Output:
|
1038
|
+
# -------
|
1039
|
+
# Can't divide a number by 0
|
1040
|
+
# Oh no, something went wrong!
|
1041
|
+
|
1042
|
+
Divide.
|
1043
|
+
.call(a: 2, b: '2').
|
1044
|
+
.on_success { |result| puts result[:division] }
|
1045
|
+
.on_exception(TypeError) { puts 'Please, use only numeric attributes.' }
|
1046
|
+
.on_exception(ZeroDivisionError) { |_error| puts "Can't divide a number by 0." }
|
1047
|
+
.on_exception { |_error, _use_case| puts 'Oh no, something went wrong!' }
|
1048
|
+
|
1049
|
+
# Output:
|
1050
|
+
# -------
|
1051
|
+
# Please, use only numeric attributes.
|
1052
|
+
# Oh no, something went wrong!
|
960
1053
|
```
|
961
1054
|
|
1055
|
+
As you can see, this hook has the same behavior of `result.on_failure(:exception)`, but, the ideia here is to have a better communication in the code, making an explicit reference when some failure happened because of an exception.
|
1056
|
+
|
962
1057
|
[⬆️ Back to Top](#table-of-contents-)
|
963
1058
|
|
964
1059
|
### `u-case/with_activemodel_validation` - How to validate use case attributes?
|
@@ -978,23 +1073,27 @@ class Multiply < Micro::Case
|
|
978
1073
|
validates :a, :b, presence: true, numericality: true
|
979
1074
|
|
980
1075
|
def call!
|
981
|
-
return Failure
|
1076
|
+
return Failure :validation_error, result: { errors: self.errors } if invalid?
|
982
1077
|
|
983
|
-
Success
|
1078
|
+
Success result: { number: a * b }
|
984
1079
|
end
|
985
1080
|
end
|
1081
|
+
```
|
986
1082
|
|
987
|
-
|
988
|
-
# But if do you want an automatic way to fail
|
989
|
-
# your use cases on validation errors, you can use:
|
1083
|
+
But if do you want an automatic way to fail your use cases on validation errors, you can:
|
990
1084
|
|
991
|
-
|
992
|
-
require 'u-case/with_activemodel_validation' # or require 'micro/case/with_validation'
|
1085
|
+
1. **require 'u-case/with_activemodel_validation'** mode
|
993
1086
|
|
994
|
-
|
995
|
-
gem 'u-case', require: 'u-case/with_activemodel_validation'
|
1087
|
+
```ruby
|
1088
|
+
gem 'u-case', require: 'u-case/with_activemodel_validation'
|
1089
|
+
```
|
1090
|
+
|
1091
|
+
2. Use the `Micro::Case.config` to enable it. [Link to](#microcaseconfig) this section.
|
996
1092
|
|
997
|
-
|
1093
|
+
Using this approach, you can rewrite the previous example with less code. e.g:
|
1094
|
+
|
1095
|
+
```ruby
|
1096
|
+
require 'u-case/with_activemodel_validation'
|
998
1097
|
|
999
1098
|
class Multiply < Micro::Case
|
1000
1099
|
attributes :a, :b
|
@@ -1002,7 +1101,7 @@ class Multiply < Micro::Case
|
|
1002
1101
|
validates :a, :b, presence: true, numericality: true
|
1003
1102
|
|
1004
1103
|
def call!
|
1005
|
-
Success
|
1104
|
+
Success result: { number: a * b }
|
1006
1105
|
end
|
1007
1106
|
end
|
1008
1107
|
|
@@ -1014,7 +1113,7 @@ end
|
|
1014
1113
|
|
1015
1114
|
#### If I enabled the auto validation, is it possible to disable it only in specific use case classes?
|
1016
1115
|
|
1017
|
-
Answer: Yes, it is. To do this, you only need to use the `disable_auto_validation` macro. e.g:
|
1116
|
+
Answer: Yes, it is possible. To do this, you only need to use the `disable_auto_validation` macro. e.g:
|
1018
1117
|
|
1019
1118
|
```ruby
|
1020
1119
|
require 'u-case/with_activemodel_validation'
|
@@ -1027,7 +1126,7 @@ class Multiply < Micro::Case
|
|
1027
1126
|
validates :a, :b, presence: true, numericality: true
|
1028
1127
|
|
1029
1128
|
def call!
|
1030
|
-
Success
|
1129
|
+
Success result: { number: a * b }
|
1031
1130
|
end
|
1032
1131
|
end
|
1033
1132
|
|
@@ -1039,7 +1138,7 @@ Multiply.call(a: 2, b: 'a')
|
|
1039
1138
|
|
1040
1139
|
[⬆️ Back to Top](#table-of-contents-)
|
1041
1140
|
|
1042
|
-
#### Kind::Validator
|
1141
|
+
#### `Kind::Validator`
|
1043
1142
|
|
1044
1143
|
The [kind gem](https://github.com/serradura/kind) has a module to enable the validation of data type through [`ActiveModel validations`](https://guides.rubyonrails.org/active_model_basics.html#validations). So, when you require the `'u-case/with_activemodel_validation'`, this module will require the [`Kind::Validator`](https://github.com/serradura/kind#kindvalidator-activemodelvalidations).
|
1045
1144
|
|
@@ -1053,20 +1152,37 @@ class Todo::List::AddItem < Micro::Case
|
|
1053
1152
|
validates :params, kind: ActionController::Parameters
|
1054
1153
|
|
1055
1154
|
def call!
|
1056
|
-
todo_params =
|
1155
|
+
todo_params = params.require(:todo).permit(:title, :due_at)
|
1057
1156
|
|
1058
1157
|
todo = user.todos.create(todo_params)
|
1059
1158
|
|
1060
|
-
Success
|
1159
|
+
Success result: { todo: todo }
|
1061
1160
|
rescue ActionController::ParameterMissing => e
|
1062
|
-
Failure
|
1161
|
+
Failure :parameter_missing, result: { message: e.message }
|
1063
1162
|
end
|
1064
1163
|
end
|
1065
1164
|
```
|
1066
1165
|
|
1166
|
+
## `Micro::Case.config`
|
1167
|
+
|
1168
|
+
The idea of this feature is to allow the configuration of some `u-case` features/modules.
|
1169
|
+
I recommend you use it only once in your codebase. e.g. In a Rails initializer.
|
1170
|
+
|
1171
|
+
You can see below, which are all of the available configurations with their default values:
|
1172
|
+
|
1173
|
+
```ruby
|
1174
|
+
Micro::Case.config do |config|
|
1175
|
+
# Use ActiveModel to auto-validate your use cases' attributes.
|
1176
|
+
config.enable_activemodel_validations = false
|
1177
|
+
|
1178
|
+
# Use to enable/disable the `Micro::Case::Results#transitions` tracking.
|
1179
|
+
config.enable_transitions = true
|
1180
|
+
end
|
1181
|
+
```
|
1182
|
+
|
1067
1183
|
## Benchmarks
|
1068
1184
|
|
1069
|
-
### `Micro::Case`
|
1185
|
+
### `Micro::Case` (v2.6.0)
|
1070
1186
|
|
1071
1187
|
#### Best overall
|
1072
1188
|
|
@@ -1074,11 +1190,11 @@ The table below contains the average between the [Success results](#success-resu
|
|
1074
1190
|
|
1075
1191
|
| Gem / Abstraction | Iterations per second | Comparison |
|
1076
1192
|
| ---------------------- | --------------------: | ----------------: |
|
1077
|
-
| **Micro::Case** |
|
1078
|
-
| Dry::Monads |
|
1079
|
-
| Interactor |
|
1080
|
-
| Trailblazer::Operation |
|
1081
|
-
| Dry::Transaction |
|
1193
|
+
| **Micro::Case** | 105124.3 | _**The Fastest**_ |
|
1194
|
+
| Dry::Monads | 103290.1 | 0.02x slower |
|
1195
|
+
| Interactor | 21342.3 | 4.93x slower |
|
1196
|
+
| Trailblazer::Operation | 14652.7 | 7.17x slower |
|
1197
|
+
| Dry::Transaction | 5310.3 | 19.80x slower |
|
1082
1198
|
|
1083
1199
|
---
|
1084
1200
|
|
@@ -1086,44 +1202,43 @@ The table below contains the average between the [Success results](#success-resu
|
|
1086
1202
|
|
1087
1203
|
| Gem / Abstraction | Iterations per second | Comparison |
|
1088
1204
|
| ----------------- | --------------------: | ----------------: |
|
1089
|
-
| Dry::Monads |
|
1090
|
-
| **Micro::Case** |
|
1091
|
-
| Interactor |
|
1092
|
-
| Trailblazer::Operation |
|
1093
|
-
| Dry::Transaction |
|
1205
|
+
| Dry::Monads | 134801.0 | _**The Fastest**_ |
|
1206
|
+
| **Micro::Case** | 105909.2 | 1.27x slower |
|
1207
|
+
| Interactor | 29458.2 | 4.58x slower |
|
1208
|
+
| Trailblazer::Operation | 14714.9 | 9.16x slower |
|
1209
|
+
| Dry::Transaction | 5642.6 | 28.89x slower |
|
1094
1210
|
|
1095
1211
|
<details>
|
1096
1212
|
<summary>Show the full <a href="https://github.com/evanphx/benchmark-ips">benchmark/ips</a> results.</summary>
|
1097
1213
|
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
```
|
1214
|
+
```ruby
|
1215
|
+
# Warming up --------------------------------------
|
1216
|
+
# Interactor 2.897k i/100ms
|
1217
|
+
# Trailblazer::Operation 1.494k i/100ms
|
1218
|
+
# Dry::Monads 13.854k i/100ms
|
1219
|
+
# Dry::Transaction 561.000 i/100ms
|
1220
|
+
# Micro::Case 10.523k i/100ms
|
1221
|
+
# Micro::Case::Strict 7.982k i/100ms
|
1222
|
+
# Micro::Case::Safe 10.568k i/100ms
|
1223
|
+
|
1224
|
+
# Calculating -------------------------------------
|
1225
|
+
# Interactor 29.458k (± 3.4%) i/s - 147.747k in 5.021405s
|
1226
|
+
# Trailblazer::Operation 14.715k (± 1.8%) i/s - 74.700k in 5.078128s
|
1227
|
+
# Dry::Monads 134.801k (± 8.7%) i/s - 678.846k in 5.088739s
|
1228
|
+
# Dry::Transaction 5.643k (± 2.1%) i/s - 28.611k in 5.072969s
|
1229
|
+
# Micro::Case 105.909k (± 2.4%) i/s - 536.673k in 5.070329s
|
1230
|
+
# Micro::Case::Strict 84.234k (± 1.5%) i/s - 423.046k in 5.023447s
|
1231
|
+
# Micro::Case::Safe 105.725k (± 1.9%) i/s - 538.968k in 5.099817s
|
1232
|
+
|
1233
|
+
# Comparison:
|
1234
|
+
# Dry::Monads: 134801.0 i/s
|
1235
|
+
# Micro::Case: 105909.2 i/s - 1.27x (± 0.00) slower
|
1236
|
+
# Micro::Case::Safe: 105725.0 i/s - 1.28x (± 0.00) slower
|
1237
|
+
# Micro::Case::Strict: 84234.4 i/s - 1.60x (± 0.00) slower
|
1238
|
+
# Interactor: 29458.2 i/s - 4.58x (± 0.00) slower
|
1239
|
+
# Trailblazer::Operation: 14714.9 i/s - 9.16x (± 0.00) slower
|
1240
|
+
# Dry::Transaction: 5642.6 i/s - 23.89x (± 0.00) slower
|
1241
|
+
```
|
1127
1242
|
</details>
|
1128
1243
|
|
1129
1244
|
https://github.com/serradura/u-case/blob/master/benchmarks/use_case/with_success_result.rb
|
@@ -1132,100 +1247,98 @@ https://github.com/serradura/u-case/blob/master/benchmarks/use_case/with_success
|
|
1132
1247
|
|
1133
1248
|
| Gem / Abstraction | Iterations per second | Comparison |
|
1134
1249
|
| ----------------- | --------------------: | ----------------: |
|
1135
|
-
| **Micro::Case** |
|
1136
|
-
| Dry::Monads |
|
1137
|
-
| Trailblazer::Operation |
|
1138
|
-
| Interactor |
|
1139
|
-
| Dry::Transaction |
|
1250
|
+
| **Micro::Case** | 104339.4 | _**The Fastest**_ |
|
1251
|
+
| Dry::Monads | 71779.2 | 1.45x slower |
|
1252
|
+
| Trailblazer::Operation | 14590.6 | 7.15x slower |
|
1253
|
+
| Interactor | 13226.5 | 7.89x slower |
|
1254
|
+
| Dry::Transaction | 4978.1 | 20.96x slower |
|
1140
1255
|
|
1141
1256
|
<details>
|
1142
1257
|
<summary>Show the full <a href="https://github.com/evanphx/benchmark-ips">benchmark/ips</a> results.</summary>
|
1143
1258
|
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
```
|
1259
|
+
```ruby
|
1260
|
+
# Warming up --------------------------------------
|
1261
|
+
# Interactor 1.339k i/100ms
|
1262
|
+
# Trailblazer::Operation 1.393k i/100ms
|
1263
|
+
# Dry::Monads 7.208k i/100ms
|
1264
|
+
# Dry::Transaction 423.000 i/100ms
|
1265
|
+
# Micro::Case 9.620k i/100ms
|
1266
|
+
# Micro::Case::Strict 8.238k i/100ms
|
1267
|
+
# Micro::Case::Safe 9.906k i/100ms
|
1268
|
+
|
1269
|
+
# Calculating -------------------------------------
|
1270
|
+
# Interactor 13.227k (± 3.3%) i/s - 66.950k in 5.067145s
|
1271
|
+
# Trailblazer::Operation 14.591k (± 4.0%) i/s - 73.829k in 5.069162s
|
1272
|
+
# Dry::Monads 71.779k (± 2.5%) i/s - 360.400k in 5.024294s
|
1273
|
+
# Dry::Transaction 4.978k (± 3.3%) i/s - 24.957k in 5.019153s
|
1274
|
+
# Micro::Case 103.957k (± 1.8%) i/s - 529.100k in 5.091221s
|
1275
|
+
# Micro::Case::Strict 83.094k (± 2.0%) i/s - 420.138k in 5.058233s
|
1276
|
+
# Micro::Case::Safe 104.339k (± 1.7%) i/s - 525.018k in 5.033381s
|
1277
|
+
|
1278
|
+
# Comparison:
|
1279
|
+
# Micro::Case::Safe: 104339.4 i/s
|
1280
|
+
# Micro::Case: 103957.2 i/s - same-ish: difference falls within error
|
1281
|
+
# Micro::Case::Strict: 83094.5 i/s - 1.26x (± 0.00) slower
|
1282
|
+
# Dry::Monads: 71779.2 i/s - 1.45x (± 0.00) slower
|
1283
|
+
# Trailblazer::Operation: 14590.6 i/s - 7.15x (± 0.00) slower
|
1284
|
+
# Interactor: 13226.5 i/s - 7.89x (± 0.00) slower
|
1285
|
+
# Dry::Transaction: 4978.1 i/s - 20.96x (± 0.00) slower
|
1286
|
+
```
|
1173
1287
|
</details>
|
1174
1288
|
|
1175
1289
|
https://github.com/serradura/u-case/blob/master/benchmarks/use_case/with_failure_result.rb
|
1176
1290
|
|
1177
1291
|
---
|
1178
1292
|
|
1179
|
-
### `Micro::Case::Flow`
|
1293
|
+
### `Micro::Case::Flow` (v2.6.0)
|
1180
1294
|
|
1181
1295
|
| 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) |
|
1182
1296
|
| ------------------ | ----------------: | ----------------: |
|
1183
1297
|
| Micro::Case::Flow | _**The Fastest**_ | _**The Fastest**_ |
|
1184
1298
|
| Micro::Case::Safe::Flow | 0x slower | 0x slower |
|
1185
|
-
| Interactor::Organizer | 1.
|
1299
|
+
| Interactor::Organizer | 1.27x slower | 5.48x slower |
|
1186
1300
|
|
1187
1301
|
\* The `Dry::Monads`, `Dry::Transaction`, `Trailblazer::Operation` are out of this analysis because all of them doesn't have this kind of feature.
|
1188
1302
|
|
1189
1303
|
<details>
|
1190
1304
|
<summary><strong>Success results</strong> - Show the full benchmark/ips results.</summary>
|
1191
1305
|
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
```
|
1306
|
+
```ruby
|
1307
|
+
# Warming up --------------------------------------
|
1308
|
+
# Interactor::Organizer 4.765k i/100ms
|
1309
|
+
# Micro::Case::Flow 5.372k i/100ms
|
1310
|
+
# Micro::Case::Safe::Flow 5.855k i/100ms
|
1311
|
+
# Calculating -------------------------------------
|
1312
|
+
# Interactor::Organizer 48.598k (± 5.2%) i/s - 243.015k in 5.014307s
|
1313
|
+
# Micro::Case::Flow 61.606k (± 4.4%) i/s - 311.576k in 5.068602s
|
1314
|
+
# Micro::Case::Safe::Flow 60.688k (± 4.8%) i/s - 304.460k in 5.028877s
|
1315
|
+
|
1316
|
+
# Comparison:
|
1317
|
+
# Micro::Case::Flow: 61606.3 i/s
|
1318
|
+
# Micro::Case::Safe::Flow: 60688.3 i/s - same-ish: difference falls within error
|
1319
|
+
# Interactor::Organizer: 48598.2 i/s - 1.27x slower\
|
1320
|
+
```
|
1208
1321
|
</details>
|
1209
1322
|
|
1210
1323
|
<details>
|
1211
1324
|
<summary><strong>Failure results</strong> - Show the full benchmark/ips results.</summary>
|
1212
1325
|
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1326
|
+
```ruby
|
1327
|
+
# Warming up --------------------------------------
|
1328
|
+
# Interactor::Organizer 2.209k i/100ms
|
1329
|
+
# Micro::Case::Flow 11.508k i/100ms
|
1330
|
+
# Micro::Case::Safe::Flow 11.605k i/100ms
|
1331
|
+
|
1332
|
+
# Calculating -------------------------------------
|
1333
|
+
# Interactor::Organizer 22.592k (± 2.8%) i/s - 114.868k in 5.088685s
|
1334
|
+
# Micro::Case::Flow 123.629k (± 2.9%) i/s - 621.432k in 5.030844s
|
1335
|
+
# Micro::Case::Safe::Flow 123.862k (± 3.0%) i/s - 626.670k in 5.064097s
|
1336
|
+
|
1337
|
+
# Comparison:
|
1338
|
+
# Micro::Case::Safe::Flow: 123862.4 i/s
|
1339
|
+
# Micro::Case::Flow: 123629.3 i/s - same-ish: difference falls within error
|
1340
|
+
# Interactor::Organizer: 22592.2 i/s - 5.48x slower
|
1341
|
+
```
|
1229
1342
|
</details>
|
1230
1343
|
|
1231
1344
|
https://github.com/serradura/u-case/tree/master/benchmarks/flow
|