u-case 3.0.0.rc1 → 3.0.0.rc6
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/Gemfile +3 -2
- data/README.md +283 -266
- data/README.pt-BR.md +1390 -0
- data/lib/micro/case.rb +60 -33
- data/lib/micro/case/config.rb +23 -0
- data/lib/micro/case/error.rb +18 -17
- data/lib/micro/case/result.rb +96 -50
- data/lib/micro/case/safe.rb +2 -2
- data/lib/micro/case/utils.rb +7 -0
- data/lib/micro/case/version.rb +1 -1
- data/lib/micro/case/with_activemodel_validation.rb +3 -1
- data/lib/micro/cases/flow.rb +24 -40
- data/lib/micro/cases/safe/flow.rb +2 -2
- data/lib/u-case/with_activemodel_validation.rb +0 -2
- data/u-case.gemspec +3 -3
- metadata +14 -7
- data/lib/u-case/with_validation.rb +0 -6
data/lib/micro/case.rb
CHANGED
@@ -12,27 +12,51 @@ module Micro
|
|
12
12
|
require 'micro/case/error'
|
13
13
|
require 'micro/case/safe'
|
14
14
|
require 'micro/case/strict'
|
15
|
+
require 'micro/case/config'
|
15
16
|
|
16
17
|
require 'micro/cases'
|
17
18
|
|
18
19
|
include Micro::Attributes.without(:strict_initialize)
|
19
20
|
|
20
|
-
def self.call(options =
|
21
|
-
new(options).
|
21
|
+
def self.call(options = Kind::Empty::HASH)
|
22
|
+
new(options).__call__
|
22
23
|
end
|
23
24
|
|
24
|
-
def self.
|
25
|
-
|
25
|
+
def self.then(use_case = nil, &block)
|
26
|
+
can_yield_self = respond_to?(:yield_self)
|
27
|
+
|
28
|
+
if block
|
29
|
+
raise Error::InvalidInvocationOfTheThenMethod if use_case
|
30
|
+
raise NotImplementedError if !can_yield_self
|
31
|
+
|
32
|
+
yield_self(&block)
|
33
|
+
else
|
34
|
+
return yield_self if !use_case && can_yield_self
|
35
|
+
|
36
|
+
self.call.then(use_case)
|
37
|
+
end
|
26
38
|
end
|
27
39
|
|
28
|
-
def self.
|
29
|
-
|
40
|
+
def self.to_proc
|
41
|
+
Proc.new { |arg| call(arg) }
|
30
42
|
end
|
31
43
|
|
32
44
|
def self.flow(*args)
|
33
45
|
@__flow_use_cases = args
|
34
46
|
end
|
35
47
|
|
48
|
+
class << self
|
49
|
+
alias __call__ call
|
50
|
+
|
51
|
+
def config
|
52
|
+
yield(Config.instance)
|
53
|
+
end
|
54
|
+
|
55
|
+
def call!
|
56
|
+
self
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
36
60
|
def self.inherited(subclass)
|
37
61
|
subclass.attributes(self.attributes_data({}))
|
38
62
|
subclass.extend ::Micro::Attributes.const_get('Macros::ForSubclasses'.freeze)
|
@@ -46,41 +70,36 @@ module Micro
|
|
46
70
|
end
|
47
71
|
|
48
72
|
def self.__new__(result, arg)
|
49
|
-
|
73
|
+
input = result.__set_transitions_accessible_attributes__(arg)
|
74
|
+
|
75
|
+
instance = new(input)
|
50
76
|
instance.__set_result__(result)
|
51
77
|
instance
|
52
78
|
end
|
53
79
|
|
54
|
-
def self.
|
55
|
-
input =
|
56
|
-
arg.is_a?(Hash) ? result.__set_transitions_accessible_attributes__(arg) : arg
|
57
|
-
|
58
|
-
__new__(result, input).call
|
59
|
-
end
|
60
|
-
|
61
|
-
def self.__flow_builder
|
80
|
+
def self.__flow_builder__
|
62
81
|
Cases::Flow
|
63
82
|
end
|
64
83
|
|
65
|
-
def self.
|
84
|
+
def self.__flow_get__
|
66
85
|
return @__flow if defined?(@__flow)
|
67
86
|
end
|
68
87
|
|
69
88
|
private_class_method def self.__flow_set(args)
|
70
|
-
return if
|
89
|
+
return if __flow_get__
|
71
90
|
|
72
|
-
def self.use_cases;
|
91
|
+
def self.use_cases; __flow_get__.use_cases; end
|
73
92
|
|
74
93
|
self.class_eval('def use_cases; self.class.use_cases; end')
|
75
94
|
|
76
|
-
@__flow =
|
95
|
+
@__flow = __flow_builder__.build(args)
|
77
96
|
end
|
78
97
|
|
79
|
-
FLOW_STEP = '
|
98
|
+
FLOW_STEP = 'Self'.freeze
|
80
99
|
|
81
100
|
private_constant :FLOW_STEP
|
82
101
|
|
83
|
-
def self.
|
102
|
+
def self.__call__!
|
84
103
|
return const_get(FLOW_STEP) if const_defined?(FLOW_STEP, false)
|
85
104
|
|
86
105
|
class_eval("class #{FLOW_STEP} < #{self.name}; private def __call; __call_use_case; end; end")
|
@@ -92,11 +111,11 @@ module Micro
|
|
92
111
|
|
93
112
|
private_class_method def self.__flow_use_cases_get
|
94
113
|
Array(__flow_use_cases)
|
95
|
-
.map { |use_case| use_case == self ? self.
|
114
|
+
.map { |use_case| use_case == self ? self.__call__! : use_case }
|
96
115
|
end
|
97
116
|
|
98
|
-
def self.
|
99
|
-
__flow_set(__flow_use_cases_get) if !
|
117
|
+
def self.__flow_set__!
|
118
|
+
__flow_set(__flow_use_cases_get) if !__flow_get__ && __flow_use_cases
|
100
119
|
end
|
101
120
|
|
102
121
|
def initialize(input)
|
@@ -107,8 +126,8 @@ module Micro
|
|
107
126
|
raise NotImplementedError
|
108
127
|
end
|
109
128
|
|
110
|
-
def
|
111
|
-
__call
|
129
|
+
def __call__
|
130
|
+
__call!
|
112
131
|
end
|
113
132
|
|
114
133
|
def __set_result__(result)
|
@@ -120,15 +139,19 @@ module Micro
|
|
120
139
|
|
121
140
|
private
|
122
141
|
|
142
|
+
# This method was reserved for a new feature
|
143
|
+
def call
|
144
|
+
end
|
145
|
+
|
123
146
|
def __setup_use_case(input)
|
124
|
-
self.class.
|
147
|
+
self.class.__flow_set__!
|
125
148
|
|
126
149
|
@__input = input
|
127
150
|
|
128
151
|
self.attributes = input
|
129
152
|
end
|
130
153
|
|
131
|
-
def __call
|
154
|
+
def __call!
|
132
155
|
return __call_use_case_flow if __call_use_case_flow?
|
133
156
|
|
134
157
|
__call_use_case
|
@@ -139,15 +162,15 @@ module Micro
|
|
139
162
|
|
140
163
|
return result if result.is_a?(Result)
|
141
164
|
|
142
|
-
raise Error::UnexpectedResult.new(self.class)
|
165
|
+
raise Error::UnexpectedResult.new("#{self.class.name}#call!")
|
143
166
|
end
|
144
167
|
|
145
168
|
def __call_use_case_flow?
|
146
|
-
self.class.
|
169
|
+
self.class.__flow_get__
|
147
170
|
end
|
148
171
|
|
149
172
|
def __call_use_case_flow
|
150
|
-
self.class.
|
173
|
+
self.class.__flow_get__.call(@__input)
|
151
174
|
end
|
152
175
|
|
153
176
|
def Success(type = :ok, result: nil)
|
@@ -172,14 +195,18 @@ module Micro
|
|
172
195
|
__get_result(false, value, type)
|
173
196
|
end
|
174
197
|
|
175
|
-
def
|
198
|
+
def __result
|
176
199
|
@__result ||= Result.new
|
177
200
|
end
|
178
201
|
|
179
202
|
def __get_result(is_success, value, type)
|
180
|
-
|
203
|
+
__result.__set__(is_success, value, type, self)
|
181
204
|
end
|
182
205
|
|
183
206
|
private_constant :MapFailureType
|
184
207
|
end
|
208
|
+
|
209
|
+
def self.case_or_flow?(arg)
|
210
|
+
(arg.is_a?(Class) && arg < Case) || arg.is_a?(Cases::Flow)
|
211
|
+
end
|
185
212
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
|
5
|
+
module Micro
|
6
|
+
class Case
|
7
|
+
class Config
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
def enable_activemodel_validation=(value)
|
11
|
+
return unless Kind::Of::Boolean(value)
|
12
|
+
|
13
|
+
require 'micro/case/with_activemodel_validation'
|
14
|
+
end
|
15
|
+
|
16
|
+
def enable_transitions=(value)
|
17
|
+
Micro::Case::Result.class_variable_set(
|
18
|
+
:@@transition_tracking_enabled, Kind::Of::Boolean(value)
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/micro/case/error.rb
CHANGED
@@ -4,9 +4,11 @@ module Micro
|
|
4
4
|
class Case
|
5
5
|
module Error
|
6
6
|
class UnexpectedResult < TypeError
|
7
|
-
MESSAGE = '
|
7
|
+
MESSAGE = 'must return an instance of Micro::Case::Result'.freeze
|
8
8
|
|
9
|
-
def initialize(
|
9
|
+
def initialize(context)
|
10
|
+
super("#{context} #{MESSAGE}")
|
11
|
+
end
|
10
12
|
end
|
11
13
|
|
12
14
|
class ResultIsAlreadyDefined < ArgumentError
|
@@ -17,18 +19,21 @@ module Micro
|
|
17
19
|
def initialize; super('type must be a Symbol'.freeze); end
|
18
20
|
end
|
19
21
|
|
20
|
-
class
|
21
|
-
|
22
|
+
class InvalidResult < TypeError
|
23
|
+
def initialize(is_success, type, use_case)
|
24
|
+
base =
|
25
|
+
"The result returned from #{use_case.class.name}#call! must be a Hash."
|
22
26
|
|
23
|
-
|
24
|
-
def initialize(object)
|
25
|
-
super("Success(result: #{object.inspect}) must be a Hash or Symbol")
|
26
|
-
end
|
27
|
-
end
|
27
|
+
result = is_success ? 'Success'.freeze : 'Failure'.freeze
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
example =
|
30
|
+
if type === :ok || type === :error || type === :exception
|
31
|
+
"#{result}(result: { key: 'value' })"
|
32
|
+
else
|
33
|
+
"#{result}(:#{type}, result: { key: 'value' })"
|
34
|
+
end
|
35
|
+
|
36
|
+
super("#{base}\n\nExample:\n #{example}")
|
32
37
|
end
|
33
38
|
end
|
34
39
|
|
@@ -44,12 +49,8 @@ module Micro
|
|
44
49
|
def initialize; super('Invalid invocation of the Micro::Case::Result#then method'); end
|
45
50
|
end
|
46
51
|
|
47
|
-
class InvalidAccessToTheUseCaseObject < StandardError
|
48
|
-
def initialize; super('only a failure result can access its use case object'.freeze); end
|
49
|
-
end
|
50
|
-
|
51
52
|
def self.by_wrong_usage?(exception)
|
52
|
-
exception.is_a?(
|
53
|
+
exception.is_a?(InvalidResult) || exception.is_a?(UnexpectedResult) || exception.is_a?(ArgumentError)
|
53
54
|
end
|
54
55
|
end
|
55
56
|
end
|
data/lib/micro/case/result.rb
CHANGED
@@ -7,19 +7,16 @@ module Micro
|
|
7
7
|
class Result
|
8
8
|
Kind::Types.add(self)
|
9
9
|
|
10
|
-
@@
|
10
|
+
@@transition_tracking_enabled = true
|
11
11
|
|
12
|
-
|
13
|
-
@@transition_tracking_disabled = true
|
14
|
-
end
|
15
|
-
|
16
|
-
attr_reader :type, :data
|
12
|
+
attr_reader :type, :data, :use_case
|
17
13
|
|
18
|
-
|
14
|
+
alias value data
|
19
15
|
|
20
16
|
def initialize
|
21
|
-
@
|
22
|
-
@
|
17
|
+
@__transitions = []
|
18
|
+
@__transitions_accumulated_data = {}
|
19
|
+
@__transitions_accessible_attributes = {}
|
23
20
|
end
|
24
21
|
|
25
22
|
def to_ary
|
@@ -34,6 +31,18 @@ module Micro
|
|
34
31
|
data.values_at(*keys)
|
35
32
|
end
|
36
33
|
|
34
|
+
def key?(key)
|
35
|
+
data.key?(key)
|
36
|
+
end
|
37
|
+
|
38
|
+
def value?(value)
|
39
|
+
data.value?(value)
|
40
|
+
end
|
41
|
+
|
42
|
+
def slice(*keys)
|
43
|
+
Utils.slice_hash(data, keys)
|
44
|
+
end
|
45
|
+
|
37
46
|
def success?
|
38
47
|
@success
|
39
48
|
end
|
@@ -42,20 +51,18 @@ module Micro
|
|
42
51
|
!success?
|
43
52
|
end
|
44
53
|
|
45
|
-
def
|
46
|
-
return
|
54
|
+
def on_success(expected_type = nil)
|
55
|
+
return self unless __success_type?(expected_type)
|
47
56
|
|
48
|
-
|
49
|
-
end
|
57
|
+
hook_data = expected_type.nil? ? self : data
|
50
58
|
|
51
|
-
|
52
|
-
yield(data) if success_type?(expected_type)
|
59
|
+
yield(hook_data, @use_case)
|
53
60
|
|
54
61
|
self
|
55
62
|
end
|
56
63
|
|
57
64
|
def on_failure(expected_type = nil)
|
58
|
-
return self unless
|
65
|
+
return self unless __failure_type?(expected_type)
|
59
66
|
|
60
67
|
hook_data = expected_type.nil? ? self : data
|
61
68
|
|
@@ -65,7 +72,7 @@ module Micro
|
|
65
72
|
end
|
66
73
|
|
67
74
|
def on_exception(expected_exception = nil)
|
68
|
-
return self unless
|
75
|
+
return self unless __failure_type?(:exception)
|
69
76
|
|
70
77
|
if !expected_exception || (Kind.is(Exception, expected_exception) && data.fetch(:exception).is_a?(expected_exception))
|
71
78
|
yield(data, @use_case)
|
@@ -74,97 +81,136 @@ module Micro
|
|
74
81
|
self
|
75
82
|
end
|
76
83
|
|
77
|
-
def then(
|
84
|
+
def then(use_case = nil, attributes = nil, &block)
|
78
85
|
can_yield_self = respond_to?(:yield_self)
|
79
86
|
|
80
87
|
if block
|
81
|
-
raise Error::InvalidInvocationOfTheThenMethod if
|
88
|
+
raise Error::InvalidInvocationOfTheThenMethod if use_case
|
82
89
|
raise NotImplementedError if !can_yield_self
|
83
90
|
|
84
91
|
yield_self(&block)
|
85
92
|
else
|
86
|
-
return yield_self if !
|
93
|
+
return yield_self if !use_case && can_yield_self
|
94
|
+
return failure? ? self : __call_proc(use_case, 'then(-> {})'.freeze) if use_case.is_a?(Proc)
|
95
|
+
return failure? ? self : __call_method(use_case, attributes) if use_case.is_a?(Method)
|
87
96
|
|
88
|
-
raise Error::InvalidInvocationOfTheThenMethod
|
97
|
+
raise Error::InvalidInvocationOfTheThenMethod unless ::Micro.case_or_flow?(use_case)
|
89
98
|
|
90
99
|
return self if failure?
|
91
100
|
|
92
101
|
input = attributes.is_a?(Hash) ? self.data.merge(attributes) : self.data
|
93
102
|
|
94
|
-
|
103
|
+
if use_case.is_a?(::Micro::Cases::Flow)
|
104
|
+
use_case.call!(input: input, result: self)
|
105
|
+
else
|
106
|
+
use_case.__new__(self, input).__call__
|
107
|
+
end
|
95
108
|
end
|
96
109
|
end
|
97
110
|
|
111
|
+
def |(arg)
|
112
|
+
return self if failure?
|
113
|
+
|
114
|
+
return __call_proc(arg, '| -> {}'.freeze) if arg.is_a?(Proc)
|
115
|
+
return __call_method(arg) if arg.is_a?(Method)
|
116
|
+
|
117
|
+
raise Error::InvalidInvocationOfTheThenMethod unless ::Micro.case_or_flow?(arg)
|
118
|
+
|
119
|
+
failure? ? self : arg.__new__(self, data).__call__
|
120
|
+
end
|
121
|
+
|
98
122
|
def transitions
|
99
|
-
@
|
123
|
+
@__transitions.clone
|
100
124
|
end
|
101
125
|
|
102
|
-
FetchData = -> (data
|
126
|
+
FetchData = -> (data) do
|
103
127
|
return data if data.is_a?(Hash)
|
104
128
|
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
129
|
|
109
|
-
|
130
|
+
{ exception: data } if data.is_a?(Exception)
|
110
131
|
end
|
111
132
|
|
112
133
|
def __set__(is_success, data, type, use_case)
|
113
134
|
raise Error::InvalidResultType unless type.is_a?(Symbol)
|
114
|
-
raise Error::InvalidUseCase
|
135
|
+
raise Error::InvalidUseCase unless use_case.is_a?(::Micro::Case)
|
115
136
|
|
116
137
|
@success, @type, @use_case = is_success, type, use_case
|
117
138
|
|
118
|
-
@data = FetchData.call(data
|
139
|
+
@data = FetchData.call(data)
|
140
|
+
|
141
|
+
raise Micro::Case::Error::InvalidResult.new(is_success, type, use_case) unless @data
|
119
142
|
|
120
|
-
|
143
|
+
if @@transition_tracking_enabled
|
144
|
+
@__transitions_accumulated_data.merge!(@data)
|
145
|
+
|
146
|
+
__set_transition
|
147
|
+
end
|
121
148
|
|
122
149
|
self
|
123
150
|
end
|
124
151
|
|
125
|
-
def __set_transitions_accessible_attributes__(
|
126
|
-
return
|
152
|
+
def __set_transitions_accessible_attributes__(arg)
|
153
|
+
return arg unless @@transition_tracking_enabled
|
127
154
|
|
128
|
-
|
155
|
+
if arg.is_a?(Hash)
|
156
|
+
attributes = Utils.symbolize_hash_keys(arg)
|
157
|
+
|
158
|
+
__update_transitions_accessible_attributes(attributes)
|
159
|
+
end
|
129
160
|
end
|
130
161
|
|
131
162
|
private
|
132
163
|
|
133
|
-
def
|
134
|
-
|
164
|
+
def __call_with_accumulated_data(fn, opt = nil)
|
165
|
+
input =
|
166
|
+
__update_transitions_accessible_attributes(
|
167
|
+
opt ? opt.merge(@__transitions_accumulated_data) : @__transitions_accumulated_data
|
168
|
+
)
|
169
|
+
|
170
|
+
fn.arity.zero? ? fn.call : fn.call(input)
|
135
171
|
end
|
136
172
|
|
137
|
-
def
|
138
|
-
|
173
|
+
def __call_proc(fn, expected)
|
174
|
+
result = __call_with_accumulated_data(fn)
|
175
|
+
|
176
|
+
return self if result === self
|
177
|
+
|
178
|
+
raise Error::UnexpectedResult.new("#{Result.name}##{expected}")
|
139
179
|
end
|
140
180
|
|
141
|
-
def
|
142
|
-
|
181
|
+
def __call_method(methd, attributes = nil)
|
182
|
+
result = __call_with_accumulated_data(methd, attributes)
|
183
|
+
|
184
|
+
return self if result === self
|
185
|
+
|
186
|
+
raise Error::UnexpectedResult.new("#{use_case.class.name}#method(:#{methd.name})")
|
143
187
|
end
|
144
188
|
|
145
|
-
def
|
146
|
-
|
189
|
+
def __success_type?(expected_type)
|
190
|
+
success? && (expected_type.nil? || expected_type == type)
|
191
|
+
end
|
147
192
|
|
148
|
-
|
193
|
+
def __failure_type?(expected_type)
|
194
|
+
failure? && (expected_type.nil? || expected_type == type)
|
149
195
|
end
|
150
196
|
|
151
|
-
def
|
152
|
-
@
|
153
|
-
@
|
197
|
+
def __update_transitions_accessible_attributes(attributes)
|
198
|
+
@__transitions_accessible_attributes.merge!(attributes)
|
199
|
+
@__transitions_accessible_attributes
|
154
200
|
end
|
155
201
|
|
156
|
-
def
|
202
|
+
def __set_transition
|
157
203
|
use_case_class = @use_case.class
|
158
204
|
use_case_attributes = Utils.symbolize_hash_keys(@use_case.attributes)
|
159
205
|
|
160
|
-
|
206
|
+
__update_transitions_accessible_attributes(use_case_attributes)
|
161
207
|
|
162
208
|
result = @success ? :success : :failure
|
163
209
|
|
164
|
-
@
|
210
|
+
@__transitions << {
|
165
211
|
use_case: { class: use_case_class, attributes: use_case_attributes },
|
166
212
|
result => { type: @type, result: data },
|
167
|
-
accessible_attributes: @
|
213
|
+
accessible_attributes: @__transitions_accessible_attributes.keys
|
168
214
|
}
|
169
215
|
end
|
170
216
|
|