rodish 1.0.0 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be6ead8f4e3fb70c94b061d779c3f552842d78acb16e3828e37b64d02804772b
4
- data.tar.gz: ec3ad13d30478c9048ea2ff74d07da4cd7c8e1d2efe99e3a569bd4fba09274f4
3
+ metadata.gz: fe9eb4dc1a9f6cc1e7582a9b24dcbc3491f47e3a3aa24def4ff3f94a241d1dbc
4
+ data.tar.gz: 90748c4035108090d6576a824e6a6e1699079a755bbf405ce0484d238cd6ee1b
5
5
  SHA512:
6
- metadata.gz: 93555b428ac603ad16d90fde03a0ff26ff2d8ce7097f8b9d32a8d57d1685b7042c1e8cee4842aeba3e2396ba990eb34b47e7d5df0ef3f90135d3892bb53e97f7
7
- data.tar.gz: f306a8549e5b70abac451dc0d75add3a4266c2d66d3aa79c2f05b8efde26379505a91cfc5bbc99cfbcd72931942f8f536e91568e18df7f2e970b03dda537a028
6
+ metadata.gz: 01030a76bc9a5db02ae2b78ddd9bcdb24b18d0b32d9421dffc41f92041700a062a2f2c13260656cce79521f07abccb3ed5da7b4efeb3e93b5de06c246f2dfdfa
7
+ data.tar.gz: fe7ea63452c88a46dbe528c33b8f2a54d965f93c47ebc9a4984cd985c5a4ec73c777c4f4fc8c08973783ee0f6eb0957fc0b9804485d91d35dc41fee0e51620f3
@@ -1,35 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "option_parser"
4
- require_relative "skip_option_parser"
5
4
  require_relative "errors"
6
5
 
7
6
  module Rodish
8
7
  # Rodish::Command is the main object in Rodish's processing.
9
8
  # It handles a single command, and may have one or more
10
- # subcommands and/or post subcommands, forming a tree.
9
+ # subcommands, forming a tree.
11
10
  #
12
11
  # Rodish's argv processing starts with the root command,
13
12
  # processing options and deleting appropriately to subcommands,
14
13
  # until the requested command or subcommand is located,
15
14
  # which is then executed.
16
15
  class Command
17
- option_parser = OptionParser.new
18
- option_parser.set_banner("")
19
- option_parser.freeze
20
-
21
- # The default option parser if no options are given for
22
- # the command.
23
- DEFAULT_OPTION_PARSER = option_parser
24
-
25
16
  # A hash of subcommands for the command. Keys are
26
17
  # subcommand name strings.
27
18
  attr_reader :subcommands
28
19
 
29
- # A hash of post subcommands for the command. Keys are
30
- # post subcommand name strings.
31
- attr_reader :post_subcommands
32
-
33
20
  # The block to execute if this command is the requested
34
21
  # subcommand. May be nil if this subcommand cannot be
35
22
  # executed, and can only dispatch to subcommands.
@@ -48,31 +35,20 @@ module Rodish
48
35
  # are placed directly in the options hash.
49
36
  attr_accessor :option_key
50
37
 
51
- # The post option parser for the current command. Called
52
- # only before dispatching to post subcommands.
53
- attr_accessor :post_option_parser
54
-
55
- # Similar to +option_key+, but for post options instead
56
- # of normal subcommands.
57
- attr_accessor :post_option_key
58
-
59
- # A before hook to execute before executing the current
60
- # command or dispatching to subcommands.
61
- attr_accessor :before
62
-
63
38
  # The number of arguments the run block will accept.
64
39
  # Should be either an integer or a range of integers.
65
40
  attr_accessor :num_args
66
41
 
67
- # The error message to use if an invalid number of
68
- # arguments is provided.
69
- attr_accessor :invalid_args_message
42
+ # A description for the command
43
+ attr_accessor :desc
44
+
45
+ # A usage banner for the command or subcommands.
46
+ attr_accessor :banner
70
47
 
71
48
  def initialize(command_path)
72
49
  @command_path = command_path
73
- @command_name = command_path.join(" ").freeze
50
+ @command_name = _command_name(command_path)
74
51
  @subcommands = {}
75
- @post_subcommands = {}
76
52
  @num_args = 0
77
53
  end
78
54
 
@@ -81,92 +57,72 @@ module Rodish
81
57
  def freeze
82
58
  @subcommands.each_value(&:freeze)
83
59
  @subcommands.freeze
