cuprum 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/DEVELOPMENT.md +43 -9
- data/README.md +525 -326
- data/lib/cuprum/basic_command.rb +212 -0
- data/lib/cuprum/built_in/{identity_function.rb → identity_command.rb} +4 -4
- data/lib/cuprum/built_in/identity_operation.rb +2 -2
- data/lib/cuprum/built_in/{null_function.rb → null_command.rb} +4 -4
- data/lib/cuprum/built_in/null_operation.rb +2 -2
- data/lib/cuprum/chaining.rb +142 -0
- data/lib/cuprum/command.rb +113 -0
- data/lib/cuprum/not_implemented_error.rb +14 -0
- data/lib/cuprum/operation.rb +7 -7
- data/lib/cuprum/utils/instance_spy.rb +15 -16
- data/lib/cuprum/version.rb +1 -1
- metadata +12 -8
- data/lib/cuprum/function.rb +0 -447
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'cuprum'
|
2
|
+
|
3
|
+
module Cuprum
|
4
|
+
# Error class for calling a Command that was not given a definition block
|
5
|
+
# or have a #process method defined.
|
6
|
+
class NotImplementedError < StandardError
|
7
|
+
# Error message for a NotImplementedError.
|
8
|
+
DEFAULT_MESSAGE = 'no implementation defined for command'.freeze
|
9
|
+
|
10
|
+
def initialize message = nil
|
11
|
+
super(message || DEFAULT_MESSAGE)
|
12
|
+
end # constructor
|
13
|
+
end # class
|
14
|
+
end # module
|
data/lib/cuprum/operation.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'cuprum/
|
1
|
+
require 'cuprum/command'
|
2
2
|
|
3
3
|
module Cuprum
|
4
4
|
# Functional object that with syntactic sugar for tracking the last result.
|
@@ -32,13 +32,13 @@ module Cuprum
|
|
32
32
|
# implementation block to the constructor or by creating a subclass that
|
33
33
|
# overwrites the #process method.
|
34
34
|
#
|
35
|
-
# @see Cuprum::
|
36
|
-
class Operation < Cuprum::
|
35
|
+
# @see Cuprum::Command
|
36
|
+
class Operation < Cuprum::Command
|
37
37
|
# Module-based implementation of the Operation methods. Use this to convert
|
38
38
|
# an already-defined function into an operation.
|
39
39
|
#
|
40
40
|
# @example
|
41
|
-
# class CustomOperation <
|
41
|
+
# class CustomOperation < CustomCommand
|
42
42
|
# include Cuprum::Operation::Mixin
|
43
43
|
# end # class
|
44
44
|
module Mixin
|
@@ -60,11 +60,11 @@ module Cuprum
|
|
60
60
|
# @yield If a block argument is given, it will be passed to the
|
61
61
|
# implementation.
|
62
62
|
#
|
63
|
-
# @raise [NotImplementedError] Unless a block was passed to the
|
64
|
-
# constructor or the #process method was overriden by a
|
63
|
+
# @raise [Cuprum::NotImplementedError] Unless a block was passed to the
|
64
|
+
# constructor or the #process method was overriden by a Command
|
65
65
|
# subclass.
|
66
66
|
#
|
67
|
-
# @see Cuprum::
|
67
|
+
# @see Cuprum::Command#call
|
68
68
|
def call *args, &block
|
69
69
|
reset! if called? # Clear reference to most recent result.
|
70
70
|
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'cuprum/built_in/null_function'
|
2
1
|
require 'cuprum/utils'
|
3
2
|
|
4
3
|
module Cuprum::Utils
|
@@ -9,26 +8,26 @@ module Cuprum::Utils
|
|
9
8
|
# call a function instance.
|
10
9
|
#
|
11
10
|
# @example Observing calls to instances of a function.
|
12
|
-
# spy = Cuprum::Utils::InstanceSpy.spy_on(
|
11
|
+
# spy = Cuprum::Utils::InstanceSpy.spy_on(CustomCommand)
|
13
12
|
#
|
14
13
|
# expect(spy).to receive(:call).with(1, 2, 3, :four => '4')
|
15
14
|
#
|
16
|
-
#
|
15
|
+
# CustomCommand.new.call(1, 2, 3, :four => '4')
|
17
16
|
#
|
18
17
|
# @example Observing calls to a chained function.
|
19
|
-
# spy = Cuprum::Utils::InstanceSpy.spy_on(
|
18
|
+
# spy = Cuprum::Utils::InstanceSpy.spy_on(ChainedCommand)
|
20
19
|
#
|
21
20
|
# expect(spy).to receive(:call)
|
22
21
|
#
|
23
|
-
# Cuprum::
|
24
|
-
# chain { |result|
|
22
|
+
# Cuprum::Command.new {}.
|
23
|
+
# chain { |result| ChainedCommand.new.call(result) }.
|
25
24
|
# call
|
26
25
|
#
|
27
26
|
# @example Block syntax
|
28
|
-
# Cuprum::Utils::InstanceSpy.spy_on(
|
27
|
+
# Cuprum::Utils::InstanceSpy.spy_on(CustomCommand) do |spy|
|
29
28
|
# expect(spy).to receive(:call)
|
30
29
|
#
|
31
|
-
#
|
30
|
+
# CustomCommand.new.call
|
32
31
|
# end # spy_on
|
33
32
|
module InstanceSpy
|
34
33
|
# Minimal class that implements a #call method to mirror method calls to
|
@@ -52,13 +51,13 @@ module Cuprum::Utils
|
|
52
51
|
# spy's #call method will be invoked with the same arguments and block.
|
53
52
|
#
|
54
53
|
# @param function_class [Class, Module] The type of function to spy on.
|
55
|
-
# Must be either a Module, or a Class that extends Cuprum::
|
54
|
+
# Must be either a Module, or a Class that extends Cuprum::Command.
|
56
55
|
#
|
57
56
|
# @raise [ArgumentError] If the argument is neither a Module nor a Class
|
58
|
-
# that extends Cuprum::
|
57
|
+
# that extends Cuprum::Command.
|
59
58
|
#
|
60
59
|
# @note Calling this method for the first time will prepend the
|
61
|
-
# Cuprum::Utils::InstanceSpy module to Cuprum::
|
60
|
+
# Cuprum::Utils::InstanceSpy module to Cuprum::Command.
|
62
61
|
#
|
63
62
|
# @overload spy_on(function_class)
|
64
63
|
# @return [Cuprum::Utils::InstanceSpy::Spy] The instance spy.
|
@@ -107,17 +106,17 @@ module Cuprum::Utils
|
|
107
106
|
return if function_class.is_a?(Module) && !function_class.is_a?(Class)
|
108
107
|
|
109
108
|
return if function_class.is_a?(Class) &&
|
110
|
-
function_class <= Cuprum::
|
109
|
+
function_class <= Cuprum::Command
|
111
110
|
|
112
111
|
raise ArgumentError,
|
113
|
-
'must be a class inheriting from Cuprum::
|
112
|
+
'must be a class inheriting from Cuprum::Command',
|
114
113
|
caller(1..-1)
|
115
114
|
end # method guard_spy_class!
|
116
115
|
|
117
116
|
def instrument_call!
|
118
|
-
return if Cuprum::
|
117
|
+
return if Cuprum::Command < Cuprum::Utils::InstanceSpy
|
119
118
|
|
120
|
-
Cuprum::
|
119
|
+
Cuprum::Command.prepend(Cuprum::Utils::InstanceSpy)
|
121
120
|
end # method instrument_call!
|
122
121
|
|
123
122
|
def spies
|
@@ -129,7 +128,7 @@ module Cuprum::Utils
|
|
129
128
|
end # method spies_for
|
130
129
|
end # eigenclass
|
131
130
|
|
132
|
-
# (see Cuprum::
|
131
|
+
# (see Cuprum::Command#call)
|
133
132
|
def call *args, &block
|
134
133
|
Cuprum::Utils::InstanceSpy.send(:call_spies_for, self, *args, &block)
|
135
134
|
|
data/lib/cuprum/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cuprum
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob "Merlin" Smith
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-11-
|
11
|
+
date: 2017-11-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -80,8 +80,9 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0.15'
|
83
|
-
description:
|
84
|
-
|
83
|
+
description: An opinionated implementation of the Command pattern for Ruby applications.
|
84
|
+
Cuprum wraps your business logic in a consistent, object-oriented interface and
|
85
|
+
features status and error management, composability and control flow management.
|
85
86
|
email:
|
86
87
|
- merlin@sleepingkingstudios.com
|
87
88
|
executables: []
|
@@ -93,12 +94,15 @@ files:
|
|
93
94
|
- LICENSE
|
94
95
|
- README.md
|
95
96
|
- lib/cuprum.rb
|
97
|
+
- lib/cuprum/basic_command.rb
|
96
98
|
- lib/cuprum/built_in.rb
|
97
|
-
- lib/cuprum/built_in/
|
99
|
+
- lib/cuprum/built_in/identity_command.rb
|
98
100
|
- lib/cuprum/built_in/identity_operation.rb
|
99
|
-
- lib/cuprum/built_in/
|
101
|
+
- lib/cuprum/built_in/null_command.rb
|
100
102
|
- lib/cuprum/built_in/null_operation.rb
|
101
|
-
- lib/cuprum/
|
103
|
+
- lib/cuprum/chaining.rb
|
104
|
+
- lib/cuprum/command.rb
|
105
|
+
- lib/cuprum/not_implemented_error.rb
|
102
106
|
- lib/cuprum/operation.rb
|
103
107
|
- lib/cuprum/result.rb
|
104
108
|
- lib/cuprum/utils.rb
|
@@ -127,5 +131,5 @@ rubyforge_project:
|
|
127
131
|
rubygems_version: 2.6.13
|
128
132
|
signing_key:
|
129
133
|
specification_version: 4
|
130
|
-
summary:
|
134
|
+
summary: An opinionated implementation of the Command pattern.
|
131
135
|
test_files: []
|
data/lib/cuprum/function.rb
DELETED
@@ -1,447 +0,0 @@
|
|
1
|
-
require 'cuprum/result'
|
2
|
-
|
3
|
-
module Cuprum
|
4
|
-
# Functional object that encapsulates a business logic operation with a
|
5
|
-
# consistent interface and tracking of result value and status.
|
6
|
-
#
|
7
|
-
# A Function can be defined either by passing a block to the constructor, or
|
8
|
-
# by defining a subclass of Function and implementing the #process method.
|
9
|
-
#
|
10
|
-
# @example A Function with a block
|
11
|
-
# double_function = Cuprum::Function.new { |int| 2 * int }
|
12
|
-
# result = double_function.call(5)
|
13
|
-
#
|
14
|
-
# result.value #=> 10
|
15
|
-
#
|
16
|
-
# @example A Function subclass
|
17
|
-
# class MultiplyFunction < Cuprum::Function
|
18
|
-
# def initialize multiplier
|
19
|
-
# @multiplier = multiplier
|
20
|
-
# end # constructor
|
21
|
-
#
|
22
|
-
# private
|
23
|
-
#
|
24
|
-
# def process int
|
25
|
-
# int * @multiplier
|
26
|
-
# end # method process
|
27
|
-
# end # class
|
28
|
-
#
|
29
|
-
# triple_function = MultiplyFunction.new(3)
|
30
|
-
# result = triple_function.call(5)
|
31
|
-
#
|
32
|
-
# result.value #=> 15
|
33
|
-
#
|
34
|
-
# @example A Function with errors
|
35
|
-
# class DivideFunction < Cuprum::Function
|
36
|
-
# def initialize divisor
|
37
|
-
# @divisor = divisor
|
38
|
-
# end # constructor
|
39
|
-
#
|
40
|
-
# private
|
41
|
-
#
|
42
|
-
# def process int
|
43
|
-
# if @divisor.zero?
|
44
|
-
# errors << 'errors.messages.divide_by_zero'
|
45
|
-
#
|
46
|
-
# return
|
47
|
-
# end # if
|
48
|
-
#
|
49
|
-
# int / @divisor
|
50
|
-
# end # method process
|
51
|
-
# end # class
|
52
|
-
#
|
53
|
-
# halve_function = DivideFunction.new(2)
|
54
|
-
# result = halve_function.call(10)
|
55
|
-
#
|
56
|
-
# result.errors #=> []
|
57
|
-
# result.value #=> 5
|
58
|
-
#
|
59
|
-
# function_with_errors = DivideFunction.new(0)
|
60
|
-
# result = function_with_errors.call(10)
|
61
|
-
#
|
62
|
-
# result.errors #=> ['errors.messages.divide_by_zero']
|
63
|
-
# result.value #=> nil
|
64
|
-
#
|
65
|
-
# @example Function Chaining
|
66
|
-
# class AddFunction < Cuprum::Function
|
67
|
-
# def initialize addend
|
68
|
-
# @addend = addend
|
69
|
-
# end # constructor
|
70
|
-
#
|
71
|
-
# private
|
72
|
-
#
|
73
|
-
# def process int
|
74
|
-
# int + @addend
|
75
|
-
# end # method process
|
76
|
-
# end # class
|
77
|
-
#
|
78
|
-
# double_and_add_one = MultiplyFunction.new(2).chain(AddFunction.new(1))
|
79
|
-
# result = double_and_add_one(5)
|
80
|
-
#
|
81
|
-
# result.value #=> 5
|
82
|
-
#
|
83
|
-
# @example Conditional Chaining With #then And #else
|
84
|
-
# class EvenFunction < Cuprum::Function
|
85
|
-
# private
|
86
|
-
#
|
87
|
-
# def process int
|
88
|
-
# errors << 'errors.messages.not_even' unless int.even?
|
89
|
-
#
|
90
|
-
# int
|
91
|
-
# end # method process
|
92
|
-
# end # class
|
93
|
-
#
|
94
|
-
# # The next step in a Collatz sequence is determined as follows:
|
95
|
-
# # - If the number is even, divide it by 2.
|
96
|
-
# # - If the number is odd, multiply it by 3 and add 1.
|
97
|
-
# collatz_function =
|
98
|
-
# EvenFunction.new.
|
99
|
-
# then(DivideFunction.new(2)).
|
100
|
-
# else(MultiplyFunction.new(3).chain(AddFunction.new(1)))
|
101
|
-
#
|
102
|
-
# result = collatz_function.new(5)
|
103
|
-
# result.value #=> 16
|
104
|
-
#
|
105
|
-
# result = collatz_function.new(16)
|
106
|
-
# result.value #=> 8
|
107
|
-
class Function # rubocop:disable Metrics/ClassLength
|
108
|
-
# Error class for calling a Function that was not given a definition block
|
109
|
-
# or have a #process method defined.
|
110
|
-
class NotImplementedError < StandardError
|
111
|
-
# Error message for a NotImplementedError.
|
112
|
-
DEFAULT_MESSAGE = 'no implementation defined for function'.freeze
|
113
|
-
|
114
|
-
def initialize message = nil
|
115
|
-
super(message || DEFAULT_MESSAGE)
|
116
|
-
end # constructor
|
117
|
-
end # class
|
118
|
-
|
119
|
-
# Returns a new instance of Cuprum::Function.
|
120
|
-
#
|
121
|
-
# @yield [*arguments, **keywords, &block] If a block is given, the
|
122
|
-
# #call method will wrap the block and set the result #value to the return
|
123
|
-
# value of the block. This overrides the implementation in #process, if
|
124
|
-
# any.
|
125
|
-
def initialize &implementation
|
126
|
-
define_singleton_method :process, &implementation if implementation
|
127
|
-
end # method initialize
|
128
|
-
|
129
|
-
# @overload call(*arguments, **keywords, &block)
|
130
|
-
# Executes the logic encoded in the constructor block, or the #process
|
131
|
-
# method if no block was passed to the constructor, and returns a
|
132
|
-
# Cuprum::Result object with the return value of the block or #process,
|
133
|
-
# the success or failure status, and any errors generated.
|
134
|
-
#
|
135
|
-
# @param arguments [Array] Arguments to be passed to the implementation.
|
136
|
-
#
|
137
|
-
# @param keywords [Hash] Keywords to be passed to the implementation.
|
138
|
-
#
|
139
|
-
# @return [Cuprum::Result] The result object for the function.
|
140
|
-
#
|
141
|
-
# @yield If a block argument is given, it will be passed to the
|
142
|
-
# implementation.
|
143
|
-
#
|
144
|
-
# @raise [NotImplementedError] Unless a block was passed to the
|
145
|
-
# constructor or the #process method was overriden by a Function
|
146
|
-
# subclass.
|
147
|
-
def call *args, &block
|
148
|
-
call_chained_functions do
|
149
|
-
wrap_result do |result|
|
150
|
-
merge_results(result, process(*args, &block))
|
151
|
-
end # method wrap_result
|
152
|
-
end # call_chained_functions
|
153
|
-
end # method call
|
154
|
-
|
155
|
-
# Registers a function or block to run after the current function, or after
|
156
|
-
# the last chained function if the current function already has one or more
|
157
|
-
# chained function(s). This creates and modifies a copy of the current
|
158
|
-
# function.
|
159
|
-
#
|
160
|
-
# @param on [Symbol] Sets a condition on when the chained function can run,
|
161
|
-
# based on the status of the previous function. Valid values are :success,
|
162
|
-
# :failure, and :always. A value of :success will constrain the function
|
163
|
-
# to run only if the previous function succeeded. A value of :failure will
|
164
|
-
# constrain the function to run only if the previous function failed. A
|
165
|
-
# value of :always will ensure the function is always run, even if the
|
166
|
-
# function chain has been halted. If no value is given, the function will
|
167
|
-
# run whether the previous function was a success or a failure, but not if
|
168
|
-
# the function chain has been halted.
|
169
|
-
#
|
170
|
-
# @overload chain(function, on: nil)
|
171
|
-
# The function will be passed the #value of the previous function result
|
172
|
-
# as its parameter, and the result of the chained function will be
|
173
|
-
# returned (or passed to the next chained function, if any).
|
174
|
-
#
|
175
|
-
# @param function [Cuprum::Function] The function to call after the
|
176
|
-
# current or last chained function.
|
177
|
-
#
|
178
|
-
# @overload chain(on: :nil, &block)
|
179
|
-
# The block will be passed the #result of the previous function as its
|
180
|
-
# parameter. If your use case depends on the status of the previous
|
181
|
-
# function or on any errors generated, use the block form of #chain.
|
182
|
-
#
|
183
|
-
# If the block returns a Cuprum::Result (or an object responding to #value
|
184
|
-
# and #success?), the block result will be returned (or passed to the next
|
185
|
-
# chained function, if any). If the block returns any other value
|
186
|
-
# (including nil), the #result of the previous function will be returned
|
187
|
-
# or passed to the next function.
|
188
|
-
#
|
189
|
-
# @yieldparam result [Cuprum::Result] The #result of the previous
|
190
|
-
# function.
|
191
|
-
#
|
192
|
-
# @return [Cuprum::Function] The chained function.
|
193
|
-
def chain function = nil, on: nil, &block
|
194
|
-
clone.tap do |fn|
|
195
|
-
fn.chained_functions << build_chain_link(block || function, :on => on)
|
196
|
-
end # tap
|
197
|
-
end # method chain
|
198
|
-
|
199
|
-
# Shorthand for function.chain(:on => :failure). Registers a function or
|
200
|
-
# block to run after the current function. The chained function will only
|
201
|
-
# run if the previous function was unsuccessfully run.
|
202
|
-
#
|
203
|
-
# @overload else(function)
|
204
|
-
#
|
205
|
-
# @param function [Cuprum::Function] The function to call after the
|
206
|
-
# current or last chained function.
|
207
|
-
#
|
208
|
-
# @overload else(&block)
|
209
|
-
#
|
210
|
-
# @yieldparam result [Cuprum::Result] The #result of the previous
|
211
|
-
# function.
|
212
|
-
#
|
213
|
-
# @return [Cuprum::Function] The chained function.
|
214
|
-
#
|
215
|
-
# @see #chain
|
216
|
-
def else function = nil, &block
|
217
|
-
chain(function, :on => :failure, &block)
|
218
|
-
end # method else
|
219
|
-
|
220
|
-
# Shorthand for function.chain(:on => :success). Registers a function or
|
221
|
-
# block to run after the current function. The chained function will only
|
222
|
-
# run if the previous function was successfully run.
|
223
|
-
#
|
224
|
-
# @overload then(function)
|
225
|
-
#
|
226
|
-
# @param function [Cuprum::Function] The function to call after the
|
227
|
-
# current or last chained function.
|
228
|
-
#
|
229
|
-
# @overload then(&block)
|
230
|
-
#
|
231
|
-
# @yieldparam result [Cuprum::Result] The #result of the previous
|
232
|
-
# function.
|
233
|
-
#
|
234
|
-
# @return [Cuprum::Function] The chained function.
|
235
|
-
#
|
236
|
-
# @see #chain
|
237
|
-
def then function = nil, &block
|
238
|
-
chain(function, :on => :success, &block)
|
239
|
-
end # method then
|
240
|
-
|
241
|
-
protected
|
242
|
-
|
243
|
-
def chained_functions
|
244
|
-
@chained_functions ||= []
|
245
|
-
end # method chained_functions
|
246
|
-
|
247
|
-
private
|
248
|
-
|
249
|
-
def build_chain_link function_or_proc, on: nil
|
250
|
-
{
|
251
|
-
:proc => convert_function_or_proc_to_proc(function_or_proc),
|
252
|
-
:on => on
|
253
|
-
} # end hash
|
254
|
-
end # method build_chain_link
|
255
|
-
|
256
|
-
# @!visibility public
|
257
|
-
#
|
258
|
-
# Generates an empty errors object. When the function is called, the result
|
259
|
-
# will have its #errors property initialized to the value returned by
|
260
|
-
# #build_errors.By default, this is an array. If you want to use a custom
|
261
|
-
# errors object type, override this method in a subclass.
|
262
|
-
#
|
263
|
-
# @return [Array] an empty errors object.
|
264
|
-
def build_errors
|
265
|
-
[]
|
266
|
-
end # method build_errors
|
267
|
-
|
268
|
-
def call_chained_functions
|
269
|
-
chained_functions.reduce(yield) do |result, hsh|
|
270
|
-
next result if skip_chained_function?(result, :on => hsh[:on])
|
271
|
-
|
272
|
-
value = hsh.fetch(:proc).call(result)
|
273
|
-
|
274
|
-
convert_value_to_result(value) || result
|
275
|
-
end # reduce
|
276
|
-
end # method call_chained_functions
|
277
|
-
|
278
|
-
def convert_function_or_proc_to_proc function_or_proc
|
279
|
-
return function_or_proc if function_or_proc.is_a?(Proc)
|
280
|
-
|
281
|
-
->(result) { function_or_proc.call(result) }
|
282
|
-
end # method convert_function_or_proc_to_proc
|
283
|
-
|
284
|
-
def convert_value_to_result value
|
285
|
-
return nil unless value_is_result?(value)
|
286
|
-
|
287
|
-
if value.respond_to?(:result) && value_is_result?(value.result)
|
288
|
-
return value.result
|
289
|
-
end # if
|
290
|
-
|
291
|
-
value
|
292
|
-
end # method convert_value_to_result
|
293
|
-
|
294
|
-
# @!visibility public
|
295
|
-
#
|
296
|
-
# Provides a reference to the current result's errors object. Messages or
|
297
|
-
# error objects added to this will be included in the #errors method of the
|
298
|
-
# returned result object.
|
299
|
-
#
|
300
|
-
# @return [Array, Object] the errors object.
|
301
|
-
#
|
302
|
-
# @see Cuprum::Result#errors.
|
303
|
-
#
|
304
|
-
# @note This is a private method, and only available when executing the
|
305
|
-
# function implementation as defined in the constructor block or the
|
306
|
-
# #process method.
|
307
|
-
def errors
|
308
|
-
@result&.errors
|
309
|
-
end # method errors
|
310
|
-
|
311
|
-
# @!visibility public
|
312
|
-
#
|
313
|
-
# Marks the current result as failed. Calling #failure? on the returned
|
314
|
-
# result object will evaluate to true, whether or not the result has any
|
315
|
-
# errors.
|
316
|
-
#
|
317
|
-
# @see Cuprum::Result#failure!.
|
318
|
-
#
|
319
|
-
# @note This is a private method, and only available when executing the
|
320
|
-
# function implementation as defined in the constructor block or the
|
321
|
-
# #process method.
|
322
|
-
def failure!
|
323
|
-
@result&.failure!
|
324
|
-
end # method failure!
|
325
|
-
|
326
|
-
def halt!
|
327
|
-
@result&.halt!
|
328
|
-
end # method halt!
|
329
|
-
|
330
|
-
# :nocov:
|
331
|
-
def humanize_list list, empty_value: ''
|
332
|
-
return empty_value if list.size.zero?
|
333
|
-
|
334
|
-
return list.first.to_s if list.size == 1
|
335
|
-
|
336
|
-
return "#{list.first} and #{list.last}" if list.size == 2
|
337
|
-
|
338
|
-
"#{list[0...-1].join ', '}, and #{list.last}"
|
339
|
-
end # method humanize_list
|
340
|
-
# :nocov:
|
341
|
-
|
342
|
-
def merge_results result, other
|
343
|
-
if value_is_result?(other)
|
344
|
-
Cuprum.warn(result_not_empty_warning) unless result.empty?
|
345
|
-
|
346
|
-
convert_value_to_result(other)
|
347
|
-
else
|
348
|
-
result.value = other
|
349
|
-
|
350
|
-
result
|
351
|
-
end # if-else
|
352
|
-
end # method merge_results
|
353
|
-
|
354
|
-
# @!visibility public
|
355
|
-
# @overload process(*arguments, **keywords, &block)
|
356
|
-
# The implementation of the function, to be executed when the #call method
|
357
|
-
# is called. Can add errors to or set the status of the result, and the
|
358
|
-
# value of the result will be set to the value returned by #process. Do
|
359
|
-
# not call this method directly.
|
360
|
-
#
|
361
|
-
# @param arguments [Array] The arguments, if any, passed from #call.
|
362
|
-
#
|
363
|
-
# @param keywords [Hash] The keywords, if any, passed from #call.
|
364
|
-
#
|
365
|
-
# @yield The block, if any, passed from #call.
|
366
|
-
#
|
367
|
-
# @return [Object] the value of the result object to be returned by #call.
|
368
|
-
#
|
369
|
-
# @raise NotImplementedError
|
370
|
-
#
|
371
|
-
# @note This is a private method.
|
372
|
-
def process *_args
|
373
|
-
raise NotImplementedError, nil, caller(1..-1)
|
374
|
-
end # method process
|
375
|
-
|
376
|
-
def result_not_empty_warning # rubocop:disable Metrics/MethodLength
|
377
|
-
warnings = []
|
378
|
-
|
379
|
-
unless @result.errors.empty?
|
380
|
-
warnings << "there were already errors #{@result.errors.inspect}"
|
381
|
-
end # unless
|
382
|
-
|
383
|
-
status = @result.send(:status)
|
384
|
-
unless status.nil?
|
385
|
-
warnings << "the status was set to #{status.inspect}"
|
386
|
-
end # unless
|
387
|
-
|
388
|
-
if @result.halted?
|
389
|
-
warnings << 'the function was halted'
|
390
|
-
end # if
|
391
|
-
|
392
|
-
message = '#process returned a result, but '
|
393
|
-
message <<
|
394
|
-
humanize_list(warnings, :empty_value => 'the result was not empty')
|
395
|
-
|
396
|
-
message
|
397
|
-
end # method result_not_empty_warning
|
398
|
-
|
399
|
-
def skip_chained_function? last_result, on:
|
400
|
-
return false if on == :always
|
401
|
-
|
402
|
-
return true if last_result.respond_to?(:halted?) && last_result.halted?
|
403
|
-
|
404
|
-
case on
|
405
|
-
when :success
|
406
|
-
!last_result.success?
|
407
|
-
when :failure
|
408
|
-
!last_result.failure?
|
409
|
-
end # case
|
410
|
-
end # method skip_chained_function?
|
411
|
-
|
412
|
-
# @!visibility public
|
413
|
-
#
|
414
|
-
# Marks the current result as passing. Calling #success? on the returned
|
415
|
-
# result object will evaluate to true, whether or not the result has any
|
416
|
-
# errors.
|
417
|
-
#
|
418
|
-
# @see Cuprum::Result#success!.
|
419
|
-
#
|
420
|
-
# @note This is a private method, and only available when executing the
|
421
|
-
# function implementation as defined in the constructor block or the
|
422
|
-
# #process method.
|
423
|
-
def success!
|
424
|
-
@result&.success!
|
425
|
-
end # method success!
|
426
|
-
|
427
|
-
def value_is_result? value
|
428
|
-
value.respond_to?(:value) && value.respond_to?(:success?)
|
429
|
-
end # method value
|
430
|
-
|
431
|
-
def wrap_result
|
432
|
-
value = nil
|
433
|
-
|
434
|
-
Cuprum::Result.new(:errors => build_errors).tap do |result|
|
435
|
-
begin
|
436
|
-
@result = result
|
437
|
-
|
438
|
-
value = yield result
|
439
|
-
ensure
|
440
|
-
@result = nil
|
441
|
-
end # begin-ensure
|
442
|
-
end # tap
|
443
|
-
|
444
|
-
value
|
445
|
-
end # method wrap_result
|
446
|
-
end # class
|
447
|
-
end # module
|