u-case 2.3.1 → 3.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f5a28fb237f74e009225d20bcc65115e6d8159818a3cfdbfcb7e23c3726aef4
4
- data.tar.gz: fa34ab50f7b2d0df65ba75f4b59734e0326d8431652823ad35e406d243801853
3
+ metadata.gz: dfc46204c461937d19681ae26dca7846a8d36720b735d759023b4c1369ef2a9a
4
+ data.tar.gz: 21bfd7bc9d2c2a13c901672eac77f15da4b7975aeeea925b3f94ff7e36dd1161
5
5
  SHA512:
6
- metadata.gz: cd2a5fd871fd561ab98d96297e474f3ea251cdee866d5b14cc69e5e04bf654b98c3b2f75ef55d4c774f0d91ca6297a89746c923a29cd1fd1985e5761db6848b3
7
- data.tar.gz: 12c467840bab805b562ae9de9fe6c80ccda0129672989d6b14f844e916fd270f0f5f9c2f213fed508c439e6d17142712f2013475bbc6f0f9a22066ab0aeb84ef
6
+ metadata.gz: 1e6992400490ef920d9ad855ebf698174ee2fcf9ab555db880ab2dea065360b921af7e38e4be5eb74e28c484862a25ca476beba9e2daebbc668bae07ef1b1853
7
+ data.tar.gz: d081ddf7bf5b3209f31c385060fd8dbbc9f72a8b1a05e553b1666df2f2de1550859017e9aa85aa329118bdef965f390a1637348c2a7549139e462e3cb091c250
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
@@ -18,7 +18,27 @@ end
18
18
  group :test do
19
19
  gem 'minitest', activemodel_version < '4.1' ? '~> 4.2' : '~> 5.0'
20
20
  gem 'simplecov', require: false
21
- gem 'minitest-reporters', require: false
21
+ end
22
+
23
+ pry_byebug_version =
24
+ case RUBY_VERSION
25
+ when /\A2.2/ then '3.6'
26
+ when /\A2.3/ then '3.7'
27
+ else '3.9'
28
+ end
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
+
37
+ group :development, :test do
38
+ gem 'awesome_print', '~> 1.8'
39
+
40
+ gem 'pry', "~> #{pry_version}"
41
+ gem 'pry-byebug', "~> #{pry_byebug_version}"
22
42
  end
23
43
 
24
44
  # Specify your gem's dependencies in u-case.gemspec
