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
data/lib/micro/case.rb
CHANGED
@@ -3,38 +3,46 @@
|
|
3
3
|
require 'kind'
|
4
4
|
require 'micro/attributes'
|
5
5
|
|
6
|
+
require 'micro/case/version'
|
7
|
+
|
6
8
|
module Micro
|
7
9
|
class Case
|
8
|
-
require 'micro/case/version'
|
9
10
|
require 'micro/case/utils'
|
10
11
|
require 'micro/case/result'
|
11
12
|
require 'micro/case/error'
|
12
13
|
require 'micro/case/safe'
|
13
14
|
require 'micro/case/strict'
|
14
|
-
|
15
|
-
require 'micro/
|
16
|
-
require 'micro/case/safe/flow'
|
15
|
+
|
16
|
+
require 'micro/cases'
|
17
17
|
|
18
18
|
include Micro::Attributes.without(:strict_initialize)
|
19
19
|
|
20
|
+
def self.call(options = {})
|
21
|
+
new(options).call
|
22
|
+
end
|
23
|
+
|
20
24
|
def self.to_proc
|
21
25
|
Proc.new { |arg| call(arg) }
|
22
26
|
end
|
23
27
|
|
24
|
-
def self.
|
25
|
-
|
28
|
+
def self.call!
|
29
|
+
self
|
26
30
|
end
|
27
31
|
|
28
|
-
def self
|
29
|
-
|
32
|
+
def self.flow(*args)
|
33
|
+
@__flow_use_cases = args
|
30
34
|
end
|
31
35
|
|
32
|
-
def self
|
33
|
-
|
34
|
-
|
36
|
+
def self.inherited(subclass)
|
37
|
+
subclass.attributes(self.attributes_data({}))
|
38
|
+
subclass.extend ::Micro::Attributes.const_get('Macros::ForSubclasses'.freeze)
|
35
39
|
|
36
|
-
|
37
|
-
|
40
|
+
if self.send(:__flow_use_cases) && !subclass.name.to_s.end_with?(FLOW_STEP)
|
41
|
+
raise "Wooo, you can't do this! Inherits from a use case which has an inner flow violates "\
|
42
|
+
"one of the project principles: Solve complex business logic, by allowing the composition of use cases. "\
|
43
|
+
"Instead of doing this, declare a new class/constant with the steps needed.\n\n"\
|
44
|
+
"Related issue: https://github.com/serradura/u-case/issues/19\n"
|
45
|
+
end
|
38
46
|
end
|
39
47
|
|
40
48
|
def self.__new__(result, arg)
|
@@ -50,38 +58,32 @@ module Micro
|
|
50
58
|
__new__(result, input).call
|
51
59
|
end
|
52
60
|
|
53
|
-
|
61
|
+
def self.__flow_builder
|
62
|
+
Cases::Flow
|
63
|
+
end
|
54
64
|
|
55
|
-
|
65
|
+
def self.__flow_get
|
66
|
+
return @__flow if defined?(@__flow)
|
67
|
+
end
|
56
68
|
|
57
|
-
def self.
|
58
|
-
return
|
69
|
+
private_class_method def self.__flow_set(args)
|
70
|
+
return if __flow_get
|
59
71
|
|
60
|
-
|
61
|
-
end
|
72
|
+
def self.use_cases; __flow_get.use_cases; end
|
62
73
|
|
63
|
-
|
64
|
-
|
74
|
+
self.class_eval('def use_cases; self.class.use_cases; end')
|
75
|
+
|
76
|
+
@__flow = __flow_builder.build(args)
|
65
77
|
end
|
66
78
|
|
67
|
-
|
68
|
-
subclass.attributes(self.attributes_data({}))
|
69
|
-
subclass.extend ::Micro::Attributes.const_get('Macros::ForSubclasses'.freeze)
|
79
|
+
FLOW_STEP = 'Flow_Step'.freeze
|
70
80
|
|
71
|
-
|
72
|
-
raise "Wooo, you can't do this! Inherits from a use case which has an inner flow violates "\
|
73
|
-
"one of the project principles: Solve complex business logic, by allowing the composition of use cases. "\
|
74
|
-
"Instead of doing this, declare a new class/constant with the steps needed.\n\n"\
|
75
|
-
"Related issue: https://github.com/serradura/u-case/issues/19\n"
|
76
|
-
end
|
77
|
-
end
|
81
|
+
private_constant :FLOW_STEP
|
78
82
|
|
79
|
-
def self.
|
80
|
-
|
81
|
-
end
|
83
|
+
def self.__call!
|
84
|
+
return const_get(FLOW_STEP) if const_defined?(FLOW_STEP, false)
|
82
85
|
|
83
|
-
|
84
|
-
return @__flow if defined?(@__flow)
|
86
|
+
class_eval("class #{FLOW_STEP} < #{self.name}; private def __call; __call_use_case; end; end")
|
85
87
|
end
|
86
88
|
|
87
89
|
private_class_method def self.__flow_use_cases
|
@@ -93,28 +95,10 @@ module Micro
|
|
93
95
|
.map { |use_case| use_case == self ? self.__call! : use_case }
|
94
96
|
end
|
95
97
|
|
96
|
-
private_class_method def self.__flow_use_cases_set(args)
|
97
|
-
@__flow_use_cases = args
|
98
|
-
end
|
99
|
-
|
100
|
-
private_class_method def self.__flow_set(args)
|
101
|
-
return if __flow_get
|
102
|
-
|
103
|
-
def self.use_cases; __flow_get.use_cases; end
|
104
|
-
|
105
|
-
self.class_eval('def use_cases; self.class.use_cases; end')
|
106
|
-
|
107
|
-
@__flow = __flow_reducer.build(args)
|
108
|
-
end
|
109
|
-
|
110
98
|
def self.__flow_set!
|
111
99
|
__flow_set(__flow_use_cases_get) if !__flow_get && __flow_use_cases
|
112
100
|
end
|
113
101
|
|
114
|
-
def self.flow(*args)
|
115
|
-
__flow_use_cases_set(args)
|
116
|
-
end
|
117
|
-
|
118
102
|
def initialize(input)
|
119
103
|
__setup_use_case(input)
|
120
104
|
end
|
@@ -166,33 +150,36 @@ module Micro
|
|
166
150
|
self.class.__flow_get.call(@__input)
|
167
151
|
end
|
168
152
|
|
169
|
-
def Success(
|
170
|
-
value
|
153
|
+
def Success(type = :ok, result: nil)
|
154
|
+
value = result || type
|
171
155
|
|
172
|
-
|
156
|
+
__get_result(true, value, type)
|
173
157
|
end
|
174
158
|
|
175
|
-
|
176
|
-
|
177
|
-
|
159
|
+
MapFailureType = -> (value, type) do
|
160
|
+
return type if type != :error
|
161
|
+
return value if value.is_a?(Symbol)
|
162
|
+
return :exception if value.is_a?(Exception)
|
178
163
|
|
179
|
-
|
164
|
+
type
|
180
165
|
end
|
181
166
|
|
182
|
-
def
|
183
|
-
|
184
|
-
return arg if arg.is_a?(Symbol)
|
185
|
-
return :exception if arg.is_a?(Exception)
|
167
|
+
def Failure(type = :error, result: nil)
|
168
|
+
value = result || type
|
186
169
|
|
187
|
-
type
|
170
|
+
type = MapFailureType.call(value, type)
|
171
|
+
|
172
|
+
__get_result(false, value, type)
|
188
173
|
end
|
189
174
|
|
190
|
-
def
|
175
|
+
def __result__
|
191
176
|
@__result ||= Result.new
|
192
177
|
end
|
193
178
|
|
194
|
-
def
|
195
|
-
|
179
|
+
def __get_result(is_success, value, type)
|
180
|
+
__result__.__set__(is_success, value, type, self)
|
196
181
|
end
|
182
|
+
|
183
|
+
private_constant :MapFailureType
|
197
184
|
end
|
198
185
|
end
|
data/lib/micro/case/error.rb
CHANGED
@@ -17,6 +17,21 @@ module Micro
|
|
17
17
|
def initialize; super('type must be a Symbol'.freeze); end
|
18
18
|
end
|
19
19
|
|
20
|
+
class InvalidResultData < TypeError
|
21
|
+
end
|
22
|
+
|
23
|
+
class InvalidSuccessResult < InvalidResultData
|
24
|
+
def initialize(object)
|
25
|
+
super("Success(result: #{object.inspect}) must be a Hash or Symbol")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class InvalidFailureResult < InvalidResultData
|
30
|
+
def initialize(object)
|
31
|
+
super("Failure(result: #{object.inspect}) must be a Hash, Symbol or an Exception")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
20
35
|
class InvalidResultInstance < ArgumentError
|
21
36
|
def initialize; super('argument must be an instance of Micro::Case::Result'.freeze); end
|
22
37
|
end
|
@@ -25,26 +40,16 @@ module Micro
|
|
25
40
|
def initialize; super('use case must be a kind or an instance of Micro::Case'.freeze); end
|
26
41
|
end
|
27
42
|
|
28
|
-
class InvalidUseCases < ArgumentError
|
29
|
-
def initialize; super('argument must be a collection of `Micro::Case` classes'.freeze); end
|
30
|
-
end
|
31
|
-
|
32
43
|
class InvalidInvocationOfTheThenMethod < StandardError
|
33
44
|
def initialize; super('Invalid invocation of the Micro::Case::Result#then method'); end
|
34
45
|
end
|
35
46
|
|
36
|
-
class UndefinedFlow < ArgumentError
|
37
|
-
def initialize; super("This class hasn't declared its flow. Please, use the `flow()` macro to define one.".freeze); end
|
38
|
-
end
|
39
|
-
|
40
47
|
class InvalidAccessToTheUseCaseObject < StandardError
|
41
48
|
def initialize; super('only a failure result can access its use case object'.freeze); end
|
42
49
|
end
|
43
50
|
|
44
|
-
|
45
|
-
|
46
|
-
exception.is_a?(Error::UnexpectedResult) || exception.is_a?(ArgumentError)
|
47
|
-
end
|
51
|
+
def self.by_wrong_usage?(exception)
|
52
|
+
exception.is_a?(InvalidResultData) || exception.is_a?(Error::UnexpectedResult) || exception.is_a?(ArgumentError)
|
48
53
|
end
|
49
54
|
end
|
50
55
|
end
|
data/lib/micro/case/result.rb
CHANGED
@@ -13,34 +13,25 @@ module Micro
|
|
13
13
|
@@transition_tracking_disabled = true
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
attr_reader :value, :type
|
16
|
+
attr_reader :type, :data
|
18
17
|
|
19
|
-
|
20
|
-
@value, @type = value, type
|
21
|
-
end
|
22
|
-
|
23
|
-
def to_ary; [value, type]; end
|
24
|
-
end
|
25
|
-
|
26
|
-
private_constant :Data
|
27
|
-
|
28
|
-
attr_reader :value, :type
|
18
|
+
alias_method :value, :data
|
29
19
|
|
30
20
|
def initialize
|
31
21
|
@__transitions__ = []
|
32
22
|
@__transitions_accessible_attributes__ = {}
|
33
23
|
end
|
34
24
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
@success, @value, @type, @use_case = is_success, value, type, use_case
|
25
|
+
def to_ary
|
26
|
+
[data, type]
|
27
|
+
end
|
40
28
|
|
41
|
-
|
29
|
+
def [](key)
|
30
|
+
data[key]
|
31
|
+
end
|
42
32
|
|
43
|
-
|
33
|
+
def values_at(*keys)
|
34
|
+
data.values_at(*keys)
|
44
35
|
end
|
45
36
|
|
46
37
|
def success?
|
@@ -58,7 +49,7 @@ module Micro
|
|
58
49
|
end
|
59
50
|
|
60
51
|
def on_success(expected_type = nil)
|
61
|
-
yield(
|
52
|
+
yield(data) if success_type?(expected_type)
|
62
53
|
|
63
54
|
self
|
64
55
|
end
|
@@ -66,9 +57,9 @@ module Micro
|
|
66
57
|
def on_failure(expected_type = nil)
|
67
58
|
return self unless failure_type?(expected_type)
|
68
59
|
|
69
|
-
|
60
|
+
hook_data = expected_type.nil? ? self : data
|
70
61
|
|
71
|
-
yield(
|
62
|
+
yield(hook_data, @use_case)
|
72
63
|
|
73
64
|
self
|
74
65
|
end
|
@@ -76,8 +67,8 @@ module Micro
|
|
76
67
|
def on_exception(expected_exception = nil)
|
77
68
|
return self unless failure_type?(:exception)
|
78
69
|
|
79
|
-
if !expected_exception || (Kind.is(Exception, expected_exception) &&
|
80
|
-
yield(
|
70
|
+
if !expected_exception || (Kind.is(Exception, expected_exception) && data.fetch(:exception).is_a?(expected_exception))
|
71
|
+
yield(data, @use_case)
|
81
72
|
end
|
82
73
|
|
83
74
|
self
|
@@ -98,7 +89,7 @@ module Micro
|
|
98
89
|
|
99
90
|
return self if failure?
|
100
91
|
|
101
|
-
input = attributes.is_a?(Hash) ? self.
|
92
|
+
input = attributes.is_a?(Hash) ? self.data.merge(attributes) : self.data
|
102
93
|
|
103
94
|
arg.__call_and_set_transition__(self, input)
|
104
95
|
end
|
@@ -108,6 +99,29 @@ module Micro
|
|
108
99
|
@__transitions__.clone
|
109
100
|
end
|
110
101
|
|
102
|
+
FetchData = -> (data, is_success) do
|
103
|
+
return data if data.is_a?(Hash)
|
104
|
+
return { data => true } if data.is_a?(Symbol)
|
105
|
+
return { exception: data } if data.is_a?(Exception)
|
106
|
+
|
107
|
+
err = is_success ? :InvalidSuccessResult : :InvalidFailureResult
|
108
|
+
|
109
|
+
raise Micro::Case::Error.const_get(err), data
|
110
|
+
end
|
111
|
+
|
112
|
+
def __set__(is_success, data, type, use_case)
|
113
|
+
raise Error::InvalidResultType unless type.is_a?(Symbol)
|
114
|
+
raise Error::InvalidUseCase if !is_a_use_case?(use_case)
|
115
|
+
|
116
|
+
@success, @type, @use_case = is_success, type, use_case
|
117
|
+
|
118
|
+
@data = FetchData.call(data, is_success)
|
119
|
+
|
120
|
+
__set_transition__ unless @@transition_tracking_disabled
|
121
|
+
|
122
|
+
self
|
123
|
+
end
|
124
|
+
|
111
125
|
def __set_transitions_accessible_attributes__(attributes_data)
|
112
126
|
return attributes_data if @@transition_tracking_disabled
|
113
127
|
|
@@ -116,17 +130,6 @@ module Micro
|
|
116
130
|
|
117
131
|
private
|
118
132
|
|
119
|
-
def __set_transitions_accessible_attributes__!(attributes_data)
|
120
|
-
attributes = Utils.symbolize_hash_keys(attributes_data)
|
121
|
-
|
122
|
-
__update_transitions_accessible_attributes__(attributes)
|
123
|
-
end
|
124
|
-
|
125
|
-
def __update_transitions_accessible_attributes__(attributes)
|
126
|
-
@__transitions_accessible_attributes__.merge!(attributes)
|
127
|
-
@__transitions_accessible_attributes__
|
128
|
-
end
|
129
|
-
|
130
133
|
def success_type?(expected_type)
|
131
134
|
success? && (expected_type.nil? || expected_type == type)
|
132
135
|
end
|
@@ -139,6 +142,17 @@ module Micro
|
|
139
142
|
(arg.is_a?(Class) && arg < ::Micro::Case) || arg.is_a?(::Micro::Case)
|
140
143
|
end
|
141
144
|
|
145
|
+
def __set_transitions_accessible_attributes__!(attributes_data)
|
146
|
+
attributes = Utils.symbolize_hash_keys(attributes_data)
|
147
|
+
|
148
|
+
__update_transitions_accessible_attributes__(attributes)
|
149
|
+
end
|
150
|
+
|
151
|
+
def __update_transitions_accessible_attributes__(attributes)
|
152
|
+
@__transitions_accessible_attributes__.merge!(attributes)
|
153
|
+
@__transitions_accessible_attributes__
|
154
|
+
end
|
155
|
+
|
142
156
|
def __set_transition__
|
143
157
|
use_case_class = @use_case.class
|
144
158
|
use_case_attributes = Utils.symbolize_hash_keys(@use_case.attributes)
|
@@ -149,10 +163,12 @@ module Micro
|
|
149
163
|
|
150
164
|
@__transitions__ << {
|
151
165
|
use_case: { class: use_case_class, attributes: use_case_attributes },
|
152
|
-
result => { type: @type,
|
166
|
+
result => { type: @type, result: data },
|
153
167
|
accessible_attributes: @__transitions_accessible_attributes__.keys
|
154
168
|
}
|
155
169
|
end
|
170
|
+
|
171
|
+
private_constant :FetchData
|
156
172
|
end
|
157
173
|
end
|
158
174
|
end
|
data/lib/micro/case/safe.rb
CHANGED
@@ -3,12 +3,16 @@
|
|
3
3
|
module Micro
|
4
4
|
class Case
|
5
5
|
class Safe < ::Micro::Case
|
6
|
+
def self.__flow_builder
|
7
|
+
Cases::Safe::Flow
|
8
|
+
end
|
9
|
+
|
6
10
|
def call
|
7
11
|
__call
|
8
12
|
rescue => exception
|
9
|
-
raise exception if Error
|
13
|
+
raise exception if Error.by_wrong_usage?(exception)
|
10
14
|
|
11
|
-
Failure(exception)
|
15
|
+
Failure(result: exception)
|
12
16
|
end
|
13
17
|
end
|
14
18
|
end
|
data/lib/micro/case/version.rb
CHANGED
data/lib/micro/cases.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'micro/cases/flow'
|
4
|
+
require 'micro/cases/safe/flow'
|
5
|
+
|
6
|
+
module Micro
|
7
|
+
module Cases
|
8
|
+
def self.flow(args)
|
9
|
+
Flow.build(args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.safe_flow(args)
|
13
|
+
Safe::Flow.build(args)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|