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.
- checksums.yaml +4 -4
- data/.travis.sh +3 -2
- data/Gemfile +11 -2
- data/README.md +472 -441
- data/README.pt-BR.md +1390 -0
- data/lib/micro/case.rb +96 -77
- data/lib/micro/case/config.rb +23 -0
- data/lib/micro/case/error.rb +24 -18
- data/lib/micro/case/result.rb +94 -55
- data/lib/micro/case/safe.rb +8 -4
- data/lib/micro/case/utils.rb +7 -0
- data/lib/micro/case/version.rb +1 -1
- data/lib/micro/case/with_activemodel_validation.rb +4 -2
- data/lib/micro/cases.rb +16 -0
- data/lib/micro/cases/flow.rb +86 -0
- data/lib/micro/cases/safe/flow.rb +18 -0
- data/lib/u-case/with_activemodel_validation.rb +0 -2
- data/u-case.gemspec +3 -3
- metadata +19 -12
- data/lib/micro/case/flow.rb +0 -48
- data/lib/micro/case/flow/reducer.rb +0 -104
- data/lib/micro/case/safe/flow.rb +0 -44
- data/lib/u-case/with_validation.rb +0 -6
data/lib/micro/case.rb
CHANGED
@@ -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/
|
15
|
-
|
16
|
-
require 'micro/
|
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.
|
21
|
-
|
21
|
+
def self.call(options = Kind::Empty::HASH)
|
22
|
+
new(options).__call__
|
22
23
|
end
|
23
24
|
|
24
|
-
def self.
|
25
|
-
|
26
|
-
end
|
25
|
+
def self.then(use_case = nil, &block)
|
26
|
+
can_yield_self = respond_to?(:yield_self)
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
if block
|
29
|
+
raise Error::InvalidInvocationOfTheThenMethod if use_case
|
30
|
+
raise NotImplementedError if !can_yield_self
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
yield_self(&block)
|
33
|
+
else
|
34
|
+
return yield_self if !use_case && can_yield_self
|
35
35
|
|
36
|
-
|
37
|
-
|
36
|
+
self.call.then(use_case)
|
37
|
+
end
|
38
38
|
end
|
39
39
|
|
40
|
-
def self.
|
41
|
-
|
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.
|
47
|
-
|
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
|
-
|
54
|
-
|
55
|
-
private_constant :FLOW_STEP
|
48
|
+
class << self
|
49
|
+
alias __call__ call
|
56
50
|
|
57
|
-
|
58
|
-
|
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
|
-
|
64
|
-
|
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.
|
80
|
-
|
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.
|
84
|
-
|
85
|
-
|
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
|
-
|
88
|
-
return @__flow_use_cases if defined?(@__flow_use_cases)
|
82
|
+
__new__(result, input).__call__
|
89
83
|
end
|
90
84
|
|
91
|
-
|
92
|
-
|
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
|
-
|
97
|
-
@
|
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
|
94
|
+
return if __flow_get__
|
102
95
|
|
103
|
-
def self.use_cases;
|
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 =
|
100
|
+
@__flow = __flow_builder__.build(args)
|
108
101
|
end
|
109
102
|
|
110
|
-
|
111
|
-
|
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.
|
115
|
-
|
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
|
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.
|
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.
|
174
|
+
self.class.__flow_get__
|
163
175
|
end
|
164
176
|
|
165
177
|
def __call_use_case_flow
|
166
|
-
self.class.
|
178
|
+
self.class.__flow_get__.call(@__input)
|
167
179
|
end
|
168
180
|
|
169
|
-
def Success(
|
170
|
-
value
|
181
|
+
def Success(type = :ok, result: nil)
|
182
|
+
value = result || type
|
171
183
|
|
172
|
-
|
184
|
+
__get_result(true, value, type)
|
173
185
|
end
|
174
186
|
|
175
|
-
|
176
|
-
|
177
|
-
|
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
|
-
|
192
|
+
type
|
180
193
|
end
|
181
194
|
|
182
|
-
def
|
183
|
-
|
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
|
203
|
+
def __result
|
191
204
|
@__result ||= Result.new
|
192
205
|
end
|
193
206
|
|
194
|
-
def
|
195
|
-
|
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
|
data/lib/micro/case/error.rb
CHANGED
@@ -4,9 +4,11 @@ module Micro
|
|
4
4
|
class Case
|
5
5
|
module Error
|
6
6
|
class UnexpectedResult < TypeError
|
7
|
-
MESSAGE = '
|
7
|
+
MESSAGE = 'must return an instance of Micro::Case::Result'.freeze
|
8
8
|
|
9
|
-
def initialize(
|
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
|
-
|
37
|
-
|
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
|
data/lib/micro/case/result.rb
CHANGED
@@ -9,38 +9,37 @@ module Micro
|
|
9
9
|
|
10
10
|
@@transition_tracking_disabled = false
|
11
11
|
|
12
|
-
|
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
|
-
|
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
|
36
|
-
|
37
|
-
|
21
|
+
def to_ary
|
22
|
+
[data, type]
|
23
|
+
end
|
24
|
+
|
25
|
+
def [](key)
|
26
|
+
data[key]
|
27
|
+
end
|
38
28
|
|
39
|
-
|
29
|
+
def values_at(*keys)
|
30
|
+
data.values_at(*keys)
|
31
|
+
end
|
40
32
|
|
41
|
-
|
33
|
+
def key?(key)
|
34
|
+
data.key?(key)
|
35
|
+
end
|
42
36
|
|
43
|
-
|
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
|
55
|
-
return
|
53
|
+
def on_success(expected_type = nil)
|
54
|
+
return self unless __success_type?(expected_type)
|
56
55
|
|
57
|
-
|
58
|
-
end
|
56
|
+
hook_data = expected_type.nil? ? self : data
|
59
57
|
|
60
|
-
|
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
|
64
|
+
return self unless __failure_type?(expected_type)
|
68
65
|
|
69
|
-
|
66
|
+
hook_data = expected_type.nil? ? self : data
|
70
67
|
|
71
|
-
yield(
|
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
|
74
|
+
return self unless __failure_type?(:exception)
|
78
75
|
|
79
|
-
if !expected_exception || (Kind.is(Exception, expected_exception) &&
|
80
|
-
yield(
|
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(
|
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
|
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 !
|
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
|
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.
|
102
|
+
input = attributes.is_a?(Hash) ? self.data.merge(attributes) : self.data
|
102
103
|
|
103
|
-
|
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
|
-
|
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
|
120
|
-
|
158
|
+
def __call_proc(arg, expected:)
|
159
|
+
result = arg.arity.zero? ? arg.call : arg.call(data.clone)
|
121
160
|
|
122
|
-
|
123
|
-
end
|
161
|
+
return result if result.is_a?(Result)
|
124
162
|
|
125
|
-
|
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
|
166
|
+
def __success_type?(expected_type)
|
131
167
|
success? && (expected_type.nil? || expected_type == type)
|
132
168
|
end
|
133
169
|
|
134
|
-
def
|
170
|
+
def __failure_type?(expected_type)
|
135
171
|
failure? && (expected_type.nil? || expected_type == type)
|
136
172
|
end
|
137
173
|
|
138
|
-
def
|
139
|
-
|
174
|
+
def __update_transitions_accessible_attributes(attributes)
|
175
|
+
@__transitions_accessible_attributes__.merge!(attributes)
|
176
|
+
@__transitions_accessible_attributes__
|
140
177
|
end
|
141
178
|
|
142
|
-
def
|
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
|
-
|
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,
|
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
|