84
- @post_subcommands.each_value(&:freeze)
85
- @post_subcommands.freeze
86
60
  @option_parser.freeze
87
- @post_option_parser.freeze
88
61
  super
89
62
  end
90
63
 
91
- # Run a post subcommand using the given context (generally self),
92
- # options, and argv. Usually called inside a run block, after
93
- # shifting one or more values off the given argv:
94
- #
95
- # run do |argv, opts, command|
96
- # @name = argv.shift
97
- # command.run(self, opts, argv)
98
- # end
99
- def run(context, options, argv)
100
- begin
101
- process_options(argv, options, @post_option_key, @post_option_parser)
102
- rescue ::OptionParser::InvalidOption => e
103
- raise CommandFailure.new(e.message, @post_option_parser)
104
- end
64
+ # Return a help string for the command.
65
+ def help
66
+ help_lines.join("\n")
67
+ end
105
68
 
106
- arg = argv[0]
107
- if arg && @post_subcommands[arg]
108
- process_subcommand(@post_subcommands, context, options, argv)
109
- else
110
- process_command_failure(arg, @post_subcommands, @post_option_parser, "post ")
69
+ # Return an array of help strings for the command.
70
+ def help_lines
71
+ output = []
72
+ help_order.each do |type|
73
+ send(:"_help_#{type}", output)
111
74
  end
75
+ output
76
+ end
77
+
78
+ # Process options for the command using the option key and parser.
79
+ def process_command_options(context, options, argv)
80
+ process_options(argv, options, @option_key, @option_parser)
112
81
  end
113
- alias run_post_subcommand run
114
82
 
115
83
  # Process the current command. This first processes the options.
116
84
  # After processing the options, it checks if the first argument
117
85
  # in the remaining argv is a subcommand. If so, it dispatches to
118
86
  # that subcommand. If not, it dispatches to the run block.
119
87
  def process(context, options, argv)
120
- process_options(argv, options, @option_key, @option_parser)
88
+ process_command_options(context, options, argv)
121
89
 
122
90
  arg = argv[0]
123
91
  if argv && @subcommands[arg]
124
92
  process_subcommand(@subcommands, context, options, argv)
125
93
  elsif run_block
126
94
  if valid_args?(argv)
127
- context.instance_exec(argv, options, &before) if before
128
-
129
95
  if @num_args.is_a?(Integer)
130
96
  context.instance_exec(*argv, options, self, &run_block)
131
97
  else
132
98
  context.instance_exec(argv, options, self, &run_block)
133
99
  end
134
- elsif @invalid_args_message
135
- raise_failure("invalid arguments#{subcommand_name} (#{@invalid_args_message})")
136
100
  else
137
- raise_failure("invalid number of arguments#{subcommand_name} (accepts: #{@num_args}, given: #{argv.length})")
101
+ raise_invalid_args_failure(argv)
138
102
  end
139
103
  else
140
- process_command_failure(arg, @subcommands, @option_parser, "")
104
+ process_command_failure(arg, @subcommands, "")
141
105
  end
142
106
  rescue ::OptionParser::InvalidOption => e
143
- if @option_parser || @post_option_parser
144
- raise_failure(e.message)
145
- else
146
- raise
147
- end
107
+ raise_failure(e.message)
148
108
  end
149
109
 
150
- # This yields the current command and all subcommands and
151
- # post subcommands, recursively.
110
+ # This yields the current command and all subcommands, recursively.
152
111
  def each_subcommand(names = [].freeze, &block)
153
112
  yield names, self
154
113
  _each_subcommand(names, @subcommands, &block)
155
- _each_subcommand(names, @post_subcommands, &block)
156
114
  end
157
115
 
158
- # Raise a CommandFailure with the given error and the given
159
- # option parsers.
160
- def raise_failure(message, option_parsers = self.option_parsers)
161
- raise CommandFailure.new(message, option_parsers)
116
+ # Yield each banner string (if any) to the block.
117
+ def each_banner
118
+ yield banner if banner
119
+ nil
162
120
  end
163
121
 
164
- # Returns a string of options text for the command's option parsers.
165
- def options_text
166
- option_parsers = self.option_parsers
167
- unless option_parsers.empty?
168
- _options_text(option_parsers)
169
- end
122
+ # Raise a CommandFailure with the given error and the given
123
+ # option parsers.
124
+ def raise_failure(message)
125
+ raise CommandFailure.new(message, self)
170
126
  end
