help_parser 4.0.0 → 5.0.0

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
  SHA1:
3
- metadata.gz: 2b2c0821ccb920314039d7e8f92851e7be14382a
4
- data.tar.gz: f5184113ef5d80ae592c2f923d895527d078d954
3
+ metadata.gz: 15eed085a057dd27423f3a363e2a172cceecd664
4
+ data.tar.gz: 9159259d556060cebe4d1241d0065b6252a0280a
5
5
  SHA512:
6
- metadata.gz: 1afa018ea4504b0dfc00a443e26dda59338f4d7aa0f68f64b72b4c61c1d05d5558a3be3da9427bb07e57152da2fd6c2c77caf2e37d62729a5e3f905df18ec711
7
- data.tar.gz: 70bb2be1242c6c094bba0b61e209f7028a8cb47c63f30ed9a9c5e3088d19f23107aa55d872c89bd153dfe73201a32936c967809de63dc16c0ba4ec8e10c4f2ea
6
+ metadata.gz: 3b8aeb803ca69f593a861d49e574175178a57574fd42664ff853ba229be9b84118fb26c9bed3ed3029c52b1b60727fc3368ab6409a1987da527a645f46f41e4d
7
+ data.tar.gz: f40a072547ec26b18c174f3f313106132f64299c4356ba07a1b3e915558d73a8ee9674b28dad9533bdcecc21ed678460e4c21e96432fb31f83372d2e85cfcedf
data/LICENSE ADDED
@@ -0,0 +1,21 @@
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.
data/README.md CHANGED
@@ -1,151 +1,63 @@
1
- # HelpParser
2
- ## Version 4!!!
1
+ # Help Parser
2
+ ## V: Infamous
3
3
 
