help_parser 8.1.221206 → 9.0.240926

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: 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.