commander-tool 1.0.1 → 1.0.2
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/commander-tool.rb +128 -66
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fc12456920e61c887d63c3224bbabe9b8c8850df925733a917c782da347ad2a6
|
|
4
|
+
data.tar.gz: fc30d88eac1a17c0effd957e5e1fc1cdcada2e83eda493087e2421b568e2ea88
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f830e16aad8121ad5a59cf0a2f4ebf9ce8835e0c06ae2589e845137a06f4dc04f0bff5af628c467d25c04da944f11d393e9da67d025166035bb563819eccc24c
|
|
7
|
+
data.tar.gz: 759a7318b4a23153c992a3a5fb2422b3fd31efb6c2632cf38f2b279430aa92e7bf2fa539db240f07cceebe9fa4284d43dceeaf89c9325f51224001cdb49fc521
|
data/lib/commander-tool.rb
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
require "did_you_mean"
|
|
2
2
|
|
|
3
3
|
class Commander
|
|
4
|
-
VERSION = "1.0.
|
|
4
|
+
VERSION = "1.0.2"
|
|
5
5
|
|
|
6
6
|
class Command
|
|
7
7
|
attr_reader :name, :description, :options, :block, :subcommands
|
|
8
8
|
|
|
9
9
|
def initialize(name, description = "", &block)
|
|
10
10
|
@name = name.to_s
|
|
11
|
-
@description = description
|
|
11
|
+
@description = description.to_s
|
|
12
12
|
@options = []
|
|
13
13
|
@subcommands = {}
|
|
14
14
|
@block = block
|
|
@@ -34,12 +34,21 @@ class Commander
|
|
|
34
34
|
attr_reader :switches, :description, :key
|
|
35
35
|
|
|
36
36
|
def initialize(switches, description = "")
|
|
37
|
-
@switches = switches
|
|
38
|
-
@description = description
|
|
37
|
+
@switches = Array(switches).map(&:to_s)
|
|
38
|
+
@description = description.to_s
|
|
39
|
+
|
|
40
|
+
long =
|
|
41
|
+
@switches.find { |s| s.start_with?("--") }
|
|
42
|
+
|
|
43
|
+
short =
|
|
44
|
+
@switches.find { |s| s.start_with?("-") }
|
|
39
45
|
|
|
40
46
|
@key =
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
if long
|
|
48
|
+
long.sub(/\A--/, "").tr("-", "_")
|
|
49
|
+
elsif short
|
|
50
|
+
short.sub(/\A-/, "").tr("-", "_")
|
|
51
|
+
end
|
|
43
52
|
end
|
|
44
53
|
|
|
45
54
|
def matches?(arg)
|
|
@@ -48,8 +57,8 @@ class Commander
|
|
|
48
57
|
|
|
49
58
|
def value_required?
|
|
50
59
|
@description.include?("<") ||
|
|
51
|
-
@description.
|
|
52
|
-
@description.
|
|
60
|
+
@description.match?(/\bargument\b/i) ||
|
|
61
|
+
@description.match?(/\bvalue\b/i)
|
|
53
62
|
end
|
|
54
63
|
end
|
|
55
64
|
|
|
@@ -70,27 +79,61 @@ class Commander
|
|
|
70
79
|
end
|
|
71
80
|
|
|
72
81
|
def parse(args = ARGV)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
args = Array(args).dup
|
|
83
|
+
|
|
84
|
+
if args.empty?
|
|
85
|
+
print_usage
|
|
86
|
+
return
|
|
87
|
+
end
|
|
77
88
|
|
|
78
|
-
|
|
89
|
+
if args.first == "--version" || args.first == "-v"
|
|
90
|
+
puts "Commander CLI v#{VERSION}"
|
|
91
|
+
return
|
|
92
|
+
end
|
|
79
93
|
|
|
80
|
-
if
|
|
81
|
-
|
|
94
|
+
if args.first == "--help" || args.first == "-h"
|
|
95
|
+
print_usage
|
|
82
96
|
return
|
|
83
97
|
end
|
|
84
98
|
|
|
85
|
-
|
|
99
|
+
# Find the first registered top-level command.
|
|
100
|
+
command_index = nil
|
|
101
|
+
command = nil
|
|
102
|
+
|
|
103
|
+
args.each_with_index do |arg, index|
|
|
104
|
+
next if arg.start_with?("-")
|
|
105
|
+
|
|
106
|
+
if @commands.key?(arg)
|
|
107
|
+
command_index = index
|
|
108
|
+
command = @commands[arg]
|
|
109
|
+
break
|
|
110
|
+
end
|
|
111
|
+
end
|
|
86
112
|
|
|
87
113
|
unless command
|
|
88
|
-
|
|
114
|
+
name = args.find { |arg| !arg.start_with?("-") }
|
|
115
|
+
|
|
116
|
+
if name
|
|
117
|
+
raise_unknown_command(name)
|
|
118
|
+
else
|
|
119
|
+
raise_unknown_option(args.first, @global_options)
|
|
120
|
+
end
|
|
89
121
|
end
|
|
90
122
|
|
|
123
|
+
# Only arguments BEFORE the command belong to global options.
|
|
124
|
+
global_args = args[0...command_index]
|
|
125
|
+
|
|
126
|
+
# Everything AFTER the command belongs to that command.
|
|
127
|
+
command_args = args[(command_index + 1)..] || []
|
|
128
|
+
|
|
129
|
+
global_options, = parse_options(
|
|
130
|
+
global_args,
|
|
131
|
+
@global_options
|
|
132
|
+
)
|
|
133
|
+
|
|
91
134
|
parse_command(
|
|
92
135
|
command,
|
|
93
|
-
|
|
136
|
+
command_args,
|
|
94
137
|
global_options
|
|
95
138
|
)
|
|
96
139
|
end
|
|
@@ -98,6 +141,8 @@ class Commander
|
|
|
98
141
|
private
|
|
99
142
|
|
|
100
143
|
def parse_command(command, args, inherited_options)
|
|
144
|
+
args = args.dup
|
|
145
|
+
|
|
101
146
|
if command.has_subcommands?
|
|
102
147
|
subcommand_name = args.shift
|
|
103
148
|
|
|
@@ -106,6 +151,11 @@ class Commander
|
|
|
106
151
|
return
|
|
107
152
|
end
|
|
108
153
|
|
|
154
|
+
if subcommand_name == "--help" || subcommand_name == "-h"
|
|
155
|
+
print_command_help(command)
|
|
156
|
+
return
|
|
157
|
+
end
|
|
158
|
+
|
|
109
159
|
subcommand = command.subcommands[subcommand_name]
|
|
110
160
|
|
|
111
161
|
unless subcommand
|
|
@@ -116,7 +166,10 @@ class Commander
|
|
|
116
166
|
end
|
|
117
167
|
|
|
118
168
|
command_options, command_args =
|
|
119
|
-
parse_options(
|
|
169
|
+
parse_options(
|
|
170
|
+
args,
|
|
171
|
+
command.options
|
|
172
|
+
)
|
|
120
173
|
|
|
121
174
|
combined_options =
|
|
122
175
|
inherited_options.merge(command_options)
|
|
@@ -138,7 +191,10 @@ class Commander
|
|
|
138
191
|
end
|
|
139
192
|
|
|
140
193
|
command_options, command_args =
|
|
141
|
-
parse_options(
|
|
194
|
+
parse_options(
|
|
195
|
+
args,
|
|
196
|
+
command.options
|
|
197
|
+
)
|
|
142
198
|
|
|
143
199
|
combined_options =
|
|
144
200
|
inherited_options.merge(command_options)
|
|
@@ -152,6 +208,7 @@ class Commander
|
|
|
152
208
|
def parse_options(args, available_options)
|
|
153
209
|
parsed = {}
|
|
154
210
|
remaining = []
|
|
211
|
+
|
|
155
212
|
i = 0
|
|
156
213
|
|
|
157
214
|
while i < args.length
|
|
@@ -162,37 +219,41 @@ class Commander
|
|
|
162
219
|
break
|
|
163
220
|
end
|
|
164
221
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
222
|
+
unless arg.start_with?("-")
|
|
223
|
+
remaining << arg
|
|
224
|
+
i += 1
|
|
225
|
+
next
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
option =
|
|
229
|
+
available_options.find do |candidate|
|
|
230
|
+
candidate.matches?(arg)
|
|
168
231
|
end
|
|
169
232
|
|
|
233
|
+
unless option
|
|
170
234
|
raise_unknown_option(
|
|
171
235
|
arg,
|
|
172
236
|
available_options
|
|
173
|
-
)
|
|
237
|
+
)
|
|
238
|
+
end
|
|
174
239
|
|
|
175
|
-
|
|
240
|
+
next_arg = args[i + 1]
|
|
176
241
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
end
|
|
242
|
+
if option.value_required?
|
|
243
|
+
if next_arg.nil? || next_arg.start_with?("-")
|
|
244
|
+
raise_missing_argument(arg)
|
|
245
|
+
end
|
|
182
246
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
247
|
+
parsed[option.key.to_sym] = next_arg
|
|
248
|
+
i += 2
|
|
249
|
+
else
|
|
250
|
+
if next_arg && !next_arg.start_with?("-")
|
|
187
251
|
parsed[option.key.to_sym] = next_arg
|
|
188
252
|
i += 2
|
|
189
253
|
else
|
|
190
254
|
parsed[option.key.to_sym] = true
|
|
191
255
|
i += 1
|
|
192
256
|
end
|
|
193
|
-
else
|
|
194
|
-
remaining << arg
|
|
195
|
-
i += 1
|
|
196
257
|
end
|
|
197
258
|
end
|
|
198
259
|
|
|
@@ -204,26 +265,29 @@ class Commander
|
|
|
204
265
|
end
|
|
205
266
|
|
|
206
267
|
def raise_unknown_command(name)
|
|
207
|
-
corrector =
|
|
208
|
-
|
|
209
|
-
|
|
268
|
+
corrector =
|
|
269
|
+
DidYouMean::SpellChecker.new(
|
|
270
|
+
dictionary: @commands.keys
|
|
271
|
+
)
|
|
210
272
|
|
|
211
273
|
suggestions = corrector.correct(name)
|
|
212
274
|
|
|
213
|
-
message =
|
|
275
|
+
message =
|
|
276
|
+
"error: unknown command '#{name}'"
|
|
214
277
|
|
|
215
278
|
unless suggestions.empty?
|
|
216
|
-
message
|
|
217
|
-
message
|
|
279
|
+
message << "\n\nDid you mean?\n"
|
|
280
|
+
message << suggestions.map { |s| "\t#{s}" }.join("\n")
|
|
218
281
|
end
|
|
219
282
|
|
|
220
283
|
abort message
|
|
221
284
|
end
|
|
222
285
|
|
|
223
286
|
def raise_unknown_subcommand(name, command)
|
|
224
|
-
corrector =
|
|
225
|
-
|
|
226
|
-
|
|
287
|
+
corrector =
|
|
288
|
+
DidYouMean::SpellChecker.new(
|
|
289
|
+
dictionary: command.subcommands.keys
|
|
290
|
+
)
|
|
227
291
|
|
|
228
292
|
suggestions = corrector.correct(name)
|
|
229
293
|
|
|
@@ -231,41 +295,35 @@ class Commander
|
|
|
231
295
|
"error: unknown command '#{name}'"
|
|
232
296
|
|
|
233
297
|
unless suggestions.empty?
|
|
234
|
-
message
|
|
235
|
-
message
|
|
298
|
+
message << "\n\nDid you mean?\n"
|
|
299
|
+
message << suggestions.map { |s| "\t#{s}" }.join("\n")
|
|
236
300
|
end
|
|
237
301
|
|
|
238
302
|
abort message
|
|
239
303
|
end
|
|
240
304
|
|
|
241
305
|
def raise_unknown_option(name, options)
|
|
242
|
-
switches =
|
|
306
|
+
switches =
|
|
307
|
+
options.flat_map(&:switches)
|
|
243
308
|
|
|
244
|
-
corrector =
|
|
245
|
-
|
|
246
|
-
|
|
309
|
+
corrector =
|
|
310
|
+
DidYouMean::SpellChecker.new(
|
|
311
|
+
dictionary: switches
|
|
312
|
+
)
|
|
247
313
|
|
|
248
314
|
suggestions = corrector.correct(name)
|
|
249
315
|
|
|
250
|
-
message =
|
|
316
|
+
message =
|
|
317
|
+
"error: unknown option '#{name}'"
|
|
251
318
|
|
|
252
319
|
unless suggestions.empty?
|
|
253
|
-
message
|
|
254
|
-
message
|
|
320
|
+
message << "\n\nDid you mean?\n"
|
|
321
|
+
message << suggestions.map { |s| "\t#{s}" }.join("\n")
|
|
255
322
|
end
|
|
256
323
|
|
|
257
324
|
abort message
|
|
258
325
|
end
|
|
259
326
|
|
|
260
|
-
def handle_no_command(options)
|
|
261
|
-
if options[:version] || options[:v]
|
|
262
|
-
puts "Commander CLI v#{VERSION}"
|
|
263
|
-
return
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
print_usage
|
|
267
|
-
end
|
|
268
|
-
|
|
269
327
|
def print_usage
|
|
270
328
|
puts "Usage: <command> [options]"
|
|
271
329
|
puts
|
|
@@ -288,8 +346,12 @@ class Commander
|
|
|
288
346
|
def print_command_help(command)
|
|
289
347
|
puts "Usage: #{command.name} <subcommand> [options]"
|
|
290
348
|
puts
|
|
291
|
-
|
|
292
|
-
|
|
349
|
+
|
|
350
|
+
unless command.description.empty?
|
|
351
|
+
puts command.description
|
|
352
|
+
puts
|
|
353
|
+
end
|
|
354
|
+
|
|
293
355
|
puts "Commands:"
|
|
294
356
|
|
|
295
357
|
command.subcommands.each_value do |subcommand|
|