u-case 2.0.0.pre.2 → 2.0.0.pre.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|