171
127
 
172
128
  # Returns a Command instance for the named subcommand.
@@ -175,19 +131,114 @@ module Rodish
175
131
  _subcommand(@subcommands, name)
176
132
  end
177
133
 
178
- # Returns a Command instance for the named post subcommand.
179
- # This will autoload the post subcommand if not already loaded.
180
- def post_subcommand(name)
181
- _subcommand(@post_subcommands, name)
134
+ private
135
+
136
+ # The string to use for the usage heading in help output.
137
+ def help_usage_heading
138
+ "Usage:"
182
139
  end
183
140
 
184
- # An array of option parsers for the command. May be empty
185
- # if the command has no option parsers.
186
- def option_parsers
187
- [@option_parser, @post_option_parser].compact
141
+ # The string to use for the command heading in help output.
142
+ def help_command_heading
143
+ "Commands:"
188
144
  end
189
145
 
190
- private
146
+ # The string to use for the options heading in help output.
147
+ def help_options_heading
148
+ "Options:"
149
+ end
150
+
151
+ # Use default help order by default.
152
+ def help_order
153
+ default_help_order
154
+ end
155
+
156
+ # The default order of help sections
157
+ def default_help_order
158
+ [:desc, :banner, :commands, :options]
159
+ end
160
+
161
+ # Add description to help output.
162
+ def _help_desc(output)
163
+ if desc
164
+ output << desc << ""
165
+ end
166
+ end
167
+
168
+ # Add banner to help output.
169
+ def _help_banner(output)
170
+ if each_banner{break true}
171
+ output << help_usage_heading
172
+ each_banner do |banner|
173
+ output << " #{banner}"
174
+ end
175
+ output << ""
176
+ end
177
+ end
178
+
179
+ # Add commands to help output.
180
+ def _help_commands(output)
181
+ name_len = 0
182
+ each_local_subcommand do |name|
183
+ len = name.length
184
+ name_len = len if len > name_len
185
+ end
186
+
187
+ __help_command_hashes.each do |heading, hash|
188
+ next if hash.empty?
189
+ output << heading
190
+ command_output = []
191
+ _each_local_subcommand(hash) do |name, command|
192
+ command_output << " #{name.ljust(name_len)} #{command.desc}"
193
+ end
194
+ command_output.sort!
195
+ output.concat(command_output)
196
+ output << ""
197
+ end
198
+ end
199
+
200
+ # Hash with hash of subcommand values to potentially show help output for.
201
+ def __help_command_hashes
202
+ {help_command_heading => @subcommands}
203
+ end
204
+
205
+ # Add options to help output.
206
+ def _help_options(output)
207
+ __help_option_parser_hashes.each do |heading, parser|
208
+ next if omit_option_parser_from_help?(parser)
209
+ output << heading
210
+ output << parser.summarize(String.new)
211
+ end
212
+ end
213
+
214
+ # Hash with option parser values to potentially show help output for.
215
+ def __help_option_parser_hashes
216
+ {help_options_heading => @option_parser}
217
+ end
218
+
219
+ # Whether the given option parser should be ommitted from the
220
+ # command help output.
221
+ def omit_option_parser_from_help?(parser)
222
+ parser.nil?
223
+ end
224
+
225
+ # Raise a error when an invalid number of arguments has been provided.
226
+ def raise_invalid_args_failure(argv)
227
+ raise_failure(invalid_num_args_failure_error_message(argv))
228
+ end
229
+
230
+ # Yield each local subcommand to the block. This does not
231
+ # yield the current command or nested subcommands.
232
+ def each_local_subcommand(&block)
233
+ _each_local_subcommand(@subcommands, &block)
234
+ end
235
+
236
+ # Internals of each_local_subcommand.
237
+ def _each_local_subcommand(subcommands)
238
+ subcommands.each_key do |name|
239
+ yield name, _subcommand(subcommands, name)
240
+ end
241
+ end
191
242
 
192
243
  # Yield to the block for each subcommand in the given
193
244
  # subcommands. Internals of #each_subcommand.
@@ -214,32 +265,37 @@ module Rodish
214
265
  subcommand
215
266
  end
216
267
 
217
- # Return a string containing all option parser text.
218
- def _options_text(option_parsers)
219
- option_parsers.join("\n\n")
220
- end
221
-
222
- # Handle command failures for both subcommands and post subcommands.
223
- def process_command_failure(arg, subcommands, option_parser, prefix)
268
+ # Handle command failures for subcommands.
269
+ def process_command_failure(arg, subcommands, prefix)
224
270
  if subcommands.empty?
