u-case 3.0.0.rc8 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,32 +7,42 @@ 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
- require 'micro/case/result'
12
12
  require 'micro/case/error'
13
+ require 'micro/case/result'
13
14
  require 'micro/case/safe'
14
15
  require 'micro/case/strict'
15
16
  require 'micro/case/config'
16
17
 
17
18
  require 'micro/cases'
18
19
 
19
- include Micro::Attributes.without(:strict_initialize)
20
+ include Micro::Attributes
21
+
22
+ def self.call(input = Kind::Empty::HASH)
23
+ result = __new__(Result.new, input).__call__
24
+
25
+ return result unless block_given?
20
26
 
21
- def self.call(options = Kind::Empty::HASH)
22
- new(options).__call__
27
+ yield Result::Wrapper.new(result)
23
28
  end
24
29
 
30
+ INVALID_INVOCATION_OF_THE_THEN_METHOD =
31
+ Error::InvalidInvocationOfTheThenMethod.new("#{self.name}.")
32
+
25
33
  def self.then(use_case = nil, &block)
26
34
  can_yield_self = respond_to?(:yield_self)
27
35
 
28
36
  if block
29
- raise Error::InvalidInvocationOfTheThenMethod if use_case
37
+ raise INVALID_INVOCATION_OF_THE_THEN_METHOD if use_case
30
38
  raise NotImplementedError if !can_yield_self
31
39
 
32
40
  yield_self(&block)
33
41
  else
34
42
  return yield_self if !use_case && can_yield_self
35
43
 
44
+ raise INVALID_INVOCATION_OF_THE_THEN_METHOD unless ::Micro.case_or_flow?(use_case)
45
+
36
46
  self.call.then(use_case)
37
47
  end
38
48
  end
@@ -42,7 +52,7 @@ module Micro
42
52
  end
43
53
 
44
54
  def self.flow(*args)
45
- @__flow_use_cases = args
55
+ @__flow_use_cases = Cases::Utils.map_use_cases(args)
46
56
  end
47
57
 
48
58
  class << self
@@ -58,7 +68,8 @@ module Micro
58
68
  end
59
69
 
60
70
  def self.inherited(subclass)
61
- subclass.attributes(self.attributes_data({}))
71
+ subclass.__attributes_set_after_inherit__(self.__attributes_data__)
72
+
62
73
  subclass.extend ::Micro::Attributes.const_get('Macros::ForSubclasses'.freeze)
63
74
 
64
75
  if self.send(:__flow_use_cases) && !subclass.name.to_s.end_with?(FLOW_STEP)
@@ -70,11 +81,13 @@ module Micro
70
81
  end
71
82
 
72
83
  def self.__new__(result, arg)
73
- input = result.__set_transitions_accessible_attributes__(arg)
84
+ input = result.__set_accessible_attributes__(arg)
74
85
 
75
86
  new(input).__set_result__(result)
76
87
  end
77
88
 
89
+ private_class_method :new
90
+
78
91
  def self.__flow_builder__
79
92
  Cases::Flow
80
93
  end
@@ -116,6 +129,14 @@ module Micro
116
129
  __flow_set(__flow_use_cases_get) if !__flow_get__ && __flow_use_cases
117
130
  end
118
131
 
132
+ def self.inspect
133
+ if __flow_use_cases
134
+ '<%s (%s) use_cases=%s>' % [self, __flow_builder__, @__flow_use_cases]
135
+ else
136
+ '<%s (%s) attributes=%s>' % [self, self.superclass, attributes]
137
+ end
138
+ end
139
+
119
140
  def initialize(input)
120
141
  __setup_use_case(input)
121
142
  end
@@ -125,7 +146,7 @@ module Micro
125
146
  end
126
147
 
127
148
  def __call__
128
- __call!
149
+ __call_the_use_case_or_its_flow
129
150
  end
130
151
 
131
152
  def __set_result__(result)
@@ -139,8 +160,23 @@ module Micro
139
160
 
140
161
  private
141
162
 
