u-case 2.4.0 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +58 -5
- data/lib/micro/case.rb +18 -6
- data/lib/micro/case/result.rb +17 -3
- data/lib/micro/case/utils.rb +2 -2
- data/lib/micro/case/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 349609a8d927ab7fd8edb1eb96141dff2f78f9957f31f2b88a8975239fc0de87
|
4
|
+
data.tar.gz: 4b6293023f49f28dd8283656a0d423f7d24abbfb7f010a90ba4d0dd17f577d22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0553e560e40f71d0e8284ebbbbf7a51326d8c9bc9de6ad0de76db526466a94f633e2b038563868a2c93234800b7bdacc30a99ba57f1624ce08463fdc012d165d
|
7
|
+
data.tar.gz: 2866ca201676827b2ffc82877b1615e7ecbb6fc6e993df17007c6446b348a850266faf162a0953954ee6adfb0d35a88c303bee385d2fb52a9c228f45513655db
|
data/README.md
CHANGED
@@ -36,10 +36,12 @@ The main project goals are:
|
|
36
36
|
- [Is it possible to compose a use case flow with other ones?](#is-it-possible-to-compose-a-use-case-flow-with-other-ones)
|
37
37
|
- [Is it possible a flow accumulates its input and merges each success result to use as the argument of the next use cases?](#is-it-possible-a-flow-accumulates-its-input-and-merges-each-success-result-to-use-as-the-argument-of-the-next-use-cases)
|
38
38
|
- [How to understand what is happening during a flow execution?](#how-to-understand-what-is-happening-during-a-flow-execution)
|
39
|
-
- [Micro::Case::Result#transitions schema
|
39
|
+
- [`Micro::Case::Result#transitions` schema](#microcaseresulttransitions-schema)
|
40
40
|
- [Is it possible to declare a flow which includes the use case itself?](#is-it-possible-to-declare-a-flow-which-includes-the-use-case-itself)
|
41
41
|
- [`Micro::Case::Strict` - What is a strict use case?](#microcasestrict---what-is-a-strict-use-case)
|
42
42
|
- [`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)
|
43
|
+
- [`Micro::Case::Safe::Flow`](#microcasesafeflow)
|
44
|
+
- [`Micro::Case::Result#on_exception`](#microcaseresulton_exception)
|
43
45
|
- [`u-case/with_activemodel_validation` - How to validate use case attributes?](#u-casewith_activemodel_validation---how-to-validate-use-case-attributes)
|
44
46
|
- [If I enabled the auto validation, is it possible to disable it only in specific use case classes?](#if-i-enabled-the-auto-validation-is-it-possible-to-disable-it-only-in-specific-use-case-classes)
|
45
47
|
- [Kind::Validator](#kindvalidator)
|
@@ -710,7 +712,7 @@ class Users::ValidatePassword < Micro::Case
|
|
710
712
|
end
|
711
713
|
```
|
712
714
|
|
713
|
-
As you can see the `Users::ValidatePassword` expects a user as its input. So, how it receives the user?
|
715
|
+
As you can see the `Users::ValidatePassword` expects a user as its input. So, how does it receives the user?
|
714
716
|
It receives the user from the `Users::Find` success result!
|
715
717
|
|
716
718
|
And this, is the power of use cases composition because the output
|
@@ -773,7 +775,9 @@ And look up the `accessible_attributes` property, because it shows whats attribu
|
|
773
775
|
|
774
776
|
> **Note:** The [`Micro::Case::Result#then`](#how-to-use-the-microcaseresultthen-method) increments the `Micro::Case::Result#transitions`.
|
775
777
|
|
776
|
-
|
778
|
+
PS: Use the `Micro::Case::Result.disable_transition_tracking` global feature toggle to disable this feature (use once) and increase the use cases' performance.
|
779
|
+
|
780
|
+
##### `Micro::Case::Result#transitions` schema
|
777
781
|
```ruby
|
778
782
|
[
|
779
783
|
{
|
@@ -906,7 +910,9 @@ end
|
|
906
910
|
# Examples: https://github.com/serradura/u-case/blob/5a85fc238b63811a32737493dc6c59965f92491d/test/micro/case/safe_test.rb#L95-L123
|
907
911
|
```
|
908
912
|
|
909
|
-
|
913
|
+
[⬆️ Back to Top](#table-of-contents-)
|
914
|
+
|
915
|
+
#### `Micro::Case::Safe::Flow`
|
910
916
|
|
911
917
|
As the safe use cases, safe flows can intercept an exception in any of its steps. These are the ways to define one:
|
912
918
|
|
@@ -940,7 +946,6 @@ module Users
|
|
940
946
|
end
|
941
947
|
end
|
942
948
|
|
943
|
-
|
944
949
|
# !------------------------------------------ ! #
|
945
950
|
# ! Deprecated: Micro::Case::Safe::Flow mixin ! #
|
946
951
|
# !-------------------------------------------! #
|
@@ -961,6 +966,54 @@ end
|
|
961
966
|
|
962
967
|
[⬆️ Back to Top](#table-of-contents-)
|
963
968
|
|
969
|
+
#### `Micro::Case::Result#on_exception`
|
970
|
+
|
971
|
+
In functional programming errors/exceptions are handled as regular data, the idea is to transform the output even when it happens an unexpected behavior. For many, [exceptions are very similar to the GOTO statement](https://softwareengineering.stackexchange.com/questions/189222/are-exceptions-as-control-flow-considered-a-serious-antipattern-if-so-why), jumping the application flow to paths which could be difficult to figure out how things work in a system.
|
972
|
+
|
973
|
+
To address this the `Micro::Case::Result` has a special hook `#on_exception` to helping you to handle the control flow in the case of exceptions.
|
974
|
+
|
975
|
+
> **Note** this feature will work better if you use it with a `Micro::Case::Safe` use case/flow.
|
976
|
+
|
977
|
+
How does it work?
|
978
|
+
|
979
|
+
```ruby
|
980
|
+
class Divide < Micro::Case::Safe
|
981
|
+
attributes :a, :b
|
982
|
+
|
983
|
+
def call!
|
984
|
+
Success(division: a / b)
|
985
|
+
end
|
986
|
+
end
|
987
|
+
|
988
|
+
Divide
|
989
|
+
.call(a: 2, b: 0)
|
990
|
+
.on_success { |result| puts result[:division] }
|
991
|
+
.on_exception(TypeError) { puts 'Please, use only numeric attributes.' }
|
992
|
+
.on_exception(ZeroDivisionError) { |_error| puts "Can't divide a number by 0." }
|
993
|
+
.on_exception { |_error, _use_case| puts 'Oh no, something went wrong!' }
|
994
|
+
|
995
|
+
# Output:
|
996
|
+
# -------
|
997
|
+
# Can't divide a number by 0
|
998
|
+
# Oh no, something went wrong!
|
999
|
+
|
1000
|
+
Divide.
|
1001
|
+
.call(a: 2, b: '2').
|
1002
|
+
.on_success { |result| puts result[:division] }
|
1003
|
+
.on_exception(TypeError) { puts 'Please, use only numeric attributes.' }
|
1004
|
+
.on_exception(ZeroDivisionError) { |_error| puts "Can't divide a number by 0." }
|
1005
|
+
.on_exception { |_error, _use_case| puts 'Oh no, something went wrong!' }
|
1006
|
+
|
1007
|
+
# Output:
|
1008
|
+
# -------
|
1009
|
+
# Please, use only numeric attributes.
|
1010
|
+
# Oh no, something went wrong!
|
1011
|
+
```
|
1012
|
+
|
1013
|
+
As you can see, this hook has the same behavior of `result.on_failure(:exception)`, but, the ideia here is to have a better communication in the code, making an explicit reference when some failure happened because of an exception.
|
1014
|
+
|
1015
|
+
[⬆️ Back to Top](#table-of-contents-)
|
1016
|
+
|
964
1017
|
### `u-case/with_activemodel_validation` - How to validate use case attributes?
|
965
1018
|
|
966
1019
|
**Requirement:**
|
data/lib/micro/case.rb
CHANGED
@@ -51,20 +51,32 @@ module Micro
|
|
51
51
|
__new__(result, arg).call
|
52
52
|
end
|
53
53
|
|
54
|
+
FLOW_STEP = 'Flow_Step'.freeze
|
55
|
+
|
56
|
+
private_constant :FLOW_STEP
|
57
|
+
|
54
58
|
def self.__call!
|
55
|
-
return const_get(
|
59
|
+
return const_get(FLOW_STEP) if const_defined?(FLOW_STEP, false)
|
56
60
|
|
57
|
-
|
58
|
-
private def __call
|
59
|
-
__call_use_case
|
60
|
-
end
|
61
|
-
end)
|
61
|
+
class_eval("class #{FLOW_STEP} < #{self.name}; private def __call; __call_use_case; end; end")
|
62
62
|
end
|
63
63
|
|
64
64
|
def self.call!
|
65
65
|
self
|
66
66
|
end
|
67
67
|
|
68
|
+
def self.inherited(subclass)
|
69
|
+
subclass.attributes(self.attributes_data({}))
|
70
|
+
subclass.extend ::Micro::Attributes.const_get('Macros::ForSubclasses'.freeze)
|
71
|
+
|
72
|
+
if self.send(:__flow_use_cases) && !subclass.name.to_s.end_with?(FLOW_STEP)
|
73
|
+
raise "Wooo, you can't do this! Inherits from a use case which has an inner flow violates "\
|
74
|
+
"one of the project principles: Solve complex business logic, by allowing the composition of use cases. "\
|
75
|
+
"Instead of doing this, declare a new class/constant with the steps needed.\n\n"\
|
76
|
+
"Related issue: https://github.com/serradura/u-case/issues/19\n"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
68
80
|
def self.__flow_reducer
|
69
81
|
Flow::Reducer
|
70
82
|
end
|
data/lib/micro/case/result.rb
CHANGED
@@ -58,7 +58,9 @@ module Micro
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def on_success(expected_type = nil)
|
61
|
-
|
61
|
+
yield(value) if success_type?(expected_type)
|
62
|
+
|
63
|
+
self
|
62
64
|
end
|
63
65
|
|
64
66
|
def on_failure(expected_type = nil)
|
@@ -66,7 +68,19 @@ module Micro
|
|
66
68
|
|
67
69
|
data = expected_type.nil? ? Data.new(value, type).tap(&:freeze) : value
|
68
70
|
|
69
|
-
|
71
|
+
yield(data, @use_case)
|
72
|
+
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def on_exception(expected_exception = nil)
|
77
|
+
return self unless failure_type?(:exception)
|
78
|
+
|
79
|
+
if !expected_exception || (Kind.is(Exception, expected_exception) && value.is_a?(expected_exception))
|
80
|
+
yield(value, @use_case)
|
81
|
+
end
|
82
|
+
|
83
|
+
self
|
70
84
|
end
|
71
85
|
|
72
86
|
def then(arg = nil, &block)
|
@@ -124,7 +138,7 @@ module Micro
|
|
124
138
|
|
125
139
|
def __set_transition__
|
126
140
|
use_case_class = @use_case.class
|
127
|
-
use_case_attributes = Utils.
|
141
|
+
use_case_attributes = Utils.symbolize_hash_keys(@use_case.attributes)
|
128
142
|
|
129
143
|
__set_transitions_accessible_attributes__!(use_case_attributes.keys)
|
130
144
|
|
data/lib/micro/case/utils.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
module Micro
|
4
4
|
class Case
|
5
5
|
module Utils
|
6
|
-
def self.
|
7
|
-
if Kind
|
6
|
+
def self.symbolize_hash_keys(hash)
|
7
|
+
if Kind::Of::Hash(hash).respond_to?(:transform_keys)
|
8
8
|
hash.transform_keys { |key| key.to_sym rescue key }
|
9
9
|
else
|
10
10
|
hash.each_with_object({}) do |(k, v), memo|
|
data/lib/micro/case/version.rb
CHANGED