help_parser 4.0.0 → 5.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: 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