cuprum 0.7.0 → 0.8.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 +16 -0
- data/DEVELOPMENT.md +47 -41
- data/README.md +372 -56
- data/lib/cuprum/chaining.rb +128 -28
- data/lib/cuprum/command.rb +0 -3
- data/lib/cuprum/command_factory.rb +276 -0
- data/lib/cuprum/errors.rb +6 -0
- data/lib/cuprum/{not_implemented_error.rb → errors/process_not_implemented_error.rb} +7 -7
- data/lib/cuprum/operation.rb +3 -3
- data/lib/cuprum/processing.rb +10 -24
- data/lib/cuprum/result.rb +14 -2
- data/lib/cuprum/version.rb +1 -1
- metadata +19 -3
data/lib/cuprum/chaining.rb
CHANGED
@@ -11,6 +11,7 @@ module Cuprum
|
|
11
11
|
# # the value of the previous command.
|
12
12
|
#
|
13
13
|
# class GenerateUrlCommand
|
14
|
+
# include Cuprum::Chaining
|
14
15
|
# include Cuprum::Processing
|
15
16
|
#
|
16
17
|
# private
|
@@ -47,6 +48,7 @@ module Cuprum
|
|
47
48
|
# # the command is failing. This can be used to perform error handling.
|
48
49
|
#
|
49
50
|
# class CreateTaggingCommand
|
51
|
+
# include Cuprum::Chaining
|
50
52
|
# include Cuprum::Processing
|
51
53
|
#
|
52
54
|
# private
|
@@ -111,6 +113,7 @@ module Cuprum
|
|
111
113
|
# # ignored.
|
112
114
|
#
|
113
115
|
# class UpdatePostCommand
|
116
|
+
# include Cuprum::Chaining
|
114
117
|
# include Cuprum::Processing
|
115
118
|
#
|
116
119
|
# private
|
@@ -148,6 +151,25 @@ module Cuprum
|
|
148
151
|
# end
|
149
152
|
# end
|
150
153
|
#
|
154
|
+
# @example Protected Chaining Methods
|
155
|
+
# # Using the protected chaining methods #chain!, #tap_result!, and
|
156
|
+
# # #yield_result!, you can create a command class that composes other
|
157
|
+
# # commands.
|
158
|
+
#
|
159
|
+
# # We subclass the build command, which will be executed first.
|
160
|
+
# class CreateCommentCommand < BuildCommentCommand
|
161
|
+
# include Cuprum::Chaining
|
162
|
+
# include Cuprum::Processing
|
163
|
+
#
|
164
|
+
# def initialize
|
165
|
+
# # After the build step is run, we validate the comment.
|
166
|
+
# chain!(ValidateCommentCommand.new)
|
167
|
+
#
|
168
|
+
# # If the validation passes, we then save the comment.
|
169
|
+
# chain!(SaveCommentCommand.new, on: :success)
|
170
|
+
# end
|
171
|
+
# end
|
172
|
+
#
|
151
173
|
# @see Cuprum::Command
|
152
174
|
module Chaining
|
153
175
|
# Creates a copy of the first command, and then chains the given command or
|
@@ -193,15 +215,7 @@ module Cuprum
|
|
193
215
|
#
|
194
216
|
# @yieldparam value [Object] The value of the previous result.
|
195
217
|
def chain command = nil, on: nil, &block
|
196
|
-
command
|
197
|
-
|
198
|
-
clone.tap do |fn|
|
199
|
-
fn.chained_procs <<
|
200
|
-
{
|
201
|
-
:proc => chain_command(command),
|
202
|
-
:on => on
|
203
|
-
} # end hash
|
204
|
-
end # tap
|
218
|
+
clone.chain!(command, :on => on, &block)
|
205
219
|
end # method chain
|
206
220
|
|
207
221
|
# Shorthand for command.chain(:on => :failure). Creates a copy of the first
|
@@ -223,7 +237,7 @@ module Cuprum
|
|
223
237
|
#
|
224
238
|
# @yieldparam value [Object] The value of the previous result.
|
225
239
|
def failure command = nil, &block
|
226
|
-
chain(command, :on => :failure, &block)
|
240
|
+
clone.chain!(command, :on => :failure, &block)
|
227
241
|
end # method failure
|
228
242
|
|
229
243
|
# Shorthand for command.chain(:on => :success). Creates a copy of the first
|
@@ -245,7 +259,7 @@ module Cuprum
|
|
245
259
|
#
|
246
260
|
# @yieldparam value [Object] The value of the previous result.
|
247
261
|
def success command = nil, &block
|
248
|
-
chain(command, :on => :success, &block)
|
262
|
+
clone.chain!(command, :on => :success, &block)
|
249
263
|
end # method success
|
250
264
|
|
251
265
|
# As #yield_result, but always returns the previous result when the block is
|
@@ -259,15 +273,7 @@ module Cuprum
|
|
259
273
|
#
|
260
274
|
# @see #yield_result
|
261
275
|
def tap_result on: nil, &block
|
262
|
-
|
263
|
-
|
264
|
-
clone.tap do |fn|
|
265
|
-
fn.chained_procs <<
|
266
|
-
{
|
267
|
-
:proc => tapped,
|
268
|
-
:on => on
|
269
|
-
} # end hash
|
270
|
-
end # tap
|
276
|
+
clone.tap_result!(:on => on, &block)
|
271
277
|
end # method tap_result
|
272
278
|
|
273
279
|
# Creates a copy of the command, and then chains the block to execute after
|
@@ -291,17 +297,63 @@ module Cuprum
|
|
291
297
|
#
|
292
298
|
# @see #tap_result
|
293
299
|
def yield_result on: nil, &block
|
294
|
-
clone.
|
295
|
-
fn.chained_procs <<
|
296
|
-
{
|
297
|
-
:proc => block,
|
298
|
-
:on => on
|
299
|
-
} # end hash
|
300
|
-
end # tap
|
300
|
+
clone.yield_result!(:on => on, &block)
|
301
301
|
end # method yield_result
|
302
302
|
|
303
303
|
protected
|
304
304
|
|
305
|
+
# @!visibility public
|
306
|
+
#
|
307
|
+
# As #chain, but modifies the current command instead of creating a clone.
|
308
|
+
# This is a protected method, and is meant to be called by the command to be
|
309
|
+
# chained, such as during #initialize.
|
310
|
+
#
|
311
|
+
# @return [Cuprum::Chaining] The current command.
|
312
|
+
#
|
313
|
+
# @see #chain
|
314
|
+
#
|
315
|
+
# @overload chain!(command, on: nil)
|
316
|
+
# @param command [Cuprum::Command] The command to chain.
|
317
|
+
#
|
318
|
+
# @param on [Symbol] Sets a condition on when the chained block can run,
|
319
|
+
# based on the previous result. Valid values are :success, :failure, and
|
320
|
+
# :always. If the value is :success, the block will be called only if
|
321
|
+
# the previous result succeeded and is not halted. If the value is
|
322
|
+
# :failure, the block will be called only if the previous result failed
|
323
|
+
# and is not halted. If the value is :always, the block will be called
|
324
|
+
# regardless of the previous result status, even if the previous result
|
325
|
+
# is halted. If no value is given, the command will run whether the
|
326
|
+
# previous command was a success or a failure, but not if the command
|
327
|
+
# chain has been halted.
|
328
|
+
#
|
329
|
+
# @overload chain!(on: nil) { |value| }
|
330
|
+
# Creates an anonymous command from the given block. The command will be
|
331
|
+
# passed the value of the previous result.
|
332
|
+
#
|
333
|
+
# @param on [Symbol] Sets a condition on when the chained block can run,
|
334
|
+
# based on the previous result. Valid values are :success, :failure, and
|
335
|
+
# :always. If the value is :success, the block will be called only if
|
336
|
+
# the previous result succeeded and is not halted. If the value is
|
337
|
+
# :failure, the block will be called only if the previous result failed
|
338
|
+
# and is not halted. If the value is :always, the block will be called
|
339
|
+
# regardless of the previous result status, even if the previous result
|
340
|
+
# is halted. If no value is given, the command will run whether the
|
341
|
+
# previous command was a success or a failure, but not if the command
|
342
|
+
# chain has been halted.
|
343
|
+
#
|
344
|
+
# @yieldparam value [Object] The value of the previous result.
|
345
|
+
def chain! command = nil, on: nil, &block
|
346
|
+
command ||= Cuprum::Command.new(&block)
|
347
|
+
|
348
|
+
chained_procs <<
|
349
|
+
{
|
350
|
+
:proc => chain_command(command),
|
351
|
+
:on => on
|
352
|
+
} # end hash
|
353
|
+
|
354
|
+
self
|
355
|
+
end # method chain!
|
356
|
+
|
305
357
|
def chained_procs
|
306
358
|
@chained_procs ||= []
|
307
359
|
end # method chained_procs
|
@@ -310,6 +362,54 @@ module Cuprum
|
|
310
362
|
yield_chain(super)
|
311
363
|
end # method call
|
312
364
|
|
365
|
+
# @!visibility public
|
366
|
+
#
|
367
|
+
# As #tap_result, but modifies the current command instead of creating a
|
368
|
+
# clone. This is a protected method, and is meant to be called by the
|
369
|
+
# command to be chained, such as during #initialize.
|
370
|
+
#
|
371
|
+
# @param (see #tap_result)
|
372
|
+
#
|
373
|
+
# @yieldparam result [Cuprum::Result] The #result of the previous command.
|
374
|
+
#
|
375
|
+
# @return (see #tap_result)
|
376
|
+
#
|
377
|
+
# @see #tap_result
|
378
|
+
def tap_result! on: nil, &block
|
379
|
+
tapped = ->(result) { result.tap { block.call(result) } }
|
380
|
+
|
381
|
+
chained_procs <<
|
382
|
+
{
|
383
|
+
:proc => tapped,
|
384
|
+
:on => on
|
385
|
+
} # end hash
|
386
|
+
|
387
|
+
self
|
388
|
+
end # method tap_result!
|
389
|
+
|
390
|
+
# @!visibility public
|
391
|
+
#
|
392
|
+
# As #yield_result, but modifies the current command instead of creating a
|
393
|
+
# clone. This is a protected method, and is meant to be called by the
|
394
|
+
# command to be chained, such as during #initialize.
|
395
|
+
#
|
396
|
+
# @param (see #yield_result)
|
397
|
+
#
|
398
|
+
# @yieldparam result [Cuprum::Result] The #result of the previous command.
|
399
|
+
#
|
400
|
+
# @return (see #yield_result)
|
401
|
+
#
|
402
|
+
# @see #yield_result
|
403
|
+
def yield_result! on: nil, &block
|
404
|
+
chained_procs <<
|
405
|
+
{
|
406
|
+
:proc => block,
|
407
|
+
:on => on
|
408
|
+
} # end hash
|
409
|
+
|
410
|
+
self
|
411
|
+
end # method yield_result!
|
412
|
+
|
313
413
|
private
|
314
414
|
|
315
415
|
def chain_command command
|
@@ -342,7 +442,7 @@ module Cuprum
|
|
342
442
|
if value_is_result?(value)
|
343
443
|
value.to_result
|
344
444
|
else
|
345
|
-
build_result(value
|
445
|
+
build_result(value)
|
346
446
|
end # if-else
|
347
447
|
end # reduce
|
348
448
|
end # method yield_chain
|
data/lib/cuprum/command.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'cuprum/chaining'
|
2
2
|
require 'cuprum/processing'
|
3
|
-
require 'cuprum/result_helpers'
|
4
3
|
|
5
4
|
module Cuprum
|
6
5
|
# Functional object that encapsulates a business logic operation with a
|
@@ -112,11 +111,9 @@ module Cuprum
|
|
112
111
|
#
|
113
112
|
# @see Cuprum::Chaining
|
114
113
|
# @see Cuprum::Processing
|
115
|
-
# @see Cuprum::ResultHelpers
|
116
114
|
class Command
|
117
115
|
include Cuprum::Processing
|
118
116
|
include Cuprum::Chaining
|
119
|
-
include Cuprum::ResultHelpers
|
120
117
|
|
121
118
|
# Returns a new instance of Cuprum::Command.
|
122
119
|
#
|
@@ -0,0 +1,276 @@
|
|
1
|
+
require 'cuprum'
|
2
|
+
|
3
|
+
require 'sleeping_king_studios/tools/toolbelt'
|
4
|
+
|
5
|
+
module Cuprum
|
6
|
+
# Builder class for instantiating command objects.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# class SpaceFactory < Cuprum::CommandFactory
|
10
|
+
# command :build, BuildCommand
|
11
|
+
#
|
12
|
+
# command :fly { |launch_site:| FlyCommand.new(launch_site) }
|
13
|
+
#
|
14
|
+
# command_class :dream { DreamCommand }
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# factory = SpaceFactory.new
|
18
|
+
#
|
19
|
+
# factory::Build #=> BuildCommand
|
20
|
+
# factory.build #=> an instance of BuildCommand
|
21
|
+
#
|
22
|
+
# rocket = factory.build.call({ size: 'big' }) #=> an instance of Rocket
|
23
|
+
# rocket.size #=> 'big'
|
24
|
+
#
|
25
|
+
# command = factory.fly(launch_site: 'KSC') #=> an instance of FlyCommand
|
26
|
+
# command.call(rocket)
|
27
|
+
# #=> launches the rocket from KSC
|
28
|
+
#
|
29
|
+
# factory::Dream #=> DreamCommand
|
30
|
+
# factory.dream #=> an instance of DreamCommand
|
31
|
+
class CommandFactory < Module
|
32
|
+
# Defines the Domain-Specific Language and helper methods for dynamically
|
33
|
+
# defined commands.
|
34
|
+
class << self
|
35
|
+
# Defines a command for the factory.
|
36
|
+
#
|
37
|
+
# @overload command(name, command_class)
|
38
|
+
# Defines a command using the given factory class. For example, when a
|
39
|
+
# command is defined with the name "whirlpool" and the WhirlpoolCommand
|
40
|
+
# class:
|
41
|
+
#
|
42
|
+
# A factory instance will define the constant ::Whirlpool, and accessing
|
43
|
+
# factory::Whirlpool will return the WhirlpoolCommand class.
|
44
|
+
#
|
45
|
+
# A factory instance will define the method #whirlpool, and calling
|
46
|
+
# factory#whirlpool will return an instance of WhirlpoolCommand. Any
|
47
|
+
# arguments passed to the #whirlpool method will be forwarded to the
|
48
|
+
# constructor when building the command.
|
49
|
+
#
|
50
|
+
# @param name [String, Symbol] The name of the command.
|
51
|
+
# @param command_class [Class] The command class. Must be a subclass of
|
52
|
+
# Cuprum::Command.
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# class MoveFactory < Cuprum::CommandFactory
|
56
|
+
# command :cut, CutCommand
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# factory = MoveFactory.new
|
60
|
+
# factory::Cut #=> CutCommand
|
61
|
+
# factory.cut #=> an instance of CutCommand
|
62
|
+
#
|
63
|
+
# @overload command(name) { |*args| }
|
64
|
+
# Defines a command using the given block, which must return an instance
|
65
|
+
# of a Cuprum::Command subclass. For example, when a command is defined
|
66
|
+
# with the name "dive" and a block that returns an instance of the
|
67
|
+
# DiveCommand class:
|
68
|
+
#
|
69
|
+
# A factory instance will define the method #dive, and calling
|
70
|
+
# factory#dive will call the block and return the resulting command
|
71
|
+
# instance. Any arguments passed to the #dive method will be forwarded
|
72
|
+
# to the block when building the command.
|
73
|
+
#
|
74
|
+
# The block will be evaluated in the context of the factory instance, so
|
75
|
+
# it has access to any methods or instance variables defined for the
|
76
|
+
# factory instance.
|
77
|
+
#
|
78
|
+
# @param name [String, Symbol] The name of the command.
|
79
|
+
#
|
80
|
+
# @yield The block will be executed in the context of the factory
|
81
|
+
# instance.
|
82
|
+
# @yieldparam *args [Array] Any arguments given to the method
|
83
|
+
# factory.name() will be passed on the block.
|
84
|
+
# @yieldreturn [Cuprum::Command] The block return an instance of a
|
85
|
+
# Cuprum::Command subclass, or else raise an error.
|
86
|
+
#
|
87
|
+
# @example
|
88
|
+
# class MoveFactory < Cuprum::CommandFactory
|
89
|
+
# command :fly { |destination| FlyCommand.new(destination) }
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# factory = MoveFactory.new
|
93
|
+
# factory.fly_command('Indigo Plateau')
|
94
|
+
# #=> an instance of FlyCommand with a destination of 'Indigo Plateau'
|
95
|
+
def command(name, klass = nil, **metadata, &defn)
|
96
|
+
guard_abstract_factory!
|
97
|
+
|
98
|
+
if klass
|
99
|
+
define_command_from_class(klass, name: name, metadata: metadata)
|
100
|
+
elsif block_given?
|
101
|
+
define_command_from_block(defn, name: name, metadata: metadata)
|
102
|
+
else
|
103
|
+
require_definition!
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Defines a command using the given block, which must return a subclass of
|
108
|
+
# Cuprum::Command. For example, when a command is defined with the name
|
109
|
+
# "rock_climb" and a block returning a subclass of RockClimbCommand:
|
110
|
+
#
|
111
|
+
# A factory instance will define the constant ::RockClimb, and accessing
|
112
|
+
# factory::RockClimb will call the block and return the resulting command
|
113
|
+
# class. This value is memoized, so subsequent factory::RockClimb accesses
|
114
|
+
# on the same factory instance will return the same command class.
|
115
|
+
#
|
116
|
+
# A factory instance will define the method #rock_climb, and calling
|
117
|
+
# factory#rock_climb will access the constant at ::RockClimb and return an
|
118
|
+
# instance of that subclass of RockClimbCommand. Any arguments passed to
|
119
|
+
# the #whirlpool method will be forwarded to the constructor when building
|
120
|
+
# the command.
|
121
|
+
#
|
122
|
+
# @param name [String, Symbol] The name of the command.
|
123
|
+
# @yield The block will be executed in the context of the factory
|
124
|
+
# instance.
|
125
|
+
# @yieldparam *args [Array] Any arguments given to the method
|
126
|
+
# factory.name() will be passed on the block.
|
127
|
+
# @yieldreturn [Cuprum::Command] The block return an instance of a
|
128
|
+
# Cuprum::Command subclass, or else raise an error.
|
129
|
+
#
|
130
|
+
# @example
|
131
|
+
# class MoveFactory < Cuprum::CommandFactory
|
132
|
+
# command_class :flash do
|
133
|
+
# Class.new(FlashCommand) do
|
134
|
+
# def brightness
|
135
|
+
# :intense
|
136
|
+
# end
|
137
|
+
# end
|
138
|
+
# end
|
139
|
+
# end
|
140
|
+
#
|
141
|
+
# factory = MoveFactory.new
|
142
|
+
# factory::Flash #=> a subclass of FlashCommand
|
143
|
+
# factory.flash #=> an instance of factory::Flash
|
144
|
+
#
|
145
|
+
# command = factory.flash
|
146
|
+
# command.brightness #=> :intense
|
147
|
+
def command_class(name, **metadata, &defn)
|
148
|
+
guard_abstract_factory!
|
149
|
+
|
150
|
+
raise ArgumentError, 'must provide a block'.freeze unless block_given?
|
151
|
+
|
152
|
+
name = normalize_command_name(name)
|
153
|
+
|
154
|
+
(@command_definitions ||= {})[name] =
|
155
|
+
metadata.merge(__const_defn__: defn)
|
156
|
+
|
157
|
+
const_name = tools.string.camelize(name)
|
158
|
+
|
159
|
+
define_method(name) do |*args, &block|
|
160
|
+
command_class = const_get(const_name)
|
161
|
+
|
162
|
+
build_command(command_class, *args, &block)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
protected
|
167
|
+
|
168
|
+
def command_definitions
|
169
|
+
definitions = (@command_definitions ||= {})
|
170
|
+
|
171
|
+
return definitions unless superclass < Cuprum::CommandFactory
|
172
|
+
|
173
|
+
superclass.command_definitions.merge(definitions)
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
def abstract_factory?
|
179
|
+
self == Cuprum::CommandFactory
|
180
|
+
end
|
181
|
+
|
182
|
+
def define_command_from_block(builder, name:, metadata: {})
|
183
|
+
command_name = normalize_command_name(name)
|
184
|
+
|
185
|
+
(@command_definitions ||= {})[command_name] = metadata
|
186
|
+
|
187
|
+
define_method(command_name) do |*args|
|
188
|
+
instance_exec(*args, &builder)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def define_command_from_class(command_class, name:, metadata: {})
|
193
|
+
guard_invalid_definition!(command_class)
|
194
|
+
|
195
|
+
command_name = normalize_command_name(name)
|
196
|
+
|
197
|
+
(@command_definitions ||= {})[command_name] =
|
198
|
+
metadata.merge(__const_defn__: command_class)
|
199
|
+
|
200
|
+
define_method(command_name) do |*args, &block|
|
201
|
+
build_command(command_class, *args, &block)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def guard_abstract_factory!
|
206
|
+
return unless abstract_factory?
|
207
|
+
|
208
|
+
raise NotImplementedError,
|
209
|
+
'Cuprum::CommandFactory is an abstract class. Create a subclass to ' \
|
210
|
+
'define commands for a factory.'.freeze
|
211
|
+
end
|
212
|
+
|
213
|
+
def guard_invalid_definition!(command_class)
|
214
|
+
return if command_class.is_a?(Class) && command_class < Cuprum::Command
|
215
|
+
|
216
|
+
raise ArgumentError, 'definition must be a command class'.freeze
|
217
|
+
end
|
218
|
+
|
219
|
+
def normalize_command_name(command_name)
|
220
|
+
tools.string.underscore(command_name).intern
|
221
|
+
end
|
222
|
+
|
223
|
+
def require_definition!
|
224
|
+
raise ArgumentError, 'must provide a command class or a block'.freeze
|
225
|
+
end
|
226
|
+
|
227
|
+
def tools
|
228
|
+
SleepingKingStudios::Tools::Toolbelt.instance
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
# @return [Boolean] true if the factory defines the given command, otherwise
|
233
|
+
# false.
|
234
|
+
def command?(command_name)
|
235
|
+
command_name = normalize_command_name(command_name)
|
236
|
+
|
237
|
+
commands.include?(command_name)
|
238
|
+
end
|
239
|
+
|
240
|
+
# @return [Array<Symbol>] a list of the commands defined by the factory.
|
241
|
+
def commands
|
242
|
+
self.class.send(:command_definitions).keys
|
243
|
+
end
|
244
|
+
|
245
|
+
# @private
|
246
|
+
def const_defined?(const_name, inherit = true)
|
247
|
+
command?(const_name) || super
|
248
|
+
end
|
249
|
+
|
250
|
+
# @private
|
251
|
+
def const_missing(const_name)
|
252
|
+
definitions = self.class.send(:command_definitions)
|
253
|
+
command_name = normalize_command_name(const_name)
|
254
|
+
command_defn = definitions.dig(command_name, :__const_defn__)
|
255
|
+
|
256
|
+
return super unless command_defn
|
257
|
+
|
258
|
+
command_class =
|
259
|
+
command_defn.is_a?(Proc) ? instance_exec(&command_defn) : command_defn
|
260
|
+
|
261
|
+
const_set(const_name, command_class)
|
262
|
+
|
263
|
+
command_class
|
264
|
+
end
|
265
|
+
|
266
|
+
private
|
267
|
+
|
268
|
+
def build_command(command_class, *args, &block)
|
269
|
+
command_class.new(*args, &block)
|
270
|
+
end
|
271
|
+
|
272
|
+
def normalize_command_name(command_name)
|
273
|
+
self.class.send(:normalize_command_name, command_name)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|