nub 0.0.51 → 0.0.52

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +16 -14
  3. data/lib/nub/commander.rb +60 -39
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c58496cf243b60e9ca662e07c1e8c30a0dba10bb6cd290d748df9b9f93d7897b
4
- data.tar.gz: 5a918972eb7de66173b9c2ec31eccb396804a900d2cf3b25cb9b3c046e863256
3
+ metadata.gz: dbb6012caafc7ba2ef97052b8f8577ab631565d7b5964e111f2a02b52139c092
4
+ data.tar.gz: 29895378b9e02eae31af58d792c36406fbee2cc5759ab84dca4ede07bf7015d8
5
5
  SHA512:
6
- metadata.gz: 4eefd3a45e934ebc542d862ab1e867c3e88d8eb3f07fa5de50d525e305cb22e594027ba9523c280480ceaa37c62fe4021d2a4d820edcf8001ff8f49cb1dfa7e6
7
- data.tar.gz: 53ae4e3e35ef1c7d496539bb2fb4649b79c021a663bddc6411d671ee10ea10ed9ed7af62f1152597ecdd97792f1bc9ea78e495871426bc1592acb607014635b4
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 case or same
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 commands results using the ***:global*** symbol. Global options are given on the command
54
- line before anything else in the case of positional, but anywhere in the case of named.
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 before
57
- any commands and are added to all 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 application.
104
- Named options have a name that is prefixed with a dash (short hand) or two dashes (long hand) e.g.
105
- ***-h*** or ***--help*** and may simply be a flag or pass in a value. Option values require a
106
- ***type*** so that Commander can interpret how to use them. The supported value types are
107
- ***Flag, Integer, String, Array***. Values may be checked or not checked via the ***allowed***
108
- config param. Positional options default to type String while named options default to type Flag.
109
- Positional options are named internally with the command concatted with a an int for order ***e.g.
110
- clean0*** zero based. Positional params are always required.
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
- attr_reader(:allowed)
36
- attr_reader(:required)
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.51
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 00:00:00.000000000 Z
11
+ date: 2018-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize