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