nub 0.0.51 → 0.0.52
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -14
- data/lib/nub/commander.rb +60 -39
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dbb6012caafc7ba2ef97052b8f8577ab631565d7b5964e111f2a02b52139c092
|
4
|
+
data.tar.gz: 29895378b9e02eae31af58d792c36406fbee2cc5759ab84dca4ede07bf7015d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2274e48efc5268a48fb3690fa0e85d487dbe69516160bd34fee2f76d10557ba72a1d8fdb42255846b14b1186df0ade53fa51cccec05abf093de17b1ed25964f0
|
7
|
+
data.tar.gz: 8c74aa5a469e44d0e710143ea3f553cf8d44133dfffc0129d0ccd18ccd4cf2029b2af4b512dae512ec35593a953e4f56bd8d4d6109db65ce0d46a8452a563211
|
data/README.md
CHANGED
@@ -46,15 +46,16 @@ more in a single command line expression. Each command in a chained command expr
|
|
46
46
|
own specific options (those coming after the command but before the next command) or if options are
|
47
47
|
omitted the required options from the next command will be used. The chained command options syntax
|
48
48
|
allows one to have a cleaner multi-command line expression with reusable options. Options are said
|
49
|
-
to apply in a chained command syntax when they are of the same type in the positional
|
50
|
-
type and name in the named case.
|
49
|
+
to apply in a chained command syntax when they are of the same type and position in the positional
|
50
|
+
case or same type and name in the named case.
|
51
51
|
|
52
52
|
***Global*** options are options that are added with the ***add_global*** function and will show up
|
53
|
-
set in the
|
54
|
-
|
53
|
+
set in the command results using the ***:global*** symbol. Global positional options must be given
|
54
|
+
before any other commands but global named options may appear anywhere in the command line
|
55
|
+
expression.
|
55
56
|
|
56
|
-
***Shared*** options are options that are added with the command ***add_shared*** function
|
57
|
-
any commands
|
57
|
+
***Shared*** options are options that are added with the command ***add_shared*** function. They
|
58
|
+
should be added before any commands are added. They are added to each command as an explicit option.
|
58
59
|
|
59
60
|
***Commander.new*** must be run from the app's executable file for it to pick up the app's filename
|
60
61
|
properly.
|
@@ -100,14 +101,15 @@ Example command line expressions:
|
|
100
101
|
### Options <a name="options"></a>
|
101
102
|
There are two kinds of options available for use, ***positional*** and ***named***. Positional
|
102
103
|
options are identified by the absence of preceding dash/dashes and interpreted according to the
|
103
|
-
order in which they were found. Positional options are a value being passed into the
|
104
|
-
Named options have a name that is prefixed with a dash (short hand) or two dashes
|
105
|
-
***-h*** or ***--help*** and may simply be a flag or pass in a value. Option
|
106
|
-
***type*** so that Commander can interpret how to use them. The supported value
|
107
|
-
***Flag, Integer, String, Array***. Values may be checked or not checked via the
|
108
|
-
config param. Positional options default to type String while named options default to
|
109
|
-
Positional options are named internally with the command concatted with a an int for
|
110
|
-
clean0*** zero based. Positional
|
104
|
+
order and number in which they were found. Positional options are a value being passed into the
|
105
|
+
application. Named options have a name that is prefixed with a dash (short hand) or two dashes
|
106
|
+
(long hand) e.g. ***-h*** or ***--help*** and may simply be a flag or pass in a value. Option
|
107
|
+
values require a ***type*** so that Commander can interpret how to use them. The supported value
|
108
|
+
types are ***Flag, Integer, String, Array***. Values may be checked or not checked via the
|
109
|
+
***allowed*** config param. Positional options default to type String while named options default to
|
110
|
+
type Flag. Positional options are named internally with the command concatted with a an int for
|
111
|
+
order ***e.g. clean0*** zero based. Positional options are given sequentially so you can't
|
112
|
+
skip one and specify the second, it must be one then two etc...
|
111
113
|
|
112
114
|
**Long Hand** form is always required for named options, short hand may or may not be given.
|
113
115
|
|
data/lib/nub/commander.rb
CHANGED
@@ -32,8 +32,8 @@ class Option
|
|
32
32
|
attr_reader(:hint)
|
33
33
|
attr_reader(:desc)
|
34
34
|
attr_reader(:type)
|
35
|
-
|
36
|
-
|
35
|
+
attr_accessor(:allowed)
|
36
|
+
attr_accessor(:required)
|
37
37
|
attr_accessor(:shared)
|
38
38
|
|
39
39
|
# Create a new option instance
|
@@ -62,9 +62,6 @@ class Option
|
|
62
62
|
@hint = key[/.*=(.*)$/, 1]
|
63
63
|
@short = key[/^(-\w).*$/, 1]
|
64
64
|
@long = key[/(--[\w\-]+)(=.+)*$/, 1]
|
65
|
-
else
|
66
|
-
# Always require positional options
|
67
|
-
@required = true
|
68
65
|
end
|
69
66
|
|
70
67
|
# Validate and set type
|
@@ -74,6 +71,9 @@ class Option
|
|
74
71
|
@type = FalseClass if key and !type
|
75
72
|
@type = type if type
|
76
73
|
|
74
|
+
# Validate hint is given for non flags
|
75
|
+
Log.die("option hint must be set") if @key && !@hint && @type != FalseClass
|
76
|
+
|
77
77
|
# Validate allowed
|
78
78
|
if @allowed.any?
|
79
79
|
allowed_type = @allowed.first.class
|
@@ -169,6 +169,7 @@ class Commander
|
|
169
169
|
Log.die("duplicate shared option '#{x.desc}' given") if @shared
|
170
170
|
.any?{|y| y.key == x.key && y.desc == x.desc && y.type == x.type}
|
171
171
|
x.shared = true
|
172
|
+
x.required = true
|
172
173
|
@shared << x
|
173
174
|
}
|
174
175
|
end
|
@@ -209,51 +210,25 @@ class Commander
|
|
209
210
|
# Process command options
|
210
211
|
#---------------------------------------------------------------------------
|
211
212
|
order_globals!
|
213
|
+
expand_chained_options!
|
212
214
|
loop {
|
213
215
|
break if ARGV.first.nil?
|
214
216
|
|
215
217
|
if !(cmd = @config.find{|x| x.name == ARGV.first}).nil?
|
216
|
-
@cmds[ARGV.shift.to_sym] = {}
|
217
|
-
cmd_names.reject!{|x| x == cmd.name}
|
218
|
-
|
219
|
-
# Command options as defined in configuration
|
220
|
-
cmd_pos_opts = cmd.opts.select{|x| x.key.nil? }
|
221
|
-
cmd_named_opts = cmd.opts.select{|x| !x.key.nil? }
|
218
|
+
@cmds[ARGV.shift.to_sym] = {} # Create command results entry
|
219
|
+
cmd_names.reject!{|x| x == cmd.name} # Remove command from possible commands
|
222
220
|
|
223
221
|
# Collect command options from args to compare against
|
224
222
|
opts = ARGV.take_while{|x| !cmd_names.include?(x) }
|
225
223
|
ARGV.shift(opts.size)
|
224
|
+
|
225
|
+
# Check that all required options were given
|
226
|
+
cmd_pos_opts = cmd.opts.select{|x| x.key.nil? }
|
227
|
+
cmd_named_opts = cmd.opts.select{|x| !x.key.nil? }
|
226
228
|
|
227
|
-
# All positional options are required. If they are not given then check for the 'chained
|
228
|
-
# command expression' case for positional options in the next command that satisfy the
|
229
|
-
# previous command's requirements and so on and so forth.
|
230
|
-
if opts.size == 0 && (cmd_pos_opts.any? || cmd_named_opts.any?{|x| x.required})
|
231
|
-
i = 0
|
232
|
-
while (i += 1) < ARGV.size do
|
233
|
-
opts = ARGV[i..-1].take_while{|x| !cmd_names.include?(x) }
|
234
|
-
break if opts.any?
|
235
|
-
end
|
236
|
-
|
237
|
-
# Check that the chained command options at least match types and size
|
238
|
-
if opts.any?
|
239
|
-
cmd_required = cmd.opts.select{|x| x.key.nil? || x.required}
|
240
|
-
other = @config.find{|x| x.name == ARGV[i-1]}
|
241
|
-
other_required = other.opts.select{|x| x.key.nil? || x.required}
|
242
|
-
|
243
|
-
!puts("Error: chained commands must have equal numbers of required options!".colorize(:red)) && !puts(cmd.help) and
|
244
|
-
exit if cmd_required.size != other_required.size
|
245
|
-
cmd_required.each_with_index{|x,i|
|
246
|
-
!puts("Error: chained command options are not type consistent!".colorize(:red)) && !puts(cmd.help) and
|
247
|
-
exit if x.type != other_required[i].type || x.key != other_required[i].key
|
248
|
-
}
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
# Check that all positional options were given
|
253
229
|
!puts("Error: positional option required!".colorize(:red)) && !puts(cmd.help) and
|
254
|
-
exit if opts.size < cmd_pos_opts.size
|
230
|
+
exit if opts.select{|x| !x.start_with?('-')}.size < cmd_pos_opts.select{|x| x.required}.size
|
255
231
|
|
256
|
-
# Check that all required named options where given
|
257
232
|
named_opts = opts.select{|x| x.start_with?('-')}
|
258
233
|
cmd_named_opts.select{|x| x.required}.each{|x|
|
259
234
|
!puts("Error: required option #{x.key} not given!".colorize(:red)) && !puts(cmd.help) and
|
@@ -369,6 +344,52 @@ class Commander
|
|
369
344
|
end
|
370
345
|
end
|
371
346
|
|
347
|
+
# Find chained options, copy and insert as needed.
|
348
|
+
# Globals should have already been ordered before calling this function
|
349
|
+
# Fail if validation doesn't pass
|
350
|
+
def expand_chained_options!
|
351
|
+
args = ARGV[0..-1]
|
352
|
+
results = {}
|
353
|
+
cmd_names = @config.map{|x| x.name }
|
354
|
+
|
355
|
+
chained = []
|
356
|
+
while args.any? do
|
357
|
+
if !(cmd = @config.find{|x| x.name == args.first}).nil?
|
358
|
+
results[args.shift] = [] # Add the command to the results
|
359
|
+
cmd_names.reject!{|x| x == cmd.name} # Remove command from possible commands
|
360
|
+
cmd_required = cmd.opts.select{|x| x.required}
|
361
|
+
|
362
|
+
# Collect command options from args to compare against
|
363
|
+
opts = args.take_while{|x| !cmd_names.include?(x)}
|
364
|
+
args.shift(opts.size)
|
365
|
+
|
366
|
+
# Globals are not to be considered for chaining
|
367
|
+
results[cmd.name].concat(opts) and next if cmd.name == 'global'
|
368
|
+
|
369
|
+
# Chained case is when no options are given but some are required
|
370
|
+
if opts.size == 0 && cmd.opts.any?{|x| x.required}
|
371
|
+
chained << cmd
|
372
|
+
else
|
373
|
+
results[cmd.name].concat(opts)
|
374
|
+
|
375
|
+
chained.each{|x|
|
376
|
+
other_required = x.opts.select{|x| x.required}
|
377
|
+
!puts("Error: chained commands must have equal numbers of required options!".colorize(:red)) && !puts(x.help) and
|
378
|
+
exit if cmd_required.size != other_required.size
|
379
|
+
cmd_required.each_with_index{|y,i|
|
380
|
+
!puts("Error: chained command options are not type consistent!".colorize(:red)) && !puts(x.help) and
|
381
|
+
exit if y.type != other_required[i].type || y.key != other_required[i].key
|
382
|
+
}
|
383
|
+
results[x.name].concat(opts)
|
384
|
+
}
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
# Set results as new ARGV command line expression
|
390
|
+
ARGV.clear and results.each{|k, v| ARGV << k; ARGV.concat(v)}
|
391
|
+
end
|
392
|
+
|
372
393
|
# Match the given command line arg with a configured named option
|
373
394
|
# @param opt [String] the command line argument given
|
374
395
|
# @param cmd [Command] configured command to match against
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.52
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Patrick Crummett
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-04-
|
11
|
+
date: 2018-04-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|