u-case 2.6.0 → 3.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.sh +3 -2
- data/Gemfile +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
|