help_parser 3.0.1 → 4.0.0

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
  SHA1:
3
- metadata.gz: 33ad117bb5e036d38e871d90ea4a6434c1c004f1
4
- data.tar.gz: 2e94e3e19e4c763bbf67ae4dc958b4d6dcfecf8c
3
+ metadata.gz: 2b2c0821ccb920314039d7e8f92851e7be14382a
4
+ data.tar.gz: f5184113ef5d80ae592c2f923d895527d078d954
5
5
  SHA512:
6
- metadata.gz: 05bda5c57650c34545975a91bce8b87218dde3a7d52c23ccaf539bb037f8de7560071daedb8bf564bb1e30d68121438bec43aa7ea853c161da0f1837c1c5cc13
7
- data.tar.gz: c4e7bc126d53c3df6bb3f9c9c83b1882da560536f442dca83c040c87ee7088a33b4983ab095873bf1ac77d9ea711be33a6808fae93649dcbddf6c19a11fe14e0
6
+ metadata.gz: 1afa018ea4504b0dfc00a443e26dda59338f4d7aa0f68f64b72b4c61c1d05d5558a3be3da9427bb07e57152da2fd6c2c77caf2e37d62729a5e3f905df18ec711
7
+ data.tar.gz: 70bb2be1242c6c094bba0b61e209f7028a8cb47c63f30ed9a9c5e3088d19f23107aa55d872c89bd153dfe73201a32936c967809de63dc16c0ba4ec8e10c4f2ea
data/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # HelpParser
2
+ ## Version 4!!!
3
+
4
+ * [github](https://www.github.com/carlosjhr64/help_parser)
5
+ * [rubygems](https://rubygems.org/gems/help_parser)
6
+
7
+ ## DESCRIPTION:
8
+ HelpParser - Parses your help text to define your command line options.
9
+
10
+ ## INSTALL:
11
+
12
+ $ sudo gem install help_parser
13
+
14
+ ## LICENSE:
15
+ [MIT](https://en.wikipedia.org/wiki/MIT_License) Copyright (c) 2017 CarlosJHR64
16
+
17
+ ## MINIMAL
18
+
19
+ #!/usr/bin/env ruby
20
+ require 'help_parser'
21
+
22
+ OPTIONS = HelpParser.new
23
+ p OPTIONS._hash
24
+
25
+ ## SYNOPSIS:
26
+
27
+ #!/usr/bin/env ruby
28
+ require 'help_parser'
29
+
30
+ OPTIONS = HelpParser.new('1.2.3', <<-HELP)
31
+ Usage:
32
+ synopsis [:options] <x> <y>
33
+ Options:
34
+ --verbose
35
+ HELP
36
+
37
+ # Existentials via missing "?" methods
38
+ puts 'Whats x * y?' if OPTIONS.verbose?
39
+
40
+ # Values via missing methods.
41
+ puts OPTIONS.x.to_f * OPTIONS.y.to_f
42
+
43
+ ## MORE:
44
+
45
+ #!/usr/bin/env ruby
46
+ require 'help_parser'
47
+
48
+ # Any version string, does not have to be semantic versioning.
49
+ VERSION = '1.2.3'
50
+
51
+ HELP = <<-HELP
52
+ ### more ###########################################
53
+ # Lines starting with the pound sign are comments.
54
+ # HelpPaser parses the help text by sections
55
+ # with a heading of the form `<Section Name>:`.
56
+ # The Usage section gives the allowed forms of the
57
+ # command. Keys, arguments, and selectors in
58
+ # square bracket are optional and can be nested.
59
+ # A plus sign, `+`, marks the associated token as
60
+ # taking multiple values. Note that variable
61
+ # arguments have the form `<name>`.
62
+ ####################################################
63
+ Usage:
64
+ more --wut <price> <count> [<name>]
65
+ more [:options] <args>+
66
+ ####################################################
67
+ # Tipically you'll have an `Options:` section, but
68
+ # you can give the section any name.
69
+ # An `Options:` section should match an `:options`
70
+ # selector in at least one of your usages.
71
+ # Assigning values to keys via the command line
72
+ # is done in the form `--key=value`.
73
+ ####################################################
74
+ Options:
75
+ -h --help \tTab marks comment
76
+ -v --version \tShort and long synonyms
77
+ --val 5.0 \tProvided default to long
78
+ --number \tLike --number=3
79
+ --usage
80
+ ####################################################
81
+ # The `Defaults:` section can give default values
82
+ # to arguments and options.
83
+ # So for options you have a choice of where to set
84
+ # your defaults, but for arguments, it's done here.
85
+ ####################################################
86
+ Defaults:
87
+ name Carlos \tDefault for name
88
+ ####################################################
89
+ # HelpPaser has two automatic types, `Float` and
90
+ # `Int`, which will convert values from String to
91
+ # Floats and Integers repectively.
92
+ # You can also provide you own custom validation
93
+ # with regular expressions.
94
+ ####################################################
95
+ Types:
96
+ Float --val price \tPredefined Float and Int
97
+ Int --number count
98
+ ^[A-Z][a-z]+$ name \tUser defined type
99
+ ####################################################
100
+ # The start of a `Notes:` section tells HelpParser
101
+ # to break out of it's parsing.
102
+ # Anything else written after this heading is of
103
+ # no consequence to HelpParser.
104
+ ####################################################
105
+ Notes:
106
+ Stuff the help parser will ignore.
107
+ HELP
108
+
109
+ OPTIONS = HelpParser.new(VERSION, HELP)
110
+
111
+ # Short and long values via missing methods.
112
+ # Short and long existentials(booleans) via missing `?` methods.
113
+ # To avoid name collisions with command line argument and options,
114
+ # the HelpParser object was subclassed from BasicObject.
115
+ if OPTIONS.wut?
116
+ puts "Count: #{_=OPTIONS.count}\t#{_.class}"
117
+ puts "Price: #{_=OPTIONS.price}\t#{_.class}"
118
+ puts "Name: #{_=OPTIONS.name}\t#{_.class}"
119
+ else
120
+ puts "Args: #{OPTIONS.args.join(', ')}"
121
+ end
122
+ puts "Name: #{_=OPTIONS.name}\t#{_.class}"
123
+ puts "Val: #{_=OPTIONS.val}\t#{_.class}"
124
+ puts "Number: #{_=OPTIONS.number}\t#{_.class}"
125
+
126
+ # The objects's hash data method is `_hash`.
127
+ # You can also get the help text parsed data from `_usage._hash`
128
+ puts '###'
129
+ if OPTIONS.usage?
130
+ p OPTIONS._usage._hash
131
+ else
132
+ p OPTIONS._hash
133
+ end
134
+
135
+ ## WHY?
136
+ Even with the really nice options parsers available,
137
+ for quick little scripts
138
+ I kept writing stuff like `ARGV.include?(key)`
139
+ or `options=ARGV.select{|a|a[0]=='-'}`.
140
+ Just to have to go back and reimplement with a proper options parser
141
+ when the little script ends up being useful and grows into something bigger.
142
+ And I was doing this after I had written my own previous versions of HelpParser.
143
+ So now I should have no excuse to just use HelpParser. Always:
144
+
145
+ require 'HelpParser'
146
+ OPTIONS = HelpParser.new # DONE!
147
+
148
+ Can't be any simpler.
149
+
150
+ As the script evolves... add version.. add help text..
151
+ no need to rework the code for not having started out with a proper options parser.
data/lib/help_parser.rb CHANGED
@@ -1,9 +1,9 @@
1
- require 'help_parser/version'
2
1
  require 'help_parser/constants'
3
2
  require 'help_parser/pattern'
4
3
  require 'help_parser/usage'
5
4
  require 'help_parser/help_parser'
6
5
  HelpParser = HELP_PARSER::HelpParser
6
+ HELP_PARSER::VERSION = '4.0.0'
7
7
 
8
8
  # Requires:
9
9
  #`ruby`
@@ -1,30 +1,75 @@
1
1
  module HELP_PARSER # Constants
2
2
 
3
- NOTES,USAGE,TYPES = 'notes','usage','types'
4
- Z,M,E,P,Q,I = '','-','=','[',']',''
5
- H,LH,V,LV = '-h','--help','-v','--version'
3
+ ### OPTIONS LANGUAGE ###
6
4
 
7
- S,SNS = /\s/,/\s*\n\s*/
5
+ H,LH = :h,:help
6
+ V,LV = :v,:version
7
+
8
+ C,Z,M,E,P,Q,I = '#','-','-','=','[',']',''
9
+ MM = '--'
10
+
11
+ ### HELP TEXT KEYS ###
12
+
13
+ NOTES,USAGE,TYPES,DEFAULTS = 'notes','usage','types','defaults'
14
+
15
+ ### ERROR COLOR CODES ###
8
16
 
9
17
  COLOR = [
10
18
  "\e[0m", # no color
11
19
  "\e[1;31m", # red
12
20
  ]
13
21
 
14
- FLOAT,INT = 'Float', 'Int'
22
+ ### TYPE MAPPINGS ###
23
+
24
+ FLOAT,INT = :Float, :Int
15
25
  Types = {
16
26
  INT => /^[+-]?\d+$/,
17
27
  FLOAT => /^[+-]?\d+\.\d+$/,
18
28
  }
19
29
 
20
- # Errors
21
- class UsageError < StandardError
30
+ ### PATTERNS ###
31
+
32
+ S = /\s/ # space
33
+ SNS = /\s*\n\s*/
34
+ KEY = /^[-][-]?(.*)$/ #
35
+ SPS = /\s+/ # spaces
36
+ MNS = /^--?/
37
+ ERROR_KEY = /:\s+(\S+)/
38
+ COMMENTARY = /\s*\t.*$/ # tab marks start of field comment
39
+
40
+ SELECTION = /^(\w+):$/
41
+ SELECTION_P = /^:(.*?)([+]?)$/
42
+
43
+ VARIABLE = /^<(.*)>$/
44
+ VARIABLE_P = /^<(.*)>([+])?$/
45
+
46
+ ### Error Messages ###
47
+
48
+ MATCH_USAGE = 'Please match usage!'
49
+ DUP_KEY = 'Duplicate Key: '
50
+ ILLEGAL_KEY = 'Illegal Key: '
51
+ SECTION_REDEFINITION = 'Section Redefinition: '
52
+ UNBALANCE_BRACKETS = 'Unbalanced Brackets in Usage: '
53
+ MISSING_MINUS = 'Missing Minus For Flag: '
54
+ NOT_A_FLAG = 'No Minus For Types Or Defaults: '
55
+
56
+ ### Errors ###
57
+
58
+ class UsageError < RuntimeError
22
59
  end
23
- class HelpFormatError < StandardError
60
+ class HelpError < RuntimeError
24
61
  end
25
- class NoMatch < StandardError
62
+ class NoMatch < RuntimeError
26
63
  end
27
- class SoftwareError < StandardError
64
+
65
+ ### REFINEMENTS ###
66
+
67
+ module Refinements
68
+ refine Array do
69
+ def detect_duplicate
70
+ self.detect{|_|self.rindex(_) != self.index(_)}
71
+ end
72
+ end
28
73
  end
29
- SOFTWARE_ERROR_MSG = 'please submit a bug report'
74
+
30
75
  end
@@ -1,71 +1,99 @@
1
1
  module HELP_PARSER
2
- # HelpParser Hash will act as if a GoLang map[string]string.
3
- class HelpParser < Hash
4
- def consume(argv)
5
- n = 0
6
- while a = argv.shift
2
+
3
+ # HelpParser should not alter argv
4
+ class HelpParser < BasicObject
5
+ USAGE_ERROR = lambda do |msg|
6
+ ::Kernel::raise ::HELP_PARSER::UsageError,msg
7
+ end
8
+
9
+ Ms,Es = ::HELP_PARSER::M.to_sym, ::HELP_PARSER::E.to_sym
10
+ VALIDATE_KEY = lambda do |hsh,k|
11
+ USAGE_ERROR[DUP_KEY+k.to_s] if hsh.key?(k)
12
+ USAGE_ERROR[ILLEGAL_KEY+k.to_s] if Ms==k or Es==k
13
+ end
14
+
15
+ PARSE_ARGV = lambda do |argv|
16
+ hsh,n,i = {},0,0
17
+ argv.each do |a|
7
18
  if a[0]==M # '-'
8
- if a==M
9
- self[M] = argv
10
- break # end of parse
19
+ break if a==M
20
+ if a[1]==M
21
+ s,v = a.split(E,2) # split by '='
22
+ k = s[2..-1].to_sym
23
+ VALIDATE_KEY[hsh,k]
24
+ hsh[k] = (v)? v : true
11
25
  else
12
- if a[1]==M
13
- k,v = a.split(E,2) # split by '='
14
- self[k] = (v)? v : Z # ''
15
- else
16
- # set each flag
17
- a[1..-1].chars.each{|c|self[M+c]=Z}
26
+ # set each flag
27
+ a[1..-1].chars.each do |c|
28
+ k = c.to_sym
29
+ VALIDATE_KEY[hsh,k]
30
+ hsh[k]=true
18
31
  end
19
32
  end
20
33
  else
21
34
  # the nth argument
22
- self[n.to_s] = a
35
+ hsh[n] = a
23
36
  n+=1
24
37
  end
38
+ i+=1
25
39
  end
40
+ return hsh
26
41
  end
27
42
 
28
43
  def initialize(version=nil, help=nil, argv=nil)
29
- consume (argv.nil?)? [File.basename($0)]+ARGV : argv
30
- if version and (self[V] or self[LV]) # -v or --version
31
- puts version
32
- exit(0)
33
- end
34
- if help
35
- if self[H] or self[LH] # -h or --help
36
- puts help
37
- exit(0)
44
+ begin
45
+ hsh = PARSE_ARGV[(argv.nil?)? [::File.basename($0)]+::ARGV : argv]
46
+ if version and (hsh.key?(V) or hsh.key?(LV)) # -v or --version
47
+ $stdout.puts version
48
+ ::Kernel::exit(0)
49
+ end
50
+ if help
51
+ if hsh.key?(H) or hsh.key?(LH) # -h or --help
52
+ $stdout.puts help
53
+ ::Kernel::exit(0)
54
+ end
55
+ @usage = ::HELP_PARSER::Usage.new(help).validate(hsh)
38
56
  end
39
- begin
40
- Usage.new(help).validate(self)
41
- rescue UsageError
42
- $stderr.puts COLOR[1]+$!.message+COLOR[0]
57
+ rescue ::HELP_PARSER::UsageError => e
58
+ $stderr.puts COLOR[1]+e.message+COLOR[0]
59
+ $stderr.puts help if help
60
+ ::Kernel::exit(64) # Usage
61
+ rescue ::HELP_PARSER::HelpError => e
62
+ msg = e.message
63
+ $stderr.puts COLOR[1]+msg+COLOR[0]
64
+ if ERROR_KEY =~ msg
65
+ $stderr.puts help.gsub($1,COLOR[1]+$1+COLOR[0])
66
+ else
43
67
  $stderr.puts help
44
- exit(64) # Usage
45
68
  end
69
+ ::Kernel::exit(78) # Config
46
70
  end
71
+ @hash = hsh.freeze
72
+ end
73
+
74
+ def [](k)
75
+ @hash[k]
76
+ end
77
+
78
+ def _hash
79
+ @hash
80
+ end
81
+
82
+ def _usage
83
+ @usage
47
84
  end
48
85
 
49
- # obj.m? :: !obj['--m'].nil?
50
- # obj.method! :: obj['--method']
51
- # obj.method :: obj['method']
52
86
  def method_missing(mthd, *args, &block)
53
- if args.length==0
54
- m=mthd.to_s
55
- case m[-1]
56
- when '?'
57
- if m.length > 2
58
- return !self[M+M+m[0..-2]].nil?
59
- else
60
- return !self[M+m[0]].nil?
61
- end
62
- when '!'
63
- return self[M+M+m[0..-2]]
64
- else
65
- return self[m]
66
- end
87
+ super if block or args.length > 0
88
+ m = mthd.to_s
89
+ case m[-1]
90
+ when '?'
91
+ @hash.key? m[0..-2].to_sym
92
+ when '!'
93
+ super
94
+ else
95
+ @hash[mthd]
67
96
  end
68
- super
69
97
  end
70
98
  end
71
99
  end
@@ -1,46 +1,121 @@
1
1
  module HELP_PARSER
2
2
  class Pattern
3
+
4
+ TYPE_MAP = lambda do |type,values,&block|
5
+ case type
6
+ when INT
7
+ case values
8
+ when Array
9
+ block.call values.map{|v|v.to_i}
10
+ when String
11
+ block.call values.to_i
12
+ end
13
+ when FLOAT
14
+ case values
15
+ when Array
16
+ block.call values.map{|v|v.to_f}
17
+ when String
18
+ block.call values.to_f
19
+ end
20
+ end
21
+ end
22
+
23
+ TYPE_VALIDATE = lambda do |values,tx,msg|
24
+ [*values].each do |value|
25
+ unless tx.match(value)
26
+ raise UsageError, sprintf(msg,value)
27
+ end
28
+ end
29
+ end
30
+
3
31
  def initialize(options,usage)
4
32
  @options,@usage = options,usage
5
33
  @keys = options.keys
6
34
  @cache = {}
7
35
  end
8
36
 
37
+ def types
38
+ @usage[TYPES].each do |type,*words|
39
+ tx = Types[type] || Regexp.new(type.to_s)
40
+ words.each do |word|
41
+ values = @options[word]
42
+ next if values.nil?
43
+ TYPE_VALIDATE[values,tx,"#{word}: %s !~ /#{type}/"]
44
+ TYPE_MAP.call(type,values){|m| @options[word]=m}
45
+ end
46
+ end
47
+ end
48
+
49
+ def pad
50
+ @cache.each{|k,v|@options[k]=v}
51
+ @usage.each do |name,words|
52
+ next if [USAGE,TYPES,DEFAULTS].include?(name)
53
+ words.each do |word|
54
+ next unless word.length==2
55
+ k,v = word
56
+ if v.class==Symbol
57
+ # Synonyms
58
+ if @options[k].nil?
59
+ @options[k]=@options[v] if @options.key?(v)
60
+ else
61
+ @options[v]=@options[k] unless @options.key?(v)
62
+ end
63
+ else
64
+ # Default
65
+ @options[k]=v unless @options.key?(k)
66
+ end
67
+ end
68
+ end
69
+ if @usage.key?(DEFAULTS)
70
+ @usage[DEFAULTS].each do |words|
71
+ *ks,v = words
72
+ ks.each do |k|
73
+ @options[k]=v.to_s unless @options.key?(k)
74
+ end
75
+ end
76
+ end
77
+ end
78
+
9
79
  def _matches(pattern, i)
10
80
  pattern.each do |token|
81
+ key = @keys[i] # Might be nil, but need to proceed...
11
82
  case token
12
- when Array # it's optional
83
+ when Array # it's optional, so a key.nil? might be ok
13
84
  begin
14
85
  i = _matches(token,i)
15
86
  rescue NoMatch
16
87
  # Ok, no problem!
17
88
  end
18
89
  next
19
- when /^:(.*?)([+]?)$/ # it's a selection
20
- list,plus = @usage[$1].flatten.select{|_|_[0]==M},$2
21
- raise NoMatch unless list.include?(@keys[i])
22
- if plus=='+'
90
+ when SELECTION_P # it's a selection
91
+ raise NoMatch if key.nil?
92
+ section,plus = $1,$2
93
+ list = @usage[section]
94
+ list = list.flatten
95
+ raise NoMatch unless list.include?(key)
96
+ unless plus.nil?
23
97
  while list.include?(@keys[i+1])
24
98
  i+=1
25
99
  end
26
100
  end
27
- when /^<(.*)>([+]?)$/ # it's a variable
28
- word,plus = $1,$2
29
- raise NoMatch if @keys[i].to_i==0
30
- if plus=='+'
31
- @cache[word] = [@options[@keys[i]]]
32
- while @keys[i+1].to_i > 0
101
+ when VARIABLE_P # it's a variable
102
+ raise NoMatch if key.to_s.to_i==0 # nil.to_s.to_i==0 also works
103
+ word,plus = $1.to_sym,$2
104
+ unless plus.nil?
105
+ @cache[word] = [@options[key]]
106
+ while (key=@keys[i+1]).to_s.to_i > 0
33
107
  i+=1
34
- @cache[word].push(@options[@keys[i]])
108
+ @cache[word].push(@options[key])
35
109
  end
36
110
  else
37
- @cache[word] = @options[@keys[i]]
111
+ @cache[word] = @options[key]
38
112
  end
39
113
  else # it's a literal
114
+ raise NoMatch if key.nil?
40
115
  if token[0]==M
41
- raise NoMatch unless @keys[i]==token
116
+ raise NoMatch unless token == ((key.length>1)? MM : M)+key.to_s
42
117
  else
43
- raise NoMatch unless @options[@keys[i]]==token
118
+ raise NoMatch unless @options[key]==token
44
119
  end
45
120
  end
46
121
  i+=1
@@ -48,74 +123,18 @@ module HELP_PARSER
48
123
  return i
49
124
  end
50
125
 
51
- def pad
52
- @cache.each{|k,v|@options[k]=v}
53
- @usage.each do |name,words|
54
- next if [M,USAGE,TYPES].include?(name)
55
- words.each do |word|
56
- next unless word.length==2
57
- k,v = word
58
- next unless @options[k].nil?
59
- if k[0]==M
60
- if k[1]==M
61
- # long option default
62
- @options[k] = v
63
- elsif v[0]==M and !@options[v].nil?
64
- # long option synonym
65
- @options[k] = @options[v]
66
- end
67
- else
68
- # argument default
69
- @options[k] = v
70
- end
71
- end
72
- end
73
- end
74
-
75
- def types
76
- @usage[TYPES].each do |type,*words|
77
- tx = Types[type] || Regexp.new(type)
78
- words.each do |word|
79
- values = @options[word]
80
- next if values.nil?
81
- [*values].each do |value|
82
- unless tx.match(value)
83
- raise UsageError, "ERROR: #{word}=#{value} is not /#{type}/"
84
- end
85
- end
86
- case type
87
- when INT
88
- case values
89
- when Array
90
- @options[word]=values.map{|v|v.to_i}
91
- when String
92
- @options[word]=values.to_i
93
- end
94
- when FLOAT
95
- case values
96
- when Array
97
- @options[word]=values.map{|v|v.to_f}
98
- when String
99
- @options[word]=values.to_f
100
- end
101
- end
102
- end
103
- end
104
- end
105
-
106
126
  def matches(pattern)
107
127
  begin
108
128
  i = _matches(pattern, 0)
109
129
  raise NoMatch if i != @options.length
110
130
  pad
111
131
  types if @usage[TYPES]
112
- return true
113
132
  rescue NoMatch
114
133
  return false
115
134
  ensure
116
135
  @cache.clear
117
136
  end
118
- raise SoftwareError, SOFTWARE_ERROR_MSG
137
+ return true
119
138
  end
120
139
  end
121
140
  end
@@ -1,6 +1,31 @@
1
1
  module HELP_PARSER
2
- class Usage < Hash
3
- def parse(chars)
2
+ class Usage
3
+ using HELP_PARSER::Refinements
4
+
5
+ DUPLICATES = lambda do |hsh|
6
+ a = hsh.select{|k| k!=TYPES and k!=DEFAULTS}.values.flatten.select{|e|e.class==Symbol}
7
+ if d = a.detect_duplicate
8
+ raise HelpError, DUP_KEY+d.to_s
9
+ end
10
+ end
11
+
12
+ VALIDATE_USAGE = lambda do |tokens|
13
+ seen = {}
14
+ tokens.flatten.each do |token|
15
+ t = token
16
+ if VARIABLE=~token
17
+ t = $1
18
+ elsif KEY=~token
19
+ t = $1
20
+ end
21
+ #raise HelpError, 'Duplicate Token: '+t if seen[t]
22
+ raise HelpError, DUP_KEY+t if seen[t]
23
+ seen[t]=true
24
+ end
25
+ return tokens
26
+ end
27
+
28
+ PARSE_USAGE = lambda do |chars|
4
29
  tokens, token = [], I
5
30
  while c = chars.shift
6
31
  case c
@@ -9,7 +34,7 @@ module HELP_PARSER
9
34
  tokens.push(token)
10
35
  token = I
11
36
  end
12
- tokens.push parse(chars) if c==P
37
+ tokens.push PARSE_USAGE[chars] if c==P
13
38
  return tokens if c==Q
14
39
  else
15
40
  token += c
@@ -19,35 +44,57 @@ module HELP_PARSER
19
44
  return tokens
20
45
  end
21
46
 
47
+ BALANCED = lambda do |chars|
48
+ count = 0
49
+ chars.each do |c|
50
+ if c==P then count+=1 elsif c==Q then count-=1 end
51
+ break if count<0
52
+ end
53
+ return count==0
54
+ end
55
+
22
56
  def initialize(help)
23
- name = M # '-'
24
- self[name] = []
57
+ hsh,name = {},Z
25
58
  help.strip.split(SNS).each do |line|
26
- line.sub!(/\s*\t.*$/,'') # tabs marks comment
59
+ next if line[0]==C # skip comment lines
60
+ line.sub!(COMMENTARY,'') # strip out commentary
27
61
  case line
28
- when /^(\w+):$/
62
+ when SELECTION
29
63
  name = $1.downcase
30
64
  break if name==NOTES
31
- raise HelpFormatError, "#{name} section redefinition" if self[name]
32
- self[name] = []
33
- when /^\s*#/
34
- next
65
+ raise HelpError, SECTION_REDEFINITION+name if hsh[name]
66
+ hsh[name] = []
35
67
  else
36
- if name==USAGE
37
- self[USAGE].push(parse(line.chars))
68
+ case name
69
+ when Z
70
+ # Nothing to do
71
+ when USAGE
72
+ chars = line.chars
73
+ raise HelpError, UNBALANCE_BRACKETS+line unless BALANCED[chars]
74
+ hsh[USAGE].push(VALIDATE_USAGE[PARSE_USAGE[chars]])
75
+ when TYPES,DEFAULTS
76
+ raise HelpError, NOT_A_FLAG+line if line[0]==M
77
+ hsh[name].push line.split(SPS).map{|_|_.to_sym}
38
78
  else
39
- self[name].push line.split(/\s+/)
79
+ raise HelpError, MISSING_MINUS+line unless line[0]==M
80
+ hsh[name].push line.split(SPS).map{|_|(_[0]==M)? _.sub(MNS,'').to_sym : _}
40
81
  end
41
82
  end
42
83
  end
84
+ DUPLICATES[hsh]
85
+ @hash = hsh.freeze
86
+ end
87
+
88
+ def _hash
89
+ @hash
43
90
  end
44
91
 
45
92
  def validate(options)
46
- pattern = Pattern.new(options,self)
47
- self[USAGE].each do |cmd|
48
- return if pattern.matches(cmd)
93
+ pattern = Pattern.new(options,@hash)
94
+ @hash[USAGE].each do |cmd|
95
+ return self if pattern.matches(cmd)
49
96
  end
50
- raise UsageError, 'Please match usage!'
97
+ raise UsageError, MATCH_USAGE
51
98
  end
52
99
  end
53
100
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: help_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - carlosjhr64
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-08 00:00:00.000000000 Z
11
+ date: 2017-03-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'HelpParser - Parses your help text to define your command line options.
14
14
 
@@ -16,24 +16,20 @@ description: 'HelpParser - Parses your help text to define your command line opt
16
16
  email: carlosjhr64@gmail.com
17
17
  executables: []
18
18
  extensions: []
19
- extra_rdoc_files:
20
- - README.rdoc
19
+ extra_rdoc_files: []
21
20
  files:
22
- - README.rdoc
21
+ - README.md
23
22
  - lib/help_parser.rb
24
23
  - lib/help_parser/constants.rb
25
24
  - lib/help_parser/help_parser.rb
26
25
  - lib/help_parser/pattern.rb
27
26
  - lib/help_parser/usage.rb
28
- - lib/help_parser/version.rb
29
27
  homepage: https://github.com/carlosjhr64/help_parser
30
28
  licenses:
31
29
  - MIT
32
30
  metadata: {}
33
31
  post_install_message:
34
- rdoc_options:
35
- - "--main"
36
- - README.rdoc
32
+ rdoc_options: []
37
33
  require_paths:
38
34
  - lib
39
35
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -47,9 +43,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
47
43
  - !ruby/object:Gem::Version
48
44
  version: '0'
49
45
  requirements:
50
- - 'ruby: ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]'
46
+ - 'ruby: ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-linux]'
51
47
  rubyforge_project:
52
- rubygems_version: 2.5.1
48
+ rubygems_version: 2.6.8
53
49
  signing_key:
54
50
  specification_version: 4
55
51
  summary: HelpParser - Parses your help text to define your command line options.
data/README.rdoc DELETED
@@ -1,63 +0,0 @@
1
- = HelpParser
2
-
3
- This is version 3!
4
-
5
- github :: https://www.github.com/carlosjhr64/help_parser
6
- rubygems :: https://rubygems.org/gems/help_parser
7
-
8
- == DESCRIPTION:
9
-
10
- HelpParser - Parses your help text to define your command line options.
11
-
12
- == FEATURES/PROBLEMS:
13
-
14
- * long to short option mapping
15
-
16
- == SYNOPSIS:
17
-
18
- #!/usr/bin/env ruby
19
- require 'help_parser'
20
- VERSION = '1.2.3'
21
- HELP = <<HELP
22
- ### cmd ###
23
- Usage:
24
- cmd --wut <price> <count> <name>
25
- cmd [:options] <args>+
26
- Options:
27
- -h --help \tTab marks comment
28
- -v --version\tShort and long synonyms
29
- -q --quiet
30
- -V --verbose
31
- --val 5.0 \tProvided default to long
32
- --number \tLike --number=3
33
- Types:
34
- Float --val price \tPredefined Float and Int
35
- Int --number count
36
- ^[A-Z][a-z]+$ name \tUser defined type
37
- Notes:
38
- Stuff the help parser will ignore.
39
- HELP
40
- OPTIONS = HelpParser.new(VERSION, HELP)
41
- # Shorts and longs as booleans via missing ? methods
42
- "Got Options!" unless OPTIONS.q?
43
- puts OPTIONS if OPTIONS.V?
44
- # Long values via missing ! methods
45
- puts "val = #{OPTIONS.val!}"
46
- # Arguments via plain missing methods
47
- if OPTIONS.wut?
48
- puts "price = '#{OPTIONS.price}'"
49
- puts "count = '#{OPTIONS.count}'"
50
- puts "name = '#{OPTIONS.name}'"
51
- else
52
- puts "args = '#{OPTIONS.args.join(',')}'"
53
- end
54
-
55
- == INSTALL:
56
-
57
- $ sudo gem install help_parser
58
-
59
- == LICENSE:
60
-
61
- (The "Can't touch this, b/c it's all mine!" License)
62
-
63
- Copyright (c) 2016 CarlosJHR64
@@ -1,3 +0,0 @@
1
- module HELP_PARSER
2
- VERSION = '3.0.1'
3
- end