help_parser 8.1.221206 → 9.0.240926

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: 157ae793a16a5b92ca1633306080465fcd2e9671aaed67dc01fb47cb318f0824
4
- data.tar.gz: cc72ed9f03c94ef2f5455897807a49ba69e79786ceae5daa3d158d3265e9a931
3
+ metadata.gz: 60dd76f994f506e15f1593b7561ee1b23cc59c0cf3cd53cc1140dcbc59b7b0f5
4
+ data.tar.gz: c113698c66f7790c4977d57c7906dad639774dd315158677091e59c5db188cef
5
5
  SHA512:
6
- metadata.gz: aea35622e25f8fd0cf84cc3115ce15b524440e698f516ebbd89188fa56d30c098dcc8711d61cee4b07aa7b77f935091222c2b0ff71465480d0e9cf76530810f5
7
- data.tar.gz: 88a2613d9487be0b11e0da90bef805abc5fc1a7ca0012a97b32d5aa62540f382cc91e38b6714e4279bdba319475b323126625b4cf092e037742f30ba1c4ee22a
6
+ metadata.gz: f40ba2d34b2340c12d69812d8554a942dac423a2e5e764ac15d58cf644f978eb937e2104bef8ce288dd79f0875d8bda92998ad692e50ebbe6ff0f5cb6d130ec3
7
+ data.tar.gz: f12bfbd92bfe9101292598c69ceff2beb81bd92838b975f39d61e15e16fc6268ffb525896f5983186bced970e3e63d4e6a27cbd5d63cad91943a264236b184b4
data/README.md CHANGED
@@ -1,114 +1,126 @@
1
- # Help Parser VIII: Helpland
1
+ # Help Parser IX: Revelations
2
2
 
