u-case 2.6.0 → 3.0.0.rc1

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,38 +3,46 @@
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
+
16
+ require 'micro/cases'
17
17
 
18
18
  include Micro::Attributes.without(:strict_initialize)
19
19
 
20
+ def self.call(options = {})
21
+ new(options).call
22
+ end
23
+
20
24
  def self.to_proc
21
25
  Proc.new { |arg| call(arg) }
22
26
  end
23
27
 
24
- def self.Flow(args)
25
- Flow::Reducer.build(Array(args))
28
+ def self.call!
29
+ self
26
30
  end
27
31
 
28
- def self.>>(use_case)
29
- Flow([self, use_case])
32
+ def self.flow(*args)
33
+ @__flow_use_cases = args
30
34
  end
31
35
 
32
- def self.&(use_case)
33
- Safe::Flow([self, use_case])
34
- end
36
+ def self.inherited(subclass)
37
+ subclass.attributes(self.attributes_data({}))
38
+ subclass.extend ::Micro::Attributes.const_get('Macros::ForSubclasses'.freeze)
35
39
 
36
- def self.call(options = {})
37
- new(options).call
40
+ if self.send(:__flow_use_cases) && !subclass.name.to_s.end_with?(FLOW_STEP)
41
+ raise "Wooo, you can't do this! Inherits from a use case which has an inner flow violates "\
42
+ "one of the project principles: Solve complex business logic, by allowing the composition of use cases. "\
43
+ "Instead of doing this, declare a new class/constant with the steps needed.\n\n"\
44
+ "Related issue: https://github.com/serradura/u-case/issues/19\n"
45
+ end
38
46
  end
39
47
 
40
48
  def self.__new__(result, arg)
@@ -50,38 +58,32 @@ module Micro
50
58
  __new__(result, input).call
51
59
  end
52
60
 
53
- FLOW_STEP = 'Flow_Step'.freeze
61
+ def self.__flow_builder
62
+ Cases::Flow
63
+ end
54
64
 
55
- private_constant :FLOW_STEP
65
+ def self.__flow_get
66
+ return @__flow if defined?(@__flow)
67
+ end
56
68
 
57
- def self.__call!
58
- return const_get(FLOW_STEP) if const_defined?(FLOW_STEP, false)
69
+ private_class_method def self.__flow_set(args)
70
+ return if __flow_get
59
71
 
60
- class_eval("class #{FLOW_STEP} < #{self.name}; private def __call; __call_use_case; end; end")
61
- end
72
+ def self.use_cases; __flow_get.use_cases; end
62
73
 
63
- def self.call!
64
- self
74
+ self.class_eval('def use_cases; self.class.use_cases; end')
75
+
76
+ @__flow = __flow_builder.build(args)
65
77
  end
66
78
 
67
- def self.inherited(subclass)
68
- subclass.attributes(self.attributes_data({}))
69
- subclass.extend ::Micro::Attributes.const_get('Macros::ForSubclasses'.freeze)
79
+ FLOW_STEP = 'Flow_Step'.freeze
70
80
 
71
- if self.send(:__flow_use_cases) && !subclass.name.to_s.end_with?(FLOW_STEP)
72
- raise "Wooo, you can't do this! Inherits from a use case which has an inner flow violates "\
73
- "one of the project principles: Solve complex business logic, by allowing the composition of use cases. "\
74
- "Instead of doing this, declare a new class/constant with the steps needed.\n\n"\
75
- "Related issue: https://github.com/serradura/u-case/issues/19\n"
76
- end
77
- end
81
+ private_constant :FLOW_STEP
78
82
 
79
- def self.__flow_reducer
80
- Flow::Reducer
81
- end
83
+ def self.__call!
84
+ return const_get(FLOW_STEP) if const_defined?(FLOW_STEP, false)
82
85
 
83
- def self.__flow_get
84
- return @__flow if defined?(@__flow)
86
+ class_eval("class #{FLOW_STEP} < #{self.name}; private def __call; __call_use_case; end; end")
85
87
  end
