u-case 3.0.0.rc1 → 3.0.0.rc6

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.
@@ -12,27 +12,51 @@ module Micro
12
12
  require 'micro/case/error'
13
13
  require 'micro/case/safe'
14
14
  require 'micro/case/strict'
15
+ require 'micro/case/config'
15
16
 
16
17
  require 'micro/cases'
17
18
 
18
19
  include Micro::Attributes.without(:strict_initialize)
19
20
 
20
- def self.call(options = {})
21
- new(options).call
21
+ def self.call(options = Kind::Empty::HASH)
22
+ new(options).__call__
22
23
  end
23
24
 
24
- def self.to_proc
25
- Proc.new { |arg| call(arg) }
25
+ def self.then(use_case = nil, &block)
26
+ can_yield_self = respond_to?(:yield_self)
27
+
28
+ if block
29
+ raise Error::InvalidInvocationOfTheThenMethod if use_case
30
+ raise NotImplementedError if !can_yield_self
31
+
32
+ yield_self(&block)
33
+ else
34
+ return yield_self if !use_case && can_yield_self
35
+
36
+ self.call.then(use_case)
37
+ end
26
38
  end
27
39
 
28
- def self.call!
29
- self
40
+ def self.to_proc
41
+ Proc.new { |arg| call(arg) }
30
42
  end
31
43
 
32
44
  def self.flow(*args)
33
45
  @__flow_use_cases = args
34
46
  end
35
47
 
48
+ class << self
49
+ alias __call__ call
50
+
51
+ def config
52
+ yield(Config.instance)
53
+ end
54
+
55
+ def call!
56
+ self
57
+ end
58
+ end
59
+
36
60
  def self.inherited(subclass)
37
61
  subclass.attributes(self.attributes_data({}))
38
62
  subclass.extend ::Micro::Attributes.const_get('Macros::ForSubclasses'.freeze)
@@ -46,41 +70,36 @@ module Micro
46
70
  end
47
71
 
48
72
  def self.__new__(result, arg)
49
- instance = new(arg)
73
+ input = result.__set_transitions_accessible_attributes__(arg)
74
+
75
+ instance = new(input)
50
76
  instance.__set_result__(result)
51
77
  instance
52
78
  end
53
79
 
54
- def self.__call_and_set_transition__(result, arg)
55
- input =
56
- arg.is_a?(Hash) ? result.__set_transitions_accessible_attributes__(arg) : arg
57
-
58
- __new__(result, input).call
59
- end
60
-
61
- def self.__flow_builder
80
+ def self.__flow_builder__
62
81
  Cases::Flow
63
82
  end
64
83
 
65
- def self.__flow_get
84
+ def self.__flow_get__
66
85
  return @__flow if defined?(@__flow)
67
86
  end
68
87
 
69
88
  private_class_method def self.__flow_set(args)
70
- return if __flow_get
89
+ return if __flow_get__
71
90
 
72
- def self.use_cases; __flow_get.use_cases; end
91
+ def self.use_cases; __flow_get__.use_cases; end
73
92
 
74
93
  self.class_eval('def use_cases; self.class.use_cases; end')
75
94
 
76
- @__flow = __flow_builder.build(args)
95
+ @__flow = __flow_builder__.build(args)
77
96
  end
78
97
 
79
- FLOW_STEP = 'Flow_Step'.freeze
98
+ FLOW_STEP = 'Self'.freeze
80
99
 
81
100
  private_constant :FLOW_STEP
82
101
 
83
- def self.__call!
102
+ def self.__call__!
84
103
  return const_get(FLOW_STEP) if const_defined?(FLOW_STEP, false)
85
104
 
86
105
  class_eval("class #{FLOW_STEP} < #{self.name}; private def __call; __call_use_case; end; end")
@@ -92,11 +111,11 @@ module Micro
92
111
 
93
112
  private_class_method def self.__flow_use_cases_get
94
113
  Array(__flow_use_cases)
95
- .map { |use_case| use_case == self ? self.__call! : use_case }
114
+ .map { |use_case| use_case == self ? self.__call__! : use_case }
96
115
  end
97
116
 
98
- def self.__flow_set!
99
- __flow_set(__flow_use_cases_get) if !__flow_get && __flow_use_cases
117
+ def self.__flow_set__!
118
+ __flow_set(__flow_use_cases_get) if !__flow_get__ && __flow_use_cases
100
119
  end
101
120
 
102
121
  def initialize(input)
@@ -107,8 +126,8 @@ module Micro
107
126
  raise NotImplementedError
108
127
  end
109
128
 
110
- def call
111
- __call
129
+ def __call__
130
+ __call!
112
131
  end
113
132
 
114
133
  def __set_result__(result)
@@ -120,15 +139,19 @@ module Micro
120
139
 
121
140
  private
122
141
 
142
+ # This method was reserved for a new feature
143
+ def call
144
+ end
145
+
123
146
  def __setup_use_case(input)
124
- self.class.__flow_set!
147
+ self.class.__flow_set__!
125
148
 
126
149
  @__input = input
127
150
 
128
151
  self.attributes = input
129
152
  end
130
153
 
131
- def __call
154
+ def __call!
132
155
  return __call_use_case_flow if __call_use_case_flow?
133
156
 
134
157
  __call_use_case
@@ -139,15 +162,15 @@ module Micro
139
162
 
140
163
  return result if result.is_a?(Result)
141
164
 
142
- raise Error::UnexpectedResult.new(self.class)
165
+ raise Error::UnexpectedResult.new("#{self.class.name}#call!")
143
166
  end
144
167
 
145
168
  def __call_use_case_flow?
146
- self.class.__flow_get
169
+ self.class.__flow_get__
147
170
  end
148
171
 
149
172
  def __call_use_case_flow
150
- self.class.__flow_get.call(@__input)
173
+ self.class.__flow_get__.call(@__input)
151
174
  end
152
175
 
153
176
  def Success(type = :ok, result: nil)
@@ -172,14 +195,18 @@ module Micro
172
195
  __get_result(false, value, type)
173
196
  end
174
197
 
175
- def __result__
198
+ def __result
176
199
  @__result ||= Result.new
177
200
  end
178
201
 
179
202
  def __get_result(is_success, value, type)
180
- __result__.__set__(is_success, value, type, self)
203
+ __result.__set__(is_success, value, type, self)
181
204
  end
182
205
 
183
206
  private_constant :MapFailureType
184
207
  end
