help_parser 8.0.210917 → 8.2.230210

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 89d031ef15e33460fc4f23ccec23cc98f01cb9808fd77bb478c63be6a638f034
4
- data.tar.gz: 9521262caa0b0a1617fc52bdeec42c4e305a4fc4ce34c5871128c64eecf5aae7
3
+ metadata.gz: f19d24ec56736d0d1bbcce1e0a847ac45e9d82defc89c7799c7b9c8c7c4ae078
4
+ data.tar.gz: fe92cf2d740771329bee184dd205d59bc72c1265d79ac50af3a6c4d0fa735297
5
5
  SHA512:
6
- metadata.gz: 4e6c0faa99fd04e8b8e2f289b0f1894538747defaeb557400b36904f7c330961b9cc068aa8bdbe06493f8339af19cd4397cfdca9e1f82bc20ddbf84b91553955
7
- data.tar.gz: 135b86c92d72faa5b49cdfedd7b6c66a9e568e592ae5b8afc86c612efa23020c828acb45693435d5f5349679f61a963bac81041da6d6bdae9549c1ab2de3214b
6
+ metadata.gz: f20dad3a1453e625236af9cb176328652352e9f2f35f00da02cc6ab1853671c82ae13d5ec8226247be116cbd82bdbb4aee24490200f04b2ddba79e380822f36b
7
+ data.tar.gz: 0aebde782784afb644dd1047f32af8de07d07af4c53435ec47ed8c8f7e54a3997a6e6a08c372f23e1a38f37c10f6e0604c26afcb5f16b8d7677120891f10ed71
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Help Parser VIII: Helpland
2
2
 