86
88
 
87
89
  private_class_method def self.__flow_use_cases
@@ -93,28 +95,10 @@ module Micro
93
95
  .map { |use_case| use_case == self ? self.__call! : use_case }
94
96
  end
95
97
 
96
- private_class_method def self.__flow_use_cases_set(args)
97
- @__flow_use_cases = args
98
- end
99
-
100
- private_class_method def self.__flow_set(args)
101
- return if __flow_get
102
-
103
- def self.use_cases; __flow_get.use_cases; end
104
-
105
- self.class_eval('def use_cases; self.class.use_cases; end')
106
-
107
- @__flow = __flow_reducer.build(args)
108
- end
109
-
110
98
  def self.__flow_set!
111
99
  __flow_set(__flow_use_cases_get) if !__flow_get && __flow_use_cases
112
100
  end
113
101
 
114
- def self.flow(*args)
115
- __flow_use_cases_set(args)
116
- end
117
-
118
102
  def initialize(input)
119
103
  __setup_use_case(input)
120
104
  end
@@ -166,33 +150,36 @@ module Micro
166
150
  self.class.__flow_get.call(@__input)
167
151
  end
168
152
 
169
- def Success(arg = :ok)
170
- value, type = block_given? ? [yield, arg] : [arg, :ok]
153
+ def Success(type = :ok, result: nil)
154
+ value = result || type
171
155
 
172
- __get_result_with(true, value, type)
156
+ __get_result(true, value, type)
173
157
  end
174
158
 
175
- def Failure(arg = :error)
176
- value = block_given? ? yield : arg
177
- type = __map_failure_type(value, block_given? ? arg : :error)
159
+ MapFailureType = -> (value, type) do
160
+ return type if type != :error
161
+ return value if value.is_a?(Symbol)
162
+ return :exception if value.is_a?(Exception)
178
163
 
179
- __get_result_with(false, value, type)
164
+ type
180
165
  end
181
166
 
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)
167
+ def Failure(type = :error, result: nil)
168
+ value = result || type
186
169
 
187
- type
170
+ type = MapFailureType.call(value, type)
171
+
172
+ __get_result(false, value, type)
188
173
  end
189
174
 
190
- def __get_result__
175
+ def __result__
191
176
  @__result ||= Result.new
192
177
  end
193
178
 
194
- def __get_result_with(is_success, value, type)
195
- __get_result__.__set__(is_success, value, type, self)
179
+ def __get_result(is_success, value, type)
180
+ __result__.__set__(is_success, value, type, self)
196
181
  end
182
+
183
+ private_constant :MapFailureType
197
184
  end
198
185
  end
@@ -17,6 +17,21 @@ module Micro
17
17
  def initialize; super('type must be a Symbol'.freeze); end
18
18
  end
19
19
 
20
+ class InvalidResultData < TypeError
21
+ end
22
+
23
+ class InvalidSuccessResult < InvalidResultData
24
+ def initialize(object)
25
+ super("Success(result: #{object.inspect}) must be a Hash or Symbol")
26
+ end
27
+ end
28
+
29
+ class InvalidFailureResult < InvalidResultData
30
+ def initialize(object)
31
+ super("Failure(result: #{object.inspect}) must be a Hash, Symbol or an Exception")
32
+ end
33
+ end
34
+
20
35
  class InvalidResultInstance < ArgumentError
21
36
  def initialize; super('argument must be an instance of Micro::Case::Result'.freeze); end
22
37
  end
@@ -25,26 +40,16 @@ module Micro
25
40
  def initialize; super('use case must be a kind or an instance of Micro::Case'.freeze); end
26
41
  end
27
42
 
28
- class InvalidUseCases < ArgumentError
29
- def initialize; super('argument must be a collection of `Micro::Case` classes'.freeze); end
30
- end
31
-
32
43
  class InvalidInvocationOfTheThenMethod < StandardError
