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