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,212 @@
|
|
1
|
+
require 'cuprum/not_implemented_error'
|
2
|
+
|
3
|
+
module Cuprum
|
4
|
+
# Functional object that encapsulates a business logic operation with a
|
5
|
+
# standardized interface and returns a result object.
|
6
|
+
class BasicCommand
|
7
|
+
# Returns a new instance of Cuprum::BasicCommand.
|
8
|
+
#
|
9
|
+
# @yield [*arguments, **keywords, &block] If a block is given, the
|
10
|
+
# #call method will wrap the block and set the result #value to the return
|
11
|
+
# value of the block. This overrides the implementation in #process, if
|
12
|
+
# any.
|
13
|
+
def initialize &implementation
|
14
|
+
define_singleton_method :process, &implementation if implementation
|
15
|
+
end # method initialize
|
16
|
+
|
17
|
+
# @overload call(*arguments, **keywords, &block)
|
18
|
+
# Executes the logic encoded in the constructor block, or the #process
|
19
|
+
# method if no block was passed to the constructor, and returns a
|
20
|
+
# Cuprum::Result object with the return value of the block or #process,
|
21
|
+
# the success or failure status, and any errors generated.
|
22
|
+
#
|
23
|
+
# @param arguments [Array] Arguments to be passed to the implementation.
|
24
|
+
#
|
25
|
+
# @param keywords [Hash] Keywords to be passed to the implementation.
|
26
|
+
#
|
27
|
+
# @return [Cuprum::Result] The result object for the command.
|
28
|
+
#
|
29
|
+
# @yield If a block argument is given, it will be passed to the
|
30
|
+
# implementation.
|
31
|
+
#
|
32
|
+
# @raise [Cuprum::NotImplementedError] Unless a block was passed to the
|
33
|
+
# constructor or the #process method was overriden by a Command
|
34
|
+
# subclass.
|
35
|
+
def call *args, &block
|
36
|
+
wrap_result { |result| merge_results(result, process(*args, &block)) }
|
37
|
+
end # method call
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# @!visibility public
|
42
|
+
#
|
43
|
+
# Generates an empty errors object. When the function is called, the result
|
44
|
+
# will have its #errors property initialized to the value returned by
|
45
|
+
# #build_errors. By default, this is an array. If you want to use a custom
|
46
|
+
# errors object type, override this method in a subclass.
|
47
|
+
#
|
48
|
+
# @return [Array] An empty errors object.
|
49
|
+
def build_errors
|
50
|
+
[]
|
51
|
+
end # method build_errors
|
52
|
+
|
53
|
+
def convert_value_to_result value
|
54
|
+
return nil unless value_is_result?(value)
|
55
|
+
|
56
|
+
if value.respond_to?(:result) && value_is_result?(value.result)
|
57
|
+
return value.result
|
58
|
+
end # if
|
59
|
+
|
60
|
+
value
|
61
|
+
end # method convert_value_to_result
|
62
|
+
|
63
|
+
# @!visibility public
|
64
|
+
#
|
65
|
+
# Provides a reference to the current result's errors object. Messages or
|
66
|
+
# error objects added to this will be included in the #errors method of the
|
67
|
+
# returned result object.
|
68
|
+
#
|
69
|
+
# @return [Array, Object] The errors object.
|
70
|
+
#
|
71
|
+
# @see Cuprum::Result#errors.
|
72
|
+
#
|
73
|
+
# @note This is a private method, and only available when executing the
|
74
|
+
# function implementation as defined in the constructor block or the
|
75
|
+
# #process method.
|
76
|
+
def errors
|
77
|
+
@result&.errors
|
78
|
+
end # method errors
|
79
|
+
|
80
|
+
# @!visibility public
|
81
|
+
#
|
82
|
+
# Marks the current result as failed. Calling #failure? on the returned
|
83
|
+
# result object will evaluate to true, whether or not the result has any
|
84
|
+
# errors.
|
85
|
+
#
|
86
|
+
# @see Cuprum::Result#failure!.
|
87
|
+
#
|
88
|
+
# @note This is a private method, and only available when executing the
|
89
|
+
# function implementation as defined in the constructor block or the
|
90
|
+
# #process method.
|
91
|
+
def failure!
|
92
|
+
@result&.failure!
|
93
|
+
end # method failure!
|
94
|
+
|
95
|
+
# @!visibility public
|
96
|
+
#
|
97
|
+
# Marks the current result as halted.
|
98
|
+
#
|
99
|
+
# @see Cuprum::Result#halt!.
|
100
|
+
#
|
101
|
+
# @note This is a private method, and only available when executing the
|
102
|
+
# function implementation as defined in the constructor block or the
|
103
|
+
# #process method.
|
104
|
+
def halt!
|
105
|
+
@result&.halt!
|
106
|
+
end # method halt!
|
107
|
+
|
108
|
+
# :nocov:
|
109
|
+
def humanize_list list, empty_value: ''
|
110
|
+
return empty_value if list.size.zero?
|
111
|
+
|
112
|
+
return list.first.to_s if list.size == 1
|
113
|
+
|
114
|
+
return "#{list.first} and #{list.last}" if list.size == 2
|
115
|
+
|
116
|
+
"#{list[0...-1].join ', '}, and #{list.last}"
|
117
|
+
end # method humanize_list
|
118
|
+
# :nocov:
|
119
|
+
|
120
|
+
def merge_results result, other
|
121
|
+
if value_is_result?(other)
|
122
|
+
Cuprum.warn(result_not_empty_warning) unless result.empty?
|
123
|
+
|
124
|
+
convert_value_to_result(other)
|
125
|
+
else
|
126
|
+
result.value = other
|
127
|
+
|
128
|
+
result
|
129
|
+
end # if-else
|
130
|
+
end # method merge_results
|
131
|
+
|
132
|
+
# @!visibility public
|
133
|
+
# @overload process(*arguments, **keywords, &block)
|
134
|
+
# The implementation of the function, to be executed when the #call method
|
135
|
+
# is called. Can add errors to or set the status of the result, and the
|
136
|
+
# value of the result will be set to the value returned by #process. Do
|
137
|
+
# not call this method directly.
|
138
|
+
#
|
139
|
+
# @param arguments [Array] The arguments, if any, passed from #call.
|
140
|
+
#
|
141
|
+
# @param keywords [Hash] The keywords, if any, passed from #call.
|
142
|
+
#
|
143
|
+
# @yield The block, if any, passed from #call.
|
144
|
+
#
|
145
|
+
# @return [Object] the value of the result object to be returned by #call.
|
146
|
+
#
|
147
|
+
# @raise [Cuprum::NotImplementedError] Unless a block was passed to the
|
148
|
+
# constructor or the #process method was overriden by a Command
|
149
|
+
# subclass.
|
150
|
+
#
|
151
|
+
# @note This is a private method.
|
152
|
+
def process *_args
|
153
|
+
raise Cuprum::NotImplementedError, nil, caller(1..-1)
|
154
|
+
end # method process
|
155
|
+
|
156
|
+
def result_not_empty_warning # rubocop:disable Metrics/MethodLength
|
157
|
+
warnings = []
|
158
|
+
|
159
|
+
unless @result.errors.empty?
|
160
|
+
warnings << "there were already errors #{@result.errors.inspect}"
|
161
|
+
end # unless
|
162
|
+
|
163
|
+
status = @result.send(:status)
|
164
|
+
unless status.nil?
|
165
|
+
warnings << "the status was set to #{status.inspect}"
|
166
|
+
end # unless
|
167
|
+
|
168
|
+
if @result.halted?
|
169
|
+
warnings << 'the function was halted'
|
170
|
+
end # if
|
171
|
+
|
172
|
+
message = '#process returned a result, but '
|
173
|
+
message <<
|
174
|
+
humanize_list(warnings, :empty_value => 'the result was not empty')
|
175
|
+
|
176
|
+
message
|
177
|
+
end # method result_not_empty_warning
|
178
|
+
|
179
|
+
# @!visibility public
|
180
|
+
#
|
181
|
+
# Marks the current result as passing. Calling #success? on the returned
|
182
|
+
# result object will evaluate to true, whether or not the result has any
|
183
|
+
# errors.
|
184
|
+
#
|
185
|
+
# @see Cuprum::Result#success!.
|
186
|
+
#
|
187
|
+
# @note This is a private method, and only available when executing the
|
188
|
+
# function implementation as defined in the constructor block or the
|
189
|
+
# #process method.
|
190
|
+
def success!
|
191
|
+
@result&.success!
|
192
|
+
end # method success!
|
193
|
+
|
194
|
+
def value_is_result? value
|
195
|
+
value.respond_to?(:value) && value.respond_to?(:success?)
|
196
|
+
end # method value
|
197
|
+
|
198
|
+
def wrap_result
|
199
|
+
result = Cuprum::Result.new(:errors => build_errors)
|
200
|
+
|
201
|
+
begin
|
202
|
+
@result = result
|
203
|
+
|
204
|
+
result = yield result
|
205
|
+
ensure
|
206
|
+
@result = nil
|
207
|
+
end # begin-ensure
|
208
|
+
|
209
|
+
result
|
210
|
+
end # method wrap_result
|
211
|
+
end # class
|
212
|
+
end # module
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'cuprum/built_in'
|
2
|
-
require 'cuprum/
|
2
|
+
require 'cuprum/command'
|
3
3
|
|
4
4
|
module Cuprum::BuiltIn
|
5
5
|
# A predefined function that returns the value or result it was called with.
|
6
6
|
#
|
7
7
|
# @example With a value.
|
8
|
-
# result =
|
8
|
+
# result = IdentityCommand.new.call('custom value')
|
9
9
|
# result.value
|
10
10
|
# #=> 'custom value'
|
11
11
|
# result.success?
|
@@ -14,14 +14,14 @@ module Cuprum::BuiltIn
|
|
14
14
|
# @example With a result.
|
15
15
|
# errors = ['errors.messages.unknown']
|
16
16
|
# value = Cuprum::Result.new('result value', :errors => errors)
|
17
|
-
# result =
|
17
|
+
# result = IdentityCommand.new.call(value)
|
18
18
|
# result.value
|
19
19
|
# #=> 'result value'
|
20
20
|
# result.success?
|
21
21
|
# #=> false
|
22
22
|
# result.errors
|
23
23
|
# #=> ['errors.messages.unknown']
|
24
|
-
class
|
24
|
+
class IdentityCommand < Cuprum::Command
|
25
25
|
private
|
26
26
|
|
27
27
|
def process value = nil
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'cuprum/built_in/
|
1
|
+
require 'cuprum/built_in/identity_command'
|
2
2
|
require 'cuprum/operation'
|
3
3
|
|
4
4
|
module Cuprum::BuiltIn
|
@@ -21,7 +21,7 @@ module Cuprum::BuiltIn
|
|
21
21
|
# #=> false
|
22
22
|
# operation.errors
|
23
23
|
# #=> ['errors.messages.unknown']
|
24
|
-
class IdentityOperation < Cuprum::BuiltIn::
|
24
|
+
class IdentityOperation < Cuprum::BuiltIn::IdentityCommand
|
25
25
|
include Cuprum::Operation::Mixin
|
26
26
|
end # class
|
27
27
|
end # module
|
@@ -1,16 +1,16 @@
|
|
1
1
|
require 'cuprum/built_in'
|
2
|
-
require 'cuprum/
|
2
|
+
require 'cuprum/command'
|
3
3
|
|
4
4
|
module Cuprum::BuiltIn
|
5
|
-
# A predefined
|
5
|
+
# A predefined command that does nothing when called.
|
6
6
|
#
|
7
7
|
# @example
|
8
|
-
# result =
|
8
|
+
# result = NullCommand.new.call
|
9
9
|
# result.value
|
10
10
|
# #=> nil
|
11
11
|
# result.success?
|
12
12
|
# #=> true
|
13
|
-
class
|
13
|
+
class NullCommand < Cuprum::Command
|
14
14
|
private
|
15
15
|
|
16
16
|
def process *_args; end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'cuprum/built_in/
|
1
|
+
require 'cuprum/built_in/null_command'
|
2
2
|
require 'cuprum/operation'
|
3
3
|
|
4
4
|
module Cuprum::BuiltIn
|
@@ -10,7 +10,7 @@ module Cuprum::BuiltIn
|
|
10
10
|
# #=> nil
|
11
11
|
# operation.success?
|
12
12
|
# #=> true
|
13
|
-
class NullOperation < Cuprum::BuiltIn::
|
13
|
+
class NullOperation < Cuprum::BuiltIn::NullCommand
|
14
14
|
include Cuprum::Operation::Mixin
|
15
15
|
end # class
|
16
16
|
end # module
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'cuprum'
|
2
|
+
|
3
|
+
module Cuprum
|
4
|
+
# Mixin to implement command chaining functionality for a command class.
|
5
|
+
# Chaining commands allows you to define complex logic by composing it from
|
6
|
+
# simpler commands, including branching logic and error handling.
|
7
|
+
#
|
8
|
+
# @see Cuprum::Command
|
9
|
+
module Chaining
|
10
|
+
# (see Cuprum::BasicCommand#call)
|
11
|
+
def call *args, &block
|
12
|
+
call_chained_functions(super)
|
13
|
+
end # method call
|
14
|
+
|
15
|
+
# Registers a function or block to run after the current function, or after
|
16
|
+
# the last chained function if the current function already has one or more
|
17
|
+
# chained function(s). This creates and modifies a copy of the current
|
18
|
+
# function.
|
19
|
+
#
|
20
|
+
# @param on [Symbol] Sets a condition on when the chained function can run,
|
21
|
+
# based on the status of the previous function. Valid values are :success,
|
22
|
+
# :failure, and :always. A value of :success will constrain the function
|
23
|
+
# to run only if the previous function succeeded. A value of :failure will
|
24
|
+
# constrain the function to run only if the previous function failed. A
|
25
|
+
# value of :always will ensure the function is always run, even if the
|
26
|
+
# function chain has been halted. If no value is given, the function will
|
27
|
+
# run whether the previous function was a success or a failure, but not if
|
28
|
+
# the function chain has been halted.
|
29
|
+
#
|
30
|
+
# @overload chain(function, on: nil)
|
31
|
+
# The function will be passed the #value of the previous function result
|
32
|
+
# as its parameter, and the result of the chained function will be
|
33
|
+
# returned (or passed to the next chained function, if any).
|
34
|
+
#
|
35
|
+
# @param function [Cuprum::Command] The function to call after the
|
36
|
+
# current or last chained function.
|
37
|
+
#
|
38
|
+
# @overload chain(on: :nil, &block)
|
39
|
+
# The block will be passed the #result of the previous function as its
|
40
|
+
# parameter. If your use case depends on the status of the previous
|
41
|
+
# function or on any errors generated, use the block form of #chain.
|
42
|
+
#
|
43
|
+
# If the block returns a Cuprum::Result (or an object responding to #value
|
44
|
+
# and #success?), the block result will be returned (or passed to the next
|
45
|
+
# chained function, if any). If the block returns any other value
|
46
|
+
# (including nil), the #result of the previous function will be returned
|
47
|
+
# or passed to the next function.
|
48
|
+
#
|
49
|
+
# @yieldparam result [Cuprum::Result] The #result of the previous
|
50
|
+
# function.
|
51
|
+
#
|
52
|
+
# @return [Cuprum::Command] The chained function.
|
53
|
+
def chain function = nil, on: nil, &block
|
54
|
+
clone.tap do |fn|
|
55
|
+
fn.chained_functions <<
|
56
|
+
{
|
57
|
+
:proc => convert_function_or_proc_to_proc(block || function),
|
58
|
+
:on => on
|
59
|
+
} # end hash
|
60
|
+
end # tap
|
61
|
+
end # method chain
|
62
|
+
|
63
|
+
# Shorthand for function.chain(:on => :failure). Registers a function or
|
64
|
+
# block to run after the current function. The chained function will only
|
65
|
+
# run if the previous function was unsuccessfully run.
|
66
|
+
#
|
67
|
+
# @overload else(function)
|
68
|
+
#
|
69
|
+
# @param function [Cuprum::Command] The function to call after the
|
70
|
+
# current or last chained function.
|
71
|
+
#
|
72
|
+
# @overload else(&block)
|
73
|
+
#
|
74
|
+
# @yieldparam result [Cuprum::Result] The #result of the previous
|
75
|
+
# function.
|
76
|
+
#
|
77
|
+
# @return [Cuprum::Command] The chained function.
|
78
|
+
#
|
79
|
+
# @see #chain
|
80
|
+
def else function = nil, &block
|
81
|
+
chain(function, :on => :failure, &block)
|
82
|
+
end # method else
|
83
|
+
|
84
|
+
# Shorthand for function.chain(:on => :success). Registers a function or
|
85
|
+
# block to run after the current function. The chained function will only
|
86
|
+
# run if the previous function was successfully run.
|
87
|
+
#
|
88
|
+
# @overload then(function)
|
89
|
+
#
|
90
|
+
# @param function [Cuprum::Command] The function to call after the
|
91
|
+
# current or last chained function.
|
92
|
+
#
|
93
|
+
# @overload then(&block)
|
94
|
+
#
|
95
|
+
# @yieldparam result [Cuprum::Result] The #result of the previous
|
96
|
+
# function.
|
97
|
+
#
|
98
|
+
# @return [Cuprum::Command] The chained function.
|
99
|
+
#
|
100
|
+
# @see #chain
|
101
|
+
def then function = nil, &block
|
102
|
+
chain(function, :on => :success, &block)
|
103
|
+
end # method then
|
104
|
+
|
105
|
+
protected
|
106
|
+
|
107
|
+
def chained_functions
|
108
|
+
@chained_functions ||= []
|
109
|
+
end # method chained_functions
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def call_chained_functions first_result
|
114
|
+
chained_functions.reduce(first_result) do |result, hsh|
|
115
|
+
next result if skip_chained_function?(result, :on => hsh[:on])
|
116
|
+
|
117
|
+
value = hsh.fetch(:proc).call(result)
|
118
|
+
|
119
|
+
convert_value_to_result(value) || result
|
120
|
+
end # reduce
|
121
|
+
end # method call_chained_functions
|
122
|
+
|
123
|
+
def convert_function_or_proc_to_proc function_or_proc
|
124
|
+
return function_or_proc if function_or_proc.is_a?(Proc)
|
125
|
+
|
126
|
+
->(result) { function_or_proc.call(result) }
|
127
|
+
end # method convert_function_or_proc_to_proc
|
128
|
+
|
129
|
+
def skip_chained_function? last_result, on:
|
130
|
+
return false if on == :always
|
131
|
+
|
132
|
+
return true if last_result.respond_to?(:halted?) && last_result.halted?
|
133
|
+
|
134
|
+
case on
|
135
|
+
when :success
|
136
|
+
!last_result.success?
|
137
|
+
when :failure
|
138
|
+
!last_result.failure?
|
139
|
+
end # case
|
140
|
+
end # method skip_chained_function?
|
141
|
+
end # module
|
142
|
+
end # modue
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'cuprum/basic_command'
|
2
|
+
require 'cuprum/chaining'
|
3
|
+
require 'cuprum/not_implemented_error'
|
4
|
+
require 'cuprum/result'
|
5
|
+
|
6
|
+
module Cuprum
|
7
|
+
# Functional object that encapsulates a business logic operation with a
|
8
|
+
# consistent interface and tracking of result value and status.
|
9
|
+
#
|
10
|
+
# A Command can be defined either by passing a block to the constructor, or
|
11
|
+
# by defining a subclass of Command and implementing the #process method.
|
12
|
+
#
|
13
|
+
# @example A Command with a block
|
14
|
+
# double_function = Cuprum::Command.new { |int| 2 * int }
|
15
|
+
# result = double_function.call(5)
|
16
|
+
#
|
17
|
+
# result.value #=> 10
|
18
|
+
#
|
19
|
+
# @example A Command subclass
|
20
|
+
# class MultiplyCommand < Cuprum::Command
|
21
|
+
# def initialize multiplier
|
22
|
+
# @multiplier = multiplier
|
23
|
+
# end # constructor
|
24
|
+
#
|
25
|
+
# private
|
26
|
+
#
|
27
|
+
# def process int
|
28
|
+
# int * @multiplier
|
29
|
+
# end # method process
|
30
|
+
# end # class
|
31
|
+
#
|
32
|
+
# triple_function = MultiplyCommand.new(3)
|
33
|
+
# result = triple_function.call(5)
|
34
|
+
#
|
35
|
+
# result.value #=> 15
|
36
|
+
#
|
37
|
+
# @example A Command with errors
|
38
|
+
# class DivideCommand < Cuprum::Command
|
39
|
+
# def initialize divisor
|
40
|
+
# @divisor = divisor
|
41
|
+
# end # constructor
|
42
|
+
#
|
43
|
+
# private
|
44
|
+
#
|
45
|
+
# def process int
|
46
|
+
# if @divisor.zero?
|
47
|
+
# errors << 'errors.messages.divide_by_zero'
|
48
|
+
#
|
49
|
+
# return
|
50
|
+
# end # if
|
51
|
+
#
|
52
|
+
# int / @divisor
|
53
|
+
# end # method process
|
54
|
+
# end # class
|
55
|
+
#
|
56
|
+
# halve_function = DivideCommand.new(2)
|
57
|
+
# result = halve_function.call(10)
|
58
|
+
#
|
59
|
+
# result.errors #=> []
|
60
|
+
# result.value #=> 5
|
61
|
+
#
|
62
|
+
# function_with_errors = DivideCommand.new(0)
|
63
|
+
# result = function_with_errors.call(10)
|
64
|
+
#
|
65
|
+
# result.errors #=> ['errors.messages.divide_by_zero']
|
66
|
+
# result.value #=> nil
|
67
|
+
#
|
68
|
+
# @example Command Chaining
|
69
|
+
# class AddCommand < Cuprum::Command
|
70
|
+
# def initialize addend
|
71
|
+
# @addend = addend
|
72
|
+
# end # constructor
|
73
|
+
#
|
74
|
+
# private
|
75
|
+
#
|
76
|
+
# def process int
|
77
|
+
# int + @addend
|
78
|
+
# end # method process
|
79
|
+
# end # class
|
80
|
+
#
|
81
|
+
# double_and_add_one = MultiplyCommand.new(2).chain(AddCommand.new(1))
|
82
|
+
# result = double_and_add_one(5)
|
83
|
+
#
|
84
|
+
# result.value #=> 5
|
85
|
+
#
|
86
|
+
# @example Conditional Chaining With #then And #else
|
87
|
+
# class EvenCommand < Cuprum::Command
|
88
|
+
# private
|
89
|
+
#
|
90
|
+
# def process int
|
91
|
+
# errors << 'errors.messages.not_even' unless int.even?
|
92
|
+
#
|
93
|
+
# int
|
94
|
+
# end # method process
|
95
|
+
# end # class
|
96
|
+
#
|
97
|
+
# # The next step in a Collatz sequence is determined as follows:
|
98
|
+
# # - If the number is even, divide it by 2.
|
99
|
+
# # - If the number is odd, multiply it by 3 and add 1.
|
100
|
+
# collatz_function =
|
101
|
+
# EvenCommand.new.
|
102
|
+
# then(DivideCommand.new(2)).
|
103
|
+
# else(MultiplyCommand.new(3).chain(AddCommand.new(1)))
|
104
|
+
#
|
105
|
+
# result = collatz_function.new(5)
|
106
|
+
# result.value #=> 16
|
107
|
+
#
|
108
|
+
# result = collatz_function.new(16)
|
109
|
+
# result.value #=> 8
|
110
|
+
class Command < Cuprum::BasicCommand
|
111
|
+
include Cuprum::Chaining
|
112
|
+
end # class
|
113
|
+
end # module
|