33
44
  def initialize; super('Invalid invocation of the Micro::Case::Result#then method'); end
34
45
  end
35
46
 
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
47
  class InvalidAccessToTheUseCaseObject < StandardError
41
48
  def initialize; super('only a failure result can access its use case object'.freeze); end
42
49
  end
43
50
 
44
- module ByWrongUsage
45
- def self.check(exception)
46
- exception.is_a?(Error::UnexpectedResult) || exception.is_a?(ArgumentError)
47
- end
51
+ def self.by_wrong_usage?(exception)
52
+ exception.is_a?(InvalidResultData) || exception.is_a?(Error::UnexpectedResult) || exception.is_a?(ArgumentError)
48
53
  end
49
54
  end
50
55
  end
@@ -13,34 +13,25 @@ module Micro
13
13
  @@transition_tracking_disabled = true
14
14
  end
15
15
 
16
- class Data
17
- attr_reader :value, :type
16
+ attr_reader :type, :data
18
17
 
19
- def initialize(value, type)
20
- @value, @type = value, type
21
- end
22
-
23
- def to_ary; [value, type]; end
24
- end
25
-
26
- private_constant :Data
27
-
28
- attr_reader :value, :type
18
+ alias_method :value, :data
29
19
 
30
20
  def initialize
31
21
  @__transitions__ = []
32
22
  @__transitions_accessible_attributes__ = {}
33
23
  end
34
24
 
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)
38
-
39
- @success, @value, @type, @use_case = is_success, value, type, use_case
25
+ def to_ary
26
+ [data, type]
27
+ end
40
28
 
41
- __set_transition__ unless @@transition_tracking_disabled
29
+ def [](key)
30
+ data[key]
31
+ end
42
32
 
43
- self
33
+ def values_at(*keys)
34
+ data.values_at(*keys)
44
35
  end
45
36
 
46
37
  def success?
@@ -58,7 +49,7 @@ module Micro
58
49
  end
59
50
 
60
51
  def on_success(expected_type = nil)
61
- yield(value) if success_type?(expected_type)
52
+ yield(data) if success_type?(expected_type)
62
53
 
63
54
  self
64
55
  end
@@ -66,9 +57,9 @@ module Micro
66
57
  def on_failure(expected_type = nil)
67
58
  return self unless failure_type?(expected_type)
68
59
 
69
- data = expected_type.nil? ? Data.new(value, type).tap(&:freeze) : value
60
+ hook_data = expected_type.nil? ? self : data
70
61
 
71
- yield(data, @use_case)
62
+ yield(hook_data, @use_case)
72
63
 
73
64
  self
74
65
  end
@@ -76,8 +67,8 @@ module Micro
76
67
  def on_exception(expected_exception = nil)
77
68
  return self unless failure_type?(:exception)
78
69
 
79
- if !expected_exception || (Kind.is(Exception, expected_exception) && value.is_a?(expected_exception))
80
- yield(value, @use_case)
70
+ if !expected_exception || (Kind.is(Exception, expected_exception) && data.fetch(:exception).is_a?(expected_exception))
71
+ yield(data, @use_case)
81
72
  end
82
73
 
83
74
  self
@@ -98,7 +89,7 @@ module Micro
98
89
 
99
90
  return self if failure?
100
91
 
101
- input = attributes.is_a?(Hash) ? self.value.merge(attributes) : self.value
92
+ input = attributes.is_a?(Hash) ? self.data.merge(attributes) : self.data
102
93
 
103
94
  arg.__call_and_set_transition__(self, input)
104
95
  end
@@ -108,6 +99,29 @@ module Micro
108
99
  @__transitions__.clone
109
100
  end
110
101
 
