cli_class_tool 0.4.1 → 0.5.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 +12 -0
- data/README.md +99 -0
- data/lib/cli_class_tool/common.rb +3 -1
- data/lib/cli_class_tool/utils.rb +204 -12
- 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: 2c2833d3bd929aa6e69df3df0cbadbbc3e92030afdbf8172387869a9ad4f95f3
|
|
4
|
+
data.tar.gz: 3bac4466f6a3d4e07bc739e36eb0eaaba2929c5a308a980715e4d0c55ef11fd7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5f8ab8e7152ade287dc0a8c50598d2069cbbd99ecbcc5d0bb9cc7fd37a8a9e3fa401a889030c3bcea9a387cb2f5ac5a8ab49ef40b78980a982355ecdcba3bd53
|
|
7
|
+
data.tar.gz: d031b3ca4da59379e5f3079ad091f10fd4934b0d1150df9f846ea2de77e1d439e236ac2070763418083394956cef64ed3f7ec2f7883222c66b14cd24e3ea9854
|
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
------------------
|
|
2
|
+
0.5.0 (2026-07-22)
|
|
3
|
+
------------------
|
|
4
|
+
|
|
5
|
+
* 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
|
|
6
|
+
* Support recursive subcommand alias and shortcut resolution within deeply nested sub-CLIClasses
|
|
7
|
+
* Add CLI_COMMAND_ALIASES support to define top-level command shortcuts/aliases to deeply-nested subcommand actions
|
|
8
|
+
* Filter list_actions command output to exclude itself, preserving clean autocomplete query behavior
|
|
9
|
+
* Require optparse explicitly inside utils.rb to prevent NameErrors in production environments
|
|
10
|
+
* Safeguard attribute resolution and _runOnClass to gracefully default when ACTION_HELP/ACTION_LIST is missing
|
|
11
|
+
* Add recursive nested subcommand and namespace auto-discovery support
|
|
12
|
+
|
|
1
13
|
------------------
|
|
2
14
|
0.4.1 (2026-06-05)
|
|
3
15
|
------------------
|
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()
|
|
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)
|
|
55
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,8 +200,9 @@ 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
|
|
205
|
+
next if !x.const_defined?(:ACTION_LIST) || x::ACTION_LIST.index(action) == nil
|
|
67
206
|
|
|
68
207
|
# Resolve overridden/extended class (addon)
|
|
69
208
|
class_to_use = self.respond_to?(:getExtendedClass) ? self.getExtendedClass(x) : x
|
|
@@ -114,7 +253,7 @@ module CLIClassTool
|
|
|
114
253
|
def execAction(opts, action, error_class = nil)
|
|
115
254
|
caught_error_class = error_class || StandardError
|
|
116
255
|
|
|
117
|
-
self._runOnClass(action, nil) {|kClass|
|
|
256
|
+
ret_code = self._runOnClass(action, nil) {|kClass|
|
|
118
257
|
begin
|
|
119
258
|
# Use load factory method if defined, else fall back to .new
|
|
120
259
|
obj = kClass.respond_to?(:load) ? kClass.load() : kClass.new()
|
|
@@ -134,6 +273,15 @@ module CLIClassTool
|
|
|
134
273
|
end
|
|
135
274
|
end
|
|
136
275
|
}
|
|
276
|
+
|
|
277
|
+
if ret_code == -1 && action == :list_actions
|
|
278
|
+
actions = self.getActionAttr("ACTION_LIST").map(){|x| self.actionToString(x)}
|
|
279
|
+
actions.reject! { |x| x == "list_actions" }
|
|
280
|
+
puts actions.join("\n")
|
|
281
|
+
return 0
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
return ret_code
|
|
137
285
|
end
|
|
138
286
|
|
|
139
287
|
# Set verbose logging
|
|
@@ -193,7 +341,7 @@ module CLIClassTool
|
|
|
193
341
|
# @param opts [Hash] Initial options hash
|
|
194
342
|
# @param argv [Array<String>] Command line arguments (defaults to ARGV)
|
|
195
343
|
# @yield [parser, phase, action_opts] Custom options setup callback block
|
|
196
|
-
def run_cli(opts = {}, argv = ARGV)
|
|
344
|
+
def run_cli(opts = {}, argv = ARGV, &block)
|
|
197
345
|
# Fetch actions and action helps
|
|
198
346
|
action_helps = self.getActionAttr("ACTION_HELP")
|
|
199
347
|
|
|
@@ -215,7 +363,7 @@ module CLIClassTool
|
|
|
215
363
|
col_width = max_len + 4
|
|
216
364
|
action_helps.each do |k, x|
|
|
217
365
|
indent = col_width - self.actionToString(k).length
|
|
218
|
-
action_parser.separator "\t * " + self.actionToString(k) + (" " * indent) + x
|
|
366
|
+
action_parser.separator "\t * " + self.actionToString(k) + (" " * indent) + x.to_s
|
|
219
367
|
end
|
|
220
368
|
|
|
221
369
|
# Include any registered custom addon class listings if defined
|
|
@@ -226,6 +374,19 @@ module CLIClassTool
|
|
|
226
374
|
end
|
|
227
375
|
end
|
|
228
376
|
|
|
377
|
+
# Include command aliases if defined
|
|
378
|
+
if self.const_defined?(:CLI_COMMAND_ALIASES)
|
|
379
|
+
aliases = self::CLI_COMMAND_ALIASES
|
|
380
|
+
if aliases.is_a?(Hash) && aliases.length > 0
|
|
381
|
+
action_parser.separator ""
|
|
382
|
+
action_parser.separator "Command aliases:"
|
|
383
|
+
aliases.each do |k, v|
|
|
384
|
+
exp_str = v.is_a?(Array) ? v.join(" ") : v.to_s
|
|
385
|
+
action_parser.separator "\t * #{k} -> #{exp_str}"
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
229
390
|
rest = action_parser.order!(argv)
|
|
230
391
|
if rest.length <= 0
|
|
231
392
|
STDERR.puts("Error: No action provided")
|
|
@@ -233,7 +394,38 @@ module CLIClassTool
|
|
|
233
394
|
exit 1
|
|
234
395
|
end
|
|
235
396
|
|
|
397
|
+
# Expand command aliases
|
|
398
|
+
if self.const_defined?(:CLI_COMMAND_ALIASES)
|
|
399
|
+
aliases = self::CLI_COMMAND_ALIASES
|
|
400
|
+
if aliases.is_a?(Hash)
|
|
401
|
+
action_s = argv[0]
|
|
402
|
+
action_sym = action_s ? action_s.to_sym : nil
|
|
403
|
+
if aliases.key?(action_s) || (action_sym && aliases.key?(action_sym))
|
|
404
|
+
expansion = aliases[action_s] || aliases[action_sym]
|
|
405
|
+
expansion = expansion.split(' ') if expansion.is_a?(String)
|
|
406
|
+
argv.shift()
|
|
407
|
+
argv.unshift(*expansion)
|
|
408
|
+
end
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
|
|
236
412
|
action_s = argv[0]
|
|
413
|
+
|
|
414
|
+
# Intercept subcommands here!
|
|
415
|
+
sub_actions = self.cli_sub_actions
|
|
416
|
+
if sub_actions.key?(action_s)
|
|
417
|
+
sub_cli = sub_actions[action_s]
|
|
418
|
+
if sub_cli.respond_to?(:verbose_log=)
|
|
419
|
+
sub_cli.verbose_log = self.verbose_log
|
|
420
|
+
end
|
|
421
|
+
argv.shift()
|
|
422
|
+
if block
|
|
423
|
+
exit(sub_cli.run_cli(opts, argv, &block))
|
|
424
|
+
else
|
|
425
|
+
exit(sub_cli.run_cli(opts, argv))
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
|
|
237
429
|
action = opts[:action] = self.stringToAction(action_s)
|
|
238
430
|
argv.shift()
|
|
239
431
|
|
|
@@ -255,11 +447,11 @@ module CLIClassTool
|
|
|
255
447
|
self.setOpts(action, opts_parser, opts)
|
|
256
448
|
|
|
257
449
|
# Order remaining arguments
|
|
450
|
+
rest = opts_parser.order!(argv)
|
|
258
451
|
if opts[:ignore_opts] != true
|
|
259
|
-
rest = opts_parser.order!(argv)
|
|
260
452
|
raise("Extra Unexpected extra arguments provided: " + rest.map(){|x|"'" + x + "'"}.join(", ")) if rest.length != 0
|
|
261
453
|
else
|
|
262
|
-
opts[:extra_args] =
|
|
454
|
+
opts[:extra_args] = rest
|
|
263
455
|
end
|
|
264
456
|
|
|
265
457
|
# 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.0
|
|
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-07-22 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.
|