u-case 3.0.0.rc2 → 3.0.0.rc7

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
@@ -47,10 +49,6 @@ module Micro
47
49
  def initialize; super('Invalid invocation of the Micro::Case::Result#then method'); end
48
50
  end
49
51
 
50
- class InvalidAccessToTheUseCaseObject < StandardError
51
- def initialize; super('only a failure result can access its use case object'.freeze); end
52
- end
53
-
54
52
  def self.by_wrong_usage?(exception)
55
53
  exception.is_a?(InvalidResult) || exception.is_a?(UnexpectedResult) || exception.is_a?(ArgumentError)
56
54
  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,29 +81,46 @@ 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
126
  FetchData = -> (data) do
@@ -108,7 +132,7 @@ module Micro
108
132
 
109
133
  def __set__(is_success, data, type, use_case)
110
134
  raise Error::InvalidResultType unless type.is_a?(Symbol)
111
- raise Error::InvalidUseCase if !is_a_use_case?(use_case)
135
+ raise Error::InvalidUseCase unless use_case.is_a?(::Micro::Case)
112
136
 
113
137
  @success, @type, @use_case = is_success, type, use_case
114
138
 
@@ -116,54 +140,75 @@ module Micro
116
140
 
117
141
  raise Micro::Case::Error::InvalidResult.new(is_success, type, use_case) unless @data
118
142
 
119
- __set_transition__ unless @@transition_tracking_disabled
143
+ @__transitions_accumulated_data.merge!(@data)
144
+
145
+ __set_transition if @@transition_tracking_enabled
120
146
 
121
147
  self
122
148
  end
123
149
 
124
- def __set_transitions_accessible_attributes__(attributes_data)
125
- return attributes_data if @@transition_tracking_disabled
150
+ def __set_transitions_accessible_attributes__(arg)
151
+ return arg unless @@transition_tracking_enabled
152
+
153
+ if arg.is_a?(Hash)
154
+ attributes = Utils.symbolize_hash_keys(arg)
126
155
 
127
- __set_transitions_accessible_attributes__!(attributes_data)
156
+ __update_transitions_accessible_attributes(attributes)
157
+ end
128
158
  end
129
159
 
130
160
  private
131
161
 
132
- def success_type?(expected_type)
133
- success? && (expected_type.nil? || expected_type == type)
162
+ def __call_with_accumulated_data(fn, opt = nil)
163
+ input =
164
+ __update_transitions_accessible_attributes(
165
+ opt ? opt.merge(@__transitions_accumulated_data) : @__transitions_accumulated_data
166
+ )
167
+
168
+ fn.arity.zero? ? fn.call : fn.call(input)
134
169
  end
135
170
 
136
- def failure_type?(expected_type)
137
- failure? && (expected_type.nil? || expected_type == type)
171
+ def __call_proc(fn, expected)
172
+ result = __call_with_accumulated_data(fn)
173
+
174
+ return self if result === self
175
+
176
+ raise Error::UnexpectedResult.new("#{Result.name}##{expected}")
138
177
  end
139
178
 
140
- def is_a_use_case?(arg)
141
- (arg.is_a?(Class) && arg < ::Micro::Case) || arg.is_a?(::Micro::Case)
179
+ def __call_method(methd, attributes = nil)
180
+ result = __call_with_accumulated_data(methd, attributes)
181
+
182
+ return self if result === self
183
+
184
+ raise Error::UnexpectedResult.new("#{use_case.class.name}#method(:#{methd.name})")
142
185
  end
143
186
 
144
- def __set_transitions_accessible_attributes__!(attributes_data)
145
- attributes = Utils.symbolize_hash_keys(attributes_data)
187
+ def __success_type?(expected_type)
188
+ success? && (expected_type.nil? || expected_type == type)
189
+ end
146
190
 
147
- __update_transitions_accessible_attributes__(attributes)
191
+ def __failure_type?(expected_type)
192
+ failure? && (expected_type.nil? || expected_type == type)
148
193
  end
149
194
 
150
- def __update_transitions_accessible_attributes__(attributes)
151
- @__transitions_accessible_attributes__.merge!(attributes)
152
- @__transitions_accessible_attributes__
195
+ def __update_transitions_accessible_attributes(attributes)
196
+ @__transitions_accessible_attributes.merge!(attributes)
197
+ @__transitions_accessible_attributes
153
198
  end
154
199
 
155
- def __set_transition__
200
+ def __set_transition
156
201
  use_case_class = @use_case.class
157
202
  use_case_attributes = Utils.symbolize_hash_keys(@use_case.attributes)
158
203
 
159
- __update_transitions_accessible_attributes__(use_case_attributes)
204
+ __update_transitions_accessible_attributes(use_case_attributes)
160
205
 
161
206
  result = @success ? :success : :failure
162
207
 
163
- @__transitions__ << {
208
+ @__transitions << {
164
209
  use_case: { class: use_case_class, attributes: use_case_attributes },
165
210
  result => { type: @type, result: data },
166
- accessible_attributes: @__transitions_accessible_attributes__.keys
211
+ accessible_attributes: @__transitions_accessible_attributes.keys
167
212
  }
168
213
  end
169
214