u-case 2.5.0 → 3.0.0.rc4
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 +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
|