data/README.md CHANGED
@@ -1,15 +1,16 @@
1
+ ![Ruby](https://img.shields.io/badge/ruby-2.2+-ruby.svg?colorA=99004d&colorB=cc0066)
1
2
  [![Gem](https://img.shields.io/gem/v/u-case.svg?style=flat-square)](https://rubygems.org/gems/u-case)
2
3
  [![Build Status](https://travis-ci.com/serradura/u-case.svg?branch=master)](https://travis-ci.com/serradura/u-case)
3
4
  [![Maintainability](https://api.codeclimate.com/v1/badges/5c3c8ad1b0b943f88efd/maintainability)](https://codeclimate.com/github/serradura/u-case/maintainability)
4
5
  [![Test Coverage](https://api.codeclimate.com/v1/badges/5c3c8ad1b0b943f88efd/test_coverage)](https://codeclimate.com/github/serradura/u-case/test_coverage)
5
6
 
6
- μ-case (Micro::Case)
7
- ==========================
7
+ μ-case (Micro::Case) <!-- omit in toc -->
8
+ ====================
8
9
 
9
10
  Create simple and powerful use cases as objects.
10
11
 
11
12
  The main project goals are:
12
- 1. Be simple to use and easy to learn (input **>>** process / transform **>>** output).
13
+ 1. Easy to use and easy to learn (input **>>** process **>>** output).
13
14
  2. Promote referential transparency (transforming instead of modifying) and data integrity.
14
15
  3. No callbacks (e.g: before, after, around).
15
16
  4. Solve complex business logic, by allowing the composition of use cases.
@@ -17,45 +18,59 @@ The main project goals are:
17
18
 
18
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.
19
20
 
21
+ ## Documentation <!-- omit in toc -->
22
+
23
+ Version | Documentation
24
+ --------- | -------------
25
+ 3.0.0.rc2 | 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
+
20
29
  ## Table of Contents <!-- omit in toc -->
21
- - [μ-case (Micro::Case)](#μ-case-microcase)
22
- - [Required Ruby version](#required-ruby-version)
23
- - [Dependencies](#dependencies)
24
- - [Installation](#installation)
25
- - [Usage](#usage)
26
- - [Micro::Case - How to define a use case?](#microcase---how-to-define-a-use-case)
27
- - [Micro::Case::Result - What is a use case result?](#microcaseresult---what-is-a-use-case-result)
28
- - [What are the default result types?](#what-are-the-default-result-types)
29
- - [How to define custom result types?](#how-to-define-custom-result-types)
30
- - [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
- - [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)
33
- - [What happens if a result hook was declared multiple times?](#what-happens-if-a-result-hook-was-declared-multiple-times)
34
- - [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)
36
- - [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
- - [Is it possible a flow accumulates its input and merges each success result to use as the argument of their use cases?](#is-it-possible-a-flow-accumulates-its-input-and-merges-each-success-result-to-use-as-the-argument-of-their-use-cases)
38
- - [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)
39
- - [Micro::Case::Strict - What is a strict use case?](#microcasestrict---what-is-a-strict-use-case)
40
- - [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)
41
- - [u-case/with_validation - How to validate use case attributes?](#u-casewith_validation---how-to-validate-use-case-attributes)
42
- - [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)
43
- - [Benchmarks](#benchmarks)
44
- - [Micro::Case](#microcase)
45
- - [Best overall](#best-overall)
46
- - [Success results](#success-results)
47
- - [Failure results](#failure-results)
48
- - [Micro::Case::Flow](#microcaseflow)
49
- - [Comparisons](#comparisons)
50
- - [Examples](#examples)
51
- - [1️⃣ Rails App (API)](#1️⃣-rails-app-api)
52
- - [2️⃣ CLI calculator](#2️⃣-cli-calculator)
53
- - [3️⃣ Users creation](#3️⃣-users-creation)
54
- - [4️⃣ Rescuing exception inside of the use cases](#4️⃣-rescuing-exception-inside-of-the-use-cases)
55
- - [Development](#development)
56
- - [Contributing](#contributing)
57
- - [License](#license)
58
- - [Code of Conduct](#code-of-conduct)
30
+ - [Required Ruby version](#required-ruby-version)
31
+ - [Dependencies](#dependencies)
32
+ - [Installation](#installation)
33
+ - [Usage](#usage)
34
+ - [`Micro::Case` - How to define a use case?](#microcase---how-to-define-a-use-case)
35
+ - [`Micro::Case::Result` - What is a use case result?](#microcaseresult---what-is-a-use-case-result)
36
+ - [What are the default result types?](#what-are-the-default-result-types)
37
+ - [How to define custom result types?](#how-to-define-custom-result-types)
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)
39
+ - [How to use the result hooks?](#how-to-use-the-result-hooks)
40
+ - [Why the failure hook (without a type) exposes result itself?](#why-the-failure-hook-without-a-type-exposes-result-itself)
41
+ - [What happens if a result hook was declared multiple times?](#what-happens-if-a-result-hook-was-declared-multiple-times)
42
+ - [How to use the `Micro::Case::Result#then` method?](#how-to-use-the-microcaseresultthen-method)
43
+ - [What does happens when a `Micro::Case::Result#then` receives a block?](#what-does-happens-when-a-microcaseresultthen-receives-a-block)
44
+ - [How to make attributes data injection using this feature?](#how-to-make-attributes-data-injection-using-this-feature)
45
+ - [`Micro::Cases::Flow` - How to compose use cases?](#microcasesflow---how-to-compose-use-cases)
46
+ - [Is it possible to compose a use case flow with other ones?](#is-it-possible-to-compose-a-use-case-flow-with-other-ones)
47
+ - [Is it possible a flow accumulates its input and merges each success result to use as the argument of the next use cases?](#is-it-possible-a-flow-accumulates-its-input-and-merges-each-success-result-to-use-as-the-argument-of-the-next-use-cases)
48
+ - [How to understand what is happening during a flow execution?](#how-to-understand-what-is-happening-during-a-flow-execution)
49
+ - [`Micro::Case::Result#transitions` schema](#microcaseresulttransitions-schema)
50
+ - [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)
51
+ - [`Micro::Case::Strict` - What is a strict use case?](#microcasestrict---what-is-a-strict-use-case)
52
+ - [`Micro::Case::Safe` - Is there some feature to auto handle exceptions inside of a use case or flow?](#microcasesafe---is-there-some-feature-to-auto-handle-exceptions-inside-of-a-use-case-or-flow)
53
+ - [`Micro::Cases::Safe::Flow`](#microcasessafeflow)
54
+ - [`Micro::Case::Result#on_exception`](#microcaseresulton_exception)
55
+ - [`u-case/with_activemodel_validation` - How to validate use case attributes?](#u-casewith_activemodel_validation---how-to-validate-use-case-attributes)
56
+ - [If I enabled the auto validation, is it possible to disable it only in specific use case classes?](#if-i-enabled-the-auto-validation-is-it-possible-to-disable-it-only-in-specific-use-case-classes)
57
+ - [`Kind::Validator`](#kindvalidator)
58
+ - [Benchmarks](#benchmarks)
59
+ - [`Micro::Case` (v2.6.0)](#microcase-v260)
60
+ - [Best overall](#best-overall)
61
+ - [Success results](#success-results)
62
+ - [Failure results](#failure-results)
63
+ - [`Micro::Case::Flow` (v2.6.0)](#microcaseflow-v260)
64
+ - [Comparisons](#comparisons)
65
+ - [Examples](#examples)
66
+ - [1️⃣ Rails App (API)](#1️⃣-rails-app-api)
67
+ - [2️⃣ CLI calculator](#2️⃣-cli-calculator)
68
+ - [3️⃣ Users creation](#3️⃣-users-creation)
69
+ - [4️⃣ Rescuing exception inside of the use cases](#4️⃣-rescuing-exception-inside-of-the-use-cases)
70
+ - [Development](#development)
71
+ - [Contributing](#contributing)
72
+ - [License](#license)
73
+ - [Code of Conduct](#code-of-conduct)
59
74
 
60
75
  ## Required Ruby version
61
76
 
@@ -63,8 +78,15 @@ The main project goals are:
63
78
 
64
79
  ## Dependencies
65
80
 
66
- This project depends on [Micro::Attribute](https://github.com/serradura/u-attributes) gem.
67
- It is used to define the use case attributes.
81
+ 1. [`kind`](https://github.com/serradura/kind) gem.
82
+
83
+ A simple type system (at runtime) for Ruby.
84
+
85
+ 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.
86
+ 2. [`u-attributes`](https://github.com/serradura/u-attributes) gem.
87
+
88
+ This gem allows defining read-only attributes, that is, your objects will have only getters to access their attributes data.
89
+ It is used to define the use case attributes.
68
90
 
69
91
  ## Installation
70
92
 
@@ -94,11 +116,11 @@ class Multiply < Micro::Case
94
116
  # 2. Define the method `call!` with its business logic
95
117
  def call!
96
118
 
97
- # 3. Wrap the use case result/output using the `Success()` or `Failure()` methods
119
+ # 3. Wrap the use case result/output using the `Success(result: *)` or `Failure(result: *)` methods
98
120
  if a.is_a?(Numeric) && b.is_a?(Numeric)
99
- Success(a * b)
121
+ Success result: { number: a * b }
100
122
  else
101
- Failure { '`a` and `b` attributes must be numeric' }
123
+ Failure result: { message: '`a` and `b` attributes must be numeric' }
102
124
  end
103
125
  end
104
126
  end
@@ -112,14 +134,14 @@ end
112
134
  result = Multiply.call(a: 2, b: 2)
113
135
 
114
136
  result.success? # true
115
- result.value # 4
137
+ result.data # { number: 4 }
116
138
 
117
139
  # Failure result
118
140
 
119
141
  bad_result = Multiply.call(a: 2, b: '2')
120
142
 
121
143
  bad_result.failure? # true
122
- bad_result.value # "`a` and `b` attributes must be numeric"
144
+ bad_result.data # { message: "`a` and `b` attributes must be numeric" }
123
145
 
124
146
  #-----------------------------#
125
147
  # Calling a use case instance #
@@ -127,7 +149,7 @@ bad_result.value # "`a` and `b` attributes must be numeric"
127
149
 
128
150
  result = Multiply.new(a: 2, b: 3).call
129
151
 
130
- result.value # 6
152
+ result.value # { number: 6 }
131
153
 
132
154
  # Note:
133
155
  # ----
@@ -142,11 +164,14 @@ result.value # 6
142
164
  A `Micro::Case::Result` stores the use cases output data. These are their main methods:
143
165
  - `#success?` returns true if is a successful result.
144
166
  - `#failure?` returns true if is an unsuccessful result.
145
- - `#value` the result value itself.
167
+ - `#data` the result data itself.
146
168
  - `#type` a Symbol which gives meaning for the result, this is useful to declare different types of failures or success.
147
- - `#on_success` or `#on_failure` are hook methods that help you define the application flow.
169
+ - `#on_success` or `#on_failure` are hook methods that help you to define the application flow.
148
170
  - `#use_case` if is a failure result, the use case responsible for it will be accessible through this method. This feature is handy to handle a flow failure (this topic will be covered ahead).
149
- - `#then` allows if the current result is a success, the `then` method will allow to applying a new use case for its value.
171
+ - `#then` this method will allow applying a new use case if the current result was a success. The idea of this feature is to allow the creation of dynamic flows.
172
+ - `#[]` and `#values_at` are shortcuts to access the `#data` values.
173
+
174
+ > **Note:** for backward compatibility, you could use the `#value` method as an alias of `#data` method.
150
175
 
151
176
  [⬆️ Back to Top](#table-of-contents-)
152
177
 
@@ -161,9 +186,13 @@ class Divide < Micro::Case
161
186
  attributes :a, :b
162
187
 
163
188
  def call!
164
- invalid_attributes.empty? ? Success(a / b) : Failure(invalid_attributes)
165
- rescue => e
166
- Failure(e)
189
+ if invalid_attributes.empty?
190
+ Success result: { number: a / b }
191
+ else
192
+ Failure result: { invalid_attributes: invalid_attributes }
193
+ end
194
+ rescue => exception
195
+ Failure result: exception
167
196
  end
168
197
 
169
198
  private def invalid_attributes
@@ -176,7 +205,7 @@ end
176
205
  result = Divide.call(a: 2, b: 2)
177
206
 
178
207
  result.type # :ok
179
- result.value # 1
208
+ result.data # { number: 1 }
180
209
  result.success? # true
181
210
  result.use_case # raises `Micro::Case::Error::InvalidAccessToTheUseCaseObject: only a failure result can access its own use case`
182
211
 
@@ -185,40 +214,42 @@ result.use_case # raises `Micro::Case::Error::InvalidAccessToTheUseCaseObject: o
185
214
  bad_result = Divide.call(a: 2, b: '2')
186
215
 
187
216
  bad_result.type # :error
188
- bad_result.value # {"b"=>"2"}
217
+ bad_result.data # { invalid_attributes: { "b"=>"2" } }
189
218
  bad_result.failure? # true
190
- 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>>
219
+ 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>
191
220
 
192
221
  # Failure result (type == :exception)
193
222
 
194
223
  err_result = Divide.call(a: 2, b: 0)
195
224
 
196
225
  err_result.type # :exception
197
- err_result.value # <ZeroDivisionError: divided by 0>
226
+ err_result.data # { exception: <ZeroDivisionError: divided by 0> }
198
227
  err_result.failure? # true
199
- 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>>
228
+ 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>
200
229
 
201
230
  # Note:
202
231
  # ----
203
232
  # Any Exception instance which is wrapped by
204
- # the Failure() method will receive `:exception` instead of the `:error` type.
233
+ # the Failure(result: *) method will receive `:exception` instead of the `:error` type.
205
234
  ```
206
235
 
207
236
  [⬆️ Back to Top](#table-of-contents-)
208
237
 
209
238
  #### How to define custom result types?
210
239
 
211
- Answer: Use a symbol as the argument of `Success()`, `Failure()` methods and declare a block to set their values.
240
+ Answer: Use a symbol as the argument of `Success()`, `Failure()` methods and declare the `result:` keyword to set the result data.
212
241
 
213
242
  ```ruby
214
243
  class Multiply < Micro::Case
215
244
  attributes :a, :b
216
245
 
217
246
  def call!
218
- return Success(a * b) if a.is_a?(Numeric) && b.is_a?(Numeric)
219
-
220
- Failure(:invalid_data) do
221
- attributes.reject { |_, input| input.is_a?(Numeric) }
247
+ if a.is_a?(Numeric) && b.is_a?(Numeric)
248
+ Success result: { number: a * b }
249
+ else
250
+ Failure :invalid_data, result: {
251
+ attributes: attributes.reject { |_, input| input.is_a?(Numeric) }
252
+ }
222
253
  end
223
254
  end
224
255
  end
@@ -228,7 +259,7 @@ end
228
259
  result = Multiply.call(a: 3, b: 2)
229
260
 
230
261
  result.type # :ok
231
- result.value # 6
262
+ result.data # { number: 6 }
232
263
  result.success? # true
233
264
 
234
265
  # Failure result
@@ -236,7 +267,7 @@ result.success? # true
236
267
  bad_result = Multiply.call(a: 3, b: '2')
237
268
 
238
269
  bad_result.type # :invalid_data
239
- bad_result.value # {"b"=>"2"}
270
+ bad_result.data # { attributes: {"b"=>"2"} }
240
271
  bad_result.failure? # true
241
272
  ```
242
273
 
@@ -244,23 +275,25 @@ bad_result.failure? # true
244
275
 
245
276
  #### Is it possible to define a custom result type without a block?
246
277
 
247
- Answer: Yes, it is. But only for failure results!
278
+ Answer: Yes, it is possible. But this will have special behavior because the result data will be a hash with the given type as the key and true as its value.
248
279
 
249
280
  ```ruby
250
281
  class Multiply < Micro::Case
251
282
  attributes :a, :b
252
283
 
253
284
  def call!
254
- return Failure(:invalid_data) unless a.is_a?(Numeric) && b.is_a?(Numeric)
255
-
256
- Success(a * b)
285
+ if a.is_a?(Numeric) && b.is_a?(Numeric)
286
+ Success result: { number: a * b }
287
+ else
288
+ Failure(:invalid_data)
289
+ end
257
290
  end
258
291
  end
259
292
 
260
293
  result = Multiply.call(a: 2, b: '2')
261
294
 
262
295
  result.failure? # true
263
- result.value # :invalid_data
296
+ result.data # { :invalid_data => true }
264
297
  result.type # :invalid_data
265
298
  result.use_case.attributes # {"a"=>2, "b"=>"2"}
266
299
 
@@ -283,10 +316,10 @@ class Double < Micro::Case
283
316
  attribute :number
284
317
 
285
318
  def call!
286
- return Failure(:invalid) { 'the number must be a numeric value' } unless number.is_a?(Numeric)
287
- return Failure(:lte_zero) { 'the number must be greater than 0' } if number <= 0
319
+ return Failure :invalid, result: { msg: 'number must be a numeric value' } unless number.is_a?(Numeric)
320
+ return Failure :lte_zero, result: { msg: 'number must be greater than 0' } if number <= 0
288
321
 
289
- Success(number * 2)
322
+ Success result: { number: number * 2 }
290
323
  end
291
324
  end
292
325
 
@@ -296,9 +329,9 @@ end
296
329
 
297
330
  Double
298
331
  .call(number: 3)
299
- .on_success { |number| p number }
300
- .on_failure(:invalid) { |msg| raise TypeError, msg }
301
- .on_failure(:lte_zero) { |msg| raise ArgumentError, msg }
332
+ .on_success { |result| p result[:number] }
333
+ .on_failure(:invalid) { |result| raise TypeError, result[:msg] }
334
+ .on_failure(:lte_zero) { |result| raise ArgumentError, result[:msg] }
302
335
 
303
336
  # The output because it is a success:
304
337
  # 6
@@ -309,10 +342,10 @@ Double
309
342
 
310
343
  Double
311
344
  .call(number: -1)
312
- .on_success { |number| p number }
345
+ .on_success { |result| p result[:number] }
313
346
  .on_failure { |_result, use_case| puts "#{use_case.class.name} was the use case responsible for the failure" }
314
- .on_failure(:invalid) { |msg| raise TypeError, msg }
315
- .on_failure(:lte_zero) { |msg| raise ArgumentError, msg }
347
+ .on_failure(:invalid) { |result| raise TypeError, result[:msg] }
348
+ .on_failure(:lte_zero) { |result| raise ArgumentError, result[:msg] }
316
349
 
317
350
  # The outputs will be:
318
351
  #
@@ -324,7 +357,7 @@ Double
324
357
  # The use case responsible for the failure will be accessible as the second hook argument
325
358
  ```
326
359
 
327
- #### Why the failure hook (without a type) exposes a different kind of data?
360
+ #### Why the failure hook (without a type) exposes result itself?
328
361
 
329
362
  Answer: To allow you to define how to handle the program flow using some
330
363
  conditional statement (like an `if`, `case/when`).
@@ -335,9 +368,9 @@ class Double < Micro::Case
335
368
 
336
369
  def call!
337
370
  return Failure(:invalid) unless number.is_a?(Numeric)
338
- return Failure(:lte_zero) { number } if number <= 0
371
+ return Failure :lte_zero, result: attributes(:number) if number <= 0
339
372
 
340
- Success(number * 2)
373
+ Success result: { number: number * 2 }
341
374
  end
342
375
  end
343
376
 
@@ -349,32 +382,32 @@ Double
349
382
  .call(-1)
350
383
  .on_failure do |result, use_case|
351
384
  case result.type
352
- when :invalid then raise TypeError, 'the number must be a numeric value'
353
- when :lte_zero then raise ArgumentError, "the number `#{result.value}` must be greater than 0"
385
+ when :invalid then raise TypeError, "number must be a numeric value"
386
+ when :lte_zero then raise ArgumentError, "number `#{result[:number]}` must be greater than 0"
354
387
  else raise NotImplementedError
355
388
  end
356
389
  end
357
390
 
358
391
  # The output will be the exception:
359
392
  #
360
- # ArgumentError (the number `-1` must be greater than 0)
393
+ # ArgumentError (number `-1` must be greater than 0)
361
394
 
362
- #=====================================================#
363
- # Using decomposition to access result value and type #
364
- #=====================================================#
395
+ #=========================================================#
396
+ # Using decomposition to access the result data and type #
397
+ #=========================================================#
365
398
 
366
399
  # The syntax to decompose an Array can be used in methods, blocks and assigments.
367
- # If you doesn't know that, check out:
400
+ # If you doesn't know it, check out the Ruby doc:
368
401
  # https://ruby-doc.org/core-2.2.0/doc/syntax/assignment_rdoc.html#label-Array+Decomposition
369
402
  #
370
- # And the object exposed in the hook failure can be decomposed using this syntax. e.g:
403
+ # The object exposed in the hook failure is a Micro::Case::Result, and it can be decomposed using this syntax. e.g:
371
404
 
372
405
  Double
373
406
  .call(-2)
374
- .on_failure do |(value, type), use_case|
407
+ .on_failure do |(data, type), use_case|
375
408
  case type
376
- when :invalid then raise TypeError, 'the number must be a numeric value'
377
- when :lte_zero then raise ArgumentError, "the number `#{value}` must be greater than 0"
409
+ when :invalid then raise TypeError, 'number must be a numeric value'
410
+ when :lte_zero then raise ArgumentError, "number `#{data[:number]}` must be greater than 0"
378
411
  else raise NotImplementedError
379
412
  end
380
413
  end
@@ -395,38 +428,43 @@ class Double < Micro::Case
395
428
  attributes :number
396
429
 
397
430
  def call!
398
- return Failure(:invalid) { 'the number must be a numeric value' } unless number.is_a?(Numeric)
399
-
400
- Success(:computed) { number * 2 }
431
+ if number.is_a?(Numeric)
432
+ Success :computed, result: { number: number * 2 }
433
+ else
434
+ Failure :invalid, result: { msg: 'number must be a numeric value' }
435
+ end
401
436
  end
402
437
  end
403
438
 
404
439
  result = Double.call(number: 3)
405
- result.value # 6
406
- result.value * 4 # 24
440
+ result.data # { number: 6 }
441
+ result[:number] * 4 # 24
407
442
 
408
443
  accum = 0
409
444
 
410
- result.on_success { |number| accum += number }
411
- .on_success { |number| accum += number }
412
- .on_success(:computed) { |number| accum += number }
413
- .on_success(:computed) { |number| accum += number }
445
+ result.on_success { |result| accum += result[:number] }
446
+ .on_success { |result| accum += result[:number] }
447
+ .on_success(:computed) { |result| accum += result[:number] }
448
+ .on_success(:computed) { |result| accum += result[:number] }
414
449
 
415
450
  accum # 24
416
451
 
417
- result.value * 4 == accum # true
452
+ result[:number] * 4 == accum # true
418
453
  ```
419
454
 
420
455
  #### How to use the `Micro::Case::Result#then` method?
421
456
 
457
+ This method allows you to create dynamic flows, so, with it,
458
+ you can add new use cases or flows to continue the result transformation. e.g:
459
+
422
460
  ```ruby
423
461
  class ForbidNegativeNumber < Micro::Case
424
462
  attribute :number
425
463
 
426
464
  def call!
427
- return Success { attributes } if number >= 0
465
+ return Success result: attributes if number >= 0
428
466
 
429
- Failure { attributes }
467
+ Failure result: attributes
430
468
  end
431
469
  end
432
470
 
@@ -434,7 +472,7 @@ class Add3 < Micro::Case
434
472
  attribute :number
435
473
 
436
474
  def call!
437
- Success { { number: number + 3 } }
475
+ Success result: { number: number + 3 }
438
476
  end
439
477
  end
440
478
 
@@ -443,8 +481,7 @@ result1 =
443
481
  .call(number: -1)
444
482
  .then(Add3)
445
483
 
446
- result1.type # :error
447
- result1.value # {'number' => -1}
484
+ result1.data # {'number' => -1}
448
485
  result1.failure? # true
449
486
 
450
487
  # ---
@@ -454,16 +491,69 @@ result2 =
454
491
  .call(number: 1)
455
492
  .then(Add3)
456
493
 
457
- result2.type # :ok
458
- result2.value # {'number' => 4}
494
+ result2.data # {'number' => 4}
459
495
  result2.success? # true
460
496
  ```
461
497
 
498
+ > **Note:** this method changes the [`Micro::Case::Result#transitions`](#how-to-understand-what-is-happening-during-a-flow-execution).
499
+
500
+ [⬆️ Back to Top](#table-of-contents-)
501
+
502
+ ##### What does happens when a `Micro::Case::Result#then` receives a block?
503
+
504
+ It will yields self (a `Micro::Case::Result instance`) to the block and return the result of the block. e.g:
505
+
506
+ ```ruby
507
+ class Add < Micro::Case
508
+ attributes :a, :b
509
+
510
+ def call!
511
+ if Kind.of?(Numeric, a, b)
512
+ Success result: { sum: a + b }
513
+ else
514
+ Failure(:attributes_arent_numbers)
515
+ end
516
+ end
517
+ end
518
+
519
+ # --
520
+
521
+ success_result =
522
+ Add
523
+ .call(a: 2, b: 2)
524
+ .then { |result| result.success? ? result[:sum] : 0 }
525
+
526
+ puts success_result # 4
527
+
528
+ # --
529
+
530
+ failure_result =
531
+ Add
532
+ .call(a: 2, b: '2')
533
+ .then { |result| result.success? ? result[:sum] : 0 }
534
+
535
+ puts failure_result # 0
536
+ ```
537
+
538
+ [⬆️ Back to Top](#table-of-contents-)
539
+
540
+ ##### How to make attributes data injection using this feature?
541
+
542
+ Pass a Hash as the second argument of the `Micro::Case::Result#then` method.
543
+
544
+ ```ruby
545
+ Todo::FindAllForUser
546
+ .call(user: current_user, params: params)
547
+ .then(Paginate)
548
+ .then(Serialize::PaginatedRelationAsJson, serializer: Todo::Serializer)
549
+ .on_success { |result| render_json(200, data: result[:todos]) }
550
+ ```
551
+
462
552
  [⬆️ Back to Top](#table-of-contents-)
463
553
 
464
- ### `Micro::Case::Flow` - How to compose use cases?
554
+ ### `Micro::Cases::Flow` - How to compose use cases?
465
555
 
466
- In this case, this will be a **flow** (`Micro::Case::Flow`).
556
+ In this case, this will be a **flow** (`Micro::Cases::Flow`).
467
557
  The main idea of this feature is to use/reuse use cases as steps of a new use case.
468
558
 
469
559
  ```ruby
@@ -473,9 +563,9 @@ module Steps
473
563
 
474
564
  def call!
475
565
  if numbers.all? { |value| String(value) =~ /\d+/ }
476
- Success(numbers: numbers.map(&:to_i))
566
+ Success result: { numbers: numbers.map(&:to_i) }
477
567
  else
478
- Failure('numbers must contain only numeric types')
568
+ Failure result: { message: 'numbers must contain only numeric types' }
479
569
  end
480
570
  end
481
571
  end
@@ -484,7 +574,7 @@ module Steps
484
574
  attribute :numbers
485
575
 
486
576
  def call!
487
- Success(numbers: numbers.map { |number| number + 2 })
577
+ Success result: { numbers: numbers.map { |number| number + 2 } }
488
578
  end
489
579
  end
490
580
 
@@ -492,7 +582,7 @@ module Steps
492
582
  attribute :numbers
493
583
 
494
584
  def call!
495
- Success(numbers: numbers.map { |number| number * 2 })
585
+ Success result: { numbers: numbers.map { |number| number * 2 } }
496
586
  end
497
587
  end
498
588
 
@@ -500,24 +590,24 @@ module Steps
500
590
  attribute :numbers
501
591
 
502
592
  def call!
503
- Success(numbers: numbers.map { |number| number * number })
593
+ Success result: { numbers: numbers.map { |number| number * number } }
504
594
  end
505
595
  end
506
596
  end
507
597
 
508
- #---------------------------------------------#
509
- # Creating a flow using the collection syntax #
510
- #---------------------------------------------#
598
+ #-------------------------------------------#
599
+ # Creating a flow using Micro::Cases.flow() #
600
+ #-------------------------------------------#
511
601
 
512
- Add2ToAllNumbers = Micro::Case::Flow([
602
+ Add2ToAllNumbers = Micro::Cases.flow([
513
603
  Steps::ConvertTextToNumbers,
514
604
  Steps::Add2
515
605
  ])
516
606
 
517
607
  result = Add2ToAllNumbers.call(numbers: %w[1 1 2 2 3 4])
518
608
 
519
- p result.success? # true
520
- p result.value # {:numbers => [3, 3, 4, 4, 5, 6]}
609
+ result.success? # true
610
+ result.data # {:numbers => [3, 3, 4, 4, 5, 6]}
521
611
 
522
612
  #---------------------------------------------------#
523
613
  # An alternative way to create a flow using classes #
@@ -532,41 +622,14 @@ DoubleAllNumbers
532
622
  .call(numbers: %w[1 1 b 2 3 4])
533
623
  .on_failure { |message| p message } # "numbers must contain only numeric types"
534
624
 
535
- # !------------------------------------ ! #
536
- # ! Deprecated: Micro::Case::Flow mixin ! #
537
- # !-------------------------------------! #
538
-
539
- # The code below still works, but it will output a warning message:
540
- # Deprecation: Micro::Case::Flow mixin is being deprecated, please use `Micro::Case` inheritance instead.
541
-
542
- class DoubleAllNumbers
543
- include Micro::Case::Flow
544
-
545
- flow Steps::ConvertTextToNumbers,
546
- Steps::Double
547
- end
548
-
549
- # Note: This feature will be removed in the next major release (3.0)
550
-
551
- #-------------------------------------------------------------#
552
- # Another way to create a flow using the composition operator #
553
- #-------------------------------------------------------------#
554
-
555
- SquareAllNumbers =
556
- Steps::ConvertTextToNumbers >> Steps::Square
557
-
558
- SquareAllNumbers
559
- .call(numbers: %w[1 1 2 2 3 4])
560
- .on_success { |value| p value[:numbers] } # [1, 1, 4, 4, 9, 16]
561
-
562
625
  # Note:
563
626
  # ----
564
627
  # When happening a failure, the use case responsible
565
628
  # will be accessible in the result
566
629
 
567
- result = SquareAllNumbers.call(numbers: %w[1 1 b 2 3 4])
630
+ result = DoubleAllNumbers.call(numbers: %w[1 1 b 2 3 4])
568
631
 
569
- result.failure? # true
632
+ result.failure? # true
570
633
  result.use_case.is_a?(Steps::ConvertTextToNumbers) # true
571
634
 
572
635
  result.on_failure do |_message, use_case|
@@ -578,7 +641,7 @@ end
578
641
 
579
642
  #### Is it possible to compose a use case flow with other ones?
580
643
 
581
- Answer: Yes, it is.
644
+ Answer: Yes, it is possible.
582
645
 
583
646
  ```ruby
584
647
  module Steps
@@ -587,9 +650,9 @@ module Steps
587
650
 
588
651
  def call!
589
652
  if numbers.all? { |value| String(value) =~ /\d+/ }
590
- Success(numbers: numbers.map(&:to_i))
653
+ Success result: { numbers: numbers.map(&:to_i) }
591
654
  else
592
- Failure('numbers must contain only numeric types')
655
+ Failure result: { message: 'numbers must contain only numeric types' }
593
656
  end
594
657
  end
595
658
  end
@@ -598,7 +661,7 @@ module Steps
598
661
  attribute :numbers
599
662
 
600
663
  def call!
601
- Success(numbers: numbers.map { |number| number + 2 })
664
+ Success result: { numbers: numbers.map { |number| number + 2 } }
602
665
  end
603
666
  end
604
667
 
@@ -606,7 +669,7 @@ module Steps
606
669
  attribute :numbers
607
670
 
608
671
  def call!
609
- Success(numbers: numbers.map { |number| number * 2 })
672
+ Success result: { numbers: numbers.map { |number| number * 2 } }
610
673
  end
611
674
  end
612
675
 
@@ -614,20 +677,28 @@ module Steps
614
677
  attribute :numbers
615
678
 
616
679
  def call!
617
- Success(numbers: numbers.map { |number| number * number })
680
+ Success result: { numbers: numbers.map { |number| number * number } }
618
681
  end
619
682
  end
620
683
  end
621
684
 
622
- Add2ToAllNumbers = Steps::ConvertTextToNumbers >> Steps::Add2
623
- DoubleAllNumbers = Steps::ConvertTextToNumbers >> Steps::Double
624
- SquareAllNumbers = Steps::ConvertTextToNumbers >> Steps::Square
685
+ DoubleAllNumbers =
686
+ Micro::Cases.flow([Steps::ConvertTextToNumbers, Steps::Double])
687
+
688
+ SquareAllNumbers =
689
+ Micro::Cases.flow([Steps::ConvertTextToNumbers, Steps::Square])
690
+
691
+ DoubleAllNumbersAndAdd2 =
692
+ Micro::Cases.flow([DoubleAllNumbers, Steps::Add2])
625
693
 
626
- DoubleAllNumbersAndAdd2 = DoubleAllNumbers >> Steps::Add2
627
- SquareAllNumbersAndAdd2 = SquareAllNumbers >> Steps::Add2
694
+ SquareAllNumbersAndAdd2 =
695
+ Micro::Cases.flow([SquareAllNumbers, Steps::Add2])
628
696
 
629
- SquareAllNumbersAndDouble = SquareAllNumbersAndAdd2 >> DoubleAllNumbers
630
- DoubleAllNumbersAndSquareAndAdd2 = DoubleAllNumbers >> SquareAllNumbersAndAdd2
697
+ SquareAllNumbersAndDouble =
698
+ Micro::Cases.flow([SquareAllNumbersAndAdd2, DoubleAllNumbers])
699
+
700
+ DoubleAllNumbersAndSquareAndAdd2 =
701
+ Micro::Cases.flow([DoubleAllNumbers, SquareAllNumbersAndAdd2])
631
702
 
632
703
  SquareAllNumbersAndDouble
633
704
  .call(numbers: %w[1 1 2 2 3 4])
@@ -638,16 +709,153 @@ DoubleAllNumbersAndSquareAndAdd2
638
709
  .on_success { |value| p value[:numbers] } # [6, 6, 18, 18, 38, 66]
639
710
  ```
640
711
 
641
- 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).
712
+ 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).
642
713
 
643
714
  [⬆️ Back to Top](#table-of-contents-)
644
715
 
645
- #### Is it possible a flow accumulates its input and merges each success result to use as the argument of their use cases?
716
+ #### Is it possible a flow accumulates its input and merges each success result to use as the argument of the next use cases?
646
717
 
647
- Answer: Yes, it is! Check out these test examples [Micro::Case::Flow](https://github.com/serradura/u-case/blob/e0066d8a6e3a9404069dfcb9bf049b854f08a33c/test/micro/case/flow/reducer_test.rb) and [Micro::Case::Safe::Flow](https://github.com/serradura/u-case/blob/e0066d8a6e3a9404069dfcb9bf049b854f08a33c/test/micro/case/safe/flow/reducer_test.rb) to see different use cases sharing their own data.
718
+ Answer: Yes, it is possible! Look at the example below to understand how the data accumulation works inside of the flow execution.
719
+
720
+ ```ruby
721
+ module Users
722
+ class FindByEmail < Micro::Case
723
+ attribute :email
724
+
725
+ def call!
726
+ user = User.find_by(email: email)
727
+
728
+ return Success result: { user: user } if user
729
+
730
+ Failure(:user_not_found)
731
+ end
732
+ end
733
+ end
734
+
735
+ module Users
736
+ class ValidatePassword < Micro::Case::Strict
737
+ attributes :user, :password
738
+
739
+ def call!
740
+ return Failure(:user_must_be_persisted) if user.new_record?
741
+ return Failure(:wrong_password) if user.wrong_password?(password)
742
+
743
+ return Success result: attributes(:user)
744
+ end
745
+ end
746
+ end
747
+
748
+ module Users
749
+ Authenticate = Micro::Cases.flow([
750
+ FindByEmail,
751
+ ValidatePassword
752
+ ])
753
+ end
754
+
755
+ Users::Authenticate
756
+ .call(email: 'somebody@test.com', password: 'password')
757
+ .on_success { |result| sign_in(result[:user]) }
758
+ .on_failure(:wrong_password) { render status: 401 }
759
+ .on_failure(:user_not_found) { render status: 404 }
760
+ ```
761
+
762
+ First, lets see the attributes used by each use case:
763
+
764
+ ```ruby
765
+ class Users::FindByEmail < Micro::Case
766
+ attribute :email
767
+ end
768
+
769
+ class Users::ValidatePassword < Micro::Case
770
+ attributes :user, :password
771
+ end
772
+ ```
773
+
774
+ As you can see the `Users::ValidatePassword` expects a user as its input. So, how does it receives the user?
775
+ It receives the user from the `Users::FindByEmail` success result!
776
+
777
+ And this, is the power of use cases composition because the output
778
+ of one step will compose the input of the next use case in the flow!
779
+
780
+ > input **>>** process **>>** output
781
+
782
+ > **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.
648
783
 
649
784
  [⬆️ Back to Top](#table-of-contents-)
650
785
 
786
+ #### How to understand what is happening during a flow execution?
787
+
788
+ Use `Micro::Case::Result#transitions`!
789
+
790
+ Let's use the [previous section example](#is-it-possible-a-flow-accumulates-its-input-and-merges-each-success-result-to-use-as-the-argument-of-the-next-use-cases) to ilustrate how to use this feature.
791
+
792
+ ```ruby
793
+ user_authenticated =
794
+ Users::Authenticate.call(email: 'rodrigo@test.com', password: user_password)
795
+
796
+ user_authenticated.transitions
797
+ [
798
+ {
799
+ :use_case => {
800
+ :class => Users::FindByEmail,
801
+ :attributes => { :email => "rodrigo@test.com" }
802
+ },
803
+ :success => {
804
+ :type => :ok,
805
+ :result => {
806
+ :user => #<User:0x00007fb57b1c5f88 @email="rodrigo@test.com" ...>
807
+ }
808
+ },
809
+ :accessible_attributes => [ :email, :password ]
810
+ },
811
+ {
812
+ :use_case => {
813
+ :class => Users::ValidatePassword,
814
+ :attributes => {
815
+ :user => #<User:0x00007fb57b1c5f88 @email="rodrigo@test.com" ...>
816
+ :password => "123456"
817
+ }
818
+ },
819
+ :success => {
820
+ :type => :ok,
821
+ :result => {
822
+ :user => #<User:0x00007fb57b1c5f88 @email="rodrigo@test.com" ...>
823
+ }
824
+ },
825
+ :accessible_attributes => [ :email, :password, :user ]
826
+ }
827
+ ]
828
+ ```
829
+
830
+ The example above shows the output generated by the `Micro::Case::Result#transitions`.
831
+ 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.
832
+
833
+ 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).
834
+
835
+ > **Note:** The [`Micro::Case::Result#then`](#how-to-use-the-microcaseresultthen-method) increments the `Micro::Case::Result#transitions`.
836
+
837
+ PS: Use the `Micro::Case::Result.disable_transition_tracking` feature toggle to disable this feature (use once, because it is global) and increase the use cases' performance.
838
+
839
+ ##### `Micro::Case::Result#transitions` schema
840
+ ```ruby
841
+ [
842
+ {
843
+ use_case: {
844
+ class: <Micro::Case>,# Use case which was executed
845
+ attributes: <Hash> # (Input) The use case's attributes
846
+ },
847
+ [success:, failure:] => { # (Output)
848
+ type: <Symbol>, # Result type. Defaults:
849
+ # Success = :ok, Failure = :error/:exception
850
+ result: <Hash> # The data returned by the use case
851
+ },
852
+ accessible_attributes: <Array>, # Properties that can be accessed by the use case's attributes,
853
+ # starting with Hash used to invoke it and which are incremented
854
+ # with each result value of the flow's use cases.
855
+ }
856
+ ]
857
+ ```
858
+
651
859
  #### Is it possible to declare a flow which includes the use case itself?
652
860
 
653
861
  Answer: Yes, it is! You can use the `self.call!` macro. e.g:
@@ -657,7 +865,7 @@ class ConvertTextToNumber < Micro::Case
657
865
  attribute :text
658
866
 
659
867
  def call!
660
- Success { { number: text.to_i } }
868
+ Success result: { number: text.to_i }
661
869
  end
662
870
  end
663
871
 
@@ -665,7 +873,7 @@ class ConvertNumberToText < Micro::Case
665
873
  attribute :number
666
874
 
667
875
  def call!
668
- Success { { text: number.to_s } }
876
+ Success result: { text: number.to_s }
669
877
  end
670
878
  end
671
879
 
@@ -677,17 +885,17 @@ class Double < Micro::Case
677
885
  attribute :number
678
886
 
679
887
  def call!
680
- Success { { number: number * 2 } }
888
+ Success result: { number: number * 2 }
681
889
  end
682
890
  end
683
891
 
684
892
  result = Double.call(text: '4')
685
893
 
686
894
  result.success? # true
687
- result.value # "8"
895
+ result[:number] # "8"
688
896
 
689
897
  # NOTE: This feature can be used with the Micro::Case::Safe.
690
- # Checkout the test: test/micro/case/safe/flow/with_classes/using_itself_test.rb
898
+ # Checkout this test: https://github.com/serradura/u-case/blob/714c6b658fc6aa02617e6833ddee09eddc760f2a/test/micro/case/safe/with_inner_flow_test.rb
691
899
  ```
692
900
 
693
901
  [⬆️ Back to Top](#table-of-contents-)
@@ -701,7 +909,7 @@ class Double < Micro::Case::Strict
701
909
  attribute :numbers
702
910
 
703
911
  def call!
704
- Success(numbers.map { |number| number * 2 })
912
+ Success result: { numbers: numbers.map { |number| number * 2 } }
705
913
  end
706
914
  end
707
915
 
@@ -715,7 +923,7 @@ Double.call({})
715
923
 
716
924
  ### `Micro::Case::Safe` - Is there some feature to auto handle exceptions inside of a use case or flow?
717
925
 
718
- Answer: Yes, there is!
926
+ Answer: Yes, there is one!
719
927
 
720
928
  **Use cases:**
721
929
 
@@ -730,14 +938,18 @@ class Divide < Micro::Case::Safe
730
938
  attributes :a, :b
731
939
 
732
940
  def call!
733
- return Success(a / b) if a.is_a?(Integer) && b.is_a?(Integer)
734
- Failure(:not_an_integer)
941
+ if a.is_a?(Integer) && b.is_a?(Integer)
942
+ Success result: { number: a / b}
943
+ else
944
+ Failure(:not_an_integer)
945
+ end
735
946
  end
736
947
  end
737
948
 
738
949
  result = Divide.call(a: 2, b: 0)
739
- result.type == :exception # true
740
- result.value.is_a?(ZeroDivisionError) # true
950
+ result.type == :exception # true
951
+ result.data # { exception: #<ZeroDivisionError...> }
952
+ result[:exception].is_a?(ZeroDivisionError) # true
741
953
 
742
954
  result.on_failure(:exception) do |exception|
743
955
  AppLogger.error(exception.message) # E, [2019-08-21T00:05:44.195506 #9532] ERROR -- : divided by 0
@@ -758,25 +970,18 @@ end
758
970
  # Another note:
759
971
  # ------------
760
972
  # It is possible to rescue an exception even when is a safe use case.
761
- # Examples: https://github.com/serradura/u-case/blob/5a85fc238b63811a32737493dc6c59965f92491d/test/micro/case/safe_test.rb#L95-L123
973
+ # Examples: https://github.com/serradura/u-case/blob/714c6b658fc6aa02617e6833ddee09eddc760f2a/test/micro/case/safe_test.rb#L90-L118
762
974
  ```
763
975
 
764
- **Flows:**
976
+ [⬆️ Back to Top](#table-of-contents-)
977
+
978
+ #### `Micro::Cases::Safe::Flow`
765
979
 
766
980
  As the safe use cases, safe flows can intercept an exception in any of its steps. These are the ways to define one:
767
981
 
768
982
  ```ruby
769
983
  module Users
770
- Create = ProcessParams & ValidateParams & Persist & SendToCRM
771
- end
772
-
773
- # Note:
774
- # 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
775
-
776
- # The alternatives to declare a safe flow are:
777
-
778
- module Users
779
- Create = Micro::Case::Safe::Flow([
984
+ Create = Micro::Cases.safe_flow([
780
985
  ProcessParams,
781
986
  ValidateParams,
782
987
  Persist,
@@ -794,33 +999,63 @@ module Users
794
999
  SendToCRM
795
1000
  end
796
1001
  end
1002
+ ```
797
1003
 
1004
+ [⬆️ Back to Top](#table-of-contents-)
798
1005
 
799
- # !------------------------------------------ ! #
800
- # ! Deprecated: Micro::Case::Safe::Flow mixin ! #
801
- # !-------------------------------------------! #
1006
+ #### `Micro::Case::Result#on_exception`
802
1007
 
803
- # The code below still works, but it will output a warning message:
804
- # Deprecation: Micro::Case::Flow mixin is being deprecated, please use `Micro::Case` inheritance instead.
1008
+ In functional programming errors/exceptions are handled as regular data, the idea is to transform the output even when it happens an unexpected behavior. For many, [exceptions are very similar to the GOTO statement](https://softwareengineering.stackexchange.com/questions/189222/are-exceptions-as-control-flow-considered-a-serious-antipattern-if-so-why), jumping the application flow to paths which could be difficult to figure out how things work in a system.
805
1009
 
806
- module Users
807
- class Create
808
- include Micro::Case::Safe::Flow
1010
+ 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.
1011
+
1012
+ > **Note**: this feature will work better if you use it with a `Micro::Case::Safe` use case/flow.
809
1013
 
810
- flow ProcessParams, ValidateParams, Persist, SendToCRM
1014
+ How does it work?
1015
+
1016
+ ```ruby
1017
+ class Divide < Micro::Case::Safe
1018
+ attributes :a, :b
1019
+
1020
+ def call!
1021
+ Success result: { division: a / b }
811
1022
  end
812
1023
  end
813
1024
 
814
- # Note: This feature will be removed in the next major release (3.0)
1025
+ Divide
1026
+ .call(a: 2, b: 0)
1027
+ .on_success { |result| puts result[:division] }
1028
+ .on_exception(TypeError) { puts 'Please, use only numeric attributes.' }
1029
+ .on_exception(ZeroDivisionError) { |_error| puts "Can't divide a number by 0." }
1030
+ .on_exception { |_error, _use_case| puts 'Oh no, something went wrong!' }
1031
+
1032
+ # Output:
1033
+ # -------
1034
+ # Can't divide a number by 0
1035
+ # Oh no, something went wrong!
1036
+
1037
+ Divide.
1038
+ .call(a: 2, b: '2').
1039
+ .on_success { |result| puts result[:division] }
1040
+ .on_exception(TypeError) { puts 'Please, use only numeric attributes.' }
1041
+ .on_exception(ZeroDivisionError) { |_error| puts "Can't divide a number by 0." }
1042
+ .on_exception { |_error, _use_case| puts 'Oh no, something went wrong!' }
1043
+
1044
+ # Output:
1045
+ # -------
1046
+ # Please, use only numeric attributes.
1047
+ # Oh no, something went wrong!
815
1048
  ```
816
1049
 
1050
+ As you can see, this hook has the same behavior of `result.on_failure(:exception)`, but, the ideia here is to have a better communication in the code, making an explicit reference when some failure happened because of an exception.
1051
+
817
1052
  [⬆️ Back to Top](#table-of-contents-)
818
1053
 
819
- ### `u-case/with_validation` - How to validate use case attributes?
1054
+ ### `u-case/with_activemodel_validation` - How to validate use case attributes?
820
1055
 
821
1056
  **Requirement:**
822
1057
 
823
- To do this your application must have the [activemodel >= 3.2](https://rubygems.org/gems/activemodel) as a dependency.
1058
+ To do this your application must have the [activemodel >= 3.2, < 6.1.0](https://rubygems.org/gems/activemodel) as a dependency.
824
1059
 
825
1060
  ```ruby
826
1061
  #
@@ -833,9 +1068,9 @@ class Multiply < Micro::Case
833
1068
  validates :a, :b, presence: true, numericality: true
834
1069
 
835
1070
  def call!
836
- return Failure(:validation_error) { {errors: self.errors} } unless valid?
1071
+ return Failure :validation_error, result: { errors: self.errors } if invalid?
837
1072
 
838
- Success(number: a * b)
1073
+ Success result: { number: a * b }
839
1074
  end
840
1075
  end
841
1076
 
@@ -844,10 +1079,10 @@ end
844
1079
  # your use cases on validation errors, you can use:
845
1080
 
846
1081
  # In some file. e.g: A Rails initializer
847
- require 'u-case/with_validation' # or require 'micro/case/with_validation'
1082
+ require 'u-case/with_activemodel_validation' # or require 'micro/case/with_validation'
848
1083
 
849
1084
  # In the Gemfile
850
- gem 'u-case', require: 'u-case/with_validation'
1085
+ gem 'u-case', require: 'u-case/with_activemodel_validation'
851
1086
 
852
1087
  # Using this approach, you can rewrite the previous example with less code. e.g:
853
1088
 
@@ -857,7 +1092,7 @@ class Multiply < Micro::Case
857
1092
  validates :a, :b, presence: true, numericality: true
858
1093
 
859
1094
  def call!
860
- Success(number: a * b)
1095
+ Success result: { number: a * b }
861
1096
  end
862
1097
  end
863
1098
 
@@ -869,10 +1104,10 @@ end
869
1104
 
870
1105
  #### If I enabled the auto validation, is it possible to disable it only in specific use case classes?
871
1106
 
872
- Answer: Yes, it is. To do this, you only need to use the `disable_auto_validation` macro. e.g:
1107
+ Answer: Yes, it is possible. To do this, you only need to use the `disable_auto_validation` macro. e.g:
873
1108
 
874
1109
  ```ruby
875
- require 'u-case/with_validation'
1110
+ require 'u-case/with_activemodel_validation'
876
1111
 
877
1112
  class Multiply < Micro::Case
878
1113
  disable_auto_validation
@@ -882,7 +1117,7 @@ class Multiply < Micro::Case
882
1117
  validates :a, :b, presence: true, numericality: true
883
1118
 
884
1119
  def call!
885
- Success(number: a * b)
1120
+ Success result: { number: a * b }
886
1121
  end
887
1122
  end
888
1123
 
@@ -894,168 +1129,190 @@ Multiply.call(a: 2, b: 'a')
894
1129
 
895
1130
  [⬆️ Back to Top](#table-of-contents-)
896
1131
 
1132
+ #### `Kind::Validator`
1133
+
1134
+ 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).
1135
+
1136
+ The example below shows how to validate the attributes data types.
1137
+
1138
+ ```ruby
1139
+ class Todo::List::AddItem < Micro::Case
1140
+ attributes :user, :params
1141
+
1142
+ validates :user, kind: User
1143
+ validates :params, kind: ActionController::Parameters
1144
+
1145
+ def call!
1146
+ todo_params = params.require(:todo).permit(:title, :due_at)
1147
+
1148
+ todo = user.todos.create(todo_params)
1149
+
1150
+ Success result: { todo: todo }
1151
+ rescue ActionController::ParameterMissing => e
1152
+ Failure :parameter_missing, result: { message: e.message }
1153
+ end
1154
+ end
1155
+ ```
1156
+
897
1157
  ## Benchmarks
898
1158
 
899
- ### `Micro::Case`
1159
+ ### `Micro::Case` (v2.6.0)
900
1160
 
901
1161
  #### Best overall
902
1162
 
903
1163
  The table below contains the average between the [Success results](#success-results) and [Failure results](#failure-results) benchmarks.
904
1164
 
905
- | Gem / Abstraction | Iterations per second | Comparison |
906
- | ---------------------- | --------------------: | ---------------: |
907
- | **Micro::Case** | 116629.7 | _**The Faster**_ |
908
- | Dry::Monads | 101796.3 | 1.14x slower |
909
- | Interactor | 21230.5 | 5.49x slower |
910
- | Trailblazer::Operation | 16466.6 | 7.08x slower |
911
- | Dry::Transaction | 5069.5 | 23.00x slower |
1165
+ | Gem / Abstraction | Iterations per second | Comparison |
1166
+ | ---------------------- | --------------------: | ----------------: |
1167
+ | **Micro::Case** | 105124.3 | _**The Fastest**_ |
1168
+ | Dry::Monads | 103290.1 | 0.02x slower |
1169
+ | Interactor | 21342.3 | 4.93x slower |
1170
+ | Trailblazer::Operation | 14652.7 | 7.17x slower |
1171
+ | Dry::Transaction | 5310.3 | 19.80x slower |
912
1172
 
913
1173
  ---
914
1174
 
915
1175
  #### Success results
916
1176
 
917
- | Gem / Abstraction | Iterations per second | Comparison |
918
- | ----------------- | --------------------: | ---------------: |
919
- | Dry::Monads | 139352.5 | _**The Faster**_ |
920
- | **Micro::Case** | 124749.4 | 1.12x slower |
921
- | Interactor | 28974.4 | 4.81x slower |
922
- | Trailblazer::Operation | 17275.6 | 8.07x slower |
923
- | Dry::Transaction | 5571.7 | 25.01x slower |
1177
+ | Gem / Abstraction | Iterations per second | Comparison |
1178
+ | ----------------- | --------------------: | ----------------: |
1179
+ | Dry::Monads | 134801.0 | _**The Fastest**_ |
1180
+ | **Micro::Case** | 105909.2 | 1.27x slower |
1181
+ | Interactor | 29458.2 | 4.58x slower |
1182
+ | Trailblazer::Operation | 14714.9 | 9.16x slower |
1183
+ | Dry::Transaction | 5642.6 | 28.89x slower |
924
1184
 
925
1185
  <details>
926
1186
  <summary>Show the full <a href="https://github.com/evanphx/benchmark-ips">benchmark/ips</a> results.</summary>
927
1187
 
928
- ```ruby
929
- # Warming up --------------------------------------
930
- # Interactor 2.865k i/100ms
931
- # Trailblazer::Operation
932
- # 1.686k i/100ms
933
- # Dry::Monads 13.389k i/100ms
934
- # Dry::Transaction 551.000 i/100ms
935
- # Micro::Case 11.984k i/100ms
936
- # Micro::Case::Strict 9.102k i/100ms
937
- # Micro::Case::Safe 11.747k i/100ms
938
- # Calculating -------------------------------------
939
- # Interactor 28.974k2.7%) i/s - 146.115k in 5.046703s
940
- # Trailblazer::Operation
941
- # 17.276k1.8%) i/s - 87.672k in 5.076609s
942
- # Dry::Monads 139.353k (± 2.5%) i/s - 709.617k in 5.095599s
943
- # Dry::Transaction 5.572k3.6%) i/s - 28.101k in 5.050376s
944
- # Micro::Case 124.749k (± 1.9%) i/s - 635.152k in 5.093310s
945
- # Micro::Case::Strict 93.417k4.8%) i/s - 473.304k in 5.081341s
946
- # Micro::Case::Safe 120.607k (± 3.2%) i/s - 610.844k in 5.070394s
947
-
948
- # Comparison:
949
- # Dry::Monads: 139352.5 i/s
950
- # Micro::Case: 124749.4 i/s - 1.12x slower
951
- # Micro::Case::Safe: 120607.3 i/s - 1.16x slower
952
- # Micro::Case::Strict: 93417.3 i/s - 1.49x slower
953
- # Interactor: 28974.4 i/s - 4.81x slower
954
- # Trailblazer::Operation: 17275.6 i/s - 8.07x slower
955
- # Dry::Transaction: 5571.7 i/s - 25.01x slower
956
- ```
1188
+ ```ruby
1189
+ # Warming up --------------------------------------
1190
+ # Interactor 2.897k i/100ms
1191
+ # Trailblazer::Operation 1.494k i/100ms
1192
+ # Dry::Monads 13.854k i/100ms
1193
+ # Dry::Transaction 561.000 i/100ms
1194
+ # Micro::Case 10.523k i/100ms
1195
+ # Micro::Case::Strict 7.982k i/100ms
1196
+ # Micro::Case::Safe 10.568k i/100ms
1197
+
1198
+ # Calculating -------------------------------------
1199
+ # Interactor 29.458k3.4%) i/s - 147.747k in 5.021405s
1200
+ # Trailblazer::Operation 14.715k (± 1.8%) i/s - 74.700k in 5.078128s
1201
+ # Dry::Monads 134.801k8.7%) i/s - 678.846k in 5.088739s
1202
+ # Dry::Transaction 5.643k (± 2.1%) i/s - 28.611k in 5.072969s
1203
+ # Micro::Case 105.909k2.4%) i/s - 536.673k in 5.070329s
1204
+ # Micro::Case::Strict 84.234k (± 1.5%) i/s - 423.046k in 5.023447s
1205
+ # Micro::Case::Safe 105.725k1.9%) i/s - 538.968k in 5.099817s
1206
+
1207
+ # Comparison:
1208
+ # Dry::Monads: 134801.0 i/s
1209
+ # Micro::Case: 105909.2 i/s - 1.27x (± 0.00) slower
1210
+ # Micro::Case::Safe: 105725.0 i/s - 1.28x (± 0.00) slower
1211
+ # Micro::Case::Strict: 84234.4 i/s - 1.60x (± 0.00) slower
1212
+ # Interactor: 29458.2 i/s - 4.58x (± 0.00) slower
1213
+ # Trailblazer::Operation: 14714.9 i/s - 9.16x (± 0.00) slower
1214
+ # Dry::Transaction: 5642.6 i/s - 23.89x (± 0.00) slower
1215
+ ```
957
1216
  </details>
958
1217
 
959
1218
  https://github.com/serradura/u-case/blob/master/benchmarks/use_case/with_success_result.rb
960
1219
 
961
1220
  #### Failure results
962
1221
 
963
- | Gem / Abstraction | Iterations per second | Comparison |
964
- | ----------------- | --------------------: | ---------------: |
965
- | **Micro::Case** | 108510.0 | _**The Faster**_ |
966
- | Dry::Monads | 64240.1 | 1.69x slower |
967
- | Trailblazer::Operation | 15657.7 | 6.93x slower |
968
- | Interactor | 13486.7 | 8.05x slower |
969
- | Dry::Transaction | 4567.3 | 23.76x slower |
1222
+ | Gem / Abstraction | Iterations per second | Comparison |
1223
+ | ----------------- | --------------------: | ----------------: |
1224
+ | **Micro::Case** | 104339.4 | _**The Fastest**_ |
1225
+ | Dry::Monads | 71779.2 | 1.45x slower |
1226
+ | Trailblazer::Operation | 14590.6 | 7.15x slower |
1227
+ | Interactor | 13226.5 | 7.89x slower |
1228
+ | Dry::Transaction | 4978.1 | 20.96x slower |
970
1229
 
971
1230
  <details>
972
1231
  <summary>Show the full <a href="https://github.com/evanphx/benchmark-ips">benchmark/ips</a> results.</summary>
973
1232
 
974
- ```ruby
975
- # Warming up --------------------------------------
976
- # Interactor 1.331k i/100ms
977
- # Trailblazer::Operation
978
- # 1.544k i/100ms
979
- # Dry::Monads 6.343k i/100ms
980
- # Dry::Transaction 456.000 i/100ms
981
- # Micro::Case 10.429k i/100ms
982
- # Micro::Case::Strict 8.109k i/100ms
983
- # Micro::Case::Safe 10.280k i/100ms
984
- # Calculating -------------------------------------
985
- # Interactor 13.487k1.9%) i/s - 67.881k in 5.035059s
986
- # Trailblazer::Operation
987
- # 15.658k1.6%) i/s - 78.744k in 5.030427s
988
- # Dry::Monads 64.240k1.8%) i/s - 323.493k in 5.037461s
989
- # Dry::Transaction 4.567k (± 1.3%) i/s - 23.256k in 5.092699s
990
- # Micro::Case 108.510k (± 2.3%) i/s - 542.308k in 5.000605s
991
- # Micro::Case::Strict 83.527k (± 1.4%) i/s - 421.668k in 5.049245s
992
- # Micro::Case::Safe 105.641k (± 3.7%) i/s - 534.560k in 5.067836s
993
-
994
- # Comparison:
995
- # Micro::Case: 108510.0 i/s
996
- # Micro::Case::Safe: 105640.6 i/s - same-ish: difference falls within error
997
- # Micro::Case::Strict: 83526.8 i/s - 1.30x slower
998
- # Dry::Monads: 64240.1 i/s - 1.69x slower
999
- # Trailblazer::Operation: 15657.7 i/s - 6.93x slower
1000
- # Interactor: 13486.7 i/s - 8.05x slower
1001
- # Dry::Transaction: 4567.3 i/s - 23.76x slower
1002
- ```
1233
+ ```ruby
1234
+ # Warming up --------------------------------------
1235
+ # Interactor 1.339k i/100ms
1236
+ # Trailblazer::Operation 1.393k i/100ms
1237
+ # Dry::Monads 7.208k i/100ms
1238
+ # Dry::Transaction 423.000 i/100ms
1239
+ # Micro::Case 9.620k i/100ms
1240
+ # Micro::Case::Strict 8.238k i/100ms
1241
+ # Micro::Case::Safe 9.906k i/100ms
1242
+
1243
+ # Calculating -------------------------------------
1244
+ # Interactor 13.227k3.3%) i/s - 66.950k in 5.067145s
1245
+ # Trailblazer::Operation 14.591k (± 4.0%) i/s - 73.829k in 5.069162s
1246
+ # Dry::Monads 71.779k2.5%) i/s - 360.400k in 5.024294s
1247
+ # Dry::Transaction 4.978k3.3%) i/s - 24.957k in 5.019153s
1248
+ # Micro::Case 103.957k (± 1.8%) i/s - 529.100k in 5.091221s
1249
+ # Micro::Case::Strict 83.094k (± 2.0%) i/s - 420.138k in 5.058233s
1250
+ # Micro::Case::Safe 104.339k (± 1.7%) i/s - 525.018k in 5.033381s
1251
+
1252
+ # Comparison:
1253
+ # Micro::Case::Safe: 104339.4 i/s
1254
+ # Micro::Case: 103957.2 i/s - same-ish: difference falls within error
1255
+ # Micro::Case::Strict: 83094.5 i/s - 1.26x (± 0.00) slower
1256
+ # Dry::Monads: 71779.2 i/s - 1.45x (± 0.00) slower
1257
+ # Trailblazer::Operation: 14590.6 i/s - 7.15x (± 0.00) slower
1258
+ # Interactor: 13226.5 i/s - 7.89x (± 0.00) slower
1259
+ # Dry::Transaction: 4978.1 i/s - 20.96x (± 0.00) slower
1260
+ ```
1003
1261
  </details>
1004
1262
 
1005
1263
  https://github.com/serradura/u-case/blob/master/benchmarks/use_case/with_failure_result.rb
1006
1264
 
1007
1265
  ---
1008
1266
 
1009
- ### `Micro::Case::Flow`
1267
+ ### `Micro::Case::Flow` (v2.6.0)
1010
1268
 
1011
1269
  | 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) |
1012
- | ------------------ | ---------------: | ---------------: |
1013
- | Micro::Case::Flow | _**The Faster**_ | _**The Faster**_ |
1014
- | Micro::Case::Safe::Flow | 0x slower | 0x slower |
1015
- | Interactor::Organizer | 1.47x slower | 5.51x slower |
1270
+ | ------------------ | ----------------: | ----------------: |
1271
+ | Micro::Case::Flow | _**The Fastest**_ | _**The Fastest**_ |
1272
+ | Micro::Case::Safe::Flow | 0x slower | 0x slower |
1273
+ | Interactor::Organizer | 1.27x slower | 5.48x slower |
1016
1274
 
1017
1275
  \* The `Dry::Monads`, `Dry::Transaction`, `Trailblazer::Operation` are out of this analysis because all of them doesn't have this kind of feature.
1018
1276
 
1019
1277
  <details>
1020
1278
  <summary><strong>Success results</strong> - Show the full benchmark/ips results.</summary>
1021
1279
 
1022
- ```ruby
1023
- # Warming up --------------------------------------
1024
- # Interactor::Organizer 4.880k i/100ms
1025
- # Micro::Case::Flow 7.035k i/100ms
1026
- # Micro::Case::Safe::Flow 7.059k i/100ms
1027
-
1028
- # Calculating -------------------------------------
1029
- # Interactor::Organizer 50.208k1.3%) i/s - 253.760k in 5.055099s
1030
- # Micro::Case::Flow 73.791k0.9%) i/s - 372.855k in 5.053311s
1031
- # Micro::Case::Safe::Flow 73.314k (± 1.1%) i/s - 367.068k in 5.007473s
1032
-
1033
- # Comparison:
1034
- # Micro::Case::Flow: 73790.7 i/s
1035
- # Micro::Case::Safe::Flow: 73313.7 i/s - same-ish: difference falls within error
1036
- # Interactor::Organizer: 50207.7 i/s - 1.47x slower
1037
- ```
1280
+ ```ruby
1281
+ # Warming up --------------------------------------
1282
+ # Interactor::Organizer 4.765k i/100ms
1283
+ # Micro::Case::Flow 5.372k i/100ms
1284
+ # Micro::Case::Safe::Flow 5.855k i/100ms
1285
+ # Calculating -------------------------------------
1286
+ # Interactor::Organizer 48.598k (± 5.2%) i/s - 243.015k in 5.014307s
1287
+ # Micro::Case::Flow 61.606k4.4%) i/s - 311.576k in 5.068602s
1288
+ # Micro::Case::Safe::Flow 60.688k4.8%) i/s - 304.460k in 5.028877s
1289
+
1290
+ # Comparison:
1291
+ # Micro::Case::Flow: 61606.3 i/s
1292
+ # Micro::Case::Safe::Flow: 60688.3 i/s - same-ish: difference falls within error
1293
+ # Interactor::Organizer: 48598.2 i/s - 1.27x slower\
1294
+ ```
1038
1295
  </details>
1039
1296
 
1040
1297
  <details>
1041
1298
  <summary><strong>Failure results</strong> - Show the full benchmark/ips results.</summary>
1042
1299
 
1043
- ```ruby
1044
- # Warming up --------------------------------------
1045
- # Interactor::Organizer 2.372k i/100ms
1046
- # Micro::Case::Flow 12.802k i/100ms
1047
- # Micro::Case::Safe::Flow 12.673k i/100ms
1048
-
1049
- # Calculating -------------------------------------
1050
- # Interactor::Organizer 24.522k (± 2.0%) i/s - 123.344k in 5.032159s
1051
- # Micro::Case::Flow 135.122k1.7%) i/s - 678.506k in 5.022903s
1052
- # Micro::Case::Safe::Flow 133.980k1.4%) i/s - 671.669k in 5.014181s
1053
-
1054
- # Comparison:
1055
- # Micro::Case::Flow: 135122.0 i/s
1056
- # Micro::Case::Safe::Flow: 133979.8 i/s - same-ish: difference falls within error
1057
- # Interactor::Organizer: 24521.8 i/s - 5.51x slower
1058
- ```
1300
+ ```ruby
1301
+ # Warming up --------------------------------------
1302
+ # Interactor::Organizer 2.209k i/100ms
1303
+ # Micro::Case::Flow 11.508k i/100ms
1304
+ # Micro::Case::Safe::Flow 11.605k i/100ms
1305
+
1306
+ # Calculating -------------------------------------
1307
+ # Interactor::Organizer 22.592k (± 2.8%) i/s - 114.868k in 5.088685s
1308
+ # Micro::Case::Flow 123.629k2.9%) i/s - 621.432k in 5.030844s
1309
+ # Micro::Case::Safe::Flow 123.862k3.0%) i/s - 626.670k in 5.064097s
1310
+
1311
+ # Comparison:
1312
+ # Micro::Case::Safe::Flow: 123862.4 i/s
1313
+ # Micro::Case::Flow: 123629.3 i/s - same-ish: difference falls within error
1314
+ # Interactor::Organizer: 22592.2 i/s - 5.48x slower
1315
+ ```
1059
1316
  </details>
1060
1317
 
1061
1318
  https://github.com/serradura/u-case/tree/master/benchmarks/flow