3
- * [VERSION 8.1.221206](https://github.com/carlosjhr64/help_parser/releases)
3
+ * [VERSION 9.0.240926](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
 
7
7
  ## DESCRIPTION:
8
8
 
9
- Welcome to Help Parser!
10
- Do you have your help text?
11
- Let's parse!
9
+ Options parsing based on your help text.
12
10
 
13
11
  ## INSTALL:
14
-
15
12
  ```console
16
13
  $ gem install help_parser
17
14
  ```
18
-
19
15
  ## SYNOPSIS:
20
16
  <!-- The following PREVIEW has been approved for ALL PROGRAMMERS by CarlosJHR64.
21
17
  For the README validator that checks against me lying....
22
18
  ```ruby
23
- unless File.basename($PROGRAM_NAME) == 'party'
19
+ unless File.basename($PROGRAM_NAME) == 'revelations'
24
20
  # For example's sake say
25
- $PROGRAM_NAME = 'party'
21
+ $PROGRAM_NAME = 'revelations'
26
22
  # and ARGV is
27
- ARGV.concat ["-\-age", "-\-date=2020-09-07", 'touch', 'that']
28
- # and proceed as if run as:
29
- # awesome -\-name=Doe -\-value a b c
23
+ ARGV.concat ['1,2,3','4,5,6']
24
+ # and proceed as if run as: `revelations 1,2,3 4,5,6`
30
25
  end
31
26
  ```
32
27
  The following gem has been rated
33
- | M | Mature |
34
- -->
35
-
28
+ | M | Mature | -->
36
29
  ```ruby
37
- require "help_parser"
30
+ #!/usr/bin/env ruby
31
+ require 'help_parser'
38
32
 
39
- HELP = <<-HELP
33
+ VERSION = '1.2.3'
34
+ OPTIONS = HelpParser[VERSION, <<-HELP]
40
35
  # <= Hash here, parser skips
41
- # HelpParser: Party command example #
36
+ # HelpParser command example #
37
+
38
+ One can write any notes on the help text as long as
39
+ it does not start with a space or a "Keyword:".
40
+
42
41
  Usage:
43
- party :options+ [<args>+]
44
- party [:alternate] <arg=FLOAT>
45
- party literal <arg1=WORD> <arg2=WORD>
42
+ revelations :options+ [<arg>]
43
+ revelations :alternate <args=FLOAT>+
44
+ revelations literal <arg1=WORD> <arg2=WORD>
45
+ revelations <numbers=CSVI>+
46
+
47
+ The ":keyword" refers to a flag in defined in the "Keyword:" section.
48
+ A "[...]" is an optional part of the usage.
49
+ A "+" means one or more of it is allowed.
50
+
46
51
  Options:
47
- -v --version \t Give version and quit
48
- -h --help \t Give help and quit
49
- -s --long \t Short long synonyms
50
- --touch that \t Defaulted
51
- --date=DATE \t Typed
52
- --age=INTEGER 80 \t Typed and Defaulted
53
- -a --all=YN y \t Short, long, typed, and defaulted
54
- --to_be
55
- --not_to_be
52
+ -v --version \t Give version and quit
53
+ -h --help \t Give help and quit
54
+ --verbose \t Set $VERBOSE true
55
+ --debug \t Set $DEBUG true
56
+
57
+ The above(version, help, verbose, debug) are built-in options.
58
+ The tab, "\t", splits the comment from the flags.
59
+ On its own a flag is set to true, else it's nil.
60
+ But you can also set a long flag value to a string as allowed by its type.
61
+
62
+ -a --all=YN y \t Short, long, typed, and defaulted
63
+
64
+ OPTIONS.all at first is nil. If set without a value, it's set to "y"
65
+ One can set it to to either "n" of "y" as allowed by YN(see below under Types:).
66
+
67
+ --stop=NUMBER \t Typed
68
+ --start=NUMBER 0 \t Typed and defaulted
69
+
56
70
  --rain
57
71
  --water
58
72
  --wet
73
+
74
+ --to_be
75
+ --not_to_be
76
+
59
77
  Exclusive:
60
- to_be not_to_be \t Tells parser these are mutually exclusive keys
78
+ to_be not_to_be \t Tells parser these are mutually exclusive keys
61
79
  Inclusive:
62
- date age \t Tells parser any of these must include all of these
80
+ start stop \t Tells parser any of these must include all of these
63
81
  Conditional:
64
- rain water wet \t Tells parser if first then all
82
+ rain water wet \t Tells parser if first then all
83
+ \t Note how one can continue the comment as needed
84
+
65
85
  Alternate:
66
- --invoke
67
- --wut
86
+ --sum
87
+ --multiply
88
+
68
89
  Types:
69
90
  WORD /^[A-Za-z]+$/
70
- DATE /^\\d\\d\\d\\d-\\d\\d-\\d\\d$/
71
- INTEGER /^\\d+$/
91
+ NUMBER /^\\d+$/
72
92
  FLOAT /^\\d+\\.\\d+$/
73
93
  YN /^[YNyn]$/
94
+ CSVI /^\\d+(,\\d+)*$/
74
95
  # <= Hash here, parser breaks out
75
- # Notes #
76
- I wouldn't touch that!
96
+ And now one can freely write whatever....
77
97
  HELP
78
98
 
79
- VERSION = "1.2.3"
80
-
81
- OPTIONS = HelpParser[VERSION, HELP] #~> HelpParser
82
-
83
- # Macros:
84
- HelpParser.strings?(:args) # for OPTIONS.args : Array(String) | Nil
85
- HelpParser.int?(:age) # for OPTIONS.age? : Integer | Nil
86
- HelpParser.float(:arg) # for options.arg : Float
87
- HelpParser.string(:arg1, :arg2, :arg3) # for OPTIONS.arg1, etc : String
88
- #=> [:arg1, :arg2, :arg3]
89
-
90
- ## If run as:
91
- ## party --age --date=2020-09-07 touch that
92
- OPTIONS.age? #=> 80
93
- OPTIONS.age?.class #=> Integer
94
- OPTIONS.args? #=> ["touch", "that"]
95
- OPTIONS.args?.class #=> Array
96
- OPTIONS.arg? and OPTIONS.arg #=> false
97
- OPTIONS.arg?.class #=> FalseClass
98
- ```
99
+ # Tell HelpParser how to remap the string values:
100
+ HelpParser.integer(:stop, :start) # HelpParser.map(:stop, :start, map: :to_i)
101
+ HelpParser.float(:args) # HelpParser.map(:args, map: :to_f)
102
+ # Also available: HelpParser.rational(*name) = HelpParser.map(*names, map: to_r)
99
103
 
104
+ # Tell HelpParser how to split the string values:
105
+ HelpParser.split(:numbers, sep: ',', map: :to_i)
106
+ # Also available: HelpParser.csv(*name) = HelpParser.split(*names)
107
+
108
+ # If one runs `revelations 1,2,3 4,5,6`, then:
109
+ OPTIONS.numbers #=> [[1, 2, 3], [4, 5, 6]]
110
+ # And everything else is unset:
111
+ OPTIONS.stop #=> nil
112
+ ```
100
113
  ## Features
101
114
 
102
- * `$DEBUG=true` on --debug
103
- * `$VERBOSE=true` on --verbose
104
115
  * -h and --help simultaneously will check help string for errors
116
+ * `ARGV` setup for `ARGF` when one of the "Types:" given is "ARGF"
105
117
  * `HelpParser::REDTTY[msg]` will red color output `msg` to `$stderr`.
106
118
 
107
119
  ## LICENSE:
108
120
 
109
121
  (The MIT License)
110
122
 
111
- Copyright (c) 2022 CarlosJHR64
123
+ Copyright (c) 2024 CarlosJHR64
112
124
 
113
125
  Permission is hereby granted, free of charge, to any person obtaining
114
126
  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,26 +3,35 @@ 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.
@@ -37,7 +46,7 @@ module HelpParser
37
46
  end
38
47
  end
39
48
  end
40
- typos = @hash.keys.select{|k|k.is_a? String and not dict[k]}
49
+ typos = @hash.keys.select{_1.is_a?(String) && !dict[_1]}
41
50
  raise UsageError, MSG[UNRECOGNIZED, typos] unless typos.empty?
42
51
 
43
52
  raise UsageError, MATCH_USAGE
@@ -46,26 +55,25 @@ module HelpParser
46
55
  def types
47
56
  t2r,k2t = HelpParser.t2r(@specs),HelpParser.k2t(@specs)
48
57
  @hash.each do |key,value|
49
- next unless key.is_a?(String)
50
- if type = k2t[key]
51
- regex = t2r[type]
52
- case value
53
- when String
54
- unless value=~regex
55
- raise UsageError, "--#{key}=#{value} !~ #{type}=#{regex.inspect}"
56
- end
57
- when Array
58
- value.each do |string|
59
- unless string=~regex
60
- raise UsageError,
61
- "--#{key}=#{string} !~ #{type}=#{regex.inspect}"
62
- 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}"
63
70
  end
64
- else
65
- raise UsageError, "--#{key} !~ #{type}=#{regex.inspect}"
66
71
  end
72
+ else
73
+ raise UsageError, "--#{key} !~ #{type}=#{regex.inspect}"
67
74
  end
68
75
  end
76
+ k2t
69
77
  end
70
78
 
71
79
  def pad
@@ -79,12 +87,12 @@ module HelpParser
79
87
  if second[0]=='-'
80
88
  i = second.index('=') || 0
81
89
  short,long = first[1],second[2..(i-1)]
82
- if @hash.has_key?(short)
83
- if @hash.has_key?(long)
90
+ if @hash.key?(short)
91
+ if @hash.key?(long)
84
92
  raise UsageError, MSG[REDUNDANT, short, long]
85
93
  end
86
- @hash[long] = (default.nil?) ? true : default
87
- elsif value = @hash[long]
94
+ @hash[long] = default.nil? ? true : default
95
+ elsif (value=@hash[long])
88
96
  @hash[short] = true
89
97
  if value==true && !default.nil?
90
98
  @hash.delete(long) # ArgvHash reset
@@ -117,7 +125,7 @@ module HelpParser
117
125
  # OK, NEVERMIND!
118
126
  end
119
127
  next
120
- elsif m=FLAG_GROUP.match(token)
128
+ elsif (m=FLAG_GROUP.match token)
121
129
  group,plus = m[:k],m[:p]
122
130
  key = keys[i]
123
131
  raise NoMatch unless key.is_a? String
@@ -126,11 +134,11 @@ module HelpParser
126
134
  unless plus.nil?
127
135
  loop do
128
136
  key = keys[i+1]
129
- break unless key.is_a?(String) and list.include?(key)
137
+ break unless key.is_a?(String) && list.include?(key)
130
138
  i+=1
131
139
  end
132
140
  end
133
- elsif m=VARIABLE.match(token)
141
+ elsif (m=VARIABLE.match(token))
134
142
  key = keys[i]
135
143
  raise NoMatch unless key.is_a?(Integer)
136
144
  variable,plus = m[:k],m[:p]
@@ -153,7 +161,7 @@ module HelpParser
153
161
  end
154
162
  i += 1
155
163
  end
156
- return i
164
+ i
157
165
  end
158
166
  end
159
167
  end
@@ -1,6 +1,6 @@
1
1
  module HelpParser
2
- VSN = ['v','version']
3
- 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
@@ -16,19 +16,19 @@ module HelpParser
16
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*)$/
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
@@ -3,14 +3,14 @@ module HelpParser
3
3
  def self.k2t(specs)
4
4
  k2t = NoDupHash.new
5
5
  # If specs section is not a RESERVED section, it's an options list.
6
- tokens = specs.select{|k,v| k==USAGE or not RESERVED.include?(k)}
6
+ tokens = specs.select{|k,_| k==USAGE or !RESERVED.include?(k)}
7
7
  # Tokens associating a key to a type.
8
8
  .values.flatten.select{|v|v.include?('=')}
9
9
  tokens.each do |token|
10
- if match = VARIABLE.match(token) || LONG.match(token)
10
+ if (match = VARIABLE.match(token) || LONG.match(token))
11
11
  name, type = match[:k], match[:t]
12
- if _=k2t[name]
13
- raise HelpError, MSG[INCONSISTENT,name,type,_] unless type==_
12
+ if (_=k2t[name])
13
+ raise HelpError, MSG[INCONSISTENT,name,type,_] unless type==_
14
14
  else
15
15
  k2t[name] = type
16
16
  end
@@ -19,12 +19,12 @@ module HelpParser
19
19
  raise SoftwareError, MSG[UNEXPECTED,token]
20
20
  end
21
21
  end
22
- return k2t
22
+ k2t
23
23
  end
24
24
 
25
25
  # t2r is an acronym for "type to regexp"
26
26
  def self.t2r(specs)
27
- if types = specs[TYPES]
27
+ if (types=specs[TYPES])
28
28
  t2r = NoDupHash.new
29
29
  types.each do |pair|
30
30
  type, pattern = *pair
@@ -36,6 +36,6 @@ module HelpParser
36
36
  end
37
37
  return t2r
38
38
  end
39
- return nil
39
+ nil
40
40
  end
41
41
  end
@@ -1,183 +1,29 @@
1
1
  module HelpParser
2
- def self.string(*names)
2
+ def self.map(*names, map:)
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
14
- end
15
- end
16
-
17
- def self.string?(*names)
18
- 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
30
- end
31
- end
32
-
33
- def self.strings(*names)
34
- 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
45
- end
46
- end
47
-
48
- def self.strings?(*names)
49
- 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
61
- end
62
- end
63
-
64
- def self.float(*names)
65
- names.each do |name|
66
- code = <<-CODE
67
- class Options
68
- def #{name}
69
- @hash['#{name}']&.to_f or raise
70
- rescue
71
- raise UsageError, MSG[NOT_FLOAT,'#{name}']
72
- end
73
- end
74
- CODE
75
- eval code
76
- end
77
- end
78
-
79
- def self.float?(*names)
80
- names.each do |name|
81
- code = <<-CODE
82
- class Options
83
- def #{name}?
84
- @hash['#{name}']&.to_f
85
- rescue
86
- raise UsageError, MSG[NOT_FLOAT,'#{name}']
87
- end
88
- end
89
- CODE
90
- eval code
4
+ Options.instance_eval do
5
+ define_method(name) do
6
+ v = @hash[name.to_s] and (v.is_a?(Array) ?
7
+ v.map(&map) :
8
+ v.method(map).call)
9
+ end
10
+ end
91
11
  end
92
12
  end
13
+ def self.integer(*names) = HelpParser.map(*names, map: :to_i)
14
+ def self.float(*names) = HelpParser.map(*names, map: :to_f)
15
+ def self.rational(*names) = HelpParser.map(*names, map: :to_r)
93
16
 
94
- def self.floats(*names)
17
+ def self.split(*names, sep:, map:)
95
18
  names.each do |name|
96
- code = <<-CODE
97
- class Options
98
- def #{name}
99
- @hash['#{name}'].map{_1.to_f}
100
- rescue
101
- raise UsageError, MSG[#{NOT_FLOATS},'#{name}']
102
- end
103
- end
104
- CODE
105
- eval code
106
- end
107
- end
108
-
109
- def self.floats?(*names)
110
- names.each do |name|
111
- code = <<-CODE
112
- class Options
113
- def #{name}?
114
- @hash['#{name}']&.map{_1.to_f}
115
- rescue
116
- raise UsageError, MSG[NOT_FLOATS,'#{name}']
117
- end
118
- end
119
- CODE
120
- eval code
121
- end
122
- end
123
-
124
- def self.int(*names)
125
- names.each do |name|
126
- code = <<-CODE
127
- class Options
128
- def #{name}
129
- @hash['#{name}']&.to_i or raise
130
- rescue
131
- raise UsageError, MSG[NOT_INTEGER,'#{name}']
132
- end
133
- end
134
- CODE
135
- eval code
136
- end
137
- end
138
-
139
- def self.int?(*names)
140
- names.each do |name|
141
- code = <<-CODE
142
- class Options
143
- def #{name}?
144
- @hash['#{name}']&.to_i
145
- rescue
146
- raise UsageError, MSG[NOT_INTEGER,'#{name}']
147
- end
148
- end
149
- CODE
150
- eval code
151
- end
152
- end
153
-
154
- def self.ints(*names)
155
- names.each do |name|
156
- code = <<-CODE
157
- class Options
158
- def #{name}
159
- @hash['#{name}'].map{_1.to_i}
160
- rescue
161
- raise UsageError, MSG[NOT_INTEGERS,'#{name}']
162
- end
163
- end
164
- CODE
165
- eval code
166
- end
167
- end
168
-
169
- def self.ints?(*names)
170
- names.each do |name|
171
- code = <<-CODE
172
- class Options
173
- def #{name}?
174
- @hash['#{name}']&.map{_1.to_i}
175
- rescue
176
- raise UsageError, MSG[NOT_INTEGERS,'#{name}']
177
- end
178
- end
179
- CODE
180
- eval code
19
+ Options.instance_eval do
20
+ define_method(name) do
21
+ v = @hash[name.to_s] and (v.is_a?(Array) ?
22
+ v.map{_1.split(sep).map(&map)} :
23
+ v.split(sep).map(&map))
24
+ end
25
+ end
181
26
  end
182
27
  end
28
+ def self.csv(*names) = HelpParser.split(*names, sep: ',', map: :strip)
183
29
  end
@@ -2,34 +2,35 @@ module HelpParser
2
2
  class Options
3
3
  def initialize(version, help, argv)
4
4
  @hash = HelpParser.parsea(argv)
5
- if version && VSN.any?{@hash.has_key? _1}
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
+ help = String(help)
10
11
  if HLP.any?{@hash.key? _1}
11
- HelpParser.parseh(help, validate: true) if HLP.all?{@hash.key? _1}
12
+ HelpParser.parseh(help, validate: true) if HLP.all?{@hash.key? _1}
12
13
  raise HelpException, help
13
14
  end
14
15
  specs = HelpParser.parseh(help)
15
16
  Completion.new(@hash, specs)
16
- if exclusive=specs[EXCLUSIVE]
17
+ if (exclusive=specs[EXCLUSIVE])
17
18
  exclusive.each do |x|
18
19
  count = x.count{@hash.key? _1}
19
20
  raise HelpParser::UsageError, MSG[EXCLUSIVE_KEYS,*x] if count > 1
20
21
  end
21
22
  end
22
- if inclusive=specs[INCLUSIVE]
23
+ if (inclusive=specs[INCLUSIVE])
23
24
  inclusive.each do |i|
24
25
  count = i.count{@hash.key? _1}
25
- unless count==0 or count==i.length
26
+ unless count.zero? || count==i.length
26
27
  raise HelpParser::UsageError, MSG[INCLUSIVE_KEYS,*i]
27
28
  end
28
29
  end
29
30
  end
30
- if conditional=specs[CONDITIONAL]
31
+ if (conditional=specs[CONDITIONAL])
31
32
  conditional.each do |c|
32
- if @hash.key? c[0] and not c.all?{@hash.key? _1}
33
+ if @hash.key?(c[0]) && !c.all?{@hash.key? _1}
33
34
  raise HelpParser::UsageError, MSG[CONDITIONAL_KEYS,*c]
34
35
  end
35
36
  end
@@ -47,17 +48,16 @@ module HelpParser
47
48
  @hash[k]
48
49
  end
49
50
 
50
- def method_missing(mthd, *args, &block)
51
- super if block or args.length > 0
52
- m = mthd.to_s
53
- case m[-1]
54
- when '?'
55
- @hash.key? m[0..-2]
56
- when '!'
57
- super
58
- else
59
- @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'
60
59
  end
60
+ m[-1]=='?' ? @hash.key?(m[0..-2].to_s) : @hash[m.to_s]
61
61
  end
62
62
  end
63
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,15 +4,18 @@ module HelpParser
4
4
  help.each_line do |line|
5
5
  line.chomp!
6
6
  next if line==''
7
- if md = SECTION_NAME.match(line)
7
+ if (md=SECTION_NAME.match(line))
8
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]==' ')
14
- spec = (index=line.rindex("\t"))? line[0,index].strip : line.strip
15
- raise HelpError, EXTRANEOUS_SPACES if validate and spec==''
13
+ next unless line[0]==' '
14
+ spec,comment = line.split("\t", 2).map(&:strip)
15
+ if spec.empty?
16
+ raise HelpError, EXTRANEOUS_SPACES if validate && comment.to_s.empty?
17
+ next
18
+ end
16
19
  case name
17
20
  when USAGE
18
21
  Validate.balanced_brackets(spec.chars) if validate
@@ -20,29 +23,31 @@ module HelpParser
20
23
  Validate.usage_tokens(tokens) if validate
21
24
  specs[USAGE].push tokens
22
25
  when TYPES
23
- if validate and not spec=~TYPE_DEF
26
+ if validate && !TYPE_DEF.match?(spec)
24
27
  raise HelpError, MSG[UNRECOGNIZED_TYPE,spec]
25
28
  end
26
29
  specs[TYPES].push spec.split(CSV)
27
30
  when *FLAG_CLUMPS # EXCLUSIVE,INCLUSIVE,CONDITIONAL,...
28
- if validate and not spec=~X_DEF
31
+ if validate && !X_DEF.match?(spec)
29
32
  raise HelpError, MSG[UNRECOGNIZED_X,spec]
30
33
  end
31
34
  specs[name].push spec.split(CSV)
32
35
  else
33
- raise HelpError, MSG[UNRECOGNIZED_OPTION,spec] if validate and
34
- not [SHORT, LONG, SHORT_LONG, SHORT_LONG_DEFAULT].any?{_1=~spec}
36
+ if validate &&
37
+ [SHORT, LONG, SHORT_LONG, SHORT_LONG_DEFAULT].none?{_1=~spec}
38
+ raise HelpError, MSG[UNRECOGNIZED_OPTION,spec]
39
+ end
35
40
  specs[name].push spec.split(CSV)
36
41
  end
37
42
  end
38
43
  end
39
44
  if validate
40
45
  Validate.usage_specs(specs)
41
- if t2r = HelpParser.t2r(specs)
46
+ if (t2r=HelpParser.t2r(specs))
42
47
  k2t = HelpParser.k2t(specs)
43
48
  Validate.k2t2r(specs, k2t, t2r)
44
49
  end
45
50
  end
46
- return specs
51
+ specs
47
52
  end
48
53
  end
@@ -5,7 +5,7 @@ module HelpParser
5
5
  # Tokens := Array(Token|Tokens)
6
6
  def self.parseu(chars)
7
7
  tokens,token = [],''
8
- while c = chars.shift
8
+ while (c=chars.shift)
9
9
  case c
10
10
  when ' ','[',']'
11
11
  unless token==''
@@ -19,6 +19,6 @@ module HelpParser
19
19
  end
20
20
  end
21
21
  tokens.push(token) unless token==''
22
- return tokens
22
+ tokens
23
23
  end
24
24
  end
@@ -3,14 +3,10 @@ module Validate
3
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)
@@ -18,7 +14,7 @@ module Validate
18
14
  tokens.flatten.each do |token|
19
15
  raise HelpError, MSG[UNRECOGNIZED_TOKEN,token] unless
20
16
  [FLAG,LITERAL,VARIABLE,FLAG_GROUP]
21
- .detect{_=token.match(_1) and words.push(_[:k])}
17
+ .detect{(_=token.match _1) && words.push(_[:k])}
22
18
  end
23
19
  words.each_with_index do |word,i|
24
20
  raise HelpError, MSG[DUP_WORD,word] unless i==words.rindex(word)
@@ -26,40 +22,47 @@ module Validate
26
22
  end
27
23
 
28
24
  def self.usage_specs(specs)
29
- flags = specs.select{|a,b| not RESERVED.include? a}.values.flatten
30
- .select{|f|f[0]=='-'}.map{|f| F2K[f]}
25
+ flags = specs.except(*RESERVED).values.flatten
26
+ .select{_1[0]=='-'}.map{F2K[_1]}
31
27
  FLAG_CLUMPS.each do |k|
32
- if a=specs[k]
33
- seen = {}
34
- a.each do |xs|
35
- k = xs.sort.join(' ').to_sym
36
- if seen[k] or not xs.length==xs.uniq.length
37
- raise HelpError, MSG[DUP_X,k]
38
- end
39
- seen[k] = true
40
- xs.each do |x|
41
- raise HelpError, MSG[UNSEEN_FLAG, x] unless flags.include?(x)
42
- 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)
43
38
  end
44
39
  end
45
40
  end
46
41
  flags.each_with_index do |flag,i|
47
42
  raise HelpError, MSG[DUP_FLAG,flag] unless i==flags.rindex(flag)
48
- end
49
- group = []
43
+ end
44
+ group,var = [],{}
50
45
  specs_usage = specs[USAGE]
51
- unless specs_usage.nil?
52
- specs_usage.flatten.each do |token|
53
- if match = token.match(FLAG_GROUP)
54
- key = match[:k]
55
- raise HelpError, MSG[UNDEFINED_SECTION,key] unless specs[key]
56
- group.push(key)
46
+ specs_usage&.flatten&.each do |token|
47
+ case token
48
+ when VARIABLE
49
+ key,bool = $~[:k],$~[:p].nil?
50
+ if var.key? key
51
+ unless var[key]==bool
52
+ raise HelpError, MSG[INCONSISTENT,"<#{key}>","<#{key}>+"]
53
+ end
54
+ else
55
+ var[key]=bool
57
56
  end
57
+ when FLAG_GROUP
58
+ key = $~[:k]
59
+ raise HelpError, MSG[UNDEFINED_SECTION,key] unless specs[key]
60
+ group.push(key)
58
61
  end
59
62
  end
60
63
  specs.each do |key,tokens|
61
- raise HelpError, MSG[MISSING_CASES,key] unless tokens.size>0
62
- next if specs_usage.nil? or RESERVED.include? key
64
+ raise HelpError, MSG[MISSING_CASES,key] if tokens.empty?
65
+ next if specs_usage.nil? || RESERVED.include?(key)
63
66
  raise HelpError, MSG[MISSING_USAGE,key] unless group.include?(key)
64
67
  end
65
68
  end
@@ -67,7 +70,7 @@ module Validate
67
70
  def self.k2t2r(specs, k2t, t2r)
68
71
  a,b = k2t.values.uniq.sort,t2r.keys.sort
69
72
  unless a==b
70
- c = (a+b).uniq.select{|x|!(a.include?(x) && b.include?(x))}
73
+ c = (a+b).uniq.reject{|x|a.include?(x) && b.include?(x)}
71
74
  raise HelpError, MSG[UNCOMPLETED_TYPES,c.join(',')]
72
75
  end
73
76
  specs.each do |section,tokens|
@@ -80,7 +83,7 @@ module Validate
80
83
  i = long_type.index('=')
81
84
  next if i.nil?
82
85
  long = long_type[2..(i-1)]
83
- type = long_type[(i+1)..-1]
86
+ type = long_type[(i+1)..]
84
87
  regex = t2r[type]
85
88
  unless regex=~default
86
89
  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.1.221206'
13
+ VERSION = '9.0.240926'
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,123 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: help_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.1.221206
4
+ version: 9.0.240926
5
5
  platform: ruby
6
6
  authors:
7
7
  - CarlosJHR64
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-06 00:00:00.000000000 Z
12
- dependencies: []
13
- description: "Welcome to Help Parser! \nDo you have your help text? \nLet's parse!\n"
11
+ date: 2024-09-26 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: '1.1'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.1.0
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.1'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.1.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: cucumber
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '9.2'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 9.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: '9.2'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 9.2.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: parser
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '3.3'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 3.3.5
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '3.3'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 3.3.5
73
+ - !ruby/object:Gem::Dependency
74
+ name: rubocop
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '1.66'
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.66.1
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.66'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 1.66.1
93
+ - !ruby/object:Gem::Dependency
94
+ name: test-unit
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '3.6'
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 3.6.2
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '3.6'
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 3.6.2
113
+ description: 'Options parsing based on your help text.
114
+
115
+ '
14
116
  email: carlosjhr64@gmail.com
15
117
  executables: []
16
118
  extensions: []
17
119
  extra_rdoc_files: []
18
120
  files:
19
- - LICENSE
20
121
  - README.md
21
122
  - lib/help_parser.rb
22
123
  - lib/help_parser/aliases.rb
@@ -49,9 +150,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
150
  - !ruby/object:Gem::Version
50
151
  version: '0'
51
152
  requirements:
52
- - 'ruby: ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [aarch64-linux]'
53
- rubygems_version: 3.3.7
153
+ - 'git: 2.30'
154
+ - 'ruby: 3.3'
155
+ rubygems_version: 3.5.19
54
156
  signing_key:
55
157
  specification_version: 4
56
- summary: Welcome to Help Parser! Do you have your help text? Let's parse!
158
+ summary: Options parsing based on your help text.
57
159
  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.