142
- # This method was reserved for a new feature
143
- def call
163
+ def call(use_case, defaults = Kind::Empty::HASH)
164
+ raise Error::InvalidUseCase unless ::Micro.case_or_flow?(use_case)
165
+
166
+ input =
167
+ defaults.empty? ? attributes : attributes.merge(Utils::Hashes.stringify_keys(defaults))
168
+
169
+ use_case.__new__(@__result, input).__call__
170
+ end
171
+
172
+ def apply(name)
173
+ method(name)
174
+ end
175
+
176
+ def __call_the_use_case_or_its_flow
177
+ return __call_the_use_case_flow if __call_the_use_case_flow?
178
+
179
+ __call_use_case
144
180
  end
145
181
 
146
182
  def __setup_use_case(input)
@@ -151,12 +187,6 @@ module Micro
151
187
  self.attributes = input
152
188
  end
153
189
 
154
- def __call!
155
- return __call_use_case_flow if __call_use_case_flow?
156
-
157
- __call_use_case
158
- end
159
-
160
190
  def __call_use_case
161
191
  result = call!
162
192
 
@@ -165,11 +195,11 @@ module Micro
165
195
  raise Error::UnexpectedResult.new("#{self.class.name}#call!")
166
196
  end
167
197
 
168
- def __call_use_case_flow?
198
+ def __call_the_use_case_flow?
169
199
  self.class.__flow_get__
170
200
  end
171
201
 
172
- def __call_use_case_flow
202
+ def __call_the_use_case_flow
173
203
  self.class.__flow_get__.call(@__input)
174
204
  end
175
205
 
@@ -195,18 +225,18 @@ module Micro
195
225
  __get_result(false, value, type)
196
226
  end
197
227
 
198
- def __result
199
- @__result ||= Result.new
200
- end
201
-
202
228
  def __get_result(is_success, value, type)
203
- __result.__set__(is_success, value, type, self)
229
+ @__result.__set__(is_success, value, type, self)
204
230
  end
205
231
 
206
- private_constant :MapFailureType
232
+ private_constant :MapFailureType, :INVALID_INVOCATION_OF_THE_THEN_METHOD
233
+ end
234
+
235
+ def self.case?(arg)
236
+ arg.is_a?(Class) && arg < Case
207
237
  end
208
238
 
209
239
  def self.case_or_flow?(arg)
210
- (arg.is_a?(Class) && arg < Case) || arg.is_a?(Cases::Flow)
240
+ case?(arg) || arg.is_a?(Cases::Flow)
211
241
  end
212
242
  end
@@ -7,16 +7,28 @@ module Micro
7
7
  class Config
8
8
  include Singleton
9
9
 
10
+ def enable_transitions=(value)
11
+ Micro::Case::Result.class_variable_set(
12
+ :@@transitions_enabled, Kind::Of::Boolean(value)
13
+ )
14
+ end
15
+
10
16
  def enable_activemodel_validation=(value)
11
17
  return unless Kind::Of::Boolean(value)
12
18
 
13
19
  require 'micro/case/with_activemodel_validation'
14
20
  end
15
21
 
16
- def enable_transitions=(value)
17
- Micro::Case::Result.class_variable_set(
18
- :@@transitions_enabled, Kind::Of::Boolean(value)
19
- )
22
+ def set_activemodel_validation_errors_failure=(value)
23
+ return unless value
24
+
25
+ @activemodel_validation_errors_failure = Kind.of(Symbol, value)
26
+ end
27
+
28
+ def activemodel_validation_errors_failure
29
+ return @activemodel_validation_errors_failure if defined?(@activemodel_validation_errors_failure)
30
+
31
+ @activemodel_validation_errors_failure = :invalid_attributes
20
32
  end
21
33
  end
22
34
  end
@@ -46,11 +46,16 @@ module Micro
46
46
  end
47
47
 
48
48
  class InvalidInvocationOfTheThenMethod < StandardError
49
- def initialize; super('Invalid invocation of the Micro::Case::Result#then method'); end
49
+ def initialize(class_name)
50
+ super("Invalid invocation of the #{class_name}then method")
51
+ end
50
52
  end
51
53
 
52
54
  def self.by_wrong_usage?(exception)
53
- 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
54
59
  end
55
60
  end
56
61
  end
@@ -5,8 +5,14 @@ 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
 
13
+ INVALID_INVOCATION_OF_THE_THEN_METHOD =
14
+ Error::InvalidInvocationOfTheThenMethod.new("#{self.name}#")
15
+
10
16
  @@transitions_enabled = true