225
271
  raise ProgramBug, "program bug, no run block or #{prefix}subcommands defined#{subcommand_name}"
226
272
  elsif arg
227
- raise_failure("invalid #{prefix}subcommand: #{arg}", option_parser)
273
+ raise_failure(invalid_subcommand_error_message(arg, subcommands, prefix))
228
274
  else
229
- raise_failure("no #{prefix}subcommand provided", option_parser)
275
+ raise_failure(no_subcommand_provided_error_message(arg, subcommands, prefix))
230
276
  end
231
277
  end
232
278
 
279
+ # The error message to use when an invalid number of arguments is provided.
280
+ def invalid_num_args_failure_error_message(argv)
281
+ "invalid number of arguments#{subcommand_name} (requires: #{@num_args}, given: #{argv.length})"
282
+ end
283
+
284
+ # Error message for cases where an invalid subcommand is provided.
285
+ def invalid_subcommand_error_message(arg, subcommands, prefix)
286
+ "invalid #{prefix}subcommand: #{arg}"
287
+ end
288
+
289
+ # Error message for cases where a subcommand is required and not provided.
290
+ def no_subcommand_provided_error_message(arg, subcommands, prefix)
291
+ "no #{prefix}subcommand provided"
292
+ end
293
+
233
294
  # Process options for the given command. If option_key is set,
234
295
  # parsed options are added as a options subhash under the given key.
235
296
  # Otherwise, parsed options placed directly into options.
236
297
  def process_options(argv, options, option_key, option_parser)
237
- case option_parser
238
- when SkipOptionParser
239
- # do nothing
240
- when nil
241
- DEFAULT_OPTION_PARSER.order!(argv)
242
- else
298
+ if option_parser
243
299
  command_options = option_key ? {} : options
244
300
 
245
301
  option_parser.order!(argv, into: command_options)
@@ -247,6 +303,8 @@ module Rodish
247
303
  if option_key
248
304
  options[option_key] = command_options
249
305
  end
306
+ else
307
+ self.class::DEFAULT_OPTION_PARSER.order!(argv)
250
308
  end
251
309
  end
252
310
 
@@ -255,7 +313,6 @@ module Rodish
255
313
  def process_subcommand(subcommands, context, options, argv)
256
314
  subcommand = _subcommand(subcommands, argv[0])
257
315
  argv.shift
258
- context.instance_exec(argv, options, &before) if before
259
316
  subcommand.process(context, options, argv)
260
317
  end
261
318
 
@@ -268,6 +325,12 @@ module Rodish
268
325
  end
269
326
  end
270
327
 
328
+ # Set the command name for the command. The command name is used
329
+ # in error messages.
330
+ def _command_name(command_path)
331
+ command_path.join(" ").freeze
332
+ end
333
+
271
334
  # Return whether the given argv has a valid number of arguments.
272
335
  def valid_args?(argv)
273
336
  if @num_args.is_a?(Integer)
data/lib/rodish/dsl.rb CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  require_relative "command"
4
4
  require_relative "option_parser"
5
- require_relative "skip_option_parser"
6
5
 
7
6
  module Rodish
8
7
  # The Rodish::DSL class implements Rodish's DSL. Blocks
@@ -18,8 +17,8 @@ module Rodish
18
17
  # the given block in the context of a new instance using
19
18
  # that command.
20
19
  def self.command(command_path, &block)
21
- command = Command.new(command_path)
22
- new(command).instance_exec(&block)
20
+ command = self::Command.new(command_path)
21
+ new(command).instance_exec(&block) if block
23
22
  command
24
23
  end
25
24
 
@@ -27,16 +26,14 @@ module Rodish
27
26
  @command = command
28
27
  end
29
28
 
30
- # Skip option parsing for the command. This is different
31
- # then the default option parsing, which will error if any
32
- # options are given. A banner must be provided, setting
33
- # the usage for the command.
34
- #
35
- # The main reason to use this is if you are going to pass
36
- # the entire remaining argv as the argv to another
37
- # program.
38
- def skip_option_parsing(banner)
39
- @command.option_parser = SkipOptionParser.new(banner)
29
+ # Set the description for the command.
30
+ def desc(description)
31
+ @command.desc = description
32
+ end
33
+
34
+ # Set the banner for the command execution and subcommand usage.
35
+ def banner(banner)
36
+ @command.banner = banner
40
37
  end
