u-case 2.4.0 → 2.5.0
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 +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