u-case 2.4.0 → 3.0.0.rc3

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