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.
@@ -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.without(:strict_initialize)
20
+ include Micro::Attributes
20
21
 
21
- def self.call(options = Kind::Empty::HASH)
22
- new(options).__call__
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
- INVALID_INVOCATION_OF_THE_THE_METHOD =
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 INVALID_INVOCATION_OF_THE_THE_METHOD if use_case
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.attributes(self.attributes_data({}))
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
- call
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
- return __call_use_case_flow if __call_use_case_flow?
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 __call_use_case_flow?
212
+ def __call_the_use_case_flow?
168
213
  self.class.__flow_get__
169
214
  end
170
215
 
171
- def __call_use_case_flow
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 __result
198
- @__result ||= Result.new
242
+ def __get_result(is_success, value, type)
243
+ @__result.__set__(is_success, value, type, self)
199
244
  end
200
245
 
201
- def __get_result(is_success, value, type)
202
- __result.__set__(is_success, value, type, self)
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
- private_constant :MapFailureType
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
- (arg.is_a?(Class) && arg < Case) || arg.is_a?(Cases::Flow)
268
+ case?(arg) || arg.is_a?(Cases::Flow)
210
269
  end
211
270
  end
@@ -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
@@ -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}#then method")
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
- exception.is_a?(InvalidResult) || exception.is_a?(UnexpectedResult) || exception.is_a?(ArgumentError)
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
@@ -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
- INVALID_INVOCATION_OF_THE_THE_METHOD =
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
- @__transitions = @@transitions_enabled ? [] : Kind::Empty::ARRAY
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.slice_hash(data, keys)
72
+ Utils::Hashes.slice(data, keys)
51
73
  end
52
74
 
53
75
  def success?
54
- @success
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 INVALID_INVOCATION_OF_THE_THE_METHOD if use_case
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 INVALID_INVOCATION_OF_THE_THE_METHOD unless ::Micro.case_or_flow?(use_case)
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 INVALID_INVOCATION_OF_THE_THE_METHOD unless ::Micro.case_or_flow?(arg)
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.clone
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
- @success, @type, @use_case = is_success, type, use_case
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.symbolize_hash_keys(@use_case.attributes)
192
+ use_case_attributes = Utils::Hashes.symbolize_keys(@use_case.attributes)
153
193
 
154
- __update_accessible_attributes(use_case_attributes)
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.symbolize_hash_keys(arg)
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 __fetch_accumulated_data(opt = nil)
172
- __update_accessible_attributes(
173
- opt ? opt.merge(@__accumulated_data) : @__accumulated_data
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
- input = __fetch_accumulated_data
225
+ __update_accessible_attributes(@__accumulated_data)
179
226
 
180
- result = fn.arity.zero? ? fn.call : fn.call(input)
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
- input = __fetch_accumulated_data(attributes)
235
+ __update_accessible_attributes(attributes ? attributes.merge(@__accumulated_data) : @__accumulated_data)
189
236
 
190
- result = methd.arity.zero? ? methd.call : methd.call(**input)
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
- use_case_class = @use_case.class
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
- private_constant :FetchData, :INVALID_INVOCATION_OF_THE_THE_METHOD
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