102
+ FetchData = -> (data, is_success) do
103
+ return data if data.is_a?(Hash)
104
+ 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
+
109
+ raise Micro::Case::Error.const_get(err), data
110
+ end
111
+
112
+ def __set__(is_success, data, type, use_case)
113
+ raise Error::InvalidResultType unless type.is_a?(Symbol)
114
+ raise Error::InvalidUseCase if !is_a_use_case?(use_case)
115
+
116
+ @success, @type, @use_case = is_success, type, use_case
117
+
118
+ @data = FetchData.call(data, is_success)
119
+
120
+ __set_transition__ unless @@transition_tracking_disabled
121
+
122
+ self
123
+ end
124
+
111
125
  def __set_transitions_accessible_attributes__(attributes_data)
112
126
  return attributes_data if @@transition_tracking_disabled
113
127
 
@@ -116,17 +130,6 @@ module Micro
116
130
 
117
131
  private
118
132
 
119
- def __set_transitions_accessible_attributes__!(attributes_data)
120
- attributes = Utils.symbolize_hash_keys(attributes_data)
121
-
122
- __update_transitions_accessible_attributes__(attributes)
123
- end
124
-
125
- def __update_transitions_accessible_attributes__(attributes)
126
- @__transitions_accessible_attributes__.merge!(attributes)
127
- @__transitions_accessible_attributes__
128
- end
129
-
130
133
  def success_type?(expected_type)
131
134
  success? && (expected_type.nil? || expected_type == type)
132
135
  end
@@ -139,6 +142,17 @@ module Micro
139
142
  (arg.is_a?(Class) && arg < ::Micro::Case) || arg.is_a?(::Micro::Case)
140
143
  end
141
144
 
145
+ def __set_transitions_accessible_attributes__!(attributes_data)
146
+ attributes = Utils.symbolize_hash_keys(attributes_data)
147
+
148
+ __update_transitions_accessible_attributes__(attributes)
149
+ end
150
+
151
+ def __update_transitions_accessible_attributes__(attributes)
152
+ @__transitions_accessible_attributes__.merge!(attributes)
153
+ @__transitions_accessible_attributes__
154
+ end
155
+
142
156
  def __set_transition__
143
157
  use_case_class = @use_case.class
144
158
  use_case_attributes = Utils.symbolize_hash_keys(@use_case.attributes)
@@ -149,10 +163,12 @@ module Micro
149
163
 
150
164
  @__transitions__ << {
151
165
  use_case: { class: use_case_class, attributes: use_case_attributes },
152
- result => { type: @type, value: @value },
166
+ result => { type: @type, result: data },
153
167
  accessible_attributes: @__transitions_accessible_attributes__.keys
154
168
  }
155
169
  end
170
+
171
+ private_constant :FetchData
156
172
  end
157
173
  end
158
174
  end
@@ -3,12 +3,16 @@
3
3
  module Micro
4
4
  class Case
5
5
  class Safe < ::Micro::Case
6
+ def self.__flow_builder
7
+ Cases::Safe::Flow
8
+ end
9
+
6
10
  def call
7
11
  __call
8
12
  rescue => exception
9
- raise exception if Error::ByWrongUsage.check(exception)
13
+ raise exception if Error.by_wrong_usage?(exception)
10
14
 
11
- Failure(exception)
15
+ Failure(result: exception)
12
16
  end
13
17
  end
14
18
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Micro
4
4
  class Case
5
- VERSION = '2.6.0'.freeze
5
+ VERSION = '3.0.0.rc1'.freeze
6
6
  end
7
7
  end
@@ -35,7 +35,7 @@ module Micro
35
35
  def failure_by_validation_error(object)
36
36
  errors = object.respond_to?(:errors) ? object.errors : object
37
37
 
38
- Failure(:validation_error) { { errors: errors } }
38
+ Failure :validation_error, result: { errors: errors }
39
39
  end
40
40
  end
41
41
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'micro/cases/flow'
4
+ require 'micro/cases/safe/flow'
5
+
6
+ module Micro
7
+ module Cases
8
+ def self.flow(args)
9
+ Flow.build(args)
10
+ end
11
+
12
+ def self.safe_flow(args)
13
+ Safe::Flow.build(args)
14
+ end
15
+ end
16
+ end