41
38
 
42
39
  # Set the option parser for the command to based on the
@@ -49,38 +46,18 @@ module Rodish
49
46
  #
50
47
  # If +key+ is given, parsed options
51
48
  # will be placed in a subhash using that key.
52
- #
53
- # The block is optional, allowing you to set a usage banner for
54
- # commands without allowing any options.
55
49
  def options(banner, key: nil, &block)
50
+ @command.banner = banner
56
51
  @command.option_key = key
57
- @command.option_parser = create_option_parser(banner, @command.subcommands, &block)
58
- end
59
-
60
- # Similar to +options+, but sets the option parser for post
61
- # subcommands. This option parser is only used when the
62
- # command is executed and chooses to run a post subcommand.
63
- def post_options(banner, key: nil, &block)
64
- @command.post_option_key = key
65
- @command.post_option_parser = create_option_parser(banner, @command.post_subcommands, &block)
66
- end
67
-
68
- # Sets the before block. This block is executed in the same
69
- # context as the run block would be executed, before either
70
- # subcommand execution or execution of the current command.
71
- def before(&block)
72
- @command.before = block
52
+ @command.option_parser = create_option_parser(&block)
73
53
  end
74
54
 
75
55
  # Set the number of arguments supported by this command.
76
56
  # The default is 0. To support a fixed number of arguments,
77
57
  # pass an Integer. To support a variable number of arguments,
78
- # pass a Range. The +invalid_args_message+ argument sets the
79
- # error message to use if an invalid number of arguments is
80
- # passed.
81
- def args(args, invalid_args_message: nil)
58
+ # pass a Range.
59
+ def args(args)
82
60
  @command.num_args = args
83
- @command.invalid_args_message = invalid_args_message
84
61
  end
85
62
 
86
63
  # Autoload subcommands from the given directory. Filenames
@@ -93,24 +70,12 @@ module Rodish
93
70
  _autoload_subcommand_dir(@command.subcommands, dir)
94
71
  end
95
72
 
96
- # Similar to +autoload_subcommand_dir+, but for post
97
- # subcommands instead of normal subcommands.
98
- def autoload_post_subcommand_dir(dir)
99
- _autoload_subcommand_dir(@command.post_subcommands, dir)
100
- end
101
-
102
73
  # Create a new subcommand with the given name and yield to
103
74
  # the block to configure the subcommand.
104
75
  def on(command_name, &block)
105
76
  _on(@command.subcommands, command_name, &block)
106
77
  end
107
78
 
108
- # Same as +on+, but for post subcommands instead of normal
109
- # subcommands.
110
- def run_on(command_name, &block)
111
- _on(@command.post_subcommands, command_name, &block)
112
- end
113
-
114
79
  # Set the block to run for subcommand execution. Commands
115
80
  # should have subcommands and/or a run block, otherwise it
116
81
  # is not possible to use the command successfully.
@@ -118,38 +83,9 @@ module Rodish
118
83
  @command.run_block = block
119
84
  end
120
85
 
121
- # A shortcut for calling +on+ and +run+.
122
- #
123
- # is "hello" do
124
- # :world
125
- # end
126
- #
127
- # is equivalent to:
128
- #
129
- # on "hello" do
130
- # run do
131
- # :world
132
- # end
133
- # end
134
- #
135
- # The +args+ argument sets the number of arguments supported by
136
- # the command.
137
- #
138
- # The +invalid_args_message+ arguments set the error message to
139
- # use if an invalid number of arguments is provided.
140
- def is(command_name, args: 0, invalid_args_message: nil, &block)
141
- _is(:on, command_name, args:, invalid_args_message:, &block)
142
- end
143
-
144
- # Similar to +is+, but for post subcommands instead of normal
145
- # subcommands.
146
- def run_is(command_name, args: 0, invalid_args_message: nil, &block)
147
- _is(:run_on, command_name, args:, invalid_args_message:, &block)
148
- end
149
-
150
86
  private
151
87
 
152
- # Internals of autoloading of normal and post subcommands.
88
+ # Internals of autoloading of subcommands.
153
89
  # This sets the value of the subcommand as a string instead of a
154
90
  # Command instance, and the Command#_subcommand method recognizes
155
91
  # this and handles the autoloading.
@@ -159,30 +95,17 @@ module Rodish
159
95
  end
160
96
  end
161
97
 
