cuprum 0.5.0 → 0.6.0
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/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
|