3
- * [VERSION 8.0.210917](https://github.com/carlosjhr64/help_parser/releases)
3
+ * [VERSION 8.2.230210](https://github.com/carlosjhr64/help_parser/releases)
4
4
  * [github](https://www.github.com/carlosjhr64/help_parser)
5
5
  * [rubygems](https://rubygems.org/gems/help_parser)
6
6
 
@@ -11,11 +11,9 @@ Do you have your help text?
11
11
  Let's parse!
12
12
 
13
13
  ## INSTALL:
14
-
15
14
  ```console
16
15
  $ gem install help_parser
17
16
  ```
18
-
19
17
  ## SYNOPSIS:
20
18
  <!-- The following PREVIEW has been approved for ALL PROGRAMMERS by CarlosJHR64.
21
19
  For the README validator that checks against me lying....
@@ -32,7 +30,6 @@ end
32
30
  The following gem has been rated
33
31
  | M | Mature |
34
32
  -->
35
-
36
33
  ```ruby
37
34
  require "help_parser"
38
35
 
@@ -96,19 +93,19 @@ OPTIONS.args?.class #=> Array
96
93
  OPTIONS.arg? and OPTIONS.arg #=> false
97
94
  OPTIONS.arg?.class #=> FalseClass
98
95
  ```
99
-
100
96
  ## Features
101
97
 
98
+ * `ARGV` setup for `ARGF` when one of the "Types:" given is "ARGF"
102
99
  * `$DEBUG=true` on --debug
103
100
  * `$VERBOSE=true` on --verbose
104
101
  * -h and --help simultaneously will check help string for errors
105
- * `HelpParser::REDTTY[msg]` will red color output `msg` to `STDERR`.
102
+ * `HelpParser::REDTTY[msg]` will red color output `msg` to `$stderr`.
106
103
 
107
104
  ## LICENSE:
108
105
 
109
106
  (The MIT License)
110
107
 
111
- Copyright (c) 2021 CarlosJHR64
108
+ Copyright (c) 2023 CarlosJHR64
112
109
 
113
110
  Permission is hereby granted, free of charge, to any person obtaining
114
111
  a copy of this software and associated documentation files (the
@@ -1,14 +1,14 @@
1
1
  module HelpParser
2
2
  class NoDupHash < Hash
3
3
  def []=(k,v)
4
- raise HelpError, MSG[DUP_KEY,k] if self.has_key?(k)
4
+ raise HelpError, MSG[DUP_KEY,k] if key?(k)
5
5
  super
6
6
  end
7
7
  end
8
8
 
9
9
  class ArgvHash < Hash
10
10
  def []=(k,v)
11
- raise UsageError, MSG[DUP_KEY,k] if self.has_key?(k)
11
+ raise UsageError, MSG[DUP_KEY,k] if key?(k)
12
12
  super
13
13
  end
14
14
  end
@@ -3,36 +3,50 @@ module HelpParser
3
3
  def initialize(hash, specs)
4
4
  @hash,@specs = hash,specs
5
5
  @cache = NoDupHash.new
6
- usage or diagnose if @specs.has_key?(USAGE)
6
+ usage or diagnose if @specs.key?(USAGE)
7
7
  pad
8
- types if @specs.has_key?(TYPES)
8
+ if @specs.key?(TYPES)
9
+ k2t = types
10
+ handle_argf(k2t) if k2t.detect{|_,v|v=='ARGF'}
11
+ end
12
+ end
13
+
14
+ # Prepare ARGV for ARGF.
15
+ def handle_argf(k2t)
16
+ files = @hash.select{|k,_|k2t[k]=='ARGF'}.map{|_,v|v}.flatten
17
+ e = files.reject{File.exist?_1}.join(', ')
18
+ raise UsageError, MSG[NOT_EXIST, e] unless e.empty?
19
+ ARGV.replace files
9
20
  end
10
21
 
11
22
  # Which usage does the user's command options match?
12
23
  def usage
13
24
  @specs[USAGE].each do |cmd|
14
- begin
15
- i = matches(cmd)
16
- raise NoMatch unless @hash.size==i
17
- @cache.each{|k,v|@hash[k]=v} # Variables
18
- return true # Good! Found matching usage.
19
- rescue NoMatch
20
- next
21
- ensure
22
- @cache.clear
23
- end
25
+ i = matches(cmd)
26
+ raise NoMatch unless @hash.size==i
27
+ @cache.each{|k,v|@hash[k]=v} # Variables
28
+ return true # Good! Found matching usage.
29
+ rescue NoMatch
30
+ next
31
+ ensure
32
+ @cache.clear
24
33
  end
25
- return false # Bad! Did not match any of the expected usage.
34
+ false # Bad! Did not match any of the expected usage.
26
35
  end
27
36
 
28
37
  # Diagnose user's usage.
29
38
  def diagnose
30
39
  dict = {}
31
- @specs.each do |k,v|
32
- next if RESERVED.include? k
33
- v.flatten.map{_1.scan(/\w+/).first}.each{dict[_1]=true}
40
+ @specs.each do |section,list|
41
+ next if RESERVED.include? section
42
+ list.flatten.select{_1[0]=='-'}.each do |key_type|
43
+ key_type.scan(/\w+/) do |key|
44
+ dict[key]=true
45
+ break
46
+ end
47
+ end
34
48
  end
35
- typos = @hash.keys.select{|k|k.is_a? String and not dict[k]}
49
+ typos = @hash.keys.select{_1.is_a?(String) && !dict[_1]}
36
50
  raise UsageError, MSG[UNRECOGNIZED, typos] unless typos.empty?
37
51
 
38
52
  raise UsageError, MATCH_USAGE
@@ -41,32 +55,31 @@ module HelpParser
41
55
  def types
42
56
  t2r,k2t = HelpParser.t2r(@specs),HelpParser.k2t(@specs)
43
57
  @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
- unless value=~regex
50
- raise UsageError, "--#{key}=#{value} !~ #{type}=#{regex.inspect}"
51
- end
52
- when Array
53
- value.each do |string|
54
- unless string=~regex
55
- raise UsageError,
56
- "--#{key}=#{string} !~ #{type}=#{regex.inspect}"
57
- end
58
+ next unless key.is_a?(String) && (type=k2t[key])
59
+ regex = t2r[type]
60
+ case value
61
+ when String
62
+ unless value=~regex
63
+ raise UsageError, "--#{key}=#{value} !~ #{type}=#{regex.inspect}"
64
+ end
65
+ when Array
66
+ value.each do |string|
67
+ unless string=~regex
68
+ raise UsageError,
69
+ "--#{key}=#{string} !~ #{type}=#{regex.inspect}"
58
70
  end
59
- else
60
- raise UsageError, "--#{key} !~ #{type}=#{regex.inspect}"
61
71
  end
72
+ else
73
+ raise UsageError, "--#{key} !~ #{type}=#{regex.inspect}"
62
74
  end
63
75
  end
76
+ k2t
64
77
  end
65
78
 
66
79
  def pad
67
80
  # Synonyms and defaults:
68
81
  @specs.each do |section,options|
69
- next if section==USAGE || section==TYPES
82
+ next if RESERVED.any?{section==_1}
70
83
  options.each do |words|
71
84
  next unless words.size>1
72
85
  first,second,default = words[0],words[1],words[2]
@@ -74,12 +87,12 @@ module HelpParser
74
87
  if second[0]=='-'
75
88
  i = second.index('=') || 0
76
89
  short,long = first[1],second[2..(i-1)]
77
- if @hash.has_key?(short)
78
- if @hash.has_key?(long)
90
+ if @hash.key?(short)
91
+ if @hash.key?(long)
79
92
  raise UsageError, MSG[REDUNDANT, short, long]
80
93
  end
81
- @hash[long] = (default.nil?) ? true : default
82
- elsif value = @hash[long]
94
+ @hash[long] = default.nil? ? true : default
95
+ elsif (value=@hash[long])
83
96
  @hash[short] = true
84
97
  if value==true && !default.nil?
85
98
  @hash.delete(long) # ArgvHash reset
@@ -95,6 +108,8 @@ module HelpParser
95
108
  @hash[long] = default
96
109
  end
97
110
  end
111
+ else
112
+ raise SoftwareError, MSG[UNEXPECTED, words]
98
113
  end
99
114
  end
100
115
  end
@@ -110,20 +125,20 @@ module HelpParser
110
125
  # OK, NEVERMIND!
111
126
  end
112
127
  next
113
- elsif m=FLAG_GROUP.match(token)
128
+ elsif (m=FLAG_GROUP.match token)
114
129
  group,plus = m[:k],m[:p]
115
130
  key = keys[i]
116
- raise NoMatch if key.nil? || key.is_a?(Integer)
131
+ raise NoMatch unless key.is_a? String
117
132
  list = @specs[group].flatten.select{|f|f[0]=='-'}.map{|f| F2K[f]}
118
133
  raise NoMatch unless list.include?(key)
119
134
  unless plus.nil?
120
135
  loop do
121
136
  key = keys[i+1]
122
- break if key.nil? || key.is_a?(Integer) || !list.include?(key)
137
+ break unless key.is_a?(String) && list.include?(key)
123
138
  i+=1
124
139
  end
125
140
  end
126
- elsif m=VARIABLE.match(token)
141
+ elsif (m=VARIABLE.match(token))
127
142
  key = keys[i]
128
143
  raise NoMatch unless key.is_a?(Integer)
129
144
  variable,plus = m[:k],m[:p]
@@ -146,7 +161,7 @@ module HelpParser
146
161
  end
147
162
  i += 1
148
163
  end
149
- return i
164
+ i
150
165
  end
151
166
  end
152
167
  end
@@ -1,6 +1,6 @@
1
1
  module HelpParser
2
- V,VSN = 'v','version'
3
- H,HLP = 'h','help'
2
+ VSN = %w[v version]
3
+ HLP = %w[h help]
4
4
  VRBS,DBG = 'verbose','debug'
5
5
 
6
6
  # reserved name
@@ -13,22 +13,22 @@ module HelpParser
13
13
  RESERVED = [USAGE,TYPES,EXCLUSIVE,INCLUSIVE,CONDITIONAL]
14
14
 
15
15
  # sections
16
- SECTION_NAME = /^[A-Z]\w+:$/
16
+ SECTION_NAME = /^(?<name>[A-Z]\w+):$/
17
17
 
18
18
  # usage
19
- FLAG = /^[-][-]?(?<k>\w+)$/
19
+ FLAG = /^--?(?<k>\w+)$/
20
20
  LITERAL = /^(?<k>\w[\w.-]*:?)$/
21
21
  VARIABLE = /^<(?<k>\w+)(=(?<t>[A-Z]+))?>(?<p>[+])?$/
22
22
  FLAG_GROUP = /^:(?<k>\w+)(?<p>[+])?$/
23
23
 
24
24
  # spec --?w+
25
- SHORT = /^[-](?<s>\w)$/
26
- LONG = /^[-][-](?<k>\w+)(=(?<t>[A-Z]+))?(,?\s+(?<d>[^-\s]\S*))?$/
25
+ SHORT = /^-(?<s>\w)$/
26
+ LONG = /^--(?<k>\w+)(=(?<t>[A-Z]+))?(,?\s+(?<d>[^-\s]\S*))?$/
27
27
 
28
28
  # spec -w,? --w+
29
- SHORT_LONG = /^[-](?<s>\w),?\s+[-][-](?<k>\w+)$/
29
+ SHORT_LONG = /^-(?<s>\w),?\s+--(?<k>\w+)$/
30
30
  SHORT_LONG_DEFAULT =
31
- /^[-](?<s>\w),?\s+[-][-](?<k>\w+)(=(?<t>[A-Z]+))?,?\s+(?<d>[^-\s]\S*)$/
31
+ /^-(?<s>\w),?\s+--(?<k>\w+)(=(?<t>[A-Z]+))?,?\s+(?<d>\S*)$/
32
32
 
33
33
  # spec W+ /~/
34
34
  TYPE_DEF = /^(?<t>[A-Z]+),?\s+\/(?<r>\S+)\/$/
@@ -73,15 +73,16 @@ module HelpParser
73
73
  NOT_FLOATS = 'Not all Floats'
74
74
  NOT_INTEGER = 'Not an Integer'
75
75
  NOT_INTEGERS = 'Not all Integers'
76
+ NOT_EXIST = 'Does not exist'
76
77
  # error messages, full:
77
78
  NO_MATCH = 'Software Error: NoMatch was not caught by HelpParser.'
78
79
  MATCH_USAGE = 'Please match usage.'
79
80
  EXTRANEOUS_SPACES = 'Extraneous spaces in help.'
80
81
 
81
82
  # lambda utilities
82
- MSG = lambda{|msg,*keys| "#{msg}: #{keys.join(' ')}"}
83
- F2K = lambda{|f| f[1]=='-' ? f[2..((f.index('=')||0)-1)] : f[1]}
84
- REDTTY = lambda{|msg,out=$stderr|
83
+ MSG = ->(msg,*keys){"#{msg}: #{keys.join(' ')}"}
84
+ F2K = ->(f){f[1]=='-' ? f[2..((f.index('=')||0)-1)] : f[1]}
85
+ REDTTY = lambda do |msg,out=$stderr|
85
86
  out.tty? ? out.puts("\033[0;31m#{msg}\033[0m"): out.puts(msg)
86
- }
87
+ end
87
88
  end
@@ -1,6 +1,8 @@
1
1
  module HelpParser
2
2
  class HelpParserException < Exception
3
- def _init; @code = 1; end
3
+ def _init
4
+ @code = 1
5
+ end
4
6
 
5
7
  # Must give message
6
8
  def initialize(message)
@@ -9,35 +11,45 @@ module HelpParser
9
11
  end
10
12
 
11
13
  def exit
12
- if @code > 0
13
- REDTTY[self.message]
14
+ if @code.positive?
15
+ REDTTY[message]
14
16
  else
15
- $stdout.puts self.message
17
+ $stdout.puts message
16
18
  end
17
19
  Kernel.exit @code
18
20
  end
19
21
  end
20
22
 
21
23
  class VersionException < HelpParserException
22
- def _init; @code = 0; end
24
+ def _init
25
+ @code = 0
26
+ end
23
27
  end
24
28
 
25
29
  class HelpException < HelpParserException
26
- def _init; @code = 0; end
30
+ def _init
31
+ @code = 0
32
+ end
27
33
  end
28
34
 
29
35
  class UsageError < HelpParserException
30
- def _init; @code = EX_USAGE; end
36
+ def _init
37
+ @code = EX_USAGE
38
+ end
31
39
  end
32
40
 
33
41
  class SoftwareError < HelpParserException
34
42
  # Stuff that should not happen
35
- def _init; @code = EX_SOFTWARE; end
43
+ def _init
44
+ @code = EX_SOFTWARE
45
+ end
36
46
  end
37
47
 
38
48
  class NoMatch < HelpParserException
39
49
  # used to short-circuit out
40
- def _init; @code = EX_SOFTWARE; end
50
+ def _init
51
+ @code = EX_SOFTWARE
52
+ end
41
53
 
42
54
  # Forces it's own message
43
55
  def initialize
@@ -46,6 +58,8 @@ module HelpParser
46
58
  end
47
59
 
48
60
  class HelpError < HelpParserException
49
- def _init; @code = EX_CONFIG; end
61
+ def _init
62
+ @code = EX_CONFIG
63
+ end
50
64
  end
51
65
  end
@@ -1,13 +1,16 @@
1
1
  module HelpParser
2
+ # k2t is an acronym for the "key to type" mapping
2
3
  def self.k2t(specs)
3
4
  k2t = NoDupHash.new
4
- tokens = specs.select{|k,v| !(k==TYPES)}.values.flatten
5
- .select{|v|v.include?('=')}
5
+ # If specs section is not a RESERVED section, it's an options list.
6
+ tokens = specs.select{|k,_| k==USAGE or !RESERVED.include?(k)}
7
+ # Tokens associating a key to a type.
8
+ .values.flatten.select{|v|v.include?('=')}
6
9
  tokens.each do |token|
7
- if match = VARIABLE.match(token) || LONG.match(token)
10
+ if (match = VARIABLE.match(token) || LONG.match(token))
8
11
  name, type = match[:k], match[:t]
9
- if _=k2t[name]
10
- raise HelpError, MSG[INCONSISTENT,name,type,_] unless type==_
12
+ if (_=k2t[name])
13
+ raise HelpError, MSG[INCONSISTENT,name,type,_] unless type==_
11
14
  else
12
15
  k2t[name] = type
13
16
  end
@@ -16,11 +19,12 @@ module HelpParser
16
19
  raise SoftwareError, MSG[UNEXPECTED,token]
17
20
  end
18
21
  end
19
- return k2t
22
+ k2t
20
23
  end
21
24
 
25
+ # t2r is an acronym for "type to regexp"
22
26
  def self.t2r(specs)
23
- if types = specs[TYPES]
27
+ if (types=specs[TYPES])
24
28
  t2r = NoDupHash.new
25
29
  types.each do |pair|
26
30
  type, pattern = *pair
@@ -32,6 +36,6 @@ module HelpParser
32
36
  end
33
37
  return t2r
34
38
  end
35
- return nil
39
+ nil
36
40
  end
37
41
  end
@@ -1,201 +1,145 @@
1
1
  module HelpParser
2
2
  def self.string(*names)
3
3
  names.each do |name|
4
- code = <<-CODE
5
- class Options
6
- def #{name}
7
- s = @hash['#{name}']
8
- raise UsageError, MSG[NOT_STRING,'#{name}'] unless s.is_a?(String)
9
- return s
10
- end
11
- end
12
- CODE
13
- eval code
4
+ Options.instance_eval do
5
+ define_method(name) do
6
+ s = @hash[name.to_s]
7
+ raise UsageError, MSG[NOT_STRING,name] unless s.is_a?(String)
8
+ s
9
+ end
10
+ end
14
11
  end
15
12
  end
16
13
 
17
14
  def self.string?(*names)
18
15
  names.each do |name|
19
- code = <<-CODE
20
- class Options
21
- def #{name}?
22
- s = @hash['#{name}']
23
- raise UsageError, MSG[NOT_STRING,'#{name}'] unless s.nil? ||
24
- s.is_a?(String)
25
- return s
26
- end
27
- end
28
- CODE
29
- eval code
16
+ Options.instance_eval do
17
+ define_method("#{name}?") do
18
+ s = @hash[name.to_s]
19
+ raise UsageError,MSG[NOT_STRING,name] unless s.nil? || s.is_a?(String)
20
+ s
21
+ end
22
+ end
30
23
  end
31
24
  end
32
25
 
33
26
  def self.strings(*names)
34
27
  names.each do |name|
35
- code = <<-CODE
36
- class Options
37
- def #{name}
38
- a = @hash['#{name}']
39
- raise UsageError, MSG[NOT_STRINGS,'#{name}'] unless a.is_a?(Array)
40
- return a
41
- end
42
- end
43
- CODE
44
- eval code
28
+ Options.instance_eval do
29
+ define_method(name) do
30
+ a = @hash[name.to_s]
31
+ raise UsageError, MSG[NOT_STRINGS,name] unless a.is_a?(Array)
32
+ a
33
+ end
34
+ end
45
35
  end
46
36
  end
47
37
 
48
38
  def self.strings?(*names)
49
39
  names.each do |name|
50
- code = <<-CODE
51
- class Options
52
- def #{name}?
53
- a = @hash['#{name}']
54
- raise UsageError, MSG[NOT_STRINGS,'#{name}'] unless a.nil? ||
55
- a.is_a?(Array)
56
- return a
57
- end
58
- end
59
- CODE
60
- eval code
40
+ Options.instance_eval do
41
+ define_method("#{name}?") do
42
+ a = @hash[name.to_s]
43
+ raise UsageError,MSG[NOT_STRINGS,name] unless a.nil? || a.is_a?(Array)
44
+ a
45
+ end
46
+ end
61
47
  end
62
48
  end
63
49
 
64
50
  def self.float(*names)
65
51
  names.each do |name|
66
- code = <<-CODE
67
- class Options
68
- def #{name}
69
- f = @hash['#{name}']
70
- raise if f.nil?
71
- f.to_f
72
- rescue
73
- raise UsageError, MSG[NOT_FLOAT,'#{name}']
74
- end
75
- end
76
- CODE
77
- eval code
52
+ Options.instance_eval do
53
+ define_method(name) do
54
+ @hash[name.to_s]&.to_f or raise
55
+ rescue
56
+ raise UsageError, MSG[NOT_FLOAT,name]
57
+ end
58
+ end
78
59
  end
79
60
  end
80
61
 
81
62
  def self.float?(*names)
82
63
  names.each do |name|
83
- code = <<-CODE
84
- class Options
85
- def #{name}?
86
- f = @hash['#{name}']
87
- f = f.to_f if f
88
- return f
89
- rescue
90
- raise UsageError, MSG[NOT_FLOAT,'#{name}']
91
- end
92
- end
93
- CODE
94
- eval code
64
+ Options.instance_eval do
65
+ define_method("#{name}?") do
66
+ @hash[name.to_s]&.to_f
67
+ rescue
68
+ raise UsageError, MSG[NOT_FLOAT,name]
69
+ end
70
+ end
95
71
  end
96
72
  end
97
73
 
98
74
  def self.floats(*names)
99
75
  names.each do |name|
100
- code = <<-CODE
101
- class Options
102
- def #{name}
103
- f = @hash['#{name}']
104
- raise unless f.is_a?(Array)
105
- f.map{_1.to_f}
106
- rescue
107
- raise UsageError, MSG[#{NOT_FLOATS},'#{name}']
108
- end
109
- end
110
- CODE
111
- eval code
76
+ Options.instance_eval do
77
+ define_method(name) do
78
+ @hash[name.to_s].map(&:to_f)
79
+ rescue
80
+ raise UsageError, MSG[NOT_FLOATS,name]
81
+ end
82
+ end
112
83
  end
113
84
  end
114
85
 
115
86
  def self.floats?(*names)
116
87
  names.each do |name|
117
- code = <<-CODE
118
- class Options
119
- def #{name}?
120
- f = @hash['#{name}']
121
- return nil unless f
122
- raise unless f.is_a?(Array)
123
- f.map{_1.to_f}
124
- rescue
125
- raise UsageError, MSG[NOT_FLOATS,'#{name}']
126
- end
127
- end
128
- CODE
129
- eval code
88
+ Options.instance_eval do
89
+ define_method("#{name}?") do
90
+ @hash[name.to_s]&.map(&:to_f)
91
+ rescue
92
+ raise UsageError, MSG[NOT_FLOATS,name]
93
+ end
94
+ end
130
95
  end
131
96
  end
132
97
 
133
98
  def self.int(*names)
134
99
  names.each do |name|
135
- code = <<-CODE
136
- class Options
137
- def #{name}
138
- f = @hash['#{name}']
139
- raise if f.nil?
140
- f.to_i
141
- rescue
142
- raise UsageError, MSG[NOT_INTEGER,'#{name}']
143
- end
144
- end
145
- CODE
146
- eval code
100
+ Options.instance_eval do
101
+ define_method(name) do
102
+ @hash[name.to_s]&.to_i or raise
103
+ rescue
104
+ raise UsageError, MSG[NOT_INTEGER,name]
105
+ end
106
+ end
147
107
  end
148
108
  end
149
109
 
150
110
  def self.int?(*names)
151
111
  names.each do |name|
152
- code = <<-CODE
153
- class Options
154
- def #{name}?
155
- f = @hash['#{name}']
156
- f = f.to_i if f
157
- return f
158
- rescue
159
- raise UsageError, MSG[NOT_INTEGER,'#{name}']
160
- end
161
- end
162
- CODE
163
- eval code
112
+ Options.instance_eval do
113
+ define_method("#{name}?") do
114
+ @hash[name.to_s]&.to_i
115
+ rescue
116
+ raise UsageError, MSG[NOT_INTEGER,name]
117
+ end
118
+ end
164
119
  end
165
120
  end
166
121
 
167
122
  def self.ints(*names)
168
123
  names.each do |name|
169
- code = <<-CODE
170
- class Options
171
- def #{name}
172
- f = @hash['#{name}']
173
- raise unless f.is_a?(Array)
174
- f.map{_1.to_i}
175
- rescue
176
- raise UsageError, MSG[NOT_INTEGERS,'#{name}']
177
- end
178
- end
179
- CODE
180
- eval code
124
+ Options.instance_eval do
125
+ define_method(name) do
126
+ @hash[name.to_s].map(&:to_i)
127
+ rescue
128
+ raise UsageError, MSG[NOT_INTEGERS,name]
129
+ end
130
+ end
181
131
  end
182
132
  end
183
133
 
184
134
  def self.ints?(*names)
185
135
  names.each do |name|
186
- code = <<-CODE
187
- class Options
188
- def #{name}?
189
- f = @hash['#{name}']
190
- return nil unless f
191
- raise unless f.is_a?(Array)
192
- f.map{_1.to_i}
193
- rescue
194
- raise UsageError, MSG[NOT_INTEGERS,'#{name}']
195
- end
196
- end
197
- CODE
198
- eval code
136
+ Options.instance_eval do
137
+ define_method("#{name}?") do
138
+ @hash[name.to_s]&.map(&:to_i)
139
+ rescue
140
+ raise UsageError, MSG[NOT_INTEGERS,name]
141
+ end
142
+ end
199
143
  end
200
144
  end
201
145
  end
@@ -2,35 +2,35 @@ module HelpParser
2
2
  class Options
3
3
  def initialize(version, help, argv)
4
4
  @hash = HelpParser.parsea(argv)
5
- if version && (@hash.has_key?(V) || @hash.has_key?(VSN))
5
+ if version && VSN.any?{@hash.key? _1}
6
6
  # -v or --version
7
- raise VersionException, version
7
+ raise VersionException, String(version)
8
8
  end
9
9
  if help
10
- h = [H, HLP]
11
- if h.any?{@hash.key? _1}
12
- HelpParser.parseh(help, validate: true) if h.all?{@hash.key? _1}
10
+ help = String(help)
11
+ if HLP.any?{@hash.key? _1}
12
+ HelpParser.parseh(help, validate: true) if HLP.all?{@hash.key? _1}
13
13
  raise HelpException, help
14
14
  end
15
15
  specs = HelpParser.parseh(help)
16
16
  Completion.new(@hash, specs)
17
- if exclusive=specs[EXCLUSIVE]
17
+ if (exclusive=specs[EXCLUSIVE])
18
18
  exclusive.each do |x|
19
19
  count = x.count{@hash.key? _1}
20
20
  raise HelpParser::UsageError, MSG[EXCLUSIVE_KEYS,*x] if count > 1
21
21
  end
22
22
  end
23
- if inclusive=specs[INCLUSIVE]
23
+ if (inclusive=specs[INCLUSIVE])
24
24
  inclusive.each do |i|
25
25
  count = i.count{@hash.key? _1}
26
- unless count==0 or count==i.length
26
+ unless count.zero? || count==i.length
27
27
  raise HelpParser::UsageError, MSG[INCLUSIVE_KEYS,*i]
28
28
  end
29
29
  end
30
30
  end
31
- if conditional=specs[CONDITIONAL]
31
+ if (conditional=specs[CONDITIONAL])
32
32
  conditional.each do |c|
33
- if @hash.key? c[0] and not c.all?{@hash.key? _1}
33
+ if @hash.key?(c[0]) && !c.all?{@hash.key? _1}
34
34
  raise HelpParser::UsageError, MSG[CONDITIONAL_KEYS,*c]
35
35
  end
36
36
  end
@@ -48,17 +48,16 @@ module HelpParser
48
48
  @hash[k]
49
49
  end
50
50
 
51
- def method_missing(mthd, *args, &block)
52
- super if block or args.length > 0
53
- m = mthd.to_s
54
- case m[-1]
55
- when '?'
56
- @hash.key? m[0..-2]
57
- when '!'
58
- super
59
- else
60
- @hash[m]
51
+ def respond_to_missing?(m, include_all=false)
52
+ m[-1]=='!' ? super : true
53
+ end
54
+
55
+ def method_missing(m, *args, &block)
56
+ super unless respond_to_missing?(m)
57
+ unless args.empty? && block.nil?
58
+ raise ArgumentError, 'expected neither args nor block'
61
59
  end
60
+ m[-1]=='?' ? @hash.key?(m[0..-2].to_s) : @hash[m.to_s]
62
61
  end
63
62
  end
64
63
  end
@@ -7,7 +7,7 @@ module HelpParser
7
7
  break if a.size==1 # '-' quits argv processing
8
8
  if a[1]=='-'
9
9
  break if a.size==2 # '--' also quits argv processing
10
- s = a[2..-1]
10
+ s = a[2..]
11
11
  if s.include?('=')
12
12
  k,v = s.split('=',2)
13
13
  hsh[k] = v
@@ -15,7 +15,7 @@ module HelpParser
15
15
  hsh[s] = true
16
16
  end
17
17
  else
18
- a.chars[1..-1].each do |c|
18
+ a.chars[1..].each do |c|
19
19
  hsh[c] = true
20
20
  end
21
21
  end
@@ -24,6 +24,6 @@ module HelpParser
24
24
  n += 1
25
25
  end
26
26
  end
27
- return hsh
27
+ hsh
28
28
  end
29
29
  end
@@ -4,45 +4,47 @@ module HelpParser
4
4
  help.each_line do |line|
5
5
  line.chomp!
6
6
  next if line==''
7
- if line=~SECTION_NAME
8
- name = line[0..-2].downcase
7
+ if (md=SECTION_NAME.match(line))
8
+ name = md[:name].downcase
9
9
  specs[name] = []
10
10
  else
11
11
  next if name==''
12
12
  break if line[0]=='#'
13
- next if !(line[0]==' ')
13
+ next unless line[0]==' '
14
14
  spec = (index=line.rindex("\t"))? line[0,index].strip : line.strip
15
- raise HelpError, EXTRANEOUS_SPACES if validate and spec==''
15
+ raise HelpError, EXTRANEOUS_SPACES if validate && spec.empty?
16
16
  case name
17
17
  when USAGE
18
- Validate.line_chars(spec.chars) if validate
18
+ Validate.balanced_brackets(spec.chars) if validate
19
19
  tokens = HelpParser.parseu(spec.chars)
20
20
  Validate.usage_tokens(tokens) if validate
21
21
  specs[USAGE].push tokens
22
22
  when TYPES
23
- if validate and not spec=~TYPE_DEF
23
+ if validate && !TYPE_DEF.match?(spec)
24
24
  raise HelpError, MSG[UNRECOGNIZED_TYPE,spec]
25
25
  end
26
26
  specs[TYPES].push spec.split(CSV)
27
27
  when *FLAG_CLUMPS # EXCLUSIVE,INCLUSIVE,CONDITIONAL,...
28
- if validate and not spec=~X_DEF
28
+ if validate && !X_DEF.match?(spec)
29
29
  raise HelpError, MSG[UNRECOGNIZED_X,spec]
30
30
  end
31
31
  specs[name].push spec.split(CSV)
32
32
  else
33
- raise HelpError, MSG[UNRECOGNIZED_OPTION,spec] if validate and
34
- not [SHORT, LONG, SHORT_LONG, SHORT_LONG_DEFAULT].any?{_1=~spec}
33
+ if validate &&
34
+ [SHORT, LONG, SHORT_LONG, SHORT_LONG_DEFAULT].none?{_1=~spec}
35
+ raise HelpError, MSG[UNRECOGNIZED_OPTION,spec]
36
+ end
35
37
  specs[name].push spec.split(CSV)
36
38
  end
37
39
  end
38
40
  end
39
41
  if validate
40
42
  Validate.usage_specs(specs)
41
- if t2r = HelpParser.t2r(specs)
43
+ if (t2r=HelpParser.t2r(specs))
42
44
  k2t = HelpParser.k2t(specs)
43
45
  Validate.k2t2r(specs, k2t, t2r)
44
46
  end
45
47
  end
46
- return specs
48
+ specs
47
49
  end
48
50
  end
@@ -1,7 +1,11 @@
1
1
  module HelpParser
2
+ # Chars := String.split(/\t/,2).first.strip.chars
3
+ # Token := String=~/^[^ \[\]]$/
4
+ # Note that emergent Token is String=~/^[^\s\[\]]$/
5
+ # Tokens := Array(Token|Tokens)
2
6
  def self.parseu(chars)
3
7
  tokens,token = [],''
4
- while c = chars.shift
8
+ while (c=chars.shift)
5
9
  case c
6
10
  when ' ','[',']'
7
11
  unless token==''
@@ -15,6 +19,6 @@ module HelpParser
15
19
  end
16
20
  end
17
21
  tokens.push(token) unless token==''
18
- return tokens
22
+ tokens
19
23
  end
20
24
  end
@@ -1,27 +1,20 @@
1
1
  module HelpParser
2
2
  module Validate
3
- def self.line_chars(chars)
3
+ def self.balanced_brackets(chars)
4
4
  count = 0
5
5
  chars.each do |c|
6
- if c=='['
7
- count += 1
8
- elsif c==']'
9
- count -= 1
10
- end
11
- break if count<0
6
+ c=='[' && (count+=1) or c==']' && (count-=1)
7
+ break if count.negative?
12
8
  end
13
- raise HelpError, MSG[UNBALANCED,chars.join] unless count==0
9
+ raise HelpError, MSG[UNBALANCED,chars.join] unless count.zero?
14
10
  end
15
11
 
16
12
  def self.usage_tokens(tokens)
17
13
  words = []
18
14
  tokens.flatten.each do |token|
19
- match = token.match(FLAG) ||
20
- token.match(LITERAL) ||
21
- token.match(VARIABLE) ||
22
- token.match(FLAG_GROUP)
23
- raise HelpError, MSG[UNRECOGNIZED_TOKEN,token] unless match
24
- words.push match[:k] # key
15
+ raise HelpError, MSG[UNRECOGNIZED_TOKEN,token] unless
16
+ [FLAG,LITERAL,VARIABLE,FLAG_GROUP]
17
+ .detect{(_=token.match _1) && words.push(_[:k])}
25
18
  end
26
19
  words.each_with_index do |word,i|
27
20
  raise HelpError, MSG[DUP_WORD,word] unless i==words.rindex(word)
@@ -29,40 +22,37 @@ module Validate
29
22
  end
30
23
 
31
24
  def self.usage_specs(specs)
32
- option_specs = specs.select{|a,b| not RESERVED.include? a}
33
- flags = option_specs.values.flatten.select{|f|f[0]=='-'}.map{|f| F2K[f]}
25
+ flags = specs.except(*RESERVED).values.flatten
26
+ .select{_1[0]=='-'}.map{F2K[_1]}
34
27
  FLAG_CLUMPS.each do |k|
35
- if a=specs[k]
36
- seen = {}
37
- a.each do |xs|
38
- k = xs.sort.join(' ').to_sym
39
- if seen[k] or not xs.length==xs.uniq.length
40
- raise HelpError, MSG[DUP_X,k]
41
- end
42
- seen[k] = true
43
- xs.each do |x|
44
- raise HelpError, MSG[UNSEEN_FLAG, x] unless flags.include?(x)
45
- end
28
+ next unless (a=specs[k])
29
+ seen = {}
30
+ a.each do |xs|
31
+ k = xs.sort.join(' ').to_sym
32
+ if seen[k] || xs.length!=xs.uniq.length
33
+ raise HelpError, MSG[DUP_X,k]
34
+ end
35
+ seen[k] = true
36
+ xs.each do |x|
37
+ raise HelpError, MSG[UNSEEN_FLAG, x] unless flags.include?(x)
46
38
  end
47
39
  end
48
40
  end
49
41
  flags.each_with_index do |flag,i|
50
42
  raise HelpError, MSG[DUP_FLAG,flag] unless i==flags.rindex(flag)
51
- end
43
+ end
52
44
  group = []
53
45
  specs_usage = specs[USAGE]
54
- unless specs_usage.nil?
55
- specs_usage.flatten.each do |token|
56
- if match = token.match(FLAG_GROUP)
57
- key = match[:k]
58
- raise HelpError, MSG[UNDEFINED_SECTION,key] unless specs[key]
59
- group.push(key)
60
- end
46
+ specs_usage&.flatten&.each do |token|
47
+ if (match=token.match FLAG_GROUP)
48
+ key = match[:k]
49
+ raise HelpError, MSG[UNDEFINED_SECTION,key] unless specs[key]
50
+ group.push(key)
61
51
  end
62
52
  end
63
53
  specs.each do |key,tokens|
64
- raise HelpError, MSG[MISSING_CASES,key] unless tokens.size>0
65
- next if specs_usage.nil? or RESERVED.include? key
54
+ raise HelpError, MSG[MISSING_CASES,key] if tokens.empty?
55
+ next if specs_usage.nil? || RESERVED.include?(key)
66
56
  raise HelpError, MSG[MISSING_USAGE,key] unless group.include?(key)
67
57
  end
68
58
  end
@@ -70,11 +60,11 @@ module Validate
70
60
  def self.k2t2r(specs, k2t, t2r)
71
61
  a,b = k2t.values.uniq.sort,t2r.keys.sort
72
62
  unless a==b
73
- c = (a+b).uniq.select{|x|!(a.include?(x) && b.include?(x))}
63
+ c = (a+b).uniq.reject{|x|a.include?(x) && b.include?(x)}
74
64
  raise HelpError, MSG[UNCOMPLETED_TYPES,c.join(',')]
75
65
  end
76
66
  specs.each do |section,tokens|
77
- next if section==USAGE || section==TYPES
67
+ next if RESERVED.include? section
78
68
  tokens.each do |words|
79
69
  next if words.size<2
80
70
  default = words[-1]
@@ -83,7 +73,7 @@ module Validate
83
73
  i = long_type.index('=')
84
74
  next if i.nil?
85
75
  long = long_type[2..(i-1)]
86
- type = long_type[(i+1)..-1]
76
+ type = long_type[(i+1)..]
87
77
  regex = t2r[type]
88
78
  unless regex=~default
89
79
  raise HelpError, MSG[BAD_DEFAULT,long,default,type,regex.inspect]
data/lib/help_parser.rb CHANGED
@@ -10,7 +10,7 @@ require_relative 'help_parser/options'
10
10
  require_relative 'help_parser/macros'
11
11
 
12
12
  module HelpParser
13
- VERSION = '8.0.210917'
13
+ VERSION = '8.2.230210'
14
14
  autoload :Validate, 'help_parser/validate'
15
15
 
16
16
  def self.[](
@@ -18,10 +18,10 @@ module HelpParser
18
18
  help = nil,
19
19
  argv = [File.basename($0)]+ARGV)
20
20
  Options.new(version, help, argv)
21
- rescue HelpParserException => exception
22
- exception.exit
21
+ rescue HelpParserException => e
22
+ e.exit
23
23
  end
24
24
  end
25
25
 
26
26
  # Requires:
27
- #`ruby`
27
+ # `ruby`
metadata CHANGED
@@ -1,22 +1,81 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: help_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.210917
4
+ version: 8.2.230210
5
5
  platform: ruby
6
6
  authors:
7
7
  - CarlosJHR64
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-17 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2023-02-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colorize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.8'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.8.1
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.8'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.8.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: parser
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.2'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 3.2.0
43
+ type: :development
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '3.2'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 3.2.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: test-unit
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '3.5'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 3.5.7
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '3.5'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 3.5.7
13
73
  description: "Welcome to Help Parser! \nDo you have your help text? \nLet's parse!\n"
14
74
  email: carlosjhr64@gmail.com
15
75
  executables: []
16
76
  extensions: []
17
77
  extra_rdoc_files: []
18
78
  files:
19
- - LICENSE
20
79
  - README.md
21
80
  - lib/help_parser.rb
22
81
  - lib/help_parser/aliases.rb
@@ -34,7 +93,7 @@ homepage: https://github.com/carlosjhr64/help_parser
34
93
  licenses:
35
94
  - MIT
36
95
  metadata: {}
37
- post_install_message:
96
+ post_install_message:
38
97
  rdoc_options: []
39
98
  require_paths:
40
99
  - lib
@@ -49,9 +108,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
108
  - !ruby/object:Gem::Version
50
109
  version: '0'
51
110
  requirements:
52
- - 'ruby: ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-linux]'
53
- rubygems_version: 3.2.22
54
- signing_key:
111
+ - 'git: 2.30'
112
+ rubygems_version: 3.4.6
113
+ signing_key:
55
114
  specification_version: 4
56
115
  summary: Welcome to Help Parser! Do you have your help text? Let's parse!
57
116
  test_files: []
data/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2017 carlosjhr64
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.