11
17
 
12
18
  def self.transitions_enabled?
@@ -17,16 +23,35 @@ module Micro
17
23
 
18
24
  alias value data
19
25
 
20
- def initialize
21
- @__transitions = []
22
- @__transitions_accumulated_data = {}
23
- @__transitions_accessible_attributes = {}
26
+ def initialize(transitions_mapper = Transitions::MapEverything)
27
+ enable_transitions = @@transitions_enabled
28
+
29
+ @__is_unknown = true
30
+ @__accumulated_data = {}
31
+ @__tracked_use_cases = Set.new
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}>"
24
45
  end
25
46
 
26
47
  def to_ary
27
48
  [data, type]
28
49
  end
29
50
 
51
+ def to_sym
52
+ @__success ? :success : :failure
53
+ end
54
+
30
55
  def [](key)
31
56
  data[key]
32
57
  end
@@ -44,20 +69,29 @@ module Micro
44
69
  end
45
70
 
46
71
  def slice(*keys)
47
- Utils.slice_hash(data, keys)
72
+ Utils::Hashes.slice(data, keys)
48
73
  end
49
74
 
50
75
  def success?
51
- @success
76
+ @__success
52
77
  end
53
78
 
54
79
  def failure?
55
80
  !success?
56
81
  end
57
82
 
83
+ def unknown?
84
+ @__is_unknown
85
+ end
86
+
87
+ def accessible_attributes
88
+ @__accessible_attributes.keys
89
+ end
90
+
58
91
  def on_success(expected_type = nil)
59
92
  return self unless __success_type?(expected_type)
60
93
 
94
+ @__is_unknown = false
61
95
  hook_data = expected_type.nil? ? self : data
62
96
 
63
97
  yield(hook_data, @use_case)
@@ -68,6 +102,7 @@ module Micro
68
102
  def on_failure(expected_type = nil)
69
103
  return self unless __failure_type?(expected_type)
70
104
 
105
+ @__is_unknown = false
71
106
  hook_data = expected_type.nil? ? self : data
72
107
 
73
108
  yield(hook_data, @use_case)
@@ -85,11 +120,19 @@ module Micro
85
120
  self
86
121
  end
87
122
 
123
+ def on_unknown
124
+ return self unless unknown?
125
+
126
+ yield(self, @use_case)
127
+
128
+ self
129
+ end
130
+
88
131
  def then(use_case = nil, attributes = nil, &block)
89
132
  can_yield_self = respond_to?(:yield_self)
90
133
 
91
134
  if block
92
- raise Error::InvalidInvocationOfTheThenMethod if use_case
135
+ raise INVALID_INVOCATION_OF_THE_THEN_METHOD if use_case
93
136
  raise NotImplementedError if !can_yield_self
94
137
 
95
138
  yield_self(&block)
@@ -98,7 +141,7 @@ module Micro
98
141
  return failure? ? self : __call_proc(use_case, 'then(-> {})'.freeze) if use_case.is_a?(Proc)
99
142
  return failure? ? self : __call_method(use_case, attributes) if use_case.is_a?(Method)
100
143
 
101
- raise Error::InvalidInvocationOfTheThenMethod unless ::Micro.case_or_flow?(use_case)
144
+ raise INVALID_INVOCATION_OF_THE_THEN_METHOD unless ::Micro.case_or_flow?(use_case)
102
145
 
103
146
  return self if failure?
104
147
 
@@ -118,13 +161,13 @@ module Micro
118
161
  return __call_proc(arg, '| -> {}'.freeze) if arg.is_a?(Proc)
119
162
  return __call_method(arg) if arg.is_a?(Method)
120
163
 
121
- raise Error::InvalidInvocationOfTheThenMethod unless ::Micro.case_or_flow?(arg)
164
+ raise INVALID_INVOCATION_OF_THE_THEN_METHOD unless ::Micro.case_or_flow?(arg)
122
165
 
123
166
  failure? ? self : arg.__new__(self, data).__call__
124
167
  end
125
168
 
126
169
  def transitions
127
- @__transitions.clone
170
+ @__transitions.dup
128
171
  end
129
172
 
130
173
  FetchData = -> (data) do
@@ -138,46 +181,50 @@ module Micro
138
181
  raise Error::InvalidResultType unless type.is_a?(Symbol)