208
+
209
+ def self.case_or_flow?(arg)
210
+ (arg.is_a?(Class) && arg < Case) || arg.is_a?(Cases::Flow)
211
+ end
185
212
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module Micro
6
+ class Case
7
+ class Config
8
+ include Singleton
9
+
10
+ def enable_activemodel_validation=(value)
11
+ return unless Kind::Of::Boolean(value)
12
+
13
+ require 'micro/case/with_activemodel_validation'
14
+ end
15
+
16
+ def enable_transitions=(value)
17
+ Micro::Case::Result.class_variable_set(
18
+ :@@transition_tracking_enabled, Kind::Of::Boolean(value)
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
@@ -4,9 +4,11 @@ module Micro
4
4
  class Case
5
5
  module Error
6
6
  class UnexpectedResult < TypeError
7
- MESSAGE = '#call! must return an instance of Micro::Case::Result'.freeze
7
+ MESSAGE = 'must return an instance of Micro::Case::Result'.freeze
8
8
 
9
- def initialize(klass); super(klass.name + MESSAGE); end
9
+ def initialize(context)
10
+ super("#{context} #{MESSAGE}")
11
+ end
10
12
  end
11
13
 
12
14
  class ResultIsAlreadyDefined < ArgumentError
@@ -17,18 +19,21 @@ module Micro
17
19
  def initialize; super('type must be a Symbol'.freeze); end
18
20
  end
19
21
 
20
- class InvalidResultData < TypeError
21
- end
22
+ class InvalidResult < TypeError
23
+ def initialize(is_success, type, use_case)
24
+ base =
25
+ "The result returned from #{use_case.class.name}#call! must be a Hash."
22
26
 
23
- class InvalidSuccessResult < InvalidResultData
24
- def initialize(object)
25
- super("Success(result: #{object.inspect}) must be a Hash or Symbol")
26
- end
27
- end
27
+ result = is_success ? 'Success'.freeze : 'Failure'.freeze
28
28
 
29
- class InvalidFailureResult < InvalidResultData
30
- def initialize(object)
31
- super("Failure(result: #{object.inspect}) must be a Hash, Symbol or an Exception")
29
+ example =
30
+ if type === :ok || type === :error || type === :exception
31
+ "#{result}(result: { key: 'value' })"
32
+ else
33
+ "#{result}(:#{type}, result: { key: 'value' })"
34
+ end
35
+
36
+ super("#{base}\n\nExample:\n #{example}")
32
37
  end
33
38
  end
34
39
 
@@ -44,12 +49,8 @@ module Micro
44
49
  def initialize; super('Invalid invocation of the Micro::Case::Result#then method'); end
45
50
  end
46
51
 
47
- class InvalidAccessToTheUseCaseObject < StandardError
48
- def initialize; super('only a failure result can access its use case object'.freeze); end
49
- end
50
-
51
52
  def self.by_wrong_usage?(exception)
52
- exception.is_a?(InvalidResultData) || exception.is_a?(Error::UnexpectedResult) || exception.is_a?(ArgumentError)
53
+ exception.is_a?(InvalidResult) || exception.is_a?(UnexpectedResult) || exception.is_a?(ArgumentError)
53
54
  end
54
55
  end
55
56
  end
@@ -7,19 +7,16 @@ module Micro
7
7
  class Result
8
8
  Kind::Types.add(self)
9
9
 
10
- @@transition_tracking_disabled = false
10
+ @@transition_tracking_enabled = true
11
11
 
12
- def self.disable_transition_tracking
13
- @@transition_tracking_disabled = true
14
- end
15
-
16
- attr_reader :type, :data
12
+ attr_reader :type, :data, :use_case
17
13
 
18
- alias_method :value, :data
14
+ alias value data
19
15
 
20
16
  def initialize
21
- @__transitions__ = []
22
- @__transitions_accessible_attributes__ = {}
17
+ @__transitions = []
18
+ @__transitions_accumulated_data = {}
19
+ @__transitions_accessible_attributes = {}
23
20
  end
24
21
 
25
22
  def to_ary
@@ -34,6 +31,18 @@ module Micro
34
31
  data.values_at(*keys)
35
32
  end
36
33
 
34
+ def key?(key)
35
+ data.key?(key)
36
+ end
37
+
38
+ def value?(value)
39
+ data.value?(value)
40
+ end
41
+
42
+ def slice(*keys)
43
+ Utils.slice_hash(data, keys)
44
+ end
45
+
37
46
  def success?
38
47
  @success
39
48
  end
@@ -42,20 +51,18 @@ module Micro
42
51
  !success?
43
52
  end
44
53
 
45
- def use_case
46
- return @use_case if failure?
54
+ def on_success(expected_type = nil)
55
+ return self unless __success_type?(expected_type)
47
56
 
48
- raise Error::InvalidAccessToTheUseCaseObject
49
- end
57
+ hook_data = expected_type.nil? ? self : data
50
58
 
51
- def on_success(expected_type = nil)
52
- yield(data) if success_type?(expected_type)
59
+ yield(hook_data, @use_case)
53
60
 
54
61
  self
55
62
  end
56
63
 
57
64
  def on_failure(expected_type = nil)
58
- return self unless failure_type?(expected_type)
65
+ return self unless __failure_type?(expected_type)
59
66
 
60
67
  hook_data = expected_type.nil? ? self : data
61
68
 
@@ -65,7 +72,7 @@ module Micro
65
72
  end
66
73
 
67
74
  def on_exception(expected_exception = nil)
68
- return self unless failure_type?(:exception)
75
+ return self unless __failure_type?(:exception)
69
76
 
70
77
  if !expected_exception || (Kind.is(Exception, expected_exception) && data.fetch(:exception).is_a?(expected_exception))
71
78
  yield(data, @use_case)
@@ -74,97 +81,136 @@ module Micro
74
81
  self
75
82
  end
76
83
 
77
- def then(arg = nil, attributes = nil, &block)
84
+ def then(use_case = nil, attributes = nil, &block)
78
85
  can_yield_self = respond_to?(:yield_self)
79
86
 
80
87
  if block
81
- raise Error::InvalidInvocationOfTheThenMethod if arg
88
+ raise Error::InvalidInvocationOfTheThenMethod if use_case
82
89
  raise NotImplementedError if !can_yield_self
83
90
 
84
91
  yield_self(&block)
85
92
  else
86
- return yield_self if !arg && can_yield_self
93
+ return yield_self if !use_case && can_yield_self
94
+ return failure? ? self : __call_proc(use_case, 'then(-> {})'.freeze) if use_case.is_a?(Proc)
95
+ return failure? ? self : __call_method(use_case, attributes) if use_case.is_a?(Method)
87
96
 
88
- raise Error::InvalidInvocationOfTheThenMethod if !is_a_use_case?(arg)
97
+ raise Error::InvalidInvocationOfTheThenMethod unless ::Micro.case_or_flow?(use_case)
89
98
 
90
99
  return self if failure?
91
100
 
92
101
  input = attributes.is_a?(Hash) ? self.data.merge(attributes) : self.data
93
102
 
94
- arg.__call_and_set_transition__(self, input)
103
+ if use_case.is_a?(::Micro::Cases::Flow)
104
+ use_case.call!(input: input, result: self)
105
+ else
106
+ use_case.__new__(self, input).__call__
107
+ end
95
108
  end
96
109
  end
97
110
 
111
+ def |(arg)
112
+ return self if failure?
113
+
114
+ return __call_proc(arg, '| -> {}'.freeze) if arg.is_a?(Proc)
115
+ return __call_method(arg) if arg.is_a?(Method)
116
+
117
+ raise Error::InvalidInvocationOfTheThenMethod unless ::Micro.case_or_flow?(arg)
118
+
119
+ failure? ? self : arg.__new__(self, data).__call__
120
+ end
121
+
98
122
  def transitions
99
- @__transitions__.clone
123
+ @__transitions.clone
100
124
  end
101
125
 
102
- FetchData = -> (data, is_success) do
126
+ FetchData = -> (data) do
103
127
  return data if data.is_a?(Hash)
104
128
  return { data => true } if data.is_a?(Symbol)
105
- return { exception: data } if data.is_a?(Exception)
106
-
107
- err = is_success ? :InvalidSuccessResult : :InvalidFailureResult
108
129
 
109
- raise Micro::Case::Error.const_get(err), data
130
+ { exception: data } if data.is_a?(Exception)
110
131
  end
111
132
 
112
133
  def __set__(is_success, data, type, use_case)
113
134
  raise Error::InvalidResultType unless type.is_a?(Symbol)
114
- raise Error::InvalidUseCase if !is_a_use_case?(use_case)
135
+ raise Error::InvalidUseCase unless use_case.is_a?(::Micro::Case)
115
136
 
116
137
  @success, @type, @use_case = is_success, type, use_case
117
138
 
118
- @data = FetchData.call(data, is_success)
139
+ @data = FetchData.call(data)
140
+
141
+ raise Micro::Case::Error::InvalidResult.new(is_success, type, use_case) unless @data
119
142
 
120
- __set_transition__ unless @@transition_tracking_disabled
143
+ if @@transition_tracking_enabled
144
+ @__transitions_accumulated_data.merge!(@data)
145
+
146
+ __set_transition
147
+ end
121
148
 
122
149
  self
123
150
  end
124
151
 
125
- def __set_transitions_accessible_attributes__(attributes_data)
126
- return attributes_data if @@transition_tracking_disabled
152
+ def __set_transitions_accessible_attributes__(arg)
153
+ return arg unless @@transition_tracking_enabled
127
154
 
128
- __set_transitions_accessible_attributes__!(attributes_data)
155
+ if arg.is_a?(Hash)
156
+ attributes = Utils.symbolize_hash_keys(arg)
157
+
158
+ __update_transitions_accessible_attributes(attributes)
159
+ end
129
160
  end
130
161
 
131
162
  private
132
163
 
133
- def success_type?(expected_type)
134
- success? && (expected_type.nil? || expected_type == type)
164
+ def __call_with_accumulated_data(fn, opt = nil)
165
+ input =
166
+ __update_transitions_accessible_attributes(
167
+ opt ? opt.merge(@__transitions_accumulated_data) : @__transitions_accumulated_data
168
+ )
169
+
170
+ fn.arity.zero? ? fn.call : fn.call(input)
135
171
  end
136
172
 
137
- def failure_type?(expected_type)
138
- failure? && (expected_type.nil? || expected_type == type)
173
+ def __call_proc(fn, expected)
174
+ result = __call_with_accumulated_data(fn)
175
+
176
+ return self if result === self
177
+
178
+ raise Error::UnexpectedResult.new("#{Result.name}##{expected}")
139
179
  end
140
180
 
141
- def is_a_use_case?(arg)
142
- (arg.is_a?(Class) && arg < ::Micro::Case) || arg.is_a?(::Micro::Case)
181
+ def __call_method(methd, attributes = nil)
182
+ result = __call_with_accumulated_data(methd, attributes)
183
+
184
+ return self if result === self
185
+
186
+ raise Error::UnexpectedResult.new("#{use_case.class.name}#method(:#{methd.name})")
143
187
  end
144
188
 
145
- def __set_transitions_accessible_attributes__!(attributes_data)
146
- attributes = Utils.symbolize_hash_keys(attributes_data)
189
+ def __success_type?(expected_type)
190
+ success? && (expected_type.nil? || expected_type == type)
191
+ end
147
192
 
148
- __update_transitions_accessible_attributes__(attributes)
193
+ def __failure_type?(expected_type)
194
+ failure? && (expected_type.nil? || expected_type == type)
149
195
  end
150
196
 
151
- def __update_transitions_accessible_attributes__(attributes)
152
- @__transitions_accessible_attributes__.merge!(attributes)
153
- @__transitions_accessible_attributes__
197
+ def __update_transitions_accessible_attributes(attributes)
198
+ @__transitions_accessible_attributes.merge!(attributes)
199
+ @__transitions_accessible_attributes
154
200
  end
155
201
 
156
- def __set_transition__
202
+ def __set_transition
157
203
  use_case_class = @use_case.class
158
204
  use_case_attributes = Utils.symbolize_hash_keys(@use_case.attributes)
159
205
 
160
- __update_transitions_accessible_attributes__(use_case_attributes)
206
+ __update_transitions_accessible_attributes(use_case_attributes)
161
207
 
162
208
  result = @success ? :success : :failure
163
209
 
164
- @__transitions__ << {
210
+ @__transitions << {
165
211
  use_case: { class: use_case_class, attributes: use_case_attributes },
166
212
  result => { type: @type, result: data },
167
- accessible_attributes: @__transitions_accessible_attributes__.keys
213
+ accessible_attributes: @__transitions_accessible_attributes.keys
168
214
  }
169
215
  end
170
216