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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 349609a8d927ab7fd8edb1eb96141dff2f78f9957f31f2b88a8975239fc0de87
4
- data.tar.gz: 4b6293023f49f28dd8283656a0d423f7d24abbfb7f010a90ba4d0dd17f577d22
3
+ metadata.gz: b3374584a2cf58a68aa1bed216c91bfd715cc4365d2e7972e5693a8c3acb52eb
4
+ data.tar.gz: 9bbe75661a2cd53e549cf60692811d47d1aaf5895ee71cf89f3fc7e8f8d72f0b
5
5
  SHA512:
6
- metadata.gz: 0553e560e40f71d0e8284ebbbbf7a51326d8c9bc9de6ad0de76db526466a94f633e2b038563868a2c93234800b7bdacc30a99ba57f1624ce08463fdc012d165d
7
- data.tar.gz: 2866ca201676827b2ffc82877b1615e7ecbb6fc6e993df17007c6446b348a850266faf162a0953954ee6adfb0d35a88c303bee385d2fb52a9c228f45513655db
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 failure hook (without a type) exposes a different kind of data?](#why-the-failure-hook-without-a-type-exposes-a-different-kind-of-data)
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
- - [`Micro::Case::Flow` - How to compose use cases?](#microcaseflow---how-to-compose-use-cases)
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::Case::Safe::Flow`](#microcasesafeflow)
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, expose `Kind.of.Micro::Case::Result` type checker and 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.
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(a * b)
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.value # 4
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.value # "`a` and `b` attributes must be numeric"
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
- - `#value` the result value itself.
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
- - `#on_success` or `#on_failure` are hook methods that help you define the application flow.
160
- - `#use_case` if is a failure result, the use case responsible for it will be accessible through this method. This feature is handy to handle a flow failure (this topic will be covered ahead).
161
- - `#then` allows if the current result is a success, the `then` method will allow to applying a new use case for its value.
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? ? Success(a / b) : Failure(invalid_attributes)
177
- rescue => e
178
- Failure(e)
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.value # 1
204
+ result.data # { number: 1 }
192
205
  result.success? # true
193
- result.use_case # raises `Micro::Case::Error::InvalidAccessToTheUseCaseObject: only a failure result can access its own 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.value # {"b"=>"2"}
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=#<Micro::Case::Result:0x0000 @use_case=#<Divide:0x0000 ...>, @type=:error, @value={"b"=>"2"}, @success=false>>
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.value # <ZeroDivisionError: divided by 0>
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 a block to set their values.
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
- return Success(a * b) if a.is_a?(Numeric) && b.is_a?(Numeric)
231
-
232
- Failure(:invalid_data) do
233
- attributes.reject { |_, input| input.is_a?(Numeric) }
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.value # 6
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.value # {"b"=>"2"}
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 only for failure results!
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
- return Failure(:invalid_data) unless a.is_a?(Numeric) && b.is_a?(Numeric)
267
-
268
- Success(a * b)
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.value # :invalid_data
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(:invalid) { 'the number must be a numeric value' } unless number.is_a?(Numeric)
299
- return Failure(:lte_zero) { 'the number must be greater than 0' } if number <= 0
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(number * 2)
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 { |number| p number }
312
- .on_failure(:invalid) { |msg| raise TypeError, msg }
313
- .on_failure(:lte_zero) { |msg| raise ArgumentError, msg }
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 { |number| p number }
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) { |msg| raise TypeError, msg }
327
- .on_failure(:lte_zero) { |msg| raise ArgumentError, msg }
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 failure hook (without a type) exposes a different kind of data?
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(:lte_zero) { number } if number <= 0
367
+ return Failure :lte_zero, result: attributes(:number) if number <= 0
351
368
 
352
- Success(number * 2)
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, 'the number must be a numeric value'
365
- when :lte_zero then raise ArgumentError, "the number `#{result.value}` must be greater than 0"
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 (the number `-1` must be greater than 0)
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
- # The syntax to decompose an Array can be used in methods, blocks and assigments.
379
- # If you doesn't know that, check out:
380
- # https://ruby-doc.org/core-2.2.0/doc/syntax/assignment_rdoc.html#label-Array+Decomposition
381
- #
382
- # And the object exposed in the hook failure can be decomposed using this syntax. e.g:
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 |(value, type), use_case|
399
+ .call(number: -2)
400
+ .on_failure do |(data, type), use_case|
387
401
  case type
388
- when :invalid then raise TypeError, 'the number must be a numeric value'
389
- when :lte_zero then raise ArgumentError, "the number `#{value}` must be greater than 0"
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
- return Failure(:invalid) { 'the number must be a numeric value' } unless number.is_a?(Numeric)
411
-
412
- Success(:computed) { number * 2 }
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.value # 6
418
- result.value * 4 # 24
435
+ result.data # { number: 6 }
436
+ result[:number] * 4 # 24
419
437
 
420
438
  accum = 0
421
439
 
422
- result.on_success { |number| accum += number }
423
- .on_success { |number| accum += number }
424
- .on_success(:computed) { |number| accum += number }
425
- .on_success(:computed) { |number| accum += number }
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.value * 4 == accum # true
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 { attributes } if number >= 0
461
+ return Success result: attributes if number >= 0
440
462
 
441
- Failure { attributes }
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 { { number: number + 3 } }
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.type # :error
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.type # :ok
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
- ### `Micro::Case::Flow` - How to compose use cases?
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
- In this case, this will be a **flow** (`Micro::Case::Flow`).
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(numbers: numbers.map(&:to_i))
562
+ Success result: { numbers: numbers.map(&:to_i) }
489
563
  else
490
- Failure('numbers must contain only numeric types')
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(numbers: numbers.map { |number| number + 2 })
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(numbers: numbers.map { |number| number * 2 })
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(numbers: numbers.map { |number| number * number })
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 the collection syntax #
522
- #---------------------------------------------#
594
+ #-------------------------------------------#
595
+ # Creating a flow using Micro::Cases.flow() #
596
+ #-------------------------------------------#
523
597
 
524
- Add2ToAllNumbers = Micro::Case::Flow([
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
- p result.success? # true
532
- p result.value # {:numbers => [3, 3, 4, 4, 5, 6]}
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
- .call(numbers: %w[1 1 b 2 3 4])
545
- .on_failure { |message| p message } # "numbers must contain only numeric types"
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 = SquareAllNumbers.call(numbers: %w[1 1 b 2 3 4])
626
+ result = DoubleAllNumbers.call(numbers: %w[1 1 b 2 3 4])
580
627
 
581
- result.failure? # true
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(numbers: numbers.map(&:to_i))
649
+ Success result: { numbers: numbers.map(&:to_i) }
603
650
  else
604
- Failure('numbers must contain only numeric types')
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(numbers: numbers.map { |number| number + 2 })
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(numbers: numbers.map { |number| number * 2 })
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(numbers: numbers.map { |number| number * number })
676
+ Success result: { numbers: numbers.map { |number| number * number } }
630
677
  end
631
678
  end
632
679
  end
633
680
 
634
- Add2ToAllNumbers = Steps::ConvertTextToNumbers >> Steps::Add2
635
- DoubleAllNumbers = Steps::ConvertTextToNumbers >> Steps::Double
636
- SquareAllNumbers = Steps::ConvertTextToNumbers >> Steps::Square
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
- DoubleAllNumbersAndAdd2 = DoubleAllNumbers >> Steps::Add2
639
- SquareAllNumbersAndAdd2 = SquareAllNumbers >> Steps::Add2
690
+ SquareAllNumbersAndAdd2 =
691
+ Micro::Cases.flow([SquareAllNumbers, Steps::Add2])
640
692
 
641
- SquareAllNumbersAndDouble = SquareAllNumbersAndAdd2 >> DoubleAllNumbers
642
- DoubleAllNumbersAndSquareAndAdd2 = DoubleAllNumbers >> SquareAllNumbersAndAdd2
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 { |value| p value[:numbers] } # [6, 6, 12, 12, 22, 36]
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 { |value| p value[:numbers] } # [6, 6, 18, 18, 38, 66]
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/master/test/micro/case/flow/blend_test.rb#L7-L34).
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 Find < Micro::Case
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 { { user: user } } if user
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 { attributes(:user) }
739
+ return Success result: attributes(:user)
685
740
  end
686
741
  end
687
742
  end
688
743
 
689
744
  module Users
690
- Authenticate = Micro::Case::Flow([
691
- Find,
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) { |result| render status: 401 }
700
- .on_failure(:user_not_found) { |result| render status: 404 }
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 attribute of each use case:
758
+ First, lets see the attributes used by each use case:
704
759
 
705
760
  ```ruby
706
- class Users::Find < Micro::Case
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::Find` success result!
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 flow will compose the input of the next use case in the flow!
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::Case::Flow](https://github.com/serradura/u-case/blob/b6d63b0db0caada67d2a6cf5cc5937000c0acf04/test/micro/case/flow/reducer_test.rb) and [Micro::Case::Safe::Flow](https://github.com/serradura/u-case/blob/b1d84b355f2b92d329e10d5d56d8012df1d32681/test/micro/case/safe/flow/reducer_test.rb) to see different use cases sharing their own data.
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::Find,
796
+ :class => Users::FindByEmail,
742
797
  :attributes => { :email => "rodrigo@test.com" }
743
798
  },
744
799
  :success => {
745
800
  :type => :ok,
746
- :value => {
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
- :value => {
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.value`) in the entire execution.
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, because 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 [flow data accumulation](#is-it-possible-a-flow-accumulates-its-input-and-merges-each-success-result-to-use-as-the-argument-of-the-next-use-cases).
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
- value: <Hash> # The data returned by the use case
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 { { number: text.to_i } }
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 { { text: number.to_s } }
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 { { number: number * 2 } }
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.value # "8"
893
+ result[:number] # "8"
837
894
 
838
895
  # NOTE: This feature can be used with the Micro::Case::Safe.
839
- # Checkout the test: test/micro/case/safe/flow/with_classes/using_itself_test.rb
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(numbers.map { |number| number * 2 })
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
- return Success(a / b) if a.is_a?(Integer) && b.is_a?(Integer)
883
- Failure(:not_an_integer)
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 # true
889
- result.value.is_a?(ZeroDivisionError) # true
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 |exception|
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 |exception, use_case|
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/5a85fc238b63811a32737493dc6c59965f92491d/test/micro/case/safe_test.rb#L95-L123
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::Case::Safe::Flow`
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 = ProcessParams & ValidateParams & Persist & SendToCRM
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** this feature will work better if you use it with a `Micro::Case::Safe` use case/flow.
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(division: a / b)
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(:validation_error) { {errors: self.errors} } unless valid?
1069
+ return Failure :validation_error, result: { errors: self.errors } if invalid?
1035
1070
 
1036
- Success(number: a * b)
1071
+ Success result: { number: a * b }
1037
1072
  end
1038
1073
  end
1074
+ ```
1039
1075
 
1040
- #
1041
- # But if do you want an automatic way to fail
1042
- # your use cases on validation errors, you can use:
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
- # In some file. e.g: A Rails initializer
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
- # In the Gemfile
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
- # Using this approach, you can rewrite the previous example with less code. e.g:
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(number: a * b)
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(number: a * b)
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 = Todo::Params.to_save(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 { { todo: todo} }
1152
+ Success result: { todo: todo }
1114
1153
  rescue ActionController::ParameterMissing => e
1115
- Failure(:parameter_missing) { { message: e.message } }
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** | 116629.7 | _**The Fastest**_ |
1131
- | Dry::Monads | 101796.3 | 1.14x slower |
1132
- | Interactor | 21230.5 | 5.49x slower |
1133
- | Trailblazer::Operation | 16466.6 | 7.08x slower |
1134
- | Dry::Transaction | 5069.5 | 23.00x slower |
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 | 139352.5 | _**The Fastest**_ |
1143
- | **Micro::Case** | 124749.4 | 1.12x slower |
1144
- | Interactor | 28974.4 | 4.81x slower |
1145
- | Trailblazer::Operation | 17275.6 | 8.07x slower |
1146
- | Dry::Transaction | 5571.7 | 25.01x slower |
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
- ```ruby
1152
- # Warming up --------------------------------------
1153
- # Interactor 2.865k i/100ms
1154
- # Trailblazer::Operation
1155
- # 1.686k i/100ms
1156
- # Dry::Monads 13.389k i/100ms
1157
- # Dry::Transaction 551.000 i/100ms
1158
- # Micro::Case 11.984k i/100ms
1159
- # Micro::Case::Strict 9.102k i/100ms
1160
- # Micro::Case::Safe 11.747k i/100ms
1161
- # Calculating -------------------------------------
1162
- # Interactor 28.974k2.7%) i/s - 146.115k in 5.046703s
1163
- # Trailblazer::Operation
1164
- # 17.276k1.8%) i/s - 87.672k in 5.076609s
1165
- # Dry::Monads 139.353k (± 2.5%) i/s - 709.617k in 5.095599s
1166
- # Dry::Transaction 5.572k3.6%) i/s - 28.101k in 5.050376s
1167
- # Micro::Case 124.749k (± 1.9%) i/s - 635.152k in 5.093310s
1168
- # Micro::Case::Strict 93.417k4.8%) i/s - 473.304k in 5.081341s
1169
- # Micro::Case::Safe 120.607k (± 3.2%) i/s - 610.844k in 5.070394s
1170
-
1171
- # Comparison:
1172
- # Dry::Monads: 139352.5 i/s
1173
- # Micro::Case: 124749.4 i/s - 1.12x slower
1174
- # Micro::Case::Safe: 120607.3 i/s - 1.16x slower
1175
- # Micro::Case::Strict: 93417.3 i/s - 1.49x slower
1176
- # Interactor: 28974.4 i/s - 4.81x slower
1177
- # Trailblazer::Operation: 17275.6 i/s - 8.07x slower
1178
- # Dry::Transaction: 5571.7 i/s - 25.01x slower
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.458k3.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.801k8.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.909k2.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.725k1.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** | 108510.0 | _**The Fastest**_ |
1189
- | Dry::Monads | 64240.1 | 1.69x slower |
1190
- | Trailblazer::Operation | 15657.7 | 6.93x slower |
1191
- | Interactor | 13486.7 | 8.05x slower |
1192
- | Dry::Transaction | 4567.3 | 23.76x slower |
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
- ```ruby
1198
- # Warming up --------------------------------------
1199
- # Interactor 1.331k i/100ms
1200
- # Trailblazer::Operation
1201
- # 1.544k i/100ms
1202
- # Dry::Monads 6.343k i/100ms
1203
- # Dry::Transaction 456.000 i/100ms
1204
- # Micro::Case 10.429k i/100ms
1205
- # Micro::Case::Strict 8.109k i/100ms
1206
- # Micro::Case::Safe 10.280k i/100ms
1207
- # Calculating -------------------------------------
1208
- # Interactor 13.487k1.9%) i/s - 67.881k in 5.035059s
1209
- # Trailblazer::Operation
1210
- # 15.658k1.6%) i/s - 78.744k in 5.030427s
1211
- # Dry::Monads 64.240k1.8%) i/s - 323.493k in 5.037461s
1212
- # Dry::Transaction 4.567k (± 1.3%) i/s - 23.256k in 5.092699s
1213
- # Micro::Case 108.510k (± 2.3%) i/s - 542.308k in 5.000605s
1214
- # Micro::Case::Strict 83.527k (± 1.4%) i/s - 421.668k in 5.049245s
1215
- # Micro::Case::Safe 105.641k (± 3.7%) i/s - 534.560k in 5.067836s
1216
-
1217
- # Comparison:
1218
- # Micro::Case: 108510.0 i/s
1219
- # Micro::Case::Safe: 105640.6 i/s - same-ish: difference falls within error
1220
- # Micro::Case::Strict: 83526.8 i/s - 1.30x slower
1221
- # Dry::Monads: 64240.1 i/s - 1.69x slower
1222
- # Trailblazer::Operation: 15657.7 i/s - 6.93x slower
1223
- # Interactor: 13486.7 i/s - 8.05x slower
1224
- # Dry::Transaction: 4567.3 i/s - 23.76x slower
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.227k3.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.779k2.5%) i/s - 360.400k in 5.024294s
1270
+ # Dry::Transaction 4.978k3.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.47x slower | 5.51x slower |
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
- ```ruby
1246
- # Warming up --------------------------------------
1247
- # Interactor::Organizer 4.880k i/100ms
1248
- # Micro::Case::Flow 7.035k i/100ms
1249
- # Micro::Case::Safe::Flow 7.059k i/100ms
1250
-
1251
- # Calculating -------------------------------------
1252
- # Interactor::Organizer 50.208k1.3%) i/s - 253.760k in 5.055099s
1253
- # Micro::Case::Flow 73.791k0.9%) i/s - 372.855k in 5.053311s
1254
- # Micro::Case::Safe::Flow 73.314k (± 1.1%) i/s - 367.068k in 5.007473s
1255
-
1256
- # Comparison:
1257
- # Micro::Case::Flow: 73790.7 i/s
1258
- # Micro::Case::Safe::Flow: 73313.7 i/s - same-ish: difference falls within error
1259
- # Interactor::Organizer: 50207.7 i/s - 1.47x slower
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.606k4.4%) i/s - 311.576k in 5.068602s
1311
+ # Micro::Case::Safe::Flow 60.688k4.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
- ```ruby
1267
- # Warming up --------------------------------------
1268
- # Interactor::Organizer 2.372k i/100ms
1269
- # Micro::Case::Flow 12.802k i/100ms
1270
- # Micro::Case::Safe::Flow 12.673k i/100ms
1271
-
1272
- # Calculating -------------------------------------
1273
- # Interactor::Organizer 24.522k (± 2.0%) i/s - 123.344k in 5.032159s
1274
- # Micro::Case::Flow 135.122k1.7%) i/s - 678.506k in 5.022903s
1275
- # Micro::Case::Safe::Flow 133.980k1.4%) i/s - 671.669k in 5.014181s
1276
-
1277
- # Comparison:
1278
- # Micro::Case::Flow: 135122.0 i/s
1279
- # Micro::Case::Safe::Flow: 133979.8 i/s - same-ish: difference falls within error
1280
- # Interactor::Organizer: 24521.8 i/s - 5.51x slower
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.629k2.9%) i/s - 621.432k in 5.030844s
1332
+ # Micro::Case::Safe::Flow 123.862k3.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