cli_class_tool 0.4.1 → 0.5.1
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 +18 -0
- data/README.md +99 -0
- data/lib/cli_class_tool/common.rb +3 -1
- data/lib/cli_class_tool/utils.rb +205 -14
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 79f28a830c3277c28e747b142fb6a12d40c8a69e5b868c4fef7f58e296f79dce
|
|
4
|
+
data.tar.gz: ca5b3535463ee163b56c2cdda397dd925111ea325d6695268c78acf5091b19cc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 467252b31976d4fcb70cc795dc5ceaa06d2ad24576cd75a8b5ffaef3c3d5ef93f945da0f45414dfa40a80aeb7e3f5f882308ac8e48f5b2d4629267a6254eb078
|
|
7
|
+
data.tar.gz: 8abc7831b9c862d928ca3632a7b9d9f99e289b5058af2fe7c095cec6845a617de7d6111f8357f879b95c79711733ede70a93e86127abaa65e3e43e0861605bb9
|
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
------------------
|
|
2
|
+
0.5.1 (2026-09-07)
|
|
3
|
+
------------------
|
|
4
|
+
|
|
5
|
+
* Fix actions not working on extended classes if not defined in main ones
|
|
6
|
+
|
|
7
|
+
------------------
|
|
8
|
+
0.5.0 (2026-07-22)
|
|
9
|
+
------------------
|
|
10
|
+
|
|
11
|
+
* Support CLI_HELP_EXPAND on sub-CLIClass modules to recursively list sub-actions in parent help outputs, including support for boolean or custom string headers
|
|
12
|
+
* Support recursive subcommand alias and shortcut resolution within deeply nested sub-CLIClasses
|
|
13
|
+
* Add CLI_COMMAND_ALIASES support to define top-level command shortcuts/aliases to deeply-nested subcommand actions
|
|
14
|
+
* Filter list_actions command output to exclude itself, preserving clean autocomplete query behavior
|
|
15
|
+
* Require optparse explicitly inside utils.rb to prevent NameErrors in production environments
|
|
16
|
+
* Safeguard attribute resolution and _runOnClass to gracefully default when ACTION_HELP/ACTION_LIST is missing
|
|
17
|
+
* Add recursive nested subcommand and namespace auto-discovery support
|
|
18
|
+
|
|
1
19
|
------------------
|
|
2
20
|
0.4.1 (2026-06-05)
|
|
3
21
|
------------------
|
data/README.md
CHANGED
|
@@ -294,4 +294,103 @@ MyProject.run_cli(opts) do |parser, phase, action_opts|
|
|
|
294
294
|
end
|
|
295
295
|
end
|
|
296
296
|
```
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Nested CLI Subcommands (Subcommands & Recursive Routing)
|
|
301
|
+
|
|
302
|
+
`CLIClassTool` natively supports nested CLI subcommands, enabling hierarchical structures of the form:
|
|
303
|
+
```
|
|
304
|
+
$ mytool <subcommand> <action> [options]
|
|
305
|
+
$ mytool <subcommand> <sub_subcommand> <action> [options]
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
This allows you to decouple complex tools into self-contained sub-CLI components while executing them seamlessly under a single binary entry point.
|
|
309
|
+
|
|
310
|
+
### 1. Auto-Discovery & Setup
|
|
311
|
+
|
|
312
|
+
Any nested module or class defined within your parent CLI module that extends `CLIClassTool::Utils` is **automatically discovered** as a subcommand:
|
|
313
|
+
|
|
314
|
+
```ruby
|
|
315
|
+
module MyProject
|
|
316
|
+
extend CLIClassTool::Utils # Parent CLI
|
|
317
|
+
|
|
318
|
+
# 1. Nesting a subcommand module
|
|
319
|
+
module ConfigCLI
|
|
320
|
+
# Define custom subcommand help description
|
|
321
|
+
CLI_DESCRIPTION = "Manage application configurations"
|
|
322
|
+
|
|
323
|
+
# Optional: Customize the command name (defaults to "config_cli")
|
|
324
|
+
CLI_COMMAND_NAME = "config"
|
|
325
|
+
|
|
326
|
+
class ConfigError < StandardError; end
|
|
327
|
+
|
|
328
|
+
class Common < CLIClassTool::Common
|
|
329
|
+
def parent_module; ConfigCLI; end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
class ConfigAction < Common
|
|
333
|
+
ACTION_LIST = [ :set_val ]
|
|
334
|
+
ACTION_HELP = { :set_val => "Set a config value" }
|
|
335
|
+
|
|
336
|
+
def set_val(opts)
|
|
337
|
+
# business logic...
|
|
338
|
+
return 0
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
ACTION_CLASS = [ ConfigAction ]
|
|
343
|
+
extend CLIClassTool::Utils
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### 2. Help Aggregation & Name Resolution
|
|
349
|
+
|
|
350
|
+
- **Naming:** By default, subcommand trigger words are derived from the inner module/class name converted to `snake_case` (e.g., `ConfigCLI` becomes `config_cli`). Define `CLI_COMMAND_NAME = "custom_name"` on your submodule to override this behavior.
|
|
351
|
+
- **Help Menus:** Nested subcommands and their descriptions (`CLI_DESCRIPTION` or `HELP` constants) are automatically collected and listed in the parent CLI's usage output under `Possible actions:`.
|
|
352
|
+
- **Option Forwarding:** Global customization blocks and verbosity flags are recursively passed down to the active subcommand.
|
|
353
|
+
- **Help Expansion:** By default, `--help` lists the trigger name and description of subcommands. You can declare `CLI_HELP_EXPAND = true` on a sub-CLI module to recursively list all of its individual actions directly in the parent's help menu. If `CLI_HELP_EXPAND` is a `String` (e.g., `"Config options:"`), that string will be printed as a dedicated help header separator inside the parent help menu.
|
|
354
|
+
|
|
355
|
+
### 3. Command Aliases (Shortcuts)
|
|
356
|
+
|
|
357
|
+
You can define command shortcuts/aliases at any CLI level by declaring a `CLI_COMMAND_ALIASES` Hash constant mapping shortcut names (symbols or strings) to subcommand/action pathways:
|
|
358
|
+
|
|
359
|
+
```ruby
|
|
360
|
+
module MyProject
|
|
361
|
+
extend CLIClassTool::Utils
|
|
362
|
+
|
|
363
|
+
# Map shortcuts to deeply-nested actions
|
|
364
|
+
CLI_COMMAND_ALIASES = {
|
|
365
|
+
"myalias" => ["config", "set_val"],
|
|
366
|
+
"string_alias" => "config set_val"
|
|
367
|
+
}
|
|
368
|
+
end
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Running `$ mytool myalias --foo bar` will automatically expand and route arguments exactly as if the user had typed `$ mytool config set_val --foo bar`.
|
|
372
|
+
These shortcuts are also dynamically collected and formatted under a dedicated `Command aliases:` section when printing `--help`.
|
|
373
|
+
|
|
374
|
+
### 4. Seamless Subcommand Exception Handling
|
|
375
|
+
|
|
376
|
+
`CLIClassTool` provides dynamic subclass exception matching to solve a common design challenge: *rescuing any exception originating from nested sub-CLIs under a single, central parent rescue block.*
|
|
377
|
+
|
|
378
|
+
When you extend `CLIClassTool::Utils` on a parent module, `CLIClassTool` overrides the case-equality (`===`) operator on the parent's base error class. This ensures:
|
|
379
|
+
- Any child subcommand exception (such as `MyProject::ConfigCLI::RunError`) is **seamlessly rescued** by the parent base error class.
|
|
380
|
+
- The original exception classes and names remain **completely unchanged**, meaning they can still be rescued by their specific sub-CLI exception class as normal.
|
|
381
|
+
|
|
382
|
+
This enables infinite sub-CLI nesting while preserving modular reuse across different parent projects or as standalone binaries:
|
|
383
|
+
|
|
384
|
+
```ruby
|
|
385
|
+
# In bin/mytool
|
|
386
|
+
begin
|
|
387
|
+
MyProject.run_cli
|
|
388
|
+
rescue MyProjectError => e
|
|
389
|
+
# Seamlessly catches errors from MyProject::RunError,
|
|
390
|
+
# MyProject::ConfigCLI::RunError, or any deeper sublevel!
|
|
391
|
+
STDERR.puts "# ERROR: #{e.message}"
|
|
392
|
+
exit e.respond_to?(:err_code) ? e.err_code : 1
|
|
393
|
+
end
|
|
394
|
+
```
|
|
395
|
+
|
|
297
396
|
```
|
|
@@ -222,7 +222,9 @@ module CLIClassTool
|
|
|
222
222
|
# @param opts [Hash] Options hash
|
|
223
223
|
# @return [Integer] 0
|
|
224
224
|
def list_actions(opts)
|
|
225
|
-
|
|
225
|
+
actions = parent_module.getActionAttr("ACTION_LIST").map(){|x| parent_module.actionToString(x)}
|
|
226
|
+
actions.reject! { |x| x == "list_actions" }
|
|
227
|
+
puts actions.join("\n")
|
|
226
228
|
return 0
|
|
227
229
|
end
|
|
228
230
|
end
|
data/lib/cli_class_tool/utils.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'optparse'
|
|
2
|
+
|
|
1
3
|
module CLIClassTool
|
|
2
4
|
# Generic utilities for CLI class-based actions
|
|
3
5
|
module Utils
|
|
@@ -13,7 +15,95 @@ module CLIClassTool
|
|
|
13
15
|
raise("Could not find a base error class named #{superclass_name}")
|
|
14
16
|
end
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
error_class = Object.const_get(superclass_name)
|
|
19
|
+
|
|
20
|
+
# Override === on the error class to match nested subcommand errors recursively
|
|
21
|
+
error_class.singleton_class.class_eval do
|
|
22
|
+
define_method(:_cli_host_module) { base }
|
|
23
|
+
|
|
24
|
+
def ===(other)
|
|
25
|
+
return true if super
|
|
26
|
+
|
|
27
|
+
if other.is_a?(StandardError) && other.respond_to?(:err_code)
|
|
28
|
+
other_class_name = other.class.name
|
|
29
|
+
if other_class_name
|
|
30
|
+
parts = other_class_name.split('::')
|
|
31
|
+
if parts.size > 1
|
|
32
|
+
parent_mod_name = parts[0...-1].join('::')
|
|
33
|
+
host = _cli_host_module
|
|
34
|
+
if host.respond_to?(:cli_sub_actions)
|
|
35
|
+
all_sub_names = _all_sub_module_names(host)
|
|
36
|
+
return all_sub_names.include?(parent_mod_name)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
false
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
define_method(:_all_sub_module_names) do |host_mod|
|
|
46
|
+
names = []
|
|
47
|
+
if host_mod.respond_to?(:cli_sub_actions)
|
|
48
|
+
host_mod.cli_sub_actions.values.each do |sub_cli|
|
|
49
|
+
if sub_cli.name
|
|
50
|
+
names << sub_cli.name
|
|
51
|
+
names.concat(_all_sub_module_names(sub_cli))
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
names.uniq
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
CLIClassTool.define_run_error(base, error_class)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Convert CamelCase to snake_case
|
|
63
|
+
# @param str [String]
|
|
64
|
+
# @return [String]
|
|
65
|
+
def _to_snake_case(str)
|
|
66
|
+
str.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
67
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
68
|
+
.tr('-', '_')
|
|
69
|
+
.downcase
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Dynamically discover and return a map of sub-CLI tools
|
|
73
|
+
#
|
|
74
|
+
# @return [Hash<String, Module>] Map of subcommand string to CLI modules
|
|
75
|
+
def cli_sub_actions
|
|
76
|
+
sub_actions = {}
|
|
77
|
+
|
|
78
|
+
# First, check if manual CLI_SUB_ACTIONS mapping exists
|
|
79
|
+
if self.const_defined?(:CLI_SUB_ACTIONS)
|
|
80
|
+
manual_actions = self::CLI_SUB_ACTIONS
|
|
81
|
+
if manual_actions.is_a?(Hash)
|
|
82
|
+
manual_actions.each do |k, v|
|
|
83
|
+
sub_actions[k.to_s] = v
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Then, dynamically discover any inner modules/classes extending CLIClassTool::Utils
|
|
89
|
+
self.constants(false).each do |const_sym|
|
|
90
|
+
begin
|
|
91
|
+
const_val = self.const_get(const_sym)
|
|
92
|
+
if const_val.is_a?(Module) && const_val.is_a?(CLIClassTool::Utils)
|
|
93
|
+
cmd_name = if const_val.const_defined?(:CLI_COMMAND_NAME)
|
|
94
|
+
const_val::CLI_COMMAND_NAME.to_s
|
|
95
|
+
else
|
|
96
|
+
_to_snake_case(const_sym.to_s)
|
|
97
|
+
end
|
|
98
|
+
# Only add if not already manually specified
|
|
99
|
+
sub_actions[cmd_name] ||= const_val
|
|
100
|
+
end
|
|
101
|
+
rescue NameError, LoadError
|
|
102
|
+
# ignore uninitialized autoloads
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
return sub_actions
|
|
17
107
|
end
|
|
18
108
|
|
|
19
109
|
# Convert a string to an action symbol, validating it against available actions
|
|
@@ -40,19 +130,67 @@ module CLIClassTool
|
|
|
40
130
|
# @param attr [Symbol] Attribute name (e.g., "ACTION_LIST")
|
|
41
131
|
# @return [Hash, Array] Aggregated attributes
|
|
42
132
|
def getActionAttr(attr)
|
|
43
|
-
|
|
44
|
-
|
|
133
|
+
common_class = self.const_defined?(:Common) ? self::Common : CLIClassTool::Common
|
|
134
|
+
is_hash = if common_class.const_defined?(attr)
|
|
135
|
+
common_class.const_get(attr).is_a?(Hash)
|
|
136
|
+
else
|
|
137
|
+
attr.to_s.include?("HELP")
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
action_classes = self.const_defined?(:ACTION_CLASS) ? self::ACTION_CLASS : []
|
|
45
141
|
|
|
46
142
|
# Resolve overridden/extended class (addon) if getExtendedClass is defined
|
|
47
143
|
resolved_classes = action_classes.map do |x|
|
|
48
144
|
self.respond_to?(:getExtendedClass) ? self.getExtendedClass(x) : x
|
|
49
145
|
end
|
|
50
146
|
|
|
51
|
-
|
|
52
|
-
|
|
147
|
+
res = if is_hash
|
|
148
|
+
resolved_classes.inject({}) do |h, x|
|
|
149
|
+
x.const_defined?(attr) ? h.merge(x.const_get(attr)) : h
|
|
150
|
+
end
|
|
53
151
|
else
|
|
54
|
-
|
|
152
|
+
resolved_classes.map do |x|
|
|
153
|
+
x.const_defined?(attr) ? x.const_get(attr) : []
|
|
154
|
+
end.flatten()
|
|
55
155
|
end
|
|
156
|
+
|
|
157
|
+
# If it's ACTION_LIST, append discovered subcommand names
|
|
158
|
+
if attr.to_s == "ACTION_LIST"
|
|
159
|
+
sub_actions = self.cli_sub_actions
|
|
160
|
+
res += sub_actions.keys.map(&:to_sym)
|
|
161
|
+
res << :list_actions unless res.include?(:list_actions)
|
|
162
|
+
# If it's ACTION_HELP, merge subcommand helps
|
|
163
|
+
elsif attr.to_s == "ACTION_HELP"
|
|
164
|
+
sub_helps = {}
|
|
165
|
+
self.cli_sub_actions.each do |cmd_name, sub_cli|
|
|
166
|
+
expand_val = sub_cli.const_defined?(:CLI_HELP_EXPAND) ? sub_cli::CLI_HELP_EXPAND : nil
|
|
167
|
+
if expand_val
|
|
168
|
+
if expand_val.is_a?(String)
|
|
169
|
+
sub_helps[expand_val] = ""
|
|
170
|
+
end
|
|
171
|
+
sub_helps_rec = sub_cli.getActionAttr("ACTION_HELP")
|
|
172
|
+
sub_helps_rec.each do |k, desc|
|
|
173
|
+
sub_helps["#{cmd_name} #{k}".to_sym] = desc
|
|
174
|
+
end
|
|
175
|
+
else
|
|
176
|
+
desc = ""
|
|
177
|
+
if sub_cli.const_defined?(:CLI_DESCRIPTION)
|
|
178
|
+
desc = sub_cli::CLI_DESCRIPTION
|
|
179
|
+
elsif sub_cli.const_defined?(:HELP)
|
|
180
|
+
desc = sub_cli::HELP
|
|
181
|
+
end
|
|
182
|
+
sub_helps[cmd_name.to_sym] = desc
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
if self.const_defined?(:CLI_SUB_ACTIONS_HELP)
|
|
186
|
+
self::CLI_SUB_ACTIONS_HELP.each do |k, desc|
|
|
187
|
+
sub_helps[k.to_sym] = desc
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
res = res.merge(sub_helps)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
return res
|
|
56
194
|
end
|
|
57
195
|
|
|
58
196
|
# Run a block on the class responsible for a specific action
|
|
@@ -62,12 +200,12 @@ module CLIClassTool
|
|
|
62
200
|
# @yield [Class] The class handling the action
|
|
63
201
|
# @return [Object] Result of the block or error code
|
|
64
202
|
def _runOnClass(action, sym, &block)
|
|
203
|
+
return -1 unless self.const_defined?(:ACTION_CLASS)
|
|
65
204
|
self::ACTION_CLASS.each(){|x|
|
|
66
|
-
next if x::ACTION_LIST.index(action) == nil
|
|
67
|
-
|
|
68
205
|
# Resolve overridden/extended class (addon)
|
|
69
206
|
class_to_use = self.respond_to?(:getExtendedClass) ? self.getExtendedClass(x) : x
|
|
70
|
-
|
|
207
|
+
next if !class_to_use.const_defined?(:ACTION_LIST) ||
|
|
208
|
+
class_to_use::ACTION_LIST.index(action) == nil
|
|
71
209
|
if sym != nil
|
|
72
210
|
has_base = x.singleton_methods().index(sym) != nil
|
|
73
211
|
has_addon = class_to_use != x && class_to_use.singleton_methods().index(sym) != nil
|
|
@@ -114,7 +252,7 @@ module CLIClassTool
|
|
|
114
252
|
def execAction(opts, action, error_class = nil)
|
|
115
253
|
caught_error_class = error_class || StandardError
|
|
116
254
|
|
|
117
|
-
self._runOnClass(action, nil) {|kClass|
|
|
255
|
+
ret_code = self._runOnClass(action, nil) {|kClass|
|
|
118
256
|
begin
|
|
119
257
|
# Use load factory method if defined, else fall back to .new
|
|
120
258
|
obj = kClass.respond_to?(:load) ? kClass.load() : kClass.new()
|
|
@@ -134,6 +272,15 @@ module CLIClassTool
|
|
|
134
272
|
end
|
|
135
273
|
end
|
|
136
274
|
}
|
|
275
|
+
|
|
276
|
+
if ret_code == -1 && action == :list_actions
|
|
277
|
+
actions = self.getActionAttr("ACTION_LIST").map(){|x| self.actionToString(x)}
|
|
278
|
+
actions.reject! { |x| x == "list_actions" }
|
|
279
|
+
puts actions.join("\n")
|
|
280
|
+
return 0
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
return ret_code
|
|
137
284
|
end
|
|
138
285
|
|
|
139
286
|
# Set verbose logging
|
|
@@ -193,7 +340,7 @@ module CLIClassTool
|
|
|
193
340
|
# @param opts [Hash] Initial options hash
|
|
194
341
|
# @param argv [Array<String>] Command line arguments (defaults to ARGV)
|
|
195
342
|
# @yield [parser, phase, action_opts] Custom options setup callback block
|
|
196
|
-
def run_cli(opts = {}, argv = ARGV)
|
|
343
|
+
def run_cli(opts = {}, argv = ARGV, &block)
|
|
197
344
|
# Fetch actions and action helps
|
|
198
345
|
action_helps = self.getActionAttr("ACTION_HELP")
|
|
199
346
|
|
|
@@ -215,7 +362,7 @@ module CLIClassTool
|
|
|
215
362
|
col_width = max_len + 4
|
|
216
363
|
action_helps.each do |k, x|
|
|
217
364
|
indent = col_width - self.actionToString(k).length
|
|
218
|
-
action_parser.separator "\t * " + self.actionToString(k) + (" " * indent) + x
|
|
365
|
+
action_parser.separator "\t * " + self.actionToString(k) + (" " * indent) + x.to_s
|
|
219
366
|
end
|
|
220
367
|
|
|
221
368
|
# Include any registered custom addon class listings if defined
|
|
@@ -226,6 +373,19 @@ module CLIClassTool
|
|
|
226
373
|
end
|
|
227
374
|
end
|
|
228
375
|
|
|
376
|
+
# Include command aliases if defined
|
|
377
|
+
if self.const_defined?(:CLI_COMMAND_ALIASES)
|
|
378
|
+
aliases = self::CLI_COMMAND_ALIASES
|
|
379
|
+
if aliases.is_a?(Hash) && aliases.length > 0
|
|
380
|
+
action_parser.separator ""
|
|
381
|
+
action_parser.separator "Command aliases:"
|
|
382
|
+
aliases.each do |k, v|
|
|
383
|
+
exp_str = v.is_a?(Array) ? v.join(" ") : v.to_s
|
|
384
|
+
action_parser.separator "\t * #{k} -> #{exp_str}"
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
|
|
229
389
|
rest = action_parser.order!(argv)
|
|
230
390
|
if rest.length <= 0
|
|
231
391
|
STDERR.puts("Error: No action provided")
|
|
@@ -233,7 +393,38 @@ module CLIClassTool
|
|
|
233
393
|
exit 1
|
|
234
394
|
end
|
|
235
395
|
|
|
396
|
+
# Expand command aliases
|
|
397
|
+
if self.const_defined?(:CLI_COMMAND_ALIASES)
|
|
398
|
+
aliases = self::CLI_COMMAND_ALIASES
|
|
399
|
+
if aliases.is_a?(Hash)
|
|
400
|
+
action_s = argv[0]
|
|
401
|
+
action_sym = action_s ? action_s.to_sym : nil
|
|
402
|
+
if aliases.key?(action_s) || (action_sym && aliases.key?(action_sym))
|
|
403
|
+
expansion = aliases[action_s] || aliases[action_sym]
|
|
404
|
+
expansion = expansion.split(' ') if expansion.is_a?(String)
|
|
405
|
+
argv.shift()
|
|
406
|
+
argv.unshift(*expansion)
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
end
|
|
410
|
+
|
|
236
411
|
action_s = argv[0]
|
|
412
|
+
|
|
413
|
+
# Intercept subcommands here!
|
|
414
|
+
sub_actions = self.cli_sub_actions
|
|
415
|
+
if sub_actions.key?(action_s)
|
|
416
|
+
sub_cli = sub_actions[action_s]
|
|
417
|
+
if sub_cli.respond_to?(:verbose_log=)
|
|
418
|
+
sub_cli.verbose_log = self.verbose_log
|
|
419
|
+
end
|
|
420
|
+
argv.shift()
|
|
421
|
+
if block
|
|
422
|
+
exit(sub_cli.run_cli(opts, argv, &block))
|
|
423
|
+
else
|
|
424
|
+
exit(sub_cli.run_cli(opts, argv))
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
237
428
|
action = opts[:action] = self.stringToAction(action_s)
|
|
238
429
|
argv.shift()
|
|
239
430
|
|
|
@@ -255,11 +446,11 @@ module CLIClassTool
|
|
|
255
446
|
self.setOpts(action, opts_parser, opts)
|
|
256
447
|
|
|
257
448
|
# Order remaining arguments
|
|
449
|
+
rest = opts_parser.order!(argv)
|
|
258
450
|
if opts[:ignore_opts] != true
|
|
259
|
-
rest = opts_parser.order!(argv)
|
|
260
451
|
raise("Extra Unexpected extra arguments provided: " + rest.map(){|x|"'" + x + "'"}.join(", ")) if rest.length != 0
|
|
261
452
|
else
|
|
262
|
-
opts[:extra_args] =
|
|
453
|
+
opts[:extra_args] = rest
|
|
263
454
|
end
|
|
264
455
|
|
|
265
456
|
# Validate options and execute action
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cli_class_tool
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nicolas Morey
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-09-07 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: rake
|
|
@@ -83,7 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
83
83
|
- !ruby/object:Gem::Version
|
|
84
84
|
version: '0'
|
|
85
85
|
requirements: []
|
|
86
|
-
rubygems_version: 4.0.
|
|
86
|
+
rubygems_version: 4.0.16
|
|
87
87
|
specification_version: 4
|
|
88
88
|
summary: A lightweight object-oriented framework for class-based command-line interface
|
|
89
89
|
(CLI) applications.
|