u-case 2.6.0 → 3.0.0.rc5

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