4
- * [github](https://www.github.com/carlosjhr64/help_parser)
4
+ * [github](https://www.github.com/carlosjhr64/Ruby-HelpParser)
5
5
  * [rubygems](https://rubygems.org/gems/help_parser)
6
6
 
7
7
  ## DESCRIPTION:
8
- HelpParser - Parses your help text to define your command line options.
8
+ All help is about to get parsed...
9
+ Again!!!
9
10
 
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
11
+ And this time,
12
+ the battle between complexity and simplicity
13
+ has as familiar text.
24
14
 
25
15
  ## SYNOPSIS:
26
16
 
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'
17
+ require "pp"
18
+ require "help_parser"
50
19
 
51
20
  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
- ####################################################
21
+ The Awesome Command.
63
22
  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
- ####################################################
23
+ awesome [:options+] <args>+
24
+ awesome :alternate <arg=NAME>
74
25
  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
- ####################################################
26
+ -v --version \t Give version and quit
27
+ -h --help \t Give help and quit
28
+ -s --long \t Short long synonyms
29
+ --name=NAME \t Typed
30
+ --number 5 \t Defaulted
31
+ --value=FLOAT 1.23 \t Typed and Defaulted
32
+ -a --all=YN y \t Short, long, typed, and defaulted
33
+ Alternate:
34
+ -V \t Just short
95
35
  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.
36
+ NAME /^[A-Z][a-z]+$/
37
+ FLOAT /^\\d+\\.\\d+$/
38
+ YN /^[YNyn]$/
107
39
  HELP
108
40
 
109
- OPTIONS = HelpParser.new(VERSION, HELP)
41
+ VERSION = "5.0.0"
110
42
 
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}"
43
+ # Macros:
44
+ HelpParser.string(name) # for options.name : String
45
+ HelpParser.strings(args) # for options.args : Array(String)
46
+ HelpParser.float(value) # for options.value : Float
47
+ HelpParser.int?(number) # for options.number : Int32 | Nil
125
48
 
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
49
+ HelpParser.run(VERSION, HELP) do |options|
50
+ hash = options._hash
51
+ pp hash # to inspect the hash
134
52
 
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:
53
+ pp options.name if hash["name"]?
54
+ pp options.args if hash["args"]?
55
+ pp options.value if hash["value"]?
56
+ pp options.number?
57
+ end
144
58
 
145
- require 'HelpParser'
146
- OPTIONS = HelpParser.new # DONE!
59
+ YES!!!
147
60
 
148
- Can't be any simpler.
61
+ ## INSTALL:
149
62
 
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.
63
+ $ sudo gem install help_parser
data/lib/help_parser.rb CHANGED
@@ -1,9 +1,37 @@
1
- require 'help_parser/constants'
2
- require 'help_parser/pattern'
3
- require 'help_parser/usage'
4
- require 'help_parser/help_parser'
5
- HelpParser = HELP_PARSER::HelpParser
6
- HELP_PARSER::VERSION = '4.0.0'
1
+ require_relative "./help_parser/constants"
2
+ require_relative "./help_parser/exceptions"
3
+ require_relative "./help_parser/aliases"
4
+ require_relative "./help_parser/parsea"
5
+ require_relative "./help_parser/validations"
6
+ require_relative "./help_parser/parseu"
7
+ require_relative "./help_parser/parseh"
8
+ require_relative "./help_parser/k2t2r"
9
+ require_relative "./help_parser/completion"
10
+ require_relative "./help_parser/options"
11
+ require_relative "./help_parser/macros"
12
+
13
+ module HelpParser
14
+ VERSION = "5.0.0"
15
+
16
+ def self.[](
17
+ version = nil,
18
+ help = nil,
19
+ argv = [File.basename($0)]+ARGV)
20
+ Options.new(version, help, argv)
21
+ rescue HelpParserException => exception
22
+ exception.exit
23
+ end
24
+
25
+ def self.run(
26
+ version = nil,
27
+ help = nil,
28
+ argv = [File.basename($0)]+ARGV)
29
+ options = Options.new(version, help, argv)
30
+ yield options
31
+ rescue HelpParserException => exception
32
+ exception.exit
33
+ end
34
+ end
7
35
 
8
36
  # Requires:
9
37
  #`ruby`
@@ -0,0 +1,19 @@
1
+ module HelpParser
2
+ class NoDupHash < Hash
3
+ def []=(k,v)
4
+ raise HelpError, "Duplicate key: #{k}" if self.has_key?(k)
5
+ super
6
+ end
7
+ end
8
+
9
+ class ArgvHash < Hash
10
+ def []=(k,v)
11
+ raise UsageError, "Duplicate key: #{k}" if self.has_key?(k)
12
+ super
13
+ end
14
+ end
15
+
16
+ def self.f2k(f)
17
+ f[1]=='-' ? f[2..((f.index('=')||0)-1)] : f[1]
18
+ end
19
+ end
@@ -0,0 +1,135 @@
1
+ module HelpParser
2
+ class Completion
3
+ def initialize(hash, specs, cache = NoDupHash.new)
4
+ @hash,@specs,@cache = hash,specs,cache
5
+ usage if @specs.has_key?(USAGE)
6
+ pad
7
+ types
8
+ end
9
+
10
+ def usage
11
+ @specs[USAGE].each do |cmd|
12
+ begin
13
+ i = matches(cmd)
14
+ raise NoMatch unless @hash.size==i
15
+ @cache.each{|k,v|@hash[k]=v} # Variables
16
+ return
17
+ rescue NoMatch
18
+ next
19
+ ensure
20
+ @cache.clear
21
+ end
22
+ end
23
+ raise UsageError, "Please match usage."
24
+ end
25
+
26
+ def types
27
+ if t2r = HelpParser.t2r(@specs)
28
+ k2t = HelpParser.k2t(@specs)
29
+ HelpParser.validate_k2t2r(@specs, k2t, t2r)
30
+ @hash.each do |key,value|
31
+ next unless key.is_a?(String)
32
+ if type = k2t[key]
33
+ regex = t2r[type]
34
+ case value
35
+ when String
36
+ raise UsageError, "Not a #{type}: #{key}" unless value=~regex
37
+ when Array
38
+ value.each do |string|
39
+ raise UsageError, "Not a #{type}: #{key}" unless string=~regex
40
+ end
41
+ else
42
+ raise UsageError, "Need a #{type}: #{key}"
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def pad
50
+ # Synonyms and defaults:
51
+ @specs.each do |section,options|
52
+ next if section==USAGE || section==TYPES
53
+ options.each do |words|
54
+ next unless words.size>1
55
+ first,second,default = words[0],words[1],words[2]
56
+ if first[0]=='-'
57
+ if second[0]=='-'
58
+ i = second.index('=') || 0
59
+ short,long = first[1],second[2..(i-1)]
60
+ if @hash.has_key?(short)
61
+ if @hash.has_key?(long)
62
+ raise UsageError, "Option #{short} is a synonym for #{long}."
63
+ end
64
+ @hash[long] = (default.nil?) ? true : default
65
+ elsif value = @hash[long]
66
+ @hash[short] = true
67
+ if value==true && !default.nil?
68
+ @hash.delete(long)
69
+ @hash[long]=default
70
+ end
71
+ end
72
+ else
73
+ i = first.index('=') || 0
74
+ long,default = first[2..(i-1)],second
75
+ value = @hash[long]
76
+ if value==true
77
+ @hash.delete(long)
78
+ @hash[long] = default
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ def matches(cmd, i = 0)
87
+ keys = @hash.keys
88
+ cmd.each do |token|
89
+ if token.is_a?(Array)
90
+ begin
91
+ i = matches(token, i)
92
+ rescue NoMatch
93
+ # OK, NEVERMIND!
94
+ end
95
+ next
96
+ elsif m=FLAG_GROUP.match(token)
97
+ group,plus = m["k"],m["p"]
98
+ key = keys[i]
99
+ raise NoMatch if key.nil? || key.is_a?(Integer)
100
+ list = @specs[group].flatten.select{|f|f[0]=='-'}.map{|f| HelpParser.f2k(f)}
101
+ raise NoMatch unless list.include?(key)
102
+ unless plus.nil?
103
+ loop do
104
+ key = keys[i+1]
105
+ break if key.nil? || key.is_a?(Integer) || !list.include?(key)
106
+ i+=1
107
+ end
108
+ end
109
+ elsif m=VARIABLE.match(token)
110
+ key = keys[i]
111
+ raise NoMatch unless key.is_a?(Integer)
112
+ variable,plus = m["k"],m["p"]
113
+ if plus.nil?
114
+ @cache[variable] = @hash[key]
115
+ else
116
+ strings = []
117
+ strings.push @hash[key]
118
+ loop do
119
+ key = keys[i+1]
120
+ break unless key.is_a?(Integer)
121
+ strings.push @hash[key]
122
+ i+=1
123
+ end
124
+ @cache[variable] = strings
125
+ end
126
+ else # literal
127
+ key = keys[i]
128
+ raise NoMatch if key.nil? || @hash[key]!=token
129
+ end
130
+ i += 1
131
+ end
132
+ return i
133
+ end
134
+ end
135
+ end
@@ -1,75 +1,23 @@
1
- module HELP_PARSER # Constants
1
+ module HelpParser
2
+ USAGE = "usage"
3
+ TYPES = "types"
2
4
 
3
- ### OPTIONS LANGUAGE ###
5
+ # usage
6
+ FLAG = /^[-][-]?(?<k>\w+)$/
7
+ LITERAL = /^(?<k>\w[\w.-]*:?)$/
8
+ VARIABLE = /^<(?<k>\w+)(=(?<t>[A-Z]+))?>(?<p>[+])?$/
9
+ FLAG_GROUP = /^:(?<k>\w+)(?<p>[+])?$/
4
10
 
5
- H,LH = :h,:help
6
- V,LV = :v,:version
11
+ # spec --?w+
12
+ SHORT = /^[-](?<s>\w)$/
13
+ LONG = /^[-][-](?<k>\w+)(=(?<t>[A-Z]+))?(,?\s+(?<d>[^-\s]\S*))?$/
7
14
 
8
- C,Z,M,E,P,Q,I = '#','-','-','=','[',']',''
9
- MM = '--'
15
+ # spec -w,? --w+
16
+ SHORT_LONG = /^[-](?<s>\w),?\s+[-][-](?<k>\w+)$/
17
+ SHORT_LONG_DEFAULT = /^[-](?<s>\w),?\s+[-][-](?<k>\w+)(=(?<t>[A-Z]+))?,?\s+(?<d>[^-\s]\S*)$/
10
18
 
11
- ### HELP TEXT KEYS ###
12
-
13
- NOTES,USAGE,TYPES,DEFAULTS = 'notes','usage','types','defaults'
14
-
15
- ### ERROR COLOR CODES ###
16
-
17
- COLOR = [
18
- "\e[0m", # no color
19
- "\e[1;31m", # red
20
- ]
21
-
22
- ### TYPE MAPPINGS ###
23
-
24
- FLOAT,INT = :Float, :Int
25
- Types = {
26
- INT => /^[+-]?\d+$/,
27
- FLOAT => /^[+-]?\d+\.\d+$/,
28
- }
29
-
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
59
- end
60
- class HelpError < RuntimeError
61
- end
62
- class NoMatch < RuntimeError
63
- end
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
73
- end
19
+ # spec W+ /~/
20
+ TYPE_DEF = /^(?<t>[A-Z]+),?\s+\/(?<r>\S+)\/$/
74
21
 
22
+ CSV = /,?\s+/
75
23
  end