139
182
  raise Error::InvalidUseCase unless use_case.is_a?(::Micro::Case)
140
183
 
141
- @success, @type, @use_case = is_success, type, use_case
184
+ @__success, @type, @use_case = is_success, type, use_case
142
185
 
143
- @data = FetchData.call(data)
186
+ @data = FetchData.call(data).freeze
144
187
 
145
188
  raise Micro::Case::Error::InvalidResult.new(is_success, type, use_case) unless @data
146
189
 
147
- @__transitions_accumulated_data.merge!(@data)
190
+ @__accumulated_data.merge!(@data)
148
191
 
149
- use_case_attributes = Utils.symbolize_hash_keys(@use_case.attributes)
192
+ use_case_attributes = Utils::Hashes.symbolize_keys(@use_case.attributes)
150
193
 
151
- __update_transitions_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)
152
196
 
153
- __set_transition(use_case_attributes) if @@transitions_enabled
197
+ __update_accessible_attributes(use_case_attributes)
198
+ end
199
+
200
+ __set_transition(use_case_attributes) unless @__transitions.frozen?
154
201
 
155
202
  self
156
203
  end
157
204
 
158
- def __set_transitions_accessible_attributes__(arg)
205
+ def __set_accessible_attributes__(arg)
159
206
  return arg unless arg.is_a?(Hash)
160
207
 
161
- attributes = Utils.symbolize_hash_keys(arg)
208
+ attributes = Utils::Hashes.symbolize_keys(arg)
162
209
 
163
- __update_transitions_accessible_attributes(attributes)
210
+ __update_accessible_attributes(attributes)
211
+ __fetch_accessible_attributes
164
212
  end
165
213
 
166
214
  private
167
215
 
168
- def __call_with_accumulated_data(fn, opt = nil)
169
- input =
170
- __update_transitions_accessible_attributes(
171
- opt ? opt.merge(@__transitions_accumulated_data) : @__transitions_accumulated_data
172
- )
173
-
174
- return fn.call if fn.arity.zero?
216
+ def __update_accessible_attributes(attributes)
217
+ @__accessible_attributes.merge!(attributes)
218
+ end
175
219
 
176
- opt ? fn.call(**input) : fn.call(input)
220
+ def __fetch_accessible_attributes
221
+ @__accessible_attributes.dup
177
222
  end
178
223
 
179
224
  def __call_proc(fn, expected)
180
- result = __call_with_accumulated_data(fn)
225
+ __update_accessible_attributes(@__accumulated_data)
226
+
227
+ result = fn.arity.zero? ? fn.call : fn.call(__fetch_accessible_attributes)
181
228
 
182
229
  return self if result === self
183
230
 
@@ -185,7 +232,9 @@ module Micro
185
232
  end
186
233
 
187
234
  def __call_method(methd, attributes = nil)
188
- result = __call_with_accumulated_data(methd, attributes)
235
+ __update_accessible_attributes(attributes ? attributes.merge(@__accumulated_data) : @__accumulated_data)
236
+
237
+ result = methd.arity.zero? ? methd.call : methd.call(**__fetch_accessible_attributes)
189
238
 
190
239
  return self if result === self
191
240
 
@@ -200,24 +249,11 @@ module Micro
200
249
  failure? && (expected_type.nil? || expected_type == type)
201
250
  end
202
251
 
203
- def __update_transitions_accessible_attributes(attributes)
204
- @__transitions_accessible_attributes.merge!(attributes)
205
- @__transitions_accessible_attributes
206
- end
207
-
208
252
  def __set_transition(use_case_attributes)
209
- use_case_class = @use_case.class
210
-
211
- result = @success ? :success : :failure
212
-
213
- @__transitions << {
214
- use_case: { class: use_case_class, attributes: use_case_attributes },
215
- result => { type: @type, result: data },
216
- accessible_attributes: @__transitions_accessible_attributes.keys
217
- }
253
+ @__transitions << @__transitions_mapper.call(self, use_case_attributes)
218
254
  end
219
255
 
220
- private_constant :FetchData
256
+ private_constant :FetchData, :INVALID_INVOCATION_OF_THE_THEN_METHOD
221
257
  end
222
258
  end
223
259
  end