162
- # Internals of +is+ and +run_is+.
163
- def _is(meth, command_name, args:, invalid_args_message: nil, &block)
164
- public_send(meth, command_name) do
165
- args(args, invalid_args_message:)
166
- run(&block)
167
- end
168
- end
169
-
170
- # Internals of +on+ and +run_on+.
98
+ # Internals of +on+.
171
99
  def _on(hash, command_name, &block)
172
100
  command_path = @command.command_path + [command_name]
173
- hash[command_name] = DSL.command(command_path.freeze, &block)
101
+ hash[command_name] = self.class.command(command_path.freeze, &block)
174
102
  end
175
103
 
176
- # Internals of +options+ and +post_options+.
177
- def create_option_parser(banner, subcommands, &block)
178
- option_parser = OptionParser.new
179
- option_parser.set_banner("Usage: #{banner}")
180
- if block
181
- option_parser.separator ""
182
- option_parser.separator "Options:"
183
- option_parser.instance_exec(&block)
184
- end
185
- option_parser.subcommands = subcommands
104
+ # Internals of +options+.
105
+ def create_option_parser(&block)
106
+ option_parser = self.class::OptionParser.new
107
+ option_parser.banner = "" # Avoids issues when parser is frozen
108
+ option_parser.instance_exec(&block)
186
109
  option_parser
187
110
  end
188
111
  end
data/lib/rodish/errors.rb CHANGED
@@ -26,9 +26,10 @@ module Rodish
26
26
  # * Invalid subcommands
27
27
  # * No subcommand given for a command that only supports subcommands
28
28
  class CommandFailure < CommandExit
29
- def initialize(message, option_parsers = [])
30
- option_parsers = [option_parsers] unless option_parsers.is_a?(Array)
31
- @option_parsers = option_parsers.compact
29
+ attr_reader :command
30
+
31
+ def initialize(message, command=nil)
32
+ @command = command
32
33
  super(message)
33
34
  end
34
35
 
@@ -41,10 +42,11 @@ module Rodish
41
42
  # parsers. This can be used to show usage an options along with
42
43
  # error messages for failing commands.
43
44
  def message_with_usage
44
- if @option_parsers.empty?
45
+ help = @command&.help || ''
46
+ if help.empty?
45
47
  message
46
48
  else
47
- "#{message}\n\n#{@option_parsers.join("\n\n")}"
49
+ "#{message}\n\n#{help}"
48
50
  end
49
51
  end
50
52
  end
@@ -7,58 +7,11 @@ module Rodish
7
7
  # Rodish::OptionPaser is a subclass of Ruby's standard OptionParser
8
8
  # (from the optparse library).
9
9
  class OptionParser < ::OptionParser
10
- # A hash of subcommands for the option parser. If not empty,
11
- # shows available subcommands when showing options.
12
- attr_accessor :subcommands
13
-
14
10
  # Don't add officious, which includes options that call exit.
15
11
  # With Rodish, there are no secret options, only options you define.
16
12
  def add_officious
17
13
  end
18
14
 
19
- # Add the available subcommands to the returned string if there are
20
- # any subcommands.
21
- def to_s
22
- string = super
23
-
24
- if subcommands.length > 6
25
- string += "\nSubcommands:\n #{subcommands.keys.sort.join("\n ")}\n"
26
- elsif !subcommands.empty?
27
- string += "\nSubcommands: #{subcommands.keys.sort.join(" ")}\n"
28
- end
29
-
30
- string
31
- end
32
-
33
- # Helper method that takes an array of values, wraps them to the given
34
- # limit, and adds each line as a separator. This is useful when you
35
- # have a large amount of information you want to display and you want
36
- # to wrap if for display to the user when showing options.
37
- def wrap(prefix, values, separator: " ", limit: 80)
38
- line = [prefix]
39
- lines = [line]
40
- prefix_length = length = prefix.length
41
- sep_length = separator.length
42
- indent = " " * prefix_length
43
-
44
- values.each do |value|
45
- value_length = value.length
46
- new_length = sep_length + length + value_length
47
- if new_length > limit
48
- line = [indent, separator, value]
49
- lines << line
50
- length = prefix_length
51
- else
52
- line << separator << value
53
- end
54
- length += sep_length + value_length
55
- end
56
-
57
- lines.each do |line|
58
- separator line.join
59
- end
60
- end
61
-
62
15
  # Halt processing with a CommandExit using the given string.
63
16
  # This can be used to implement early exits, by calling this
64
17
  # method in a block: