help_parser 6.3.0 → 7.0.200907

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b8f371bee63827fcbd8051796c5fce3421da5a3fc41bf6223e546c28ec99362c
4
- data.tar.gz: 4484cae785efb013dfccdbcec34a7f17ed89fe013b0a5018c31f4515e87a9439
3
+ metadata.gz: 7df1feedeed249286efe5cd5f1e2cc187a91d348eab654d5a6f110292d0c3476
4
+ data.tar.gz: a3f142a3d2cf4c6bf038b25e3552dfc3f56bfbb3469749386994b0763ccaea4c
5
5
  SHA512:
6
- metadata.gz: ad1de10f9ba6c1788db1dcae160c5fe8ea8c61432d388c097aab22efe8edfa6f86e22131e43159f8ab944615e8e3aa9e66fa714131e0347a241ded901aba8926
7
- data.tar.gz: c4086cff775108315243f191acb81d243b792fdc18718b5390070b67f9b2174f8f5a517bf3a665807a4122bce80118799559f5883142e914f748eaa5f8895884
6
+ metadata.gz: c3df75b2ae4640bc268e86774facbc7f60e74fda4d58c3262022e3a571cb3d716af2be92e7d1652e2ffa5469a1852da4a412e582141f155fe849f37e62d4e4dc
7
+ data.tar.gz: 447dfb4939662fec618c4c315f104785e1923a6a625b82ed028981a9ff98c35c9049b23586501c8bd06e244f21de8da4cec2c491bf815a17d3c2d38f93495e4e
data/README.md CHANGED
@@ -1,73 +1,127 @@
1
- # Help Parser VI: Tweeker
1
+ # Help Parser VII: Deader
2
2
 
3
+ * [VERSION 7.0.200907](https://github.com/carlosjhr64/Ruby-HelpParser/releases)
3
4
  * [github](https://www.github.com/carlosjhr64/Ruby-HelpParser)
4
5
  * [rubygems](https://rubygems.org/gems/help_parser)
5
6
 
6
7
  ## DESCRIPTION:
7
- Welcome to the best Help Parser of all!
8
- Tweeker!
9
- Which do you find most helpful?
10
- Hard?
11
- I prefer easy.
8
+
9
+ Can't help YOU???
10
+ You're not the first to say that...
11
+
12
+ I will parse your help!
12
13
 
13
14
  ## SYNOPSIS:
15
+ <!-- The following PREVIEW has been approved for ALL PROGRAMMERS by CarlosJHR64.
16
+ For the README validator that checks against me lying....
17
+ ```ruby
18
+ unless File.basename($PROGRAM_NAME) == 'deader'
19
+ # For example's sake say
20
+ $PROGRAM_NAME = 'deader'
21
+ # and ARGV is
22
+ ARGV.concat ["-\-age", "-\-date=2020-09-07", 'invoke', 'the', 'command']
23
+ # and proceed as if run as:
24
+ # awesome -\-name=Doe -\-value a b c
25
+ end
26
+ ```
27
+ The following gem has been rated
28
+ | M | Mature |
29
+ -->
30
+
31
+ > Who ever you are, you were meant to find me today...
32
+ > there is no turning back!
33
+ > Above all, don't invoke the command!
34
+
35
+ ```ruby
36
+ require "help_parser"
37
+
38
+ HELP = <<-HELP
39
+ # <= Hash here, parser skips
40
+ # HelpParser: Deader command example #
41
+ Usage:
42
+ deader :options+ [<args>+]
43
+ deader [:alternate] <arg=FLOAT>
44
+ deader literal <arg1=WORD> <arg2=WORD> <arg3=WORD>
45
+ Options:
46
+ -v --version \t Give version and quit
47
+ -h --help \t Give help and quit
48
+ -s --long \t Short long synonyms
49
+ --command invoke \t Defaulted
50
+ --date=DATE \t Typed
51
+ --age=INTEGER 80 \t Typed and Defaulted
52
+ -a --all=YN y \t Short, long, typed, and defaulted
53
+ --to_be
54
+ --not_to_be
55
+ Exclusive:
56
+ to_be not_to_be \t Tells parser these are mutually exclusive keys
57
+ Inclusive:
58
+ date age \t Tells parser any of these must include all of these
59
+ Alternate:
60
+ --invoke
61
+ --wut
62
+ Types:
63
+ WORD /^[A-Za-z]+$/
64
+ DATE /^\\d\\d\\d\\d-\\d\\d-\\d\\d$/
65
+ INTEGER /^\\d+$/
66
+ FLOAT /^\\d+\\.\\d+$/
67
+ YN /^[YNyn]$/
68
+ # <= Hash here, parser breaks out
69
+ # Notes #
70
+ Don't invoke the command.
71
+ HELP
72
+
73
+ VERSION = "1.2.3"
74
+
75
+ OPTIONS = HelpParser[VERSION, HELP] #~> HelpParser
76
+
77
+ # Macros:
78
+ HelpParser.strings?(:args) # for OPTIONS.args : Array(String) | Nil
79
+ HelpParser.int?(:age) # for OPTIONS.age? : Integer | Nil
80
+ HelpParser.float(:arg) # for options.arg : Float
81
+ HelpParser.string(:arg1, :arg2, :arg3) # for OPTIONS.arg1, etc : String
82
+ #=> [:arg1, :arg2, :arg3]
83
+
84
+ ## If run as:
85
+ ## deader --age --date=2020-09-07 invoke the command
86
+ OPTIONS.age #=> 80
87
+ OPTIONS.args #=> ["invoke", "the", "command"]
88
+ OPTIONS.arg? and OPTIONS.arg #=> false
89
+ ```
14
90
 
15
- require "pp"
16
- require "help_parser"
17
-
18
- HELP = <<-HELP
19
- # <= Hash here, parser skips
20
- # The Awesome Command.
21
- Usage:
22
- awesome [:options+] <args>+
23
- awesome :alternate <arg=NAME>
24
- Options:
25
- -v --version \t Give version and quit
26
- -h --help \t Give help and quit
27
- -s --long \t Short long synonyms
28
- --name=NAME \t Typed
29
- --number 5 \t Defaulted
30
- --value=FLOAT 1.23 \t Typed and Defaulted
31
- -a --all=YN y \t Short, long, typed, and defaulted
32
- Alternate:
33
- -V \t Just short
34
- Types:
35
- NAME /^[A-Z][a-z]+$/
36
- FLOAT /^\\d+\\.\\d+$/
37
- YN /^[YNyn]$/
38
- Exclusive:
39
- version help \t Tells parser these are exclusive keys
40
- # <= Hash here, parser breaks out
41
- # Notes #
42
- Blah blah blah
43
- HELP
44
-
45
- VERSION = "6.3.0"
46
-
47
- # Macros:
48
- HelpParser.string(name) # for options.name : String
49
- HelpParser.strings(args) # for options.args : Array(String)
50
- HelpParser.float(value) # for options.value : Float
51
- HelpParser.int?(number) # for options.number? : Int32 | Nil
52
-
53
- HelpParser.run(VERSION, HELP) do |options|
54
- hash = options._hash
55
- pp hash # to inspect the hash
56
-
57
- pp options.name if hash["name"]?
58
- pp options.args if hash["args"]?
59
- pp options.value if hash["value"]?
60
- pp options.number?
61
- end
62
-
63
- Well, what do you think?
64
- PERFECT!
65
-
66
- ## New for 6.1.0:
67
-
68
- Running your `awesome` command with the `--help` flag will also check your help text for errors,
69
- on top of giving the help text. Otherwise, the parser no longer checks for help text errors.
91
+ YOU HAVE INVOKED THE COMMAND...
92
+ YOUR HELP BELONGS TO ME!!!
93
+
94
+ ## Features
95
+
96
+ * $DEBUG=true on --debug
97
+ * $VERBOSE=true on --verbose
98
+ * -h and --help simultaneously will check help string for errors
70
99
 
71
100
  ## INSTALL:
72
101
 
73
102
  $ sudo gem install help_parser
103
+
104
+ ## LICENSE:
105
+
106
+ (The MIT License)
107
+
108
+ Copyright (c) 2020 CarlosJHR64
109
+
110
+ Permission is hereby granted, free of charge, to any person obtaining
111
+ a copy of this software and associated documentation files (the
112
+ 'Software'), to deal in the Software without restriction, including
113
+ without limitation the rights to use, copy, modify, merge, publish,
114
+ distribute, sublicense, and/or sell copies of the Software, and to
115
+ permit persons to whom the Software is furnished to do so, subject to
116
+ the following conditions:
117
+
118
+ The above copyright notice and this permission notice shall be
119
+ included in all copies or substantial portions of the Software.
120
+
121
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
122
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
123
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
124
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
125
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
126
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
127
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -11,7 +11,7 @@ require_relative './help_parser/options'
11
11
  require_relative './help_parser/macros'
12
12
 
13
13
  module HelpParser
14
- VERSION = '6.3.0'
14
+ VERSION = '7.0.200907'
15
15
 
16
16
  def self.[](
17
17
  version = nil,
@@ -3,44 +3,56 @@ module HelpParser
3
3
  def initialize(hash, specs)
4
4
  @hash,@specs = hash,specs
5
5
  @cache = NoDupHash.new
6
- usage if @specs.has_key?(USAGE)
6
+ usage or diagnose if @specs.has_key?(USAGE)
7
7
  pad
8
- types
8
+ types if @specs.has_key?(TYPES)
9
9
  end
10
10
 
11
+ # Which usage does the user's command options match?
11
12
  def usage
12
13
  @specs[USAGE].each do |cmd|
13
14
  begin
14
15
  i = matches(cmd)
15
16
  raise NoMatch unless @hash.size==i
16
17
  @cache.each{|k,v|@hash[k]=v} # Variables
17
- return
18
+ return true # Good! Found matching usage.
18
19
  rescue NoMatch
19
20
  next
20
21
  ensure
21
22
  @cache.clear
22
23
  end
23
24
  end
25
+ return false # Bad! Did not match any of the expected usage.
26
+ end
27
+
28
+ # Diagnose user's usage.
29
+ def diagnose
30
+ dict = {}
31
+ @specs.each do |k,v|
32
+ next if RESERVED[k]
33
+ v.flatten.map{|_|_.scan(/\w+/).first}.each{|_|dict[_]=true}
34
+ end
35
+ typos = @hash.keys.select{|k|k.is_a? String and not dict[k]}
36
+ raise UsageError, MSG[UNRECOGNIZED, typos] unless typos.empty?
37
+
24
38
  raise UsageError, MATCH_USAGE
25
39
  end
26
40
 
27
41
  def types
28
- if t2r = HelpParser.t2r(@specs)
29
- k2t = HelpParser.k2t(@specs)
30
- @hash.each do |key,value|
31
- next unless key.is_a?(String)
32
- if type = k2t[key]
33
- regex = t2r[type]
34
- case value
35
- when String
36
- raise UsageError, "--#{key}=#{value} !~ #{type}=#{regex.inspect}" unless value=~regex
37
- when Array
38
- value.each do |string|
39
- raise UsageError, "--#{key}=#{string} !~ #{type}=#{regex.inspect}" unless string=~regex
40
- end
41
- else
42
- raise UsageError, "--#{key} !~ #{type}=#{regex.inspect}"
42
+ t2r,k2t = HelpParser.t2r(@specs),HelpParser.k2t(@specs)
43
+ @hash.each do |key,value|
44
+ next unless key.is_a?(String)
45
+ if type = k2t[key]
46
+ regex = t2r[type]
47
+ case value
48
+ when String
49
+ raise UsageError, "--#{key}=#{value} !~ #{type}=#{regex.inspect}" unless value=~regex
50
+ when Array
51
+ value.each do |string|
52
+ raise UsageError, "--#{key}=#{string} !~ #{type}=#{regex.inspect}" unless string=~regex
43
53
  end
54
+ else
55
+ raise UsageError, "--#{key} !~ #{type}=#{regex.inspect}"
44
56
  end
45
57
  end
46
58
  end
@@ -65,7 +77,7 @@ module HelpParser
65
77
  elsif value = @hash[long]
66
78
  @hash[short] = true
67
79
  if value==true && !default.nil?
68
- @hash.delete(long)
80
+ @hash.delete(long) # ArgvHash reset
69
81
  @hash[long]=default
70
82
  end
71
83
  end
@@ -74,7 +86,7 @@ module HelpParser
74
86
  long,default = first[2..(i-1)],second
75
87
  value = @hash[long]
76
88
  if value==true
77
- @hash.delete(long)
89
+ @hash.delete(long) # ArgvHash reset
78
90
  @hash[long] = default
79
91
  end
80
92
  end
@@ -1,15 +1,8 @@
1
1
  module HelpParser
2
- @@validate = false
3
- def self.validate!
4
- @@validate = true
5
- end
6
- def self.validate?
7
- @@validate
8
- end
9
-
10
2
  USAGE = 'usage'
11
3
  TYPES = 'types'
12
4
  EXCLUSIVE = 'exclusive'
5
+ INCLUSIVE = 'inclusive'
13
6
 
14
7
  # usage
15
8
  FLAG = /^[-][-]?(?<k>\w+)$/
@@ -29,7 +22,7 @@ module HelpParser
29
22
  TYPE_DEF = /^(?<t>[A-Z]+),?\s+\/(?<r>\S+)\/$/
30
23
 
31
24
  # spec w+( w+)+
32
- X_DEF = /^\w+( +\w+)+$/
25
+ X_DEF = /^\w+( +\w+)+$/ # for both exclusive and inclusive specs
33
26
 
34
27
  CSV = /,?\s+/
35
28
 
@@ -42,18 +35,20 @@ module HelpParser
42
35
  DUP_KEY = 'Duplicate key'
43
36
  DUP_WORD = 'Duplicate word'
44
37
  DUP_FLAG = 'Duplicate flag'
45
- DUP_X = 'Duplicate exclusive spec'
38
+ DUP_X = 'Duplicate exclusive/inclusive spec'
46
39
  UNSEEN_FLAG = 'Undefined flag'
47
40
  INCONSISTENT = 'Inconsistent use of variable'
48
41
  UNEXPECTED = 'Unexpected string in help text'
49
42
  BAD_REGEX = 'Bad regex'
50
43
  REDUNDANT = 'Redundant'
51
44
  EXCLUSIVE_KEYS = 'Exclusive keys'
45
+ INCLUSIVE_KEYS = 'Inclusive keys'
52
46
  UNBALANCED = 'Unbalanced brackets'
53
47
  UNRECOGNIZED_TOKEN = 'Unrecognized usage token'
54
48
  UNRECOGNIZED_TYPE = 'Unrecognized type spec'
55
- UNRECOGNIZED_X = 'Unrecognized exclusive spec'
49
+ UNRECOGNIZED_X = 'Unrecognized exclusive/inclusive spec'
56
50
  UNRECOGNIZED_OPTION = 'Unrecognized option spec'
51
+ UNRECOGNIZED = 'Unrecognized'
57
52
  UNDEFINED_SECTION = 'Section not defined'
58
53
  MISSING_CASES = 'Missing cases'
59
54
  MISSING_USAGE = 'Missing usage'
@@ -71,7 +66,8 @@ module HelpParser
71
66
  EXTRANEOUS_SPACES = 'Extraneous spaces in help.'
72
67
 
73
68
  # lambda utilities
74
- MSG = lambda{|msg,*keys| "\033[0;31m#{msg}: #{keys.join(' ')}\033[0m"}
69
+ MSG = lambda{|msg,*keys| "#{msg}: #{keys.join(' ')}"}
75
70
  F2K = lambda{|f| f[1]=='-' ? f[2..((f.index('=')||0)-1)] : f[1]}
76
- RESERVED = lambda{|k| [USAGE,TYPES,EXCLUSIVE].include?(k)} # reserved
71
+ RESERVED = lambda{|k| [USAGE,TYPES,EXCLUSIVE,INCLUSIVE].include?(k)} # reserved
72
+ REDTTY = lambda{|msg,out=$stderr| out.tty? ? out.puts("\033[0;31m#{msg}\033[0m"): out.puts(msg)}
77
73
  end
@@ -10,9 +10,9 @@ module HelpParser
10
10
 
11
11
  def exit
12
12
  if @code > 0
13
- STDERR.puts self.message
13
+ REDTTY[self.message]
14
14
  else
15
- puts self.message
15
+ $stdout.puts self.message
16
16
  end
17
17
  Kernel.exit @code
18
18
  end
@@ -36,10 +36,10 @@ module HelpParser
36
36
  end
37
37
 
38
38
  class NoMatch < HelpParserException
39
- # used to shortcircuit out
39
+ # used to short-circuit out
40
40
  def _init; @code = EX_SOFTWARE; end
41
41
 
42
- # Forces it's owm message
42
+ # Forces it's own message
43
43
  def initialize
44
44
  super(NO_MATCH)
45
45
  end
@@ -5,8 +5,11 @@ module HelpParser
5
5
  tokens.each do |token|
6
6
  if match = VARIABLE.match(token) || LONG.match(token)
7
7
  name, type = match['k'], match['t']
8
- k2t[name] = type if !k2t.has_key?(name)
9
- raise HelpError, MSG[INCONSISTENT,name,type,k2t[name]] if !(type==k2t[name])
8
+ if _=k2t[name]
9
+ raise HelpError, MSG[INCONSISTENT,name,type,_] unless type==_
10
+ else
11
+ k2t[name] = type
12
+ end
10
13
  else
11
14
  # Expected these to be caught earlier...
12
15
  raise SoftwareError, MSG[UNEXPECTED,token]
@@ -7,22 +7,28 @@ module HelpParser
7
7
  raise VersionException, version
8
8
  end
9
9
  if help
10
- # -h or --help
11
- if @hash.has_key?('h') || _=@hash.has_key?('help')
12
- begin
13
- # validates help
14
- HelpParser.parseh(help, true)
15
- rescue HelpError
16
- $stderr.puts $!
17
- end if _
10
+ h = ['h', 'help']
11
+ if h.any?{|_|@hash.key?_}
12
+ HelpParser.parseh(help, validate: true) if h.all?{|_|@hash.key?_}
18
13
  raise HelpException, help
19
14
  end
20
- specs = HelpParser.parseh(help, HelpParser.validate?)
15
+ specs = HelpParser.parseh(help)
21
16
  Completion.new(@hash, specs)
22
17
  if exclusive=specs[EXCLUSIVE]
23
- exclusive.each{|xs| raise HelpParser::UsageError, MSG[EXCLUSIVE_KEYS,*xs] if @hash.keys.count{|k|xs.include?(k)}>1}
18
+ exclusive.each do |x|
19
+ count = @hash.keys.count{|k|x.include?(k)}
20
+ raise HelpParser::UsageError, MSG[EXCLUSIVE_KEYS,*x] if count > 1
21
+ end
22
+ end
23
+ if inclusive=specs[INCLUSIVE]
24
+ inclusive.each do |i|
25
+ count = @hash.keys.count{|k|i.include?(k)}
26
+ raise HelpParser::UsageError, MSG[INCLUSIVE_KEYS,*i] unless count==0 or count==i.length
27
+ end
24
28
  end
25
29
  end
30
+ $VERBOSE = true if @hash['verbose']==true
31
+ $DEBUG = true if @hash['debug']==true
26
32
  end
27
33
 
28
34
  def _hash
@@ -1,5 +1,5 @@
1
1
  module HelpParser
2
- def self.parseh(help, validate=true)
2
+ def self.parseh(help, validate: false)
3
3
  specs,name = NoDupHash.new,''
4
4
  help.each_line do |line|
5
5
  line.chomp!
@@ -18,11 +18,11 @@ module HelpParser
18
18
  HelpParser.validate_line_chars(spec.chars) if validate
19
19
  tokens = HelpParser.parseu(spec.chars)
20
20
  HelpParser.validate_usage_tokens(tokens) if validate
21
- specs[name].push tokens
21
+ specs[USAGE].push tokens
22
22
  when TYPES
23
23
  raise HelpError, MSG[UNRECOGNIZED_TYPE,spec] if validate and not spec=~TYPE_DEF
24
- specs[name].push spec.split(CSV)
25
- when EXCLUSIVE
24
+ specs[TYPES].push spec.split(CSV)
25
+ when EXCLUSIVE,INCLUSIVE
26
26
  raise HelpError, MSG[UNRECOGNIZED_X,spec] if validate and not spec=~X_DEF
27
27
  specs[name].push spec.split(CSV)
28
28
  else
@@ -30,14 +30,16 @@ module HelpParser
30
30
  def self.validate_usage_specs(specs)
31
31
  option_specs = specs.select{|a,b| !RESERVED[a]}
32
32
  flags = option_specs.values.flatten.select{|f|f[0]=='-'}.map{|f| F2K[f]}
33
- if exclusive=specs[EXCLUSIVE]
34
- seen = {}
35
- exclusive.each do |xs|
36
- k = xs.sort.join(' ').to_sym
37
- raise HelpError, MSG[DUP_X,k] if seen[k]
38
- seen[k] = true
39
- xs.each do |x|
40
- raise HelpError, MSG[UNSEEN_FLAG, x] unless flags.include?(x)
33
+ [EXCLUSIVE,INCLUSIVE].each do |k|
34
+ if a=specs[k]
35
+ seen = {}
36
+ a.each do |xs|
37
+ k = xs.sort.join(' ').to_sym
38
+ raise HelpError, MSG[DUP_X,k] if seen[k] or not xs.length==xs.uniq.length
39
+ seen[k] = true
40
+ xs.each do |x|
41
+ raise HelpError, MSG[UNSEEN_FLAG, x] unless flags.include?(x)
42
+ end
41
43
  end
42
44
  end
43
45
  end
metadata CHANGED
@@ -1,21 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: help_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.3.0
4
+ version: 7.0.200907
5
5
  platform: ruby
6
6
  authors:
7
7
  - carlosjhr64
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-01 00:00:00.000000000 Z
11
+ date: 2020-09-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
- Welcome to the best Help Parser of all!
15
- Tweeker!
16
- Which do you find most helpful?
17
- Hard?
18
- I prefer easy.
14
+ Can't help YOU???
15
+ You're not the first to say that...
16
+
17
+ I will parse your help!
19
18
  email: carlosjhr64@gmail.com
20
19
  executables: []
21
20
  extensions: []
@@ -54,11 +53,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
54
53
  - !ruby/object:Gem::Version
55
54
  version: '0'
56
55
  requirements:
57
- - 'ruby: ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]'
58
- rubyforge_project:
59
- rubygems_version: 2.7.3
56
+ - 'ruby: ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]'
57
+ rubygems_version: 3.1.2
60
58
  signing_key:
61
59
  specification_version: 4
62
- summary: Welcome to the best Help Parser of all! Tweeker! Which do you find most helpful?
63
- Hard? I prefer easy.
60
+ summary: Can't help YOU??? You're not the first to say that...
64
61
  test_files: []