u-case 2.0.0.pre.2 → 2.0.0.pre.3
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/README.md +89 -22
- data/lib/micro/case/result.rb +24 -8
- data/lib/micro/case/safe.rb +0 -4
- data/lib/micro/case/safe/flow.rb +4 -4
- data/lib/micro/case/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1faaf3599f7b9e24aca4fcab71c448b7078756a6ca9abce84426a456673c4f50
|
4
|
+
data.tar.gz: 329095160dc157bd6ac3dda112c83637e8fe169050497bd5e3e2698c095415cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f02f2e9db0044e56e9ec690d69f69d302f666b99ccff37106369a63cf6a39c0d15cefe9d07d28464a75bbb1f236b1fd6ad3eddc40f25dd473e54b0ee2d77c02d
|
7
|
+
data.tar.gz: 6b81d3c72c67596229689b3bc9cccc3a9ef20c9d7fd45d506913215d3fa759e174aa94d7105aeba6dfb184278b54fbfd1a9e5d7e56cdb4468968a06a96e4f7cd
|
data/README.md
CHANGED
@@ -17,6 +17,7 @@ The main goals of this project are:
|
|
17
17
|
## Table of Contents <!-- omit in toc -->
|
18
18
|
- [μ-case (Micro::Case)](#%ce%bc-case-microcase)
|
19
19
|
- [Required Ruby version](#required-ruby-version)
|
20
|
+
- [Dependencies](#dependencies)
|
20
21
|
- [Installation](#installation)
|
21
22
|
- [Usage](#usage)
|
22
23
|
- [How to define a use case?](#how-to-define-a-use-case)
|
@@ -25,6 +26,7 @@ The main goals of this project are:
|
|
25
26
|
- [How to define custom result types?](#how-to-define-custom-result-types)
|
26
27
|
- [Is it possible to define a custom result type without a block?](#is-it-possible-to-define-a-custom-result-type-without-a-block)
|
27
28
|
- [How to use the result hooks?](#how-to-use-the-result-hooks)
|
29
|
+
- [Why the on_failure result hook exposes a different kind of data?](#why-the-onfailure-result-hook-exposes-a-different-kind-of-data)
|
28
30
|
- [What happens if a result hook is declared multiple times?](#what-happens-if-a-result-hook-is-declared-multiple-times)
|
29
31
|
- [How to compose uses cases to represents complex ones?](#how-to-compose-uses-cases-to-represents-complex-ones)
|
30
32
|
- [Is it possible to compose a use case flow with other ones?](#is-it-possible-to-compose-a-use-case-flow-with-other-ones)
|
@@ -43,6 +45,11 @@ The main goals of this project are:
|
|
43
45
|
|
44
46
|
> \>= 2.2.0
|
45
47
|
|
48
|
+
## Dependencies
|
49
|
+
|
50
|
+
This project depends on [Micro::Attribute](https://github.com/serradura/u-attributes) gem.
|
51
|
+
It is used to define the use case attributes.
|
52
|
+
|
46
53
|
## Installation
|
47
54
|
|
48
55
|
Add this line to your application's Gemfile:
|
@@ -71,7 +78,7 @@ class Multiply < Micro::Case
|
|
71
78
|
# 2. Define the method `call!` with its business logic
|
72
79
|
def call!
|
73
80
|
|
74
|
-
# 3. Wrap the use case result/output using the `Success()`
|
81
|
+
# 3. Wrap the use case result/output using the `Success()` or `Failure()` methods
|
75
82
|
if a.is_a?(Numeric) && b.is_a?(Numeric)
|
76
83
|
Success(a * b)
|
77
84
|
else
|
@@ -116,12 +123,12 @@ result.value # 6
|
|
116
123
|
|
117
124
|
### What is a `Micro::Case::Result`?
|
118
125
|
|
119
|
-
A `Micro::Case::Result` stores use cases output data. These are their main methods:
|
126
|
+
A `Micro::Case::Result` stores the use cases output data. These are their main methods:
|
120
127
|
- `#success?` returns true if is a successful result.
|
121
128
|
- `#failure?` returns true if is an unsuccessful result.
|
122
129
|
- `#value` the result value itself.
|
123
130
|
- `#type` a Symbol which gives meaning for the result, this is useful to declare different types of failures or success.
|
124
|
-
- `#on_success` or `#on_failure` are hook methods
|
131
|
+
- `#on_success` or `#on_failure` are hook methods that help you define the application flow.
|
125
132
|
- `#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).
|
126
133
|
|
127
134
|
[⬆️ Back to Top](#table-of-contents-)
|
@@ -256,7 +263,7 @@ The examples below show how to use them:
|
|
256
263
|
|
257
264
|
```ruby
|
258
265
|
class Double < Micro::Case
|
259
|
-
|
266
|
+
attribute :number
|
260
267
|
|
261
268
|
def call!
|
262
269
|
return Failure(:invalid) { 'the number must be a numeric value' } unless number.is_a?(Numeric)
|
@@ -286,25 +293,85 @@ Double
|
|
286
293
|
Double
|
287
294
|
.call(number: -1)
|
288
295
|
.on_success { |number| p number }
|
289
|
-
.on_failure { |
|
296
|
+
.on_failure { |_result, use_case| puts "#{use_case.class.name} was the use case responsible for the failure" }
|
290
297
|
.on_failure(:invalid) { |msg| raise TypeError, msg }
|
291
298
|
.on_failure(:lte_zero) { |msg| raise ArgumentError, msg }
|
292
299
|
|
293
|
-
# The outputs
|
294
|
-
#
|
295
|
-
#
|
296
|
-
#
|
300
|
+
# The outputs will be:
|
301
|
+
#
|
302
|
+
# 1. Prints the message: Double was the use case responsible for the failure
|
303
|
+
# 2. Raises the exception: ArgumentError (the number must be greater than 0)
|
297
304
|
|
298
305
|
# Note:
|
299
306
|
# ----
|
300
307
|
# The use case responsible for the failure will be accessible as the second hook argument
|
301
308
|
```
|
302
309
|
|
310
|
+
#### Why the on_failure result hook exposes a different kind of data?
|
311
|
+
|
312
|
+
Answer: To allow you to define how to handle the program flow using some
|
313
|
+
conditional statement (like an `if`, `case/when`).
|
314
|
+
|
315
|
+
```ruby
|
316
|
+
class Double < Micro::Case
|
317
|
+
attribute :number
|
318
|
+
|
319
|
+
def call!
|
320
|
+
return Failure(:invalid) unless number.is_a?(Numeric)
|
321
|
+
return Failure(:lte_zero) if number <= 0
|
322
|
+
|
323
|
+
Success(number * 2)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
#=================================#
|
328
|
+
# Using the result type and value #
|
329
|
+
#=================================#
|
330
|
+
|
331
|
+
Double
|
332
|
+
.call(-1)
|
333
|
+
.on_failure do |result, use_case|
|
334
|
+
case result.type
|
335
|
+
when :invalid then raise TypeError, 'the number must be a numeric value'
|
336
|
+
when :lte_zero then raise ArgumentError, "the number `#{result.value}` must be greater than 0"
|
337
|
+
else raise NotImplementedError
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
# The output will be the exception:
|
342
|
+
#
|
343
|
+
# ArgumentError (the number `-1` must be greater than 0)
|
344
|
+
|
345
|
+
#=====================================================#
|
346
|
+
# Using decomposition to access result value and type #
|
347
|
+
#=====================================================#
|
348
|
+
|
349
|
+
# The syntax to decompose an Array can be used in methods, blocks and assigments.
|
350
|
+
# If you doesn't know that, check out:
|
351
|
+
# https://ruby-doc.org/core-2.2.0/doc/syntax/assignment_rdoc.html#label-Array+Decomposition
|
352
|
+
#
|
353
|
+
# And the object exposed in the hook failure can be decomposed using this syntax. e.g:
|
354
|
+
|
355
|
+
Double
|
356
|
+
.call(-2)
|
357
|
+
.on_failure do |(value, type), use_case|
|
358
|
+
case type
|
359
|
+
when :invalid then raise TypeError, 'the number must be a numeric value'
|
360
|
+
when :lte_zero then raise ArgumentError, "the number `#{value}` must be greater than 0"
|
361
|
+
else raise NotImplementedError
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
# The output will be the exception:
|
366
|
+
#
|
367
|
+
# ArgumentError (the number `-2` must be greater than 0)
|
368
|
+
```
|
369
|
+
|
303
370
|
[⬆️ Back to Top](#table-of-contents-)
|
304
371
|
|
305
372
|
#### What happens if a result hook is declared multiple times?
|
306
373
|
|
307
|
-
Answer: The hook will be triggered if it matches the result type.
|
374
|
+
Answer: The hook always will be triggered if it matches the result type.
|
308
375
|
|
309
376
|
```ruby
|
310
377
|
class Double < Micro::Case
|
@@ -337,11 +404,11 @@ result.value * 4 == accum # true
|
|
337
404
|
|
338
405
|
### How to compose uses cases to represents complex ones?
|
339
406
|
|
340
|
-
In this case, this will be
|
407
|
+
In this case, this will be a **flow**. The main idea is to use/reuse use cases as steps of a new use case.
|
341
408
|
|
342
409
|
```ruby
|
343
410
|
module Steps
|
344
|
-
class
|
411
|
+
class ConvertTextToNumbers < Micro::Case
|
345
412
|
attribute :numbers
|
346
413
|
|
347
414
|
def call!
|
@@ -383,7 +450,7 @@ end
|
|
383
450
|
#---------------------------------------------#
|
384
451
|
|
385
452
|
Add2ToAllNumbers = Micro::Case::Flow([
|
386
|
-
Steps::
|
453
|
+
Steps::ConvertTextToNumbers,
|
387
454
|
Steps::Add2
|
388
455
|
])
|
389
456
|
|
@@ -399,7 +466,7 @@ p result.value # {:numbers => [3, 3, 4, 4, 5, 6]}
|
|
399
466
|
class DoubleAllNumbers
|
400
467
|
include Micro::Case::Flow
|
401
468
|
|
402
|
-
flow Steps::
|
469
|
+
flow Steps::ConvertTextToNumbers, Steps::Double
|
403
470
|
end
|
404
471
|
|
405
472
|
DoubleAllNumbers
|
@@ -411,7 +478,7 @@ DoubleAllNumbers
|
|
411
478
|
#-------------------------------------------------------------#
|
412
479
|
|
413
480
|
SquareAllNumbers =
|
414
|
-
Steps::
|
481
|
+
Steps::ConvertTextToNumbers >> Steps::Square
|
415
482
|
|
416
483
|
SquareAllNumbers
|
417
484
|
.call(numbers: %w[1 1 2 2 3 4])
|
@@ -425,10 +492,10 @@ SquareAllNumbers
|
|
425
492
|
result = SquareAllNumbers.call(numbers: %w[1 1 b 2 3 4])
|
426
493
|
|
427
494
|
result.failure? # true
|
428
|
-
result.use_case.is_a?(Steps::
|
495
|
+
result.use_case.is_a?(Steps::ConvertTextToNumbers) # true
|
429
496
|
|
430
497
|
result.on_failure do |_message, use_case|
|
431
|
-
puts "#{use_case.class.name} was the use case responsible for the failure" # Steps::
|
498
|
+
puts "#{use_case.class.name} was the use case responsible for the failure" # Steps::ConvertTextToNumbers was the use case responsible for the failure
|
432
499
|
end
|
433
500
|
```
|
434
501
|
|
@@ -440,7 +507,7 @@ Answer: Yes, it is.
|
|
440
507
|
|
441
508
|
```ruby
|
442
509
|
module Steps
|
443
|
-
class
|
510
|
+
class ConvertTextToNumbers < Micro::Case
|
444
511
|
attribute :numbers
|
445
512
|
|
446
513
|
def call!
|
@@ -477,9 +544,9 @@ module Steps
|
|
477
544
|
end
|
478
545
|
end
|
479
546
|
|
480
|
-
Add2ToAllNumbers = Steps::
|
481
|
-
DoubleAllNumbers = Steps::
|
482
|
-
SquareAllNumbers = Steps::
|
547
|
+
Add2ToAllNumbers = Steps::ConvertTextToNumbers >> Steps::Add2
|
548
|
+
DoubleAllNumbers = Steps::ConvertTextToNumbers >> Steps::Double
|
549
|
+
SquareAllNumbers = Steps::ConvertTextToNumbers >> Steps::Square
|
483
550
|
|
484
551
|
DoubleAllNumbersAndAdd2 = DoubleAllNumbers >> Steps::Add2
|
485
552
|
SquareAllNumbersAndAdd2 = SquareAllNumbers >> Steps::Add2
|
@@ -515,7 +582,7 @@ end
|
|
515
582
|
|
516
583
|
Double.call({})
|
517
584
|
|
518
|
-
# The output
|
585
|
+
# The output will be the following exception:
|
519
586
|
# ArgumentError (missing keyword: :numbers)
|
520
587
|
```
|
521
588
|
|
data/lib/micro/case/result.rb
CHANGED
@@ -3,6 +3,18 @@
|
|
3
3
|
module Micro
|
4
4
|
class Case
|
5
5
|
class Result
|
6
|
+
class Data
|
7
|
+
attr_reader :value, :type
|
8
|
+
|
9
|
+
def initialize(value, type)
|
10
|
+
@value, @type = value, type
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_ary; [value, type]; end
|
14
|
+
end
|
15
|
+
|
16
|
+
private_constant :Data
|
17
|
+
|
6
18
|
attr_reader :value, :type
|
7
19
|
|
8
20
|
def __set__(is_success, value, type, use_case)
|
@@ -28,22 +40,26 @@ module Micro
|
|
28
40
|
raise Error::InvalidAccessToTheUseCaseObject
|
29
41
|
end
|
30
42
|
|
31
|
-
def on_success(
|
32
|
-
self.tap { yield(value) if success_type?(
|
43
|
+
def on_success(expected_type = nil)
|
44
|
+
self.tap { yield(value) if success_type?(expected_type) }
|
33
45
|
end
|
34
46
|
|
35
|
-
def on_failure(
|
36
|
-
self
|
47
|
+
def on_failure(expected_type = nil)
|
48
|
+
return self unless failure_type?(expected_type)
|
49
|
+
|
50
|
+
data = expected_type.nil? ? Data.new(value, type).tap(&:freeze) : value
|
51
|
+
|
52
|
+
self.tap { yield(data, @use_case) }
|
37
53
|
end
|
38
54
|
|
39
55
|
private
|
40
56
|
|
41
|
-
def success_type?(
|
42
|
-
success? && (
|
57
|
+
def success_type?(expected_type)
|
58
|
+
success? && (expected_type.nil? || expected_type == type)
|
43
59
|
end
|
44
60
|
|
45
|
-
def failure_type?(
|
46
|
-
failure? && (
|
61
|
+
def failure_type?(expected_type)
|
62
|
+
failure? && (expected_type.nil? || expected_type == type)
|
47
63
|
end
|
48
64
|
|
49
65
|
def is_a_use_case?(arg)
|
data/lib/micro/case/safe.rb
CHANGED
data/lib/micro/case/safe/flow.rb
CHANGED
@@ -10,10 +10,6 @@ module Micro
|
|
10
10
|
def base.flow_reducer; Reducer; end
|
11
11
|
end
|
12
12
|
|
13
|
-
def self.Flow(args)
|
14
|
-
Reducer.build(Array(args))
|
15
|
-
end
|
16
|
-
|
17
13
|
class Reducer < ::Micro::Case::Flow::Reducer
|
18
14
|
def call(arg = {})
|
19
15
|
@use_cases.reduce(initial_result(arg)) do |result, use_case|
|
@@ -43,6 +39,10 @@ module Micro
|
|
43
39
|
end
|
44
40
|
end
|
45
41
|
end
|
42
|
+
|
43
|
+
def self.Flow(args)
|
44
|
+
Flow::Reducer.build(Array(args))
|
45
|
+
end
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|
data/lib/micro/case/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: u-case
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.pre.
|
4
|
+
version: 2.0.0.pre.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rodrigo Serradura
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-11-
|
11
|
+
date: 2019-11-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: u-attributes
|