u-case 3.0.0 → 4.2.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/.travis.sh +25 -10
- data/Gemfile +28 -9
- data/README.md +185 -142
- data/README.pt-BR.md +206 -164
- data/lib/micro/case.rb +78 -19
- data/lib/micro/case/config.rb +1 -1
- data/lib/micro/case/error.rb +5 -2
- data/lib/micro/case/result.rb +72 -38
- data/lib/micro/case/result/transitions.rb +17 -0
- data/lib/micro/case/result/wrapper.rb +45 -0
- data/lib/micro/case/safe.rb +2 -2
- data/lib/micro/case/strict.rb +2 -2
- data/lib/micro/case/utils.rb +20 -10
- data/lib/micro/case/version.rb +1 -1
- data/lib/micro/cases.rb +7 -0
- data/lib/micro/cases/error.rb +13 -0
- data/lib/micro/cases/flow.rb +30 -15
- data/lib/micro/cases/map.rb +39 -0
- data/lib/micro/cases/safe/flow.rb +2 -2
- data/lib/micro/cases/utils.rb +21 -0
- data/u-case.gemspec +3 -3
- metadata +13 -6
data/lib/micro/case.rb
CHANGED
|
@@ -7,6 +7,7 @@ require 'micro/case/version'
|
|
|
7
7
|
|
|
8
8
|
module Micro
|
|
9
9
|
class Case
|
|
10
|
+
require 'micro/cases/utils'
|
|
10
11
|
require 'micro/case/utils'
|
|
11
12
|
require 'micro/case/error'
|
|
12
13
|
require 'micro/case/result'
|
|
@@ -16,26 +17,32 @@ module Micro
|
|
|
16
17
|
|
|
17
18
|
require 'micro/cases'
|
|
18
19
|
|
|
19
|
-
include Micro::Attributes
|
|
20
|
+
include Micro::Attributes
|
|
20
21
|
|
|
21
|
-
def self.call(
|
|
22
|
-
new
|
|
22
|
+
def self.call(input = Kind::Empty::HASH)
|
|
23
|
+
result = __new__(Result.new, input).__call__
|
|
24
|
+
|
|
25
|
+
return result unless block_given?
|
|
26
|
+
|
|
27
|
+
yield Result::Wrapper.new(result)
|
|
23
28
|
end
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
Error::InvalidInvocationOfTheThenMethod.new(self.name)
|
|
30
|
+
INVALID_INVOCATION_OF_THE_THEN_METHOD =
|
|
31
|
+
Error::InvalidInvocationOfTheThenMethod.new("#{self.name}.")
|
|
27
32
|
|
|
28
33
|
def self.then(use_case = nil, &block)
|
|
29
34
|
can_yield_self = respond_to?(:yield_self)
|
|
30
35
|
|
|
31
36
|
if block
|
|
32
|
-
raise
|
|
37
|
+
raise INVALID_INVOCATION_OF_THE_THEN_METHOD if use_case
|
|
33
38
|
raise NotImplementedError if !can_yield_self
|
|
34
39
|
|
|
35
40
|
yield_self(&block)
|
|
36
41
|
else
|
|
37
42
|
return yield_self if !use_case && can_yield_self
|
|
38
43
|
|
|
44
|
+
raise INVALID_INVOCATION_OF_THE_THEN_METHOD unless ::Micro.case_or_flow?(use_case)
|
|
45
|
+
|
|
39
46
|
self.call.then(use_case)
|
|
40
47
|
end
|
|
41
48
|
end
|
|
@@ -45,7 +52,7 @@ module Micro
|
|
|
45
52
|
end
|
|
46
53
|
|
|
47
54
|
def self.flow(*args)
|
|
48
|
-
@__flow_use_cases = args
|
|
55
|
+
@__flow_use_cases = Cases::Utils.map_use_cases(args)
|
|
49
56
|
end
|
|
50
57
|
|
|
51
58
|
class << self
|
|
@@ -61,7 +68,8 @@ module Micro
|
|
|
61
68
|
end
|
|
62
69
|
|
|
63
70
|
def self.inherited(subclass)
|
|
64
|
-
subclass.
|
|
71
|
+
subclass.__attributes_set_after_inherit__(self.__attributes_data__)
|
|
72
|
+
|
|
65
73
|
subclass.extend ::Micro::Attributes.const_get('Macros::ForSubclasses'.freeze)
|
|
66
74
|
|
|
67
75
|
if self.send(:__flow_use_cases) && !subclass.name.to_s.end_with?(FLOW_STEP)
|
|
@@ -78,6 +86,8 @@ module Micro
|
|
|
78
86
|
new(input).__set_result__(result)
|
|
79
87
|
end
|
|
80
88
|
|
|
89
|
+
private_class_method :new
|
|
90
|
+
|
|
81
91
|
def self.__flow_builder__
|
|
82
92
|
Cases::Flow
|
|
83
93
|
end
|
|
@@ -119,6 +129,28 @@ module Micro
|
|
|
119
129
|
__flow_set(__flow_use_cases_get) if !__flow_get__ && __flow_use_cases
|
|
120
130
|
end
|
|
121
131
|
|
|
132
|
+
InspectKey = :__inspect_key__ # :nodoc:
|
|
133
|
+
|
|
134
|
+
def self.inspect
|
|
135
|
+
ids = (Thread.current[InspectKey] ||= [])
|
|
136
|
+
|
|
137
|
+
if ids.include?(object_id)
|
|
138
|
+
return sprintf('#<%s: {...}>', self)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
begin
|
|
142
|
+
ids << object_id
|
|
143
|
+
|
|
144
|
+
if __flow_use_cases
|
|
145
|
+
return '<%s (%s) use_cases=%s>' % [self, __flow_builder__, @__flow_use_cases]
|
|
146
|
+
else
|
|
147
|
+
return '<%s (%s) attributes=%s>' % [self, self.superclass, attributes]
|
|
148
|
+
end
|
|
149
|
+
ensure
|
|
150
|
+
ids.pop
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
122
154
|
def initialize(input)
|
|
123
155
|
__setup_use_case(input)
|
|
124
156
|
end
|
|
@@ -128,7 +160,7 @@ module Micro
|
|
|
128
160
|
end
|
|
129
161
|
|
|
130
162
|
def __call__
|
|
131
|
-
|
|
163
|
+
__call_the_use_case_or_its_flow
|
|
132
164
|
end
|
|
133
165
|
|
|
134
166
|
def __set_result__(result)
|
|
@@ -142,8 +174,21 @@ module Micro
|
|
|
142
174
|
|
|
143
175
|
private
|
|
144
176
|
|
|
145
|
-
def call
|
|
146
|
-
|
|
177
|
+
def call(use_case, defaults = Kind::Empty::HASH)
|
|
178
|
+
raise Error::InvalidUseCase unless ::Micro.case_or_flow?(use_case)
|
|
179
|
+
|
|
180
|
+
input =
|
|
181
|
+
defaults.empty? ? attributes : attributes.merge(Utils::Hashes.stringify_keys(defaults))
|
|
182
|
+
|
|
183
|
+
use_case.__new__(@__result, input).__call__
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def apply(name)
|
|
187
|
+
method(name)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def __call_the_use_case_or_its_flow
|
|
191
|
+
return __call_the_use_case_flow if __call_the_use_case_flow?
|
|
147
192
|
|
|
148
193
|
__call_use_case
|
|
149
194
|
end
|
|
@@ -164,11 +209,11 @@ module Micro
|
|
|
164
209
|
raise Error::UnexpectedResult.new("#{self.class.name}#call!")
|
|
165
210
|
end
|
|
166
211
|
|
|
167
|
-
def
|
|
212
|
+
def __call_the_use_case_flow?
|
|
168
213
|
self.class.__flow_get__
|
|
169
214
|
end
|
|
170
215
|
|
|
171
|
-
def
|
|
216
|
+
def __call_the_use_case_flow
|
|
172
217
|
self.class.__flow_get__.call(@__input)
|
|
173
218
|
end
|
|
174
219
|
|
|
@@ -194,18 +239,32 @@ module Micro
|
|
|
194
239
|
__get_result(false, value, type)
|
|
195
240
|
end
|
|
196
241
|
|
|
197
|
-
def
|
|
198
|
-
@__result
|
|
242
|
+
def __get_result(is_success, value, type)
|
|
243
|
+
@__result.__set__(is_success, value, type, self)
|
|
199
244
|
end
|
|
200
245
|
|
|
201
|
-
def
|
|
202
|
-
|
|
246
|
+
def transaction(adapter = :activerecord)
|
|
247
|
+
raise NotImplementedError unless adapter == :activerecord
|
|
248
|
+
|
|
249
|
+
result = nil
|
|
250
|
+
|
|
251
|
+
ActiveRecord::Base.transaction do
|
|
252
|
+
result = yield
|
|
253
|
+
|
|
254
|
+
raise ActiveRecord::Rollback if result.failure?
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
result
|
|
203
258
|
end
|
|
204
259
|
|
|
205
|
-
|
|
260
|
+
private_constant :MapFailureType, :INVALID_INVOCATION_OF_THE_THEN_METHOD
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def self.case?(arg)
|
|
264
|
+
arg.is_a?(Class) && arg < Case
|
|
206
265
|
end
|
|
207
266
|
|
|
208
267
|
def self.case_or_flow?(arg)
|
|
209
|
-
|
|
268
|
+
case?(arg) || arg.is_a?(Cases::Flow)
|
|
210
269
|
end
|
|
211
270
|
end
|
data/lib/micro/case/config.rb
CHANGED
|
@@ -26,7 +26,7 @@ module Micro
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def activemodel_validation_errors_failure
|
|
29
|
-
@activemodel_validation_errors_failure if defined?(@activemodel_validation_errors_failure)
|
|
29
|
+
return @activemodel_validation_errors_failure if defined?(@activemodel_validation_errors_failure)
|
|
30
30
|
|
|
31
31
|
@activemodel_validation_errors_failure = :invalid_attributes
|
|
32
32
|
end
|
data/lib/micro/case/error.rb
CHANGED
|
@@ -47,12 +47,15 @@ module Micro
|
|
|
47
47
|
|
|
48
48
|
class InvalidInvocationOfTheThenMethod < StandardError
|
|
49
49
|
def initialize(class_name)
|
|
50
|
-
super("Invalid invocation of the #{class_name}
|
|
50
|
+
super("Invalid invocation of the #{class_name}then method")
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def self.by_wrong_usage?(exception)
|
|
55
|
-
|
|
55
|
+
case exception
|
|
56
|
+
when Kind::Error, ArgumentError, InvalidResult, UnexpectedResult then true
|
|
57
|
+
else false
|
|
58
|
+
end
|
|
56
59
|
end
|
|
57
60
|
end
|
|
58
61
|
end
|
data/lib/micro/case/result.rb
CHANGED
|
@@ -5,10 +5,13 @@ require 'set'
|
|
|
5
5
|
module Micro
|
|
6
6
|
class Case
|
|
7
7
|
class Result
|
|
8
|
+
require 'micro/case/result/wrapper'
|
|
9
|
+
require 'micro/case/result/transitions'
|
|
10
|
+
|
|
8
11
|
Kind::Types.add(self)
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
Error::InvalidInvocationOfTheThenMethod.new(self.name)
|
|
13
|
+
INVALID_INVOCATION_OF_THE_THEN_METHOD =
|
|
14
|
+
Error::InvalidInvocationOfTheThenMethod.new("#{self.name}#")
|
|
12
15
|
|
|
13
16
|
@@transitions_enabled = true
|
|
14
17
|
|
|
@@ -20,16 +23,35 @@ module Micro
|
|
|
20
23
|
|
|
21
24
|
alias value data
|
|
22
25
|
|
|
23
|
-
def initialize
|
|
24
|
-
|
|
26
|
+
def initialize(transitions_mapper = Transitions::MapEverything)
|
|
27
|
+
enable_transitions = @@transitions_enabled
|
|
28
|
+
|
|
29
|
+
@__is_unknown = true
|
|
25
30
|
@__accumulated_data = {}
|
|
31
|
+
@__tracked_use_cases = Set.new
|
|
26
32
|
@__accessible_attributes = {}
|
|
33
|
+
|
|
34
|
+
@__transitions = enable_transitions ? [] : Kind::Empty::ARRAY
|
|
35
|
+
@__transitions_mapper = transitions_mapper if enable_transitions
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def inspect
|
|
39
|
+
pretty_type = @__success ? 'Success' : 'Failure'
|
|
40
|
+
|
|
41
|
+
instance_info = '%s (%s) type=:%s data=%s' % [pretty_type, self.class, @type, data]
|
|
42
|
+
transitions_info = ' transitions=%d' % [@__transitions.size] if Micro::Case::Result.transitions_enabled?
|
|
43
|
+
|
|
44
|
+
"<#{instance_info}#{transitions_info}>"
|
|
27
45
|
end
|
|
28
46
|
|
|
29
47
|
def to_ary
|
|
30
48
|
[data, type]
|
|
31
49
|
end
|
|
32
50
|
|
|
51
|
+
def to_sym
|
|
52
|
+
@__success ? :success : :failure
|
|
53
|
+
end
|
|
54
|
+
|
|
33
55
|
def [](key)
|
|
34
56
|
data[key]
|
|
35
57
|
end
|
|
@@ -47,20 +69,29 @@ module Micro
|
|
|
47
69
|
end
|
|
48
70
|
|
|
49
71
|
def slice(*keys)
|
|
50
|
-
Utils.
|
|
72
|
+
Utils::Hashes.slice(data, keys)
|
|
51
73
|
end
|
|
52
74
|
|
|
53
75
|
def success?
|
|
54
|
-
@
|
|
76
|
+
@__success
|
|
55
77
|
end
|
|
56
78
|
|
|
57
79
|
def failure?
|
|
58
80
|
!success?
|
|
59
81
|
end
|
|
60
82
|
|
|
83
|
+
def unknown?
|
|
84
|
+
@__is_unknown
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def accessible_attributes
|
|
88
|
+
@__accessible_attributes.keys
|
|
89
|
+
end
|
|
90
|
+
|
|
61
91
|
def on_success(expected_type = nil)
|
|
62
92
|
return self unless __success_type?(expected_type)
|
|
63
93
|
|
|
94
|
+
@__is_unknown = false
|
|
64
95
|
hook_data = expected_type.nil? ? self : data
|
|
65
96
|
|
|
66
97
|
yield(hook_data, @use_case)
|
|
@@ -71,6 +102,7 @@ module Micro
|
|
|
71
102
|
def on_failure(expected_type = nil)
|
|
72
103
|
return self unless __failure_type?(expected_type)
|
|
73
104
|
|
|
105
|
+
@__is_unknown = false
|
|
74
106
|
hook_data = expected_type.nil? ? self : data
|
|
75
107
|
|
|
76
108
|
yield(hook_data, @use_case)
|
|
@@ -88,11 +120,19 @@ module Micro
|
|
|
88
120
|
self
|
|
89
121
|
end
|
|
90
122
|
|
|
123
|
+
def on_unknown
|
|
124
|
+
return self unless unknown?
|
|
125
|
+
|
|
126
|
+
yield(self, @use_case)
|
|
127
|
+
|
|
128
|
+
self
|
|
129
|
+
end
|
|
130
|
+
|
|
91
131
|
def then(use_case = nil, attributes = nil, &block)
|
|
92
132
|
can_yield_self = respond_to?(:yield_self)
|
|
93
133
|
|
|
94
134
|
if block
|
|
95
|
-
raise
|
|
135
|
+
raise INVALID_INVOCATION_OF_THE_THEN_METHOD if use_case
|
|
96
136
|
raise NotImplementedError if !can_yield_self
|
|
97
137
|
|
|
98
138
|
yield_self(&block)
|
|
@@ -101,7 +141,7 @@ module Micro
|
|
|
101
141
|
return failure? ? self : __call_proc(use_case, 'then(-> {})'.freeze) if use_case.is_a?(Proc)
|
|
102
142
|
return failure? ? self : __call_method(use_case, attributes) if use_case.is_a?(Method)
|
|
103
143
|
|
|
104
|
-
raise
|
|
144
|
+
raise INVALID_INVOCATION_OF_THE_THEN_METHOD unless ::Micro.case_or_flow?(use_case)
|
|
105
145
|
|
|
106
146
|
return self if failure?
|
|
107
147
|
|
|
@@ -121,13 +161,13 @@ module Micro
|
|
|
121
161
|
return __call_proc(arg, '| -> {}'.freeze) if arg.is_a?(Proc)
|
|
122
162
|
return __call_method(arg) if arg.is_a?(Method)
|
|
123
163
|
|
|
124
|
-
raise
|
|
164
|
+
raise INVALID_INVOCATION_OF_THE_THEN_METHOD unless ::Micro.case_or_flow?(arg)
|
|
125
165
|
|
|
126
166
|
failure? ? self : arg.__new__(self, data).__call__
|
|
127
167
|
end
|
|
128
168
|
|
|
129
169
|
def transitions
|
|
130
|
-
@__transitions.
|
|
170
|
+
@__transitions.dup
|
|
131
171
|
end
|
|
132
172
|
|
|
133
173
|
FetchData = -> (data) do
|
|
@@ -141,17 +181,21 @@ module Micro
|
|
|
141
181
|
raise Error::InvalidResultType unless type.is_a?(Symbol)
|
|
142
182
|
raise Error::InvalidUseCase unless use_case.is_a?(::Micro::Case)
|
|
143
183
|
|
|
144
|
-
@
|
|
184
|
+
@__success, @type, @use_case = is_success, type, use_case
|
|
145
185
|
|
|
146
|
-
@data = FetchData.call(data)
|
|
186
|
+
@data = FetchData.call(data).freeze
|
|
147
187
|
|
|
148
188
|
raise Micro::Case::Error::InvalidResult.new(is_success, type, use_case) unless @data
|
|
149
189
|
|
|
150
190
|
@__accumulated_data.merge!(@data)
|
|
151
191
|
|
|
152
|
-
use_case_attributes = Utils.
|
|
192
|
+
use_case_attributes = Utils::Hashes.symbolize_keys(@use_case.attributes)
|
|
153
193
|
|
|
154
|
-
|
|
194
|
+
unless @__tracked_use_cases.member?(use_case_class = @use_case.class)
|
|
195
|
+
@__tracked_use_cases.add(use_case_class)
|
|
196
|
+
|
|
197
|
+
__update_accessible_attributes(use_case_attributes)
|
|
198
|
+
end
|
|
155
199
|
|
|
156
200
|
__set_transition(use_case_attributes) unless @__transitions.frozen?
|
|
157
201
|
|
|
@@ -161,23 +205,26 @@ module Micro
|
|
|
161
205
|
def __set_accessible_attributes__(arg)
|
|
162
206
|
return arg unless arg.is_a?(Hash)
|
|
163
207
|
|
|
164
|
-
attributes = Utils.
|
|
208
|
+
attributes = Utils::Hashes.symbolize_keys(arg)
|
|
165
209
|
|
|
166
210
|
__update_accessible_attributes(attributes)
|
|
211
|
+
__fetch_accessible_attributes
|
|
167
212
|
end
|
|
168
213
|
|
|
169
214
|
private
|
|
170
215
|
|
|
171
|
-
def
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
216
|
+
def __update_accessible_attributes(attributes)
|
|
217
|
+
@__accessible_attributes.merge!(attributes)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def __fetch_accessible_attributes
|
|
221
|
+
@__accessible_attributes.dup
|
|
175
222
|
end
|
|
176
223
|
|
|
177
224
|
def __call_proc(fn, expected)
|
|
178
|
-
|
|
225
|
+
__update_accessible_attributes(@__accumulated_data)
|
|
179
226
|
|
|
180
|
-
result = fn.arity.zero? ? fn.call : fn.call(
|
|
227
|
+
result = fn.arity.zero? ? fn.call : fn.call(__fetch_accessible_attributes)
|
|
181
228
|
|
|
182
229
|
return self if result === self
|
|
183
230
|
|
|
@@ -185,9 +232,9 @@ module Micro
|
|
|
185
232
|
end
|
|
186
233
|
|
|
187
234
|
def __call_method(methd, attributes = nil)
|
|
188
|
-
|
|
235
|
+
__update_accessible_attributes(attributes ? attributes.merge(@__accumulated_data) : @__accumulated_data)
|
|
189
236
|
|
|
190
|
-
result = methd.arity.zero? ? methd.call : methd.call(**
|
|
237
|
+
result = methd.arity.zero? ? methd.call : methd.call(**__fetch_accessible_attributes)
|
|
191
238
|
|
|
192
239
|
return self if result === self
|
|
193
240
|
|
|
@@ -202,24 +249,11 @@ module Micro
|
|
|
202
249
|
failure? && (expected_type.nil? || expected_type == type)
|
|
203
250
|
end
|
|
204
251
|
|
|
205
|
-
def __update_accessible_attributes(attributes)
|
|
206
|
-
@__accessible_attributes.merge!(attributes)
|
|
207
|
-
@__accessible_attributes.dup
|
|
208
|
-
end
|
|
209
|
-
|
|
210
252
|
def __set_transition(use_case_attributes)
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
result = @success ? :success : :failure
|
|
214
|
-
|
|
215
|
-
@__transitions << {
|
|
216
|
-
use_case: { class: use_case_class, attributes: use_case_attributes },
|
|
217
|
-
result => { type: @type, result: data },
|
|
218
|
-
accessible_attributes: @__accessible_attributes.keys
|
|
219
|
-
}
|
|
253
|
+
@__transitions << @__transitions_mapper.call(self, use_case_attributes)
|
|
220
254
|
end
|
|
221
255
|
|
|
222
|
-
|
|
256
|
+
private_constant :FetchData, :INVALID_INVOCATION_OF_THE_THEN_METHOD
|
|
223
257
|
end
|
|
224
258
|
end
|
|
225
259
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Micro
|
|
4
|
+
class Case
|
|
5
|
+
class Result
|
|
6
|
+
class Transitions
|
|
7
|
+
MapEverything = -> (result, use_case_attributes) do
|
|
8
|
+
{
|
|
9
|
+
use_case: { class: result.use_case.class, attributes: use_case_attributes },
|
|
10
|
+
result.to_sym => { type: result.type, result: result.data },
|
|
11
|
+
accessible_attributes: result.accessible_attributes
|
|
12
|
+
}
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|