u-case 3.0.0 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|