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 +4 -4
- data/lib/rodish/command.rb +167 -104
- data/lib/rodish/dsl.rb +22 -99
- data/lib/rodish/errors.rb +7 -5
- data/lib/rodish/option_parser.rb +0 -47
- data/lib/rodish/plugins/_context_sensitive_help.rb +46 -0
- data/lib/rodish/plugins/_wrap.rb +38 -0
- data/lib/rodish/plugins/after_options_hook.rb +39 -0
- data/lib/rodish/plugins/cache_help_output.rb +28 -0
- data/lib/rodish/plugins/help_examples.rb +48 -0
- data/lib/rodish/plugins/help_option_values.rb +80 -0
- data/lib/rodish/plugins/help_order.rb +49 -0
- data/lib/rodish/plugins/invalid_args_message.rb +74 -0
- data/lib/rodish/plugins/is.rb +65 -0
- data/lib/rodish/plugins/post_commands.rb +160 -0
- data/lib/rodish/plugins/run_is.rb +32 -0
- data/lib/rodish/plugins/skip_option_parsing.rb +51 -0
- data/lib/rodish/plugins/usages.rb +29 -0
- data/lib/rodish/plugins/wrapped_options_separator.rb +29 -0
- data/lib/rodish/plugins.rb +25 -0
- data/lib/rodish/processor.rb +54 -22
- data/lib/rodish.rb +16 -1
- metadata +18 -10
- data/CHANGELOG +0 -3
- data/README.rdoc +0 -257
- data/lib/rodish/skip_option_parser.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe9eb4dc1a9f6cc1e7582a9b24dcbc3491f47e3a3aa24def4ff3f94a241d1dbc
|
4
|
+
data.tar.gz: 90748c4035108090d6576a824e6a6e1699079a755bbf405ce0484d238cd6ee1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01030a76bc9a5db02ae2b78ddd9bcdb24b18d0b32d9421dffc41f92041700a062a2f2c13260656cce79521f07abccb3ed5da7b4efeb3e93b5de06c246f2dfdfa
|
7
|
+
data.tar.gz: fe7ea63452c88a46dbe528c33b8f2a54d965f93c47ebc9a4984cd985c5a4ec73c777c4f4fc8c08973783ee0f6eb0957fc0b9804485d91d35dc41fee0e51620f3
|
data/lib/rodish/command.rb
CHANGED
@@ -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
|
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
|
-
#
|
68
|
-
|
69
|
-
|
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
|
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
|
-
#
|
92
|
-
|
93
|
-
|
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
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
-
|
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
|
-
|
101
|
+
raise_invalid_args_failure(argv)
|
138
102
|
end
|
139
103
|
else
|
140
|
-
process_command_failure(arg, @subcommands,
|
104
|
+
process_command_failure(arg, @subcommands, "")
|
141
105
|
end
|
142
106
|
rescue ::OptionParser::InvalidOption => e
|
143
|
-
|
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
|
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
|
-
#
|
159
|
-
|
160
|
-
|
161
|
-
|
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
|
-
#
|
165
|
-
|
166
|
-
|
167
|
-
|
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
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
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
|
-
#
|
185
|
-
|
186
|
-
|
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
|
-
|
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
|
-
#
|
218
|
-
def
|
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(
|
273
|
+
raise_failure(invalid_subcommand_error_message(arg, subcommands, prefix))
|
228
274
|
else
|
229
|
-
raise_failure(
|
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
|
-
|
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
|
-
#
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
#
|
36
|
-
|
37
|
-
|
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(
|
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.
|
79
|
-
|
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
|
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 +
|
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] =
|
101
|
+
hash[command_name] = self.class.command(command_path.freeze, &block)
|
174
102
|
end
|
175
103
|
|
176
|
-
# Internals of +options
|
177
|
-
def create_option_parser(
|
178
|
-
option_parser = OptionParser.new
|
179
|
-
option_parser.
|
180
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
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
|
-
|
45
|
+
help = @command&.help || ''
|
46
|
+
if help.empty?
|
45
47
|
message
|
46
48
|
else
|
47
|
-
"#{message}\n\n#{
|
49
|
+
"#{message}\n\n#{help}"
|
48
50
|
end
|
49
51
|
end
|
50
52
|
end
|
data/lib/rodish/option_parser.rb
CHANGED
@@ -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:
|