u-case 4.0.0 → 4.2.2

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,10 +17,18 @@ module Micro
16
17
 
17
18
  require 'micro/cases'
18
19
 
19
- include Micro::Attributes.with(:initialize, :diff)
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
+ result_wrapper = Result::Wrapper.new(result)
28
+
29
+ yield(result_wrapper)
30
+
31
+ result_wrapper.output
23
32
  end
24
33
 
25
34
  INVALID_INVOCATION_OF_THE_THEN_METHOD =
@@ -47,7 +56,7 @@ module Micro
47
56
  end
48
57
 
49
58
  def self.flow(*args)
50
- @__flow_use_cases = args
59
+ @__flow_use_cases = Cases::Utils.map_use_cases(args)
51
60
  end
52
61
 
53
62
  class << self
@@ -81,6 +90,8 @@ module Micro
81
90
  new(input).__set_result__(result)
82
91
  end
83
92
 
93
+ private_class_method :new
94
+
84
95
  def self.__flow_builder__
85
96
  Cases::Flow
86
97
  end
@@ -122,6 +133,28 @@ module Micro
122
133
  __flow_set(__flow_use_cases_get) if !__flow_get__ && __flow_use_cases
123
134
  end
124
135
 
136
+ InspectKey = :__inspect_key__ # :nodoc:
137
+
138
+ def self.inspect
139
+ ids = (Thread.current[InspectKey] ||= [])
140
+
141
+ if ids.include?(object_id)
142
+ return sprintf('#<%s: ...>', self)
143
+ end
144
+
145
+ begin
146
+ ids << object_id
147
+
148
+ if __flow_use_cases
149
+ return '<%s (%s) use_cases=%s>' % [self, __flow_builder__, @__flow_use_cases]
150
+ else
151
+ return '<%s (%s) attributes=%s>' % [self, self.superclass, attributes]
152
+ end
153
+ ensure
154
+ ids.pop
155
+ end
156
+ end
157
+
125
158
  def initialize(input)
126
159
  __setup_use_case(input)
127
160
  end
@@ -131,7 +164,7 @@ module Micro
131
164
  end
132
165
 
133
166
  def __call__
134
- call
167
+ __call_the_use_case_or_its_flow
135
168
  end
136
169
 
137
170
  def __set_result__(result)
@@ -145,12 +178,21 @@ module Micro
145
178
 
146
179
  private
147
180
 
181
+ def call(use_case, defaults = Kind::Empty::HASH)
182
+ raise Error::InvalidUseCase unless ::Micro.case_or_flow?(use_case)
183
+
184
+ input =
185
+ defaults.empty? ? attributes : attributes.merge(Utils::Hashes.stringify_keys(defaults))
186
+
187
+ use_case.__new__(@__result, input).__call__
188
+ end
189
+
148
190
  def apply(name)
149
191
  method(name)
150
192
  end
151
193
 
152
- def call
153
- return __call_use_case_flow if __call_use_case_flow?
194
+ def __call_the_use_case_or_its_flow
195
+ return __call_the_use_case_flow if __call_the_use_case_flow?
154
196
 
155
197
  __call_use_case
156
198
  end
@@ -171,11 +213,11 @@ module Micro
171
213
  raise Error::UnexpectedResult.new("#{self.class.name}#call!")
172
214
  end
173
215
 
174
- def __call_use_case_flow?
216
+ def __call_the_use_case_flow?
175
217
  self.class.__flow_get__
176
218
  end
177
219
 
178
- def __call_use_case_flow
220
+ def __call_the_use_case_flow
179
221
  self.class.__flow_get__.call(@__input)
180
222
  end
181
223
 
@@ -201,18 +243,32 @@ module Micro
201
243
  __get_result(false, value, type)
202
244
  end
203
245
 
204
- def __result
205
- @__result ||= Result.new
246
+ def __get_result(is_success, value, type)
247
+ @__result.__set__(is_success, value, type, self)
206
248
  end
207
249
 
208
- def __get_result(is_success, value, type)
209
- __result.__set__(is_success, value, type, self)
250
+ def transaction(adapter = :activerecord)
251
+ raise NotImplementedError unless adapter == :activerecord
252
+
253
+ result = nil
254
+
255
+ ActiveRecord::Base.transaction do
256
+ result = yield
257
+
258
+ raise ActiveRecord::Rollback if result.failure?
259
+ end
260
+
261
+ result
210
262
  end
211
263
 
212
264
  private_constant :MapFailureType, :INVALID_INVOCATION_OF_THE_THEN_METHOD
213
265
  end
214
266
 
267
+ def self.case?(arg)
268
+ arg.is_a?(Class) && arg < Case
269
+ end
270
+
215
271
  def self.case_or_flow?(arg)
216
- (arg.is_a?(Class) && arg < Case) || arg.is_a?(Cases::Flow)
272
+ case?(arg) || arg.is_a?(Cases::Flow)
217
273
  end
218
274
  end
@@ -5,6 +5,7 @@ require 'set'
5
5
  module Micro
6
6
  class Case
7
7
  class Result
8
+ require 'micro/case/result/wrapper'
8
9
  require 'micro/case/result/transitions'
9
10
 
10
11
  Kind::Types.add(self)
@@ -23,15 +24,26 @@ module Micro
23
24
  alias value data
24
25
 
25
26
  def initialize(transitions_mapper = Transitions::MapEverything)
27
+ enable_transitions = @@transitions_enabled
28
+
29
+ @__is_unknown = true
26
30
  @__accumulated_data = {}
31
+ @__tracked_use_cases = Set.new
27
32
  @__accessible_attributes = {}
28
33
 
29
- enable_transitions = @@transitions_enabled
30
-
31
34
  @__transitions = enable_transitions ? [] : Kind::Empty::ARRAY
32
35
  @__transitions_mapper = transitions_mapper if enable_transitions
33
36
  end
34
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}>"
45
+ end
46
+
35
47
  def to_ary
36
48
  [data, type]
37
49
  end
@@ -57,7 +69,7 @@ module Micro
57
69
  end
58
70
 
59
71
  def slice(*keys)
60
- Utils.slice_hash(data, keys)
72
+ Utils::Hashes.slice(data, keys)
61
73
  end
62
74
 
63
75
  def success?
@@ -68,6 +80,10 @@ module Micro
68
80
  !success?
69
81
  end
70
82
 
83
+ def unknown?
84
+ @__is_unknown
85
+ end
86
+
71
87
  def accessible_attributes
72
88
  @__accessible_attributes.keys
73
89
  end
@@ -75,6 +91,7 @@ module Micro
75
91
  def on_success(expected_type = nil)
76
92
  return self unless __success_type?(expected_type)
77
93
 
94
+ @__is_unknown = false
78
95
  hook_data = expected_type.nil? ? self : data
79
96
 
80
97
  yield(hook_data, @use_case)
@@ -85,6 +102,7 @@ module Micro
85
102
  def on_failure(expected_type = nil)
86
103
  return self unless __failure_type?(expected_type)
87
104
 
105
+ @__is_unknown = false
88
106
  hook_data = expected_type.nil? ? self : data
89
107
 
90
108
  yield(hook_data, @use_case)
@@ -102,6 +120,14 @@ module Micro
102
120
  self
103
121
  end
104
122
 
123
+ def on_unknown
124
+ return self unless unknown?
125
+
126
+ yield(self, @use_case)
127
+
128
+ self
129
+ end
130
+
105
131
  def then(use_case = nil, attributes = nil, &block)
106
132
  can_yield_self = respond_to?(:yield_self)
107
133
 
@@ -163,9 +189,13 @@ module Micro
163
189
 
164
190
  @__accumulated_data.merge!(@data)
165
191
 
166
- use_case_attributes = Utils.symbolize_hash_keys(@use_case.attributes)
192
+ use_case_attributes = Utils::Hashes.symbolize_keys(@use_case.attributes)
167
193
 
168
- __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
169
199
 
170
200
  __set_transition(use_case_attributes) unless @__transitions.frozen?
171
201
 
@@ -175,7 +205,7 @@ module Micro
175
205
  def __set_accessible_attributes__(arg)
176
206
  return arg unless arg.is_a?(Hash)
177
207
 
178
- attributes = Utils.symbolize_hash_keys(arg)
208
+ attributes = Utils::Hashes.symbolize_keys(arg)
179
209
 
180
210
  __update_accessible_attributes(attributes)
181
211
  __fetch_accessible_attributes
@@ -15,4 +15,3 @@ module Micro
15
15
  end
16
16
  end
17
17
  end
18
-
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Micro
4
+ class Case
5
+ class Result
6
+ class Wrapper
7
+ attr_reader :output
8
+
9
+ def initialize(result)
10
+ @result = result
11
+ @output = ::Kind::Undefined
12
+
13
+ @__is_unknown = true
14
+ end
15
+
16
+ def failure(type = nil)
17
+ return if @result.success? || !undefined_output?
18
+
19
+ set_output(yield(@result)) if result_type?(type)
20
+ end
21
+
22
+ def success(type = nil)
23
+ return if @result.failure? || !undefined_output?
24
+
25
+ set_output(yield(@result)) if result_type?(type)
26
+ end
27
+
28
+ def unknown
29
+ @output = yield(@result) if @__is_unknown && undefined_output?
30
+ end
31
+
32
+ private
33
+
34
+ def set_output(value)
35
+ @__is_unknown = false
36
+
37
+ @output = value
38
+ end
39
+
40
+ def undefined_output?
41
+ ::Kind::Undefined == @output
42
+ end
43
+
44
+ def result_type?(type)
45
+ type.nil? || @result.type == type
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -8,7 +8,7 @@ module Micro
8
8
  end
9
9
 
10
10
  def __call__
11
- call
11
+ __call_the_use_case_or_its_flow
12
12
  rescue => exception
13
13
  raise exception if Error.by_wrong_usage?(exception)
14
14
 
@@ -3,25 +3,34 @@
3
3
  module Micro
4
4
  class Case
5
5
  module Utils
6
- def self.symbolize_hash_keys(hash)
7
- if Kind::Of::Hash(hash).respond_to?(:transform_keys)
8
- hash.transform_keys { |key| key.to_sym rescue key }
9
- else
6
+
7
+ module Hashes
8
+ def self.respond_to?(hash, method)
9
+ Kind.of(Hash, hash).respond_to?(method)
10
+ end
11
+
12
+ def self.symbolize_keys(hash)
13
+ return hash.transform_keys { |key| key.to_sym rescue key } if respond_to?(hash, :transform_keys)
14
+
10
15
  hash.each_with_object({}) do |(k, v), memo|
11
16
  key = k.to_sym rescue k
12
-
13
17
  memo[key] = v
14
18
  end
15
19
  end
16
- end
17
20
 
18
- def self.slice_hash(hash, keys)
19
- if Kind::Of::Hash(hash).respond_to?(:slice)
20
- hash.slice(*keys)
21
- else
21
+ def self.stringify_keys(hash)
22
+ return hash.transform_keys(&:to_s) if respond_to?(hash, :transform_keys)
23
+
24
+ hash.each_with_object({}) { |(k, v), memo| memo[k.to_s] = v }
25
+ end
26
+
27
+ def self.slice(hash, keys)
28
+ return hash.slice(*keys) if respond_to?(hash, :slice)
29
+
22
30
  hash.select { |key, _value| keys.include?(key) }
23
31
  end
24
32
  end
33
+
25
34
  end
26
35
  end
27
36
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Micro
4
4
  class Case
5
- VERSION = '4.0.0'.freeze
5
+ VERSION = '4.2.2'.freeze
6
6
  end
7
7
  end
@@ -1,7 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'micro/cases/utils'
4
+ require 'micro/cases/error'
3
5
  require 'micro/cases/flow'
4
6
  require 'micro/cases/safe/flow'
7
+ require 'micro/cases/map'
5
8
 
6
9
  module Micro
7
10
  module Cases
@@ -12,5 +15,9 @@ module Micro
12
15
  def self.safe_flow(args)
13
16
  Safe::Flow.build(args)
14
17
  end
18
+
19
+ def self.map(args)
20
+ Map.build(args)
21
+ end
15
22
  end
16
23
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Micro
4
+ module Cases
5
+
6
+ module Error
7
+ class InvalidUseCases < ArgumentError
8
+ def initialize; super('argument must be a collection of `Micro::Case` classes'.freeze); end
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -3,20 +3,15 @@
3
3
  module Micro
4
4
  module Cases
5
5
  class Flow
6
- class InvalidUseCases < ArgumentError
7
- def initialize; super('argument must be a collection of `Micro::Case` classes'.freeze); end
8
- end
6
+ IsAUseCaseWithDefaults = -> arg { arg.is_a?(Array) && Micro.case?(arg[0]) && arg[1].is_a?(Hash) }
7
+ IsAValidUseCase = -> use_case { Micro.case?(use_case) || IsAUseCaseWithDefaults[use_case] }
9
8
 
10
9
  attr_reader :use_cases
11
10
 
12
- def self.map_use_cases(arg)
13
- arg.is_a?(Flow) ? arg.use_cases : Array(arg)
14
- end
15
-
16
11
  def self.build(args)
17
- use_cases = Array(args).flat_map { |arg| map_use_cases(arg) }
12
+ use_cases = Utils.map_use_cases(args)
18
13
 
19
- raise InvalidUseCases if use_cases.any? { |klass| !(klass < ::Micro::Case) }
14
+ raise Error::InvalidUseCases if use_cases.none?(&IsAValidUseCase)
20
15
 
21
16
  new(use_cases)
22
17
  end
@@ -27,8 +22,12 @@ module Micro
27
22
  @first = @next_ones.shift
28
23
  end
29
24
 
25
+ def inspect
26
+ '#<(%s) use_cases=%s>' % [self.class, @use_cases]
27
+ end
28
+
30
29
  def call!(input:, result:)
31
- first_result = __case_use_case(@first, result, input)
30
+ first_result = __call_use_case(@first, result, input)
32
31
 
33
32
  return first_result if @next_ones.empty?
34
33
 
@@ -36,7 +35,15 @@ module Micro
36
35
  end
37
36
 
38
37
  def call(input = Kind::Empty::HASH)
39
- call!(input: input, result: Case::Result.new)
38
+ result = call!(input: input, result: Case::Result.new)
39
+
40
+ return result unless block_given?
41
+
42
+ result_wrapper = ::Micro::Case::Result::Wrapper.new(result)
43
+
44
+ yield(result_wrapper)
45
+
46
+ result_wrapper.output
40
47
  end
41
48
 
42
49
  alias __call__ call
@@ -68,17 +75,23 @@ module Micro
68
75
  raise Case::Error::InvalidInvocationOfTheThenMethod.new("#{self.class.name}#")
69
76
  end
70
77
 
71
- def __case_use_case(use_case, result, input)
72
- use_case.__new__(result, input).__call__
78
+ def __call_use_case(use_case, result, input)
79
+ __build_use_case(use_case, result, input).__call__
73
80
  end
74
81
 
75
82
  def __call_next_use_cases(first_result)
76
83
  @next_ones.reduce(first_result) do |result, use_case|
77
84
  break result if result.failure?
78
85
 
79
- __case_use_case(use_case, result, result.data)
86
+ __call_use_case(use_case, result, result.data)
80
87
  end
81
88
  end
89
+
90
+ def __build_use_case(use_case, result, input)
91
+ return use_case.__new__(result, input) unless use_case.is_a?(Array)
92
+
93
+ use_case[0].__new__(result, input.merge(use_case[1]))
94
+ end
82
95
  end
83
96
  end
84
97
  end