u-case 2.6.0 → 3.0.0.rc5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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