argparser 1.0.1 → 2.0.0.RC1
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 +4 -4
- data/HISTORY.md +22 -0
- data/README.md +7 -6
- data/TODO.md +13 -0
- data/argparser.gemspec +10 -6
- data/lib/argparser.rb +4 -125
- data/lib/argparser/arg_parser.rb +219 -0
- data/lib/argparser/argument.rb +82 -0
- data/lib/argparser/errors.rb +9 -0
- data/lib/argparser/option.rb +13 -92
- data/lib/argparser/tools.rb +2 -32
- data/lib/argparser/version.rb +5 -0
- data/spec/argparser_spec.rb +59 -43
- data/spec/tools_spec.rb +2 -2
- metadata +15 -11
- data/lib/argparser/default_parser.rb +0 -116
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 58593645fe6bb77546efd38e6426d090f297e947
|
4
|
+
data.tar.gz: 09097746fde57e2adfe85f103fa1ae59e3b19cfa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28adb1c9eca795de1ea6ec51a5f8af9ca1b09851e49645ac458744b46db0f2e223d65c1d184e70cd66ee3f8775870613c8a5096ede50fa74aa08f3befab340d2
|
7
|
+
data.tar.gz: 7f6af691f64f2ecace8d8aec79573a615df41a747f29f1a91748b1352ecbfc137569fcdb698fc6fb35befdd04b202bf7e6f32de81e28cb430083cbc1032ad9d9
|
data/HISTORY.md
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
# 2.0.0.RC2
|
2
|
+
2015-04-28 Holidays
|
3
|
+
|
4
|
+
* All exclamation marks removed from method names
|
5
|
+
* `Option/Argument#set_value` renamed to `#add_value`
|
6
|
+
* `Option/Argument#add_value`, `set_default` and `#reset` now return `self`
|
7
|
+
* `Option/Argument#value` now is read-only
|
8
|
+
* `Option/Argument#value?` now returns true in case of parsed option w/o param
|
9
|
+
* Block(name, value) in `#parse`
|
10
|
+
|
11
|
+
# 2.0.0.RC1
|
12
|
+
2015-04-28 Holidays
|
13
|
+
|
14
|
+
* Major version changed due to extensive renamings and removals
|
15
|
+
* Whitespace in option names no longer stripped
|
16
|
+
* Separate classes for options and arguments
|
17
|
+
* `Option#argument` renamed to `Option#param`
|
18
|
+
* `Option#eval` and `Option#env` settings removed as they aren't secure, use `Option#default`, which now may be a proc/lambda
|
19
|
+
* `ArgParser.manifest=` added, which merges with manifest supplied through `ArgParser.new`
|
20
|
+
* `ArgParser.new` raises `ManifestError` instead of late `exit(2)` in `ArgParser#parse` while checking manifest
|
21
|
+
* `DefaultParser` module removed and `#parse!` method merged back into `ArgParser`
|
22
|
+
|
1
23
|
# 1.0.1
|
2
24
|
2015-04-14
|
3
25
|
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# argparser
|
2
2
|
Yet another ruby command line argument parser library.
|
3
3
|
|
4
|
-
[](https://travis-ci.org/sinm/argparser) [](http://badge.fury.io/rb/argparser) [](https://gemnasium.com/sinm/argparser) [](https://raw.githubusercontent.com/sinm/argparser/master/LICENSE.txt) [](https://codeclimate.com/github/sinm/argparser) [](https://codeclimate.com/github/sinm/argparser) [](http://inch-ci.org/github/sinm/argparser)
|
4
|
+
[](https://travis-ci.org/sinm/argparser) [](http://badge.fury.io/rb/argparser) [](https://gemnasium.com/sinm/argparser) [](https://raw.githubusercontent.com/sinm/argparser/master/LICENSE.txt) [](https://codeclimate.com/github/sinm/argparser) [](https://codeclimate.com/github/sinm/argparser) [](http://inch-ci.org/github/sinm/argparser) [](https://hakiri.io/github/sinm/argparser/master)
|
5
5
|
|
6
6
|
## Installation
|
7
7
|
`gem install argparser` as usual.
|
@@ -86,13 +86,14 @@ Unknown option: a
|
|
86
86
|
````
|
87
87
|
|
88
88
|
## Consider more rules
|
89
|
-
* `--help` and `--version` options provided
|
90
|
-
* printed synopsis provided
|
91
|
-
* `:default`
|
92
|
-
* `:env => 'ENV_VAR'` to pick default value from ENV, high priority
|
93
|
-
* `:eval => 'ruby expr'` to pick default from eval(...), useful for read defaults from config files, so it has low priority
|
89
|
+
* `--help` and `--version` options provided unless specified explicitly
|
90
|
+
* printed synopsis provided unless specified explicitly
|
91
|
+
* `:default` setting assigns default option's value if value isn't specified, may be proc/lambda
|
94
92
|
* `--` argument honored
|
95
93
|
|
94
|
+
## Tests
|
95
|
+
`bundle exec rake test`
|
96
|
+
|
96
97
|
## Documentation
|
97
98
|
This README is all i could say in a rush. No other documentation provided at this moment, see the sources.
|
98
99
|
|
data/TODO.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# argparser gem TODO list
|
2
|
+
|
3
|
+
5. Testing bash and rb scripts
|
4
|
+
4. CPU Profiling
|
5
|
+
3. + remove eval
|
6
|
+
2. More sophisticated output of printed_help
|
7
|
+
````ruby
|
8
|
+
term_width = ENV['COLUMNS'] || `tput columns` || 80
|
9
|
+
width = opts.reduce(0){|max, o| (sz = o.first.size) > max ? sz : max}
|
10
|
+
help_width = term_width - (width += 1)
|
11
|
+
if help_width < 32...
|
12
|
+
````
|
13
|
+
1. RDocs
|
data/argparser.gemspec
CHANGED
@@ -1,22 +1,26 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
require File.expand_path('../lib/argparser/version.rb', __FILE__)
|
3
|
+
|
2
4
|
Gem::Specification.new do |s|
|
3
5
|
s.name = 'argparser'
|
4
|
-
s.version =
|
6
|
+
s.version = ArgParser::VERSION
|
5
7
|
s.authors = ['sinm']
|
6
8
|
s.email = 'sinm.sinm@gmail.com'
|
7
9
|
s.summary = 'Yet another ruby command line argument parser library'
|
8
10
|
s.description = '== Yet another ruby command line argument parser library'
|
9
11
|
s.homepage = 'https://github.com/sinm/argparser'
|
10
12
|
s.license = 'MIT'
|
11
|
-
s.files = `git ls-files
|
12
|
-
s.test_files = `git ls-files
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- spec/`.split("\n")
|
13
15
|
s.require_paths = ['lib']
|
16
|
+
s.bindir = 'bin'
|
17
|
+
s.executables = `git ls-files -- bin/`.split("\n").map{|f| File.basename(f)}
|
14
18
|
# s.extra_rdoc_files = 'README.md'
|
15
19
|
# s.rdoc_options << '--title' << 'argparser' <<
|
16
20
|
# '--main' << 'README' <<
|
17
21
|
# '--markup' << 'markdown' <<
|
18
22
|
# '--line-numbers'
|
19
|
-
s.add_development_dependency 'bundler', '~> 1'
|
20
|
-
s.add_development_dependency 'rake', '~> 10'
|
21
|
-
s.add_development_dependency 'minitest', '~> 4'
|
23
|
+
s.add_development_dependency 'bundler', '~> 1.7'
|
24
|
+
s.add_development_dependency 'rake', '~> 10.1'
|
25
|
+
s.add_development_dependency 'minitest', '~> 4.7'
|
22
26
|
end
|
data/lib/argparser.rb
CHANGED
@@ -1,130 +1,9 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
require 'stringio'
|
3
3
|
|
4
|
+
require 'argparser/version'
|
4
5
|
require 'argparser/tools'
|
6
|
+
require 'argparser/argument'
|
5
7
|
require 'argparser/option'
|
6
|
-
require 'argparser/
|
7
|
-
|
8
|
-
class ArgParser
|
9
|
-
include Tools
|
10
|
-
include DefaultParser
|
11
|
-
|
12
|
-
# Output messages used in the #terminate method
|
13
|
-
OUT_VERSION = '%s%s %s'
|
14
|
-
OUT_COPYRIGHT = 'Copyright (C) %s'
|
15
|
-
OUT_LICENSE = 'License: %s'
|
16
|
-
OUT_HOMEPAGE = '%s home page: %s'
|
17
|
-
OUT_BUGS = 'Report bugs to: %s'
|
18
|
-
OUT_MANIFEST_EXPECTED = 'Property expected through the manifest: %s'
|
19
|
-
OUT_MULTIPLE_INPUTS = 'Multiple input argument not allowed in the middle: %s'
|
20
|
-
OUT_MULTIPLE_NAMES = 'Multiple names for the input argument: %s'
|
21
|
-
OUT_OPTION_NULL = 'Empty name for option'
|
22
|
-
OUT_REQUIRED = 'Required input argument after optional one: %s'
|
23
|
-
OUT_REQUIRED_DEFAULT = 'Required option has default value: %s'
|
24
|
-
OUT_UNEXPECTED_ARGUMENT = 'Unexpected argument: %s'
|
25
|
-
OUT_UNKNOWN_OPTION = 'Unknown option: %s'
|
26
|
-
OUT_OPTION_ARGUMENT_EXPECTED = 'Expected argument for the option: %s'
|
27
|
-
OUT_SINGLE_OPTION = 'Multiple options not allowed: %s'
|
28
|
-
OUT_OPTION_EXPECTED = 'Expected option: %s'
|
29
|
-
OUT_ARGUMENT_EXPECTED = 'Expected argument: %s'
|
30
|
-
OUT_UNIQUE_NAME = 'Option name should be unique: %s'
|
31
|
-
OUT_INVALID_OPTION = 'Invalid value for option: %s'
|
32
|
-
OPT_ENOUGH = '--'
|
33
|
-
|
34
|
-
# These options don't display their synopsis and given for free unless
|
35
|
-
# explicitly specified in the manifest.
|
36
|
-
OPT_HELP = 'help'
|
37
|
-
OPT_VERSION = 'version'
|
38
|
-
OPTS_RESERVED = [OPT_HELP, OPT_VERSION]
|
39
|
-
|
40
|
-
attr_reader :program # Program name, REQUIRED
|
41
|
-
attr_reader :package # Set to nil if there's no package
|
42
|
-
attr_reader :version # Follow semantic versioning rules, REQUIRED
|
43
|
-
attr_reader :copyright # '2015 Somebody, Inc.',
|
44
|
-
attr_reader :license # Give license headline
|
45
|
-
attr_reader :info # Set additional lines that would follow license
|
46
|
-
attr_reader :bugs # Address to post bug reports
|
47
|
-
attr_reader :homepage # Package or, if absent, program's home page
|
48
|
-
attr_reader :synopsis # Print this if present or construct from options
|
49
|
-
attr_reader :help # Print this if present or construct from options
|
50
|
-
attr_reader :options # Array of options,
|
51
|
-
# see ArgParser::Option class' attr_readers
|
52
|
-
|
53
|
-
# Returns option by any of its names given
|
54
|
-
def [](name)
|
55
|
-
options.find{|o| o.names.include?(name)}
|
56
|
-
end
|
57
|
-
|
58
|
-
# Returns array of input args in order
|
59
|
-
def inputs
|
60
|
-
options.select{|o| o.input}
|
61
|
-
end
|
62
|
-
|
63
|
-
def initialize(manifest)
|
64
|
-
manifest = (safe_return('$config.manifest') || {}).merge(manifest)
|
65
|
-
hash2vars!(manifest)
|
66
|
-
@options = (manifest[:options] || manifest['options'] || []).
|
67
|
-
map {|o| o.kind_of?(Option) ? o : Option.new(o)}
|
68
|
-
if !self['help']
|
69
|
-
options << Option.new(:names => OPT_HELP,
|
70
|
-
:help => 'Print this help and exit.',
|
71
|
-
:validate => (lambda { |o, p|
|
72
|
-
return true if o.count < 1
|
73
|
-
p.terminate(0, p.printed_help) }))
|
74
|
-
end
|
75
|
-
if !self['version']
|
76
|
-
options << Option.new(:names => OPT_VERSION,
|
77
|
-
:help => 'Print version and exit.',
|
78
|
-
:validate => (lambda { |o, p|
|
79
|
-
return true if o.count < 1
|
80
|
-
p.terminate(0, p.printed_version) }))
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def terminate(code, str)
|
85
|
-
s = StringIO.new
|
86
|
-
s.puts(printed_synopsis) if code != 0
|
87
|
-
s.puts(str[-1] == "\n" ? str.chop : str)
|
88
|
-
on_exit(code, s.string)
|
89
|
-
end
|
90
|
-
|
91
|
-
def on_exit(code, message)
|
92
|
-
(code == 0 ? $stdout : $stderr).print(message)
|
93
|
-
exit(code)
|
94
|
-
end
|
95
|
-
|
96
|
-
def printed_version
|
97
|
-
str = StringIO.new
|
98
|
-
pk = (pk = @package) ? " (#{pk})" : ''
|
99
|
-
str.puts(OUT_VERSION % [program, pk, version])
|
100
|
-
str.puts(OUT_COPYRIGHT % copyright) if copyright
|
101
|
-
str.puts(OUT_LICENSE % license) if license
|
102
|
-
str.string
|
103
|
-
end
|
104
|
-
|
105
|
-
def printed_help
|
106
|
-
str = StringIO.new
|
107
|
-
str.puts(printed_synopsis)
|
108
|
-
str.puts(info) if info
|
109
|
-
if help
|
110
|
-
str.puts(help)
|
111
|
-
else
|
112
|
-
(options.select{|o| !o.input} + inputs).each do |o|
|
113
|
-
str.puts(o.printed_help)
|
114
|
-
end
|
115
|
-
# term_width = ENV['COLUMNS'] || `tput columns` || 80
|
116
|
-
# width = opts.reduce(0){|max, o| (sz = o.first.size) > max ? sz : max}
|
117
|
-
# help_width = term_width - (width += 1)
|
118
|
-
# if help_width < 32...
|
119
|
-
end
|
120
|
-
str.puts(OUT_BUGS % bugs) if bugs
|
121
|
-
str.puts(OUT_HOMEPAGE % [(package||program), homepage]) if homepage
|
122
|
-
str.string
|
123
|
-
end
|
124
|
-
|
125
|
-
def printed_synopsis
|
126
|
-
s = synopsis ||
|
127
|
-
(options.select{|o| !o.input} + inputs).map{|o| o.synopsis}.join(' ')
|
128
|
-
"Usage: #{program} #{s}"
|
129
|
-
end
|
130
|
-
end
|
8
|
+
require 'argparser/errors'
|
9
|
+
require 'argparser/arg_parser'
|
@@ -0,0 +1,219 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
class ArgParser
|
4
|
+
include Tools
|
5
|
+
|
6
|
+
# Output templates used for the #terminate(0) call
|
7
|
+
OUT_VERSION = '%s%s %s'
|
8
|
+
OUT_COPYRIGHT = 'Copyright (C) %s'
|
9
|
+
OUT_LICENSE = 'License: %s'
|
10
|
+
OUT_HOMEPAGE = '%s home page: %s'
|
11
|
+
OUT_BUGS = 'Report bugs to: %s'
|
12
|
+
OUT_OPTIONS = 'OPTIONS:'
|
13
|
+
OUT_ARGUMENTS = 'ARGUMENTS:'
|
14
|
+
|
15
|
+
# Templates used for the terminate(2) call
|
16
|
+
TRM_UNEXPECTED_ARGUMENT = 'Unexpected argument: %s'
|
17
|
+
TRM_UNKNOWN = 'Unknown option: %s'
|
18
|
+
TRM_OPTION_ARGUMENT_EXPECTED = 'Expected parameter for the option: %s'
|
19
|
+
TRM_EXPECTED = 'Expected required argument/option: %s'
|
20
|
+
TRM_INVALID_OPTION = 'Invalid value for the argument/option: %s'
|
21
|
+
|
22
|
+
OPT_ENOUGH = '--'
|
23
|
+
|
24
|
+
# These options don't display their synopsis and given for free unless
|
25
|
+
# explicitly specified in the manifest.
|
26
|
+
OPTS_RESERVED = [{:func => :printed_help,
|
27
|
+
:help => 'Print this help and exit.',
|
28
|
+
:name => 'help'},
|
29
|
+
{:func => :printed_version,
|
30
|
+
:help => 'Print version and exit.',
|
31
|
+
:name => 'version'}]
|
32
|
+
|
33
|
+
attr_reader :program # Program name, REQUIRED
|
34
|
+
attr_reader :package # Set to nil if there's no package
|
35
|
+
attr_reader :version # Follow semantic versioning rules, REQUIRED
|
36
|
+
attr_reader :copyright # Like '2015 Somebody, Inc.',
|
37
|
+
attr_reader :license # Give license headline
|
38
|
+
attr_reader :info # Program info string
|
39
|
+
attr_reader :bugs # Address to post bug reports
|
40
|
+
attr_reader :homepage # Package or, if absent, program's home page
|
41
|
+
attr_reader :synopsis # Build from options if not given
|
42
|
+
attr_reader :help # Build from options if not given
|
43
|
+
attr_reader :arguments # Array of arguments
|
44
|
+
attr_reader :options # Array of options
|
45
|
+
# see ArgParser::Argument/Option classes attr_readers
|
46
|
+
|
47
|
+
class << self
|
48
|
+
def manifest=(manifest)
|
49
|
+
@@manifest = manifest
|
50
|
+
end
|
51
|
+
def manifest
|
52
|
+
@@manifest ||= {}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns option by any of its names given
|
57
|
+
def [](name)
|
58
|
+
get_argument(name) || get_option(name)
|
59
|
+
end
|
60
|
+
|
61
|
+
def all
|
62
|
+
options + arguments
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_argument(name)
|
66
|
+
arguments.find{|a| a.name == name}
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_option(name)
|
70
|
+
options.find{|o| o.names.include?(name)}
|
71
|
+
end
|
72
|
+
|
73
|
+
def initialize(manifest)
|
74
|
+
hash2vars(manifest = ArgParser.manifest.merge(manifest))
|
75
|
+
@arguments =
|
76
|
+
(@arguments || []).map {|o| o.kind_of?(Argument) ? o : Argument.new(o)}
|
77
|
+
@options =
|
78
|
+
(@options || []).map {|o| o.kind_of?(Option) ? o : Option.new(o)}
|
79
|
+
_check_manifest
|
80
|
+
end
|
81
|
+
|
82
|
+
# Uses ARGV by default, but you may supply your own arguments
|
83
|
+
# It exits if bad arguments given or they aren't validated.
|
84
|
+
def parse(argv = ARGV)
|
85
|
+
all.each(&:reset)
|
86
|
+
_check_manifest
|
87
|
+
|
88
|
+
OPTS_RESERVED.each do |res|
|
89
|
+
name = res[:name]
|
90
|
+
next unless argv.include?("--#{name}") && !get_argument(name)
|
91
|
+
terminate(0, self.send(res[:func]))
|
92
|
+
end
|
93
|
+
|
94
|
+
args = argv.dup
|
95
|
+
enough = false
|
96
|
+
while (a = args.shift)
|
97
|
+
if a == OPT_ENOUGH
|
98
|
+
enough = true
|
99
|
+
elsif enough || (a =~ /^[^-]/) || (a == '-')
|
100
|
+
_set_argument(a)
|
101
|
+
elsif a =~ /^--(.+)/
|
102
|
+
_set_long_option($1, args)
|
103
|
+
elsif a =~ /^-([^-].*)/
|
104
|
+
_set_short_options($1, args)
|
105
|
+
else
|
106
|
+
terminate(2, TRM_UNKNOWN % a)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
all.each { |o|
|
111
|
+
o.set_default
|
112
|
+
terminate(2, (TRM_EXPECTED % o.name)) if o.required && !o.value?
|
113
|
+
}
|
114
|
+
|
115
|
+
all.each { |o|
|
116
|
+
terminate(2, TRM_INVALID_OPTION % o.name) unless o.valid?(self)
|
117
|
+
}
|
118
|
+
|
119
|
+
all.select(&:value?).each {|o| yield(o.name, o.value)} if block_given?
|
120
|
+
|
121
|
+
self
|
122
|
+
end
|
123
|
+
|
124
|
+
def terminate(code, str)
|
125
|
+
s = StringIO.new
|
126
|
+
s.puts(printed_synopsis) if code != 0
|
127
|
+
s.puts(str[-1] == "\n" ? str.chop : str)
|
128
|
+
on_exit(code, s.string)
|
129
|
+
end
|
130
|
+
|
131
|
+
def on_exit(code, message)
|
132
|
+
(code == 0 ? $stdout : $stderr).print(message)
|
133
|
+
exit(code)
|
134
|
+
end
|
135
|
+
|
136
|
+
def printed_version
|
137
|
+
str = StringIO.new
|
138
|
+
pk = (pk = @package) ? " (#{pk})" : ''
|
139
|
+
str.puts(OUT_VERSION % [program, pk, version])
|
140
|
+
str.puts(OUT_COPYRIGHT % copyright) if copyright
|
141
|
+
str.puts(OUT_LICENSE % license) if license
|
142
|
+
str.string
|
143
|
+
end
|
144
|
+
|
145
|
+
def printed_help
|
146
|
+
str = StringIO.new
|
147
|
+
str.puts(printed_synopsis)
|
148
|
+
str.puts(info) if info
|
149
|
+
if help
|
150
|
+
str.puts(help)
|
151
|
+
else
|
152
|
+
unless options.empty?
|
153
|
+
str.puts(OUT_OPTIONS)
|
154
|
+
options.each {|o| str.puts(o.printed_help)}
|
155
|
+
end
|
156
|
+
OPTS_RESERVED.each do |res|
|
157
|
+
r = Option.new(res)
|
158
|
+
next if get_argument(r.name)
|
159
|
+
str.puts(r.printed_help)
|
160
|
+
end
|
161
|
+
unless arguments.empty?
|
162
|
+
str.puts(OUT_ARGUMENTS)
|
163
|
+
arguments.each {|a| str.puts(a.printed_help)}
|
164
|
+
end
|
165
|
+
end
|
166
|
+
str.puts(OUT_BUGS % bugs) if bugs
|
167
|
+
str.puts(OUT_HOMEPAGE % [(package||program), homepage]) if homepage
|
168
|
+
str.string
|
169
|
+
end
|
170
|
+
|
171
|
+
def printed_synopsis
|
172
|
+
"Usage: #{program} #{synopsis || all.map{|o| o.synopsis}.join(' ')}"
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
def _check_manifest
|
178
|
+
{:program => program, :version => version}.each do |k, v|
|
179
|
+
raise ManifestError, (ERR_MANIFEST_EXPECTED % k) if v.to_s.strip.empty?
|
180
|
+
end
|
181
|
+
|
182
|
+
arguments[0..-2].each do |i|
|
183
|
+
raise ManifestError, (ERR_MULTIPLE_INPUTS % i.name) if i.multiple
|
184
|
+
end
|
185
|
+
opt = arguments.index{|i| !i.required} || arguments.size
|
186
|
+
req = arguments.rindex{|i| i.required} || 0
|
187
|
+
raise ManifestError, (ERR_REQUIRED % arguments[req].name) if req > opt
|
188
|
+
|
189
|
+
names = all.map(&:names).flatten
|
190
|
+
raise ManifestError, ERR_UNIQUE_NAME if names.size != names.uniq.size
|
191
|
+
end
|
192
|
+
|
193
|
+
def _set_argument(a)
|
194
|
+
terminate(2, TRM_UNEXPECTED_ARGUMENT % a) unless
|
195
|
+
(input = arguments.find{|i| !i.value || i.multiple})
|
196
|
+
input.add_value(a)
|
197
|
+
end
|
198
|
+
|
199
|
+
def _set_long_option(a, tail)
|
200
|
+
terminate(2, TRM_UNKNOWN % a) unless a.size > 1 && (o = get_option(a))
|
201
|
+
terminate(2, TRM_OPTION_ARGUMENT_EXPECTED % a) if o.param && tail.empty?
|
202
|
+
o.add_value(o.param ? tail.shift : nil)
|
203
|
+
end
|
204
|
+
|
205
|
+
def _set_short_options(a, tail)
|
206
|
+
a.chars.each_with_index do |char, index|
|
207
|
+
terminate(2, TRM_UNKNOWN % char) unless (option = get_option(char))
|
208
|
+
if !option.param
|
209
|
+
option.add_value(nil)
|
210
|
+
elsif a.size-1 == index
|
211
|
+
terminate(2, TRM_OPTION_ARGUMENT_EXPECTED % char) if tail.empty?
|
212
|
+
option.add_value(tail.shift)
|
213
|
+
else
|
214
|
+
option.add_value(a[index+1..-1])
|
215
|
+
break
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
class ArgParser
|
4
|
+
class Argument
|
5
|
+
include Tools
|
6
|
+
|
7
|
+
# These attrs are from the manifest that is applied through constructor
|
8
|
+
attr_reader :name
|
9
|
+
attr_reader :help # Help string
|
10
|
+
attr_reader :validate # Proc(this, parser) to validate a value
|
11
|
+
attr_reader :default # Value or proc to set default value
|
12
|
+
attr_reader :required # Required
|
13
|
+
attr_reader :multiple # May occur multiple times?
|
14
|
+
|
15
|
+
# These attrs have their meaning after parsing was done
|
16
|
+
attr_reader :count # Occucences
|
17
|
+
attr_reader :value # Value (Array if multiple)
|
18
|
+
|
19
|
+
# Just helper
|
20
|
+
def names
|
21
|
+
[name]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Constructs from Hash of properties (see attr_readers)
|
25
|
+
def initialize(o_manifest)
|
26
|
+
hash2vars(o_manifest)
|
27
|
+
reset
|
28
|
+
raise ManifestError, ERR_OPTION_NULL if !name or name.strip.empty?
|
29
|
+
end
|
30
|
+
|
31
|
+
def synopsis
|
32
|
+
s = name.dup
|
33
|
+
s << '...' if multiple
|
34
|
+
s = "[#{s}]" if !required
|
35
|
+
s
|
36
|
+
end
|
37
|
+
|
38
|
+
# Adds value.
|
39
|
+
def add_value(v)
|
40
|
+
@count += 1
|
41
|
+
multiple ? (@value = @value + Array(v)) : @value = v
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
# Does option contain it's value?
|
46
|
+
def value?
|
47
|
+
@count > 0
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns value as string
|
51
|
+
def to_s
|
52
|
+
multiple ? value.map(&:to_s).join(', ') : value.to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
def valid?(parser)
|
56
|
+
!validate || validate.call(self, parser)
|
57
|
+
end
|
58
|
+
|
59
|
+
def reset
|
60
|
+
@value = multiple ? [] : nil
|
61
|
+
@count = 0
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
def printed_help
|
66
|
+
s = help || ''
|
67
|
+
s << "\n\tDefaults to: #{get_default}" if default
|
68
|
+
"%s\n\t%s" % [synopsis, s]
|
69
|
+
end
|
70
|
+
|
71
|
+
# Set value to default one if no value provided
|
72
|
+
def set_default
|
73
|
+
return self if !default || value?
|
74
|
+
add_value(get_default)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Get default value
|
78
|
+
def get_default
|
79
|
+
default.respond_to?(:call) ? default.call : default
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class ArgParser
|
2
|
+
ERR_OPTION_NULL = 'Empty name for an argument'
|
3
|
+
ERR_MANIFEST_EXPECTED = 'Property expected through the manifest: %s'
|
4
|
+
ERR_MULTIPLE_INPUTS = 'Multi-value argument allowed only if last: %s'
|
5
|
+
ERR_REQUIRED = 'Required argument after optional one: %s'
|
6
|
+
ERR_UNIQUE_NAME = 'All option/argument names must be unique'
|
7
|
+
class ManifestError < RuntimeError
|
8
|
+
end
|
9
|
+
end
|
data/lib/argparser/option.rb
CHANGED
@@ -1,104 +1,25 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
3
|
class ArgParser
|
4
|
-
class Option
|
5
|
-
include Tools
|
6
|
-
attr_reader :names # Names of an option (short, long, etc.)
|
7
|
-
attr_reader :argument # Name of an argument, if present
|
8
|
-
attr_reader :help # Help string for an option
|
9
|
-
attr_reader :validate # Lambda(option, parser) to validate an option
|
10
|
-
attr_reader :default # Default value for an option
|
11
|
-
attr_reader :input # Option is an input argument
|
12
|
-
attr_reader :required # Option required
|
13
|
-
attr_reader :multiple # Option may occure multiple times
|
14
|
-
attr_reader :count # Option occucences
|
15
|
-
attr_reader :env # Default option set by this ENV VAR, if any
|
16
|
-
attr_reader :eval # Default option set by this eval,
|
17
|
-
# superseded by :env, if any
|
18
|
-
# So, in order: value - env - eval - default
|
19
|
-
attr_accessor :value # Values of an option, Array if multiple
|
4
|
+
class Option < Argument
|
20
5
|
|
21
|
-
#
|
6
|
+
# These attrs are from the manifest that is applied through constructor
|
7
|
+
attr_reader :param # Parameter name, if any
|
8
|
+
# Returns first name as a 'default' one when several names given
|
22
9
|
def name
|
23
|
-
names.first
|
10
|
+
@name ||= names.first
|
24
11
|
end
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
hash2vars!(o_manifest)
|
29
|
-
@names = Array(names).map{|n| n.to_s.strip}.
|
30
|
-
sort{|n1, n2| n1.size <=> n2.size}
|
31
|
-
reset!
|
32
|
-
end
|
33
|
-
|
34
|
-
# Sets value. Do not use this directly
|
35
|
-
def set_value(v)
|
36
|
-
@count += 1
|
37
|
-
multiple ? (@value << v).flatten! : @value = v
|
38
|
-
end
|
39
|
-
|
40
|
-
# Does option contain it's value?
|
41
|
-
def value?
|
42
|
-
multiple ? !value.compact.empty? : !!value
|
43
|
-
end
|
44
|
-
|
45
|
-
def to_s
|
46
|
-
if multiple
|
47
|
-
value.empty? ? '' : value.map(&:to_s).join(', ')
|
48
|
-
else
|
49
|
-
value.to_s
|
50
|
-
end
|
12
|
+
# Names of an option (short, long, etc.)
|
13
|
+
def names
|
14
|
+
@names ||= [@name]
|
51
15
|
end
|
52
16
|
|
53
17
|
def synopsis
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
else
|
60
|
-
s = names.map{|n| n.size == 1 ? "-#{n}" : "--#{n}"}.join(', ')
|
61
|
-
s << " #{argument}" if argument
|
62
|
-
s = "[#{s}]" if !required
|
63
|
-
s << '...' if multiple
|
64
|
-
s
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def validate!(parser)
|
69
|
-
!validate || validate.call(self, parser)
|
70
|
-
end
|
71
|
-
|
72
|
-
def reset!
|
73
|
-
@value = multiple ? [] : nil
|
74
|
-
@count = 0
|
75
|
-
end
|
76
|
-
|
77
|
-
def printed_help
|
78
|
-
s = help || ''
|
79
|
-
s << "\n\tDefaults to: #{default}" if default
|
80
|
-
"%s\n\t%s" % [synopsis, s]
|
81
|
-
end
|
82
|
-
|
83
|
-
def set_default!
|
84
|
-
return self unless !value? && (argument || input)
|
85
|
-
# rubocop:disable Lint/Eval
|
86
|
-
e = ((env && ENV[env]) || (eval && safe_return(eval)) || default)
|
87
|
-
# rubocop:enable Lint/Eval
|
88
|
-
set_value(e) if e
|
89
|
-
self
|
90
|
-
end
|
91
|
-
|
92
|
-
def on_first_error
|
93
|
-
if !multiple && count > 1
|
94
|
-
yield(OUT_SINGLE_OPTION % name)
|
95
|
-
elsif required && count < 1
|
96
|
-
yield((input ? OUT_ARGUMENT_EXPECTED : OUT_OPTION_EXPECTED) % name)
|
97
|
-
elsif names.find(&:empty?)
|
98
|
-
yield(OUT_OPTION_NULL)
|
99
|
-
elsif required && default
|
100
|
-
yield(OUT_REQUIRED_DEFAULT % name)
|
101
|
-
end
|
18
|
+
s = names.map{|n| n.size == 1 ? "-#{n}" : "--#{n}"}.join(', ')
|
19
|
+
s << " #{param}" if param
|
20
|
+
s = "[#{s}]" if !required
|
21
|
+
s << '...' if multiple
|
22
|
+
s
|
102
23
|
end
|
103
24
|
end
|
104
25
|
end
|
data/lib/argparser/tools.rb
CHANGED
@@ -4,7 +4,7 @@ class ArgParser
|
|
4
4
|
# Aux tools intented to include into a class
|
5
5
|
module Tools
|
6
6
|
# Sets self state from a hash given
|
7
|
-
def hash2vars
|
7
|
+
def hash2vars(hash)
|
8
8
|
if hash.kind_of?(Hash) || (hash.respond_to?(:to_h) && (hash = hash.to_h))
|
9
9
|
hash.each do |k, v|
|
10
10
|
next unless self.respond_to?(k)
|
@@ -16,41 +16,11 @@ class ArgParser
|
|
16
16
|
self
|
17
17
|
end
|
18
18
|
|
19
|
-
# Returns a hash of self state
|
19
|
+
# Returns a hash of self state
|
20
20
|
def to_hash
|
21
21
|
instance_variables.reduce({}) { |hash, var|
|
22
22
|
hash[var[1..-1]] = instance_variable_get(var)
|
23
23
|
hash }
|
24
24
|
end
|
25
|
-
|
26
|
-
# Eval ruby code.
|
27
|
-
# Returns result of Kernel.eval or nil if some errors occure
|
28
|
-
def safe_return(str)
|
29
|
-
# rubocop:disable Lint/Eval
|
30
|
-
eval(str)
|
31
|
-
# rubocop:enable Lint/Eval
|
32
|
-
rescue NameError, NoMethodError
|
33
|
-
nil
|
34
|
-
end
|
35
|
-
|
36
|
-
=begin Deep pack
|
37
|
-
def to_hash(deep = true)
|
38
|
-
instance_variables.reduce({}) { |hash, var|
|
39
|
-
value = instance_variable_get(var)
|
40
|
-
hash[var[1..-1]] = deep ? value_to_hash(value) : value
|
41
|
-
hash }
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
def value_to_hash value
|
46
|
-
if value.respond_to?(:to_hash)
|
47
|
-
(v = value.to_hash).kind_of?(Hash) ? v : {}
|
48
|
-
elsif value.respond_to?(:to_a)
|
49
|
-
(v = value.to_a).kind_of?(Array) ? v.map{|v| value_to_hash(v)} : []
|
50
|
-
else
|
51
|
-
value
|
52
|
-
end
|
53
|
-
end
|
54
|
-
=end
|
55
25
|
end
|
56
26
|
end
|
data/spec/argparser_spec.rb
CHANGED
@@ -2,20 +2,26 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
4
|
a_manifest = {
|
5
|
-
:program
|
6
|
-
:version
|
7
|
-
:
|
5
|
+
:program => 'a_example', # Use additional properties like these:
|
6
|
+
:version => '1.0', # :info, :copyright, :license,
|
7
|
+
:copyright=> '2015 sinm',
|
8
|
+
:info => 'A Example',
|
9
|
+
:license => 'MIT',
|
10
|
+
:package => 'ArgParser Spec',
|
11
|
+
:bugs => 'https://github.com/sinm/argparser',
|
12
|
+
:homepage => 'https://github.com/sinm/argparser',
|
13
|
+
:options => [{ # :package, :bugs, :homepage
|
8
14
|
:names => %w[m mode],
|
9
|
-
:
|
15
|
+
:param => 'first|second|third',
|
10
16
|
:default => 'first',
|
11
17
|
:multiple => true,
|
12
18
|
:help => 'Example mode.',
|
13
19
|
:validate => (lambda {|this, _parser| # Validating value in-line
|
14
|
-
possible = this.
|
20
|
+
possible = this.param.split('|')
|
15
21
|
this.value.select{|v| possible.include?(v)}.size == this.value.size })
|
16
|
-
},
|
17
|
-
|
18
|
-
:
|
22
|
+
}],
|
23
|
+
:arguments => [{
|
24
|
+
:name => 'file',
|
19
25
|
:required => false,
|
20
26
|
:default => '-',
|
21
27
|
:help => 'Filename or - for stdin.'
|
@@ -29,29 +35,24 @@ describe 'manifest' do
|
|
29
35
|
o.must_be_instance_of(ArgParser::Option)
|
30
36
|
args[o.name].must_be_same_as(o)
|
31
37
|
}
|
32
|
-
(a = args.parse
|
38
|
+
(a = args.parse(%w[-])).must_be_instance_of(ArgParser)
|
33
39
|
a.must_be_same_as(args)
|
34
40
|
end
|
35
41
|
|
36
42
|
it 'should require program' do
|
37
43
|
b_manifest = a_manifest.merge(:program => '')
|
38
|
-
|
39
|
-
lambda {
|
40
|
-
args.parse!([])
|
41
|
-
}.must_raise(ExitStub).status.must_equal(2)
|
44
|
+
lambda { ArgParser.new(b_manifest) }.must_raise(ArgParser::ManifestError)
|
42
45
|
end
|
43
46
|
|
44
47
|
it 'should require version' do
|
45
48
|
b_manifest = a_manifest.merge(:version => nil)
|
46
|
-
lambda {
|
47
|
-
ArgParser.new(b_manifest).parse!([])
|
48
|
-
}.must_raise(ExitStub).status.must_equal(2)
|
49
|
+
lambda { ArgParser.new(b_manifest) }.must_raise(ArgParser::ManifestError)
|
49
50
|
end
|
50
51
|
|
51
52
|
it 'requires nothing but but program & version' do
|
52
53
|
b_manifest = a_manifest.reduce({}) {|h, (k, v)|
|
53
54
|
h[k] = v if [:program, :version].include?(k); h}
|
54
|
-
ArgParser.new(b_manifest).parse
|
55
|
+
ArgParser.new(b_manifest).parse([]).must_be_instance_of(ArgParser)
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
@@ -59,25 +60,47 @@ describe 'Built-in options' do
|
|
59
60
|
it 'prints out version and terminates' do
|
60
61
|
a = ArgParser.new(a_manifest)
|
61
62
|
e = lambda {
|
62
|
-
a.parse
|
63
|
+
a.parse(%w[any --othelp --version])
|
63
64
|
}.must_raise(ExitStub)
|
64
65
|
e.status.must_equal(0)
|
65
66
|
e.message.must_match(/#{a_manifest[:program]}.*#{a_manifest[:version]}/)
|
67
|
+
e.message.must_equal(
|
68
|
+
"a_example (ArgParser Spec) 1.0
|
69
|
+
Copyright (C) 2015 sinm
|
70
|
+
License: MIT\n")
|
66
71
|
end
|
67
72
|
|
68
73
|
it 'prints out help and terminates' do
|
69
74
|
a = ArgParser.new(a_manifest)
|
70
75
|
e = lambda {
|
71
|
-
a.parse
|
76
|
+
a.parse(%w[any --oth --help])
|
72
77
|
}.must_raise(ExitStub)
|
73
78
|
e.status.must_equal(0)
|
74
79
|
e.message.must_match(/#{a_manifest[:options].last[:help]}/)
|
80
|
+
e.message.must_equal(
|
81
|
+
"Usage: a_example [-m, --mode first|second|third]... [file]
|
82
|
+
A Example
|
83
|
+
OPTIONS:
|
84
|
+
[-m, --mode first|second|third]...
|
85
|
+
\tExample mode.
|
86
|
+
\tDefaults to: first
|
87
|
+
[--help]
|
88
|
+
\tPrint this help and exit.
|
89
|
+
[--version]
|
90
|
+
\tPrint version and exit.
|
91
|
+
ARGUMENTS:
|
92
|
+
[file]
|
93
|
+
\tFilename or - for stdin.
|
94
|
+
\tDefaults to: -
|
95
|
+
Report bugs to: https://github.com/sinm/argparser
|
96
|
+
ArgParser Spec home page: https://github.com/sinm/argparser\n")
|
97
|
+
|
75
98
|
end
|
76
99
|
|
77
100
|
it 'prints synopsys on argument error' do
|
78
101
|
a = ArgParser.new(a_manifest)
|
79
102
|
e = lambda {
|
80
|
-
a.parse
|
103
|
+
a.parse(%w[any --1287])
|
81
104
|
}.must_raise(ExitStub)
|
82
105
|
e.status.must_equal(2)
|
83
106
|
e.message.must_match(/#{a.synopsis}/)
|
@@ -87,7 +110,7 @@ describe 'Built-in options' do
|
|
87
110
|
b_manifest = a_manifest.merge(:help => 'User-defined help')
|
88
111
|
a = ArgParser.new(b_manifest)
|
89
112
|
e = lambda {
|
90
|
-
a.parse
|
113
|
+
a.parse(%w[--help])
|
91
114
|
}.must_raise(ExitStub)
|
92
115
|
e.status.must_equal(0)
|
93
116
|
e.message.must_match(/#{b_manifest[:help]}/)
|
@@ -95,7 +118,7 @@ describe 'Built-in options' do
|
|
95
118
|
|
96
119
|
it 'understands -- argument' do
|
97
120
|
@args = ArgParser.new(a_manifest)
|
98
|
-
mode = @args.parse
|
121
|
+
mode = @args.parse(%w[-- --mode])['mode']
|
99
122
|
mode.count.must_equal(1)
|
100
123
|
mode.value.first.must_equal(mode.default)
|
101
124
|
end
|
@@ -107,19 +130,19 @@ describe 'Multiple argumented options w/default value' do
|
|
107
130
|
end
|
108
131
|
|
109
132
|
it 'reads them and gives out values in order' do
|
110
|
-
@args.parse
|
133
|
+
@args.parse(%w[--mode second -m first -msecond -- -])
|
111
134
|
@args['mode'].value.must_equal(%w[second first second])
|
112
135
|
end
|
113
136
|
|
114
137
|
it 'doesn''t validate unknown value' do
|
115
138
|
lambda {
|
116
|
-
@args.parse
|
139
|
+
@args.parse(%w[--mode second -m foo -msecond -])
|
117
140
|
}.must_raise(ExitStub).status.must_equal(2)
|
118
141
|
end
|
119
142
|
|
120
143
|
it 'doesn''t understand unknown options' do
|
121
144
|
lambda {
|
122
|
-
@args.parse
|
145
|
+
@args.parse(%w[--mode second -abm foo -m first -- -])
|
123
146
|
}.must_raise(ExitStub).status.must_equal(2)
|
124
147
|
end
|
125
148
|
end
|
@@ -137,17 +160,17 @@ describe 'required option' do
|
|
137
160
|
end
|
138
161
|
|
139
162
|
it 'is really not optional' do
|
140
|
-
e = lambda { @args.parse
|
163
|
+
e = lambda { @args.parse(%w[-- file]) }.must_raise(ExitStub)
|
141
164
|
e.status.must_equal(2)
|
142
165
|
end
|
143
166
|
|
144
167
|
it 'is actually multiple too' do
|
145
|
-
@args.parse
|
168
|
+
@args.parse(%w[-rrrrr --legacy-required --required -r -- file])
|
146
169
|
@args['required'].count.must_equal(8)
|
147
170
|
end
|
148
171
|
|
149
172
|
it 'lives with an optional one' do
|
150
|
-
@args.parse
|
173
|
+
@args.parse(%w[--mode first -rmsecond])
|
151
174
|
@args['required'].count.must_equal(1)
|
152
175
|
end
|
153
176
|
end
|
@@ -155,20 +178,19 @@ end
|
|
155
178
|
describe 'input argument' do
|
156
179
|
before do
|
157
180
|
@b_manifest = a_manifest.merge({
|
158
|
-
:
|
159
|
-
:
|
160
|
-
:input => true
|
181
|
+
:arguments => (a_manifest[:arguments] + [{
|
182
|
+
:name => 'file2'
|
161
183
|
}])
|
162
184
|
})
|
163
185
|
@args = ArgParser.new(@b_manifest)
|
164
186
|
end
|
165
187
|
|
166
188
|
it 'terminates if name used as an option' do
|
167
|
-
lambda { @args.parse
|
189
|
+
lambda { @args.parse(%w[--file2 --]) }.must_raise(ExitStub)
|
168
190
|
end
|
169
191
|
|
170
192
|
it 'survives second optional argument' do
|
171
|
-
@args.parse
|
193
|
+
@args.parse(%w[file2])
|
172
194
|
@args['file'].value.must_equal('file2')
|
173
195
|
@args['file2'].value.must_equal(@args['file2'].default)
|
174
196
|
end
|
@@ -178,24 +200,18 @@ describe 'optional tiny features' do
|
|
178
200
|
before do
|
179
201
|
@b_manifest = a_manifest.merge({
|
180
202
|
:options => (a_manifest[:options] + [{
|
181
|
-
:names => ['a',
|
203
|
+
:names => ['a', 'aaaaa'],
|
182
204
|
:multiple => true,
|
183
|
-
:
|
205
|
+
:param => 'arg'
|
184
206
|
}])
|
185
207
|
})
|
186
208
|
@args = ArgParser.new(@b_manifest)
|
187
209
|
end
|
188
210
|
|
189
|
-
it 'ignores whitespace in option names' do
|
190
|
-
"\n aaaaa \t ".strip.must_equal('aaaaa')
|
191
|
-
@args.parse!(%w[--aaaaa foobar])
|
192
|
-
@args['aaaaa'].value.must_equal(['foobar'])
|
193
|
-
end
|
194
|
-
|
195
211
|
it 'allows to get value as string' do
|
196
|
-
@args.parse
|
212
|
+
@args.parse(%w[--aaaaa foo])
|
197
213
|
"#{@args['aaaaa']}".must_equal('foo')
|
198
|
-
@args.parse
|
214
|
+
@args.parse(%w[--aaaaa foo -abar])
|
199
215
|
"#{@args['aaaaa']}".must_equal('foo, bar')
|
200
216
|
end
|
201
217
|
end
|
data/spec/tools_spec.rb
CHANGED
@@ -17,9 +17,9 @@ describe 'hash behaviour' do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
it 'allows to pack/unpack' do
|
20
|
-
obj1 = StubClass.new.hash2vars
|
20
|
+
obj1 = StubClass.new.hash2vars(@hash)
|
21
21
|
hash = obj1.to_hash
|
22
|
-
obj2 = StubClass.new.hash2vars
|
22
|
+
obj2 = StubClass.new.hash2vars(hash)
|
23
23
|
obj1.hash.each{|k, v| obj2.hash[k].must_equal(v)}
|
24
24
|
obj1.array.must_equal(obj2.array)
|
25
25
|
obj1.value.must_equal(obj2.value)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: argparser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0.RC1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sinm
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,42 +16,42 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1'
|
19
|
+
version: '1.7'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1'
|
26
|
+
version: '1.7'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '10'
|
33
|
+
version: '10.1'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '10'
|
40
|
+
version: '10.1'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: minitest
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '4'
|
47
|
+
version: '4.7'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '4'
|
54
|
+
version: '4.7'
|
55
55
|
description: "== Yet another ruby command line argument parser library"
|
56
56
|
email: sinm.sinm@gmail.com
|
57
57
|
executables: []
|
@@ -67,12 +67,16 @@ files:
|
|
67
67
|
- LICENSE.txt
|
68
68
|
- README.md
|
69
69
|
- Rakefile
|
70
|
+
- TODO.md
|
70
71
|
- argparser.gemspec
|
71
72
|
- argparser.sublime-project
|
72
73
|
- lib/argparser.rb
|
73
|
-
- lib/argparser/
|
74
|
+
- lib/argparser/arg_parser.rb
|
75
|
+
- lib/argparser/argument.rb
|
76
|
+
- lib/argparser/errors.rb
|
74
77
|
- lib/argparser/option.rb
|
75
78
|
- lib/argparser/tools.rb
|
79
|
+
- lib/argparser/version.rb
|
76
80
|
- spec/argparser_spec.rb
|
77
81
|
- spec/spec_helper.rb
|
78
82
|
- spec/tools_spec.rb
|
@@ -91,9 +95,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
91
95
|
version: '0'
|
92
96
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
97
|
requirements:
|
94
|
-
- - "
|
98
|
+
- - ">"
|
95
99
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
100
|
+
version: 1.3.1
|
97
101
|
requirements: []
|
98
102
|
rubyforge_project:
|
99
103
|
rubygems_version: 2.2.2
|
@@ -1,116 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
#
|
3
|
-
|
4
|
-
class ArgParser
|
5
|
-
module DefaultParser
|
6
|
-
# Uses ARGV by default, but you may supply your own arguments
|
7
|
-
# It exits if bad arguments given or they aren't validated.
|
8
|
-
def parse!(arguments = ARGV)
|
9
|
-
options.each(&:reset!)
|
10
|
-
_check_manifest!
|
11
|
-
|
12
|
-
OPTS_RESERVED.each { |o|
|
13
|
-
next unless arguments.include?("--#{o}")
|
14
|
-
self[o].set_value(nil)
|
15
|
-
self[o].validate!(self)
|
16
|
-
self[o].reset! # If it didn't terminate while validating
|
17
|
-
}
|
18
|
-
|
19
|
-
args = arguments.dup
|
20
|
-
enough = false
|
21
|
-
while (a = args.shift)
|
22
|
-
if a == OPT_ENOUGH
|
23
|
-
enough = true
|
24
|
-
elsif enough || (a =~ /^[^-]/) || (a == '-')
|
25
|
-
_set_argument!(a)
|
26
|
-
elsif a =~ /^--(.+)/
|
27
|
-
_set_long_option!(a, args)
|
28
|
-
elsif a =~ /^-([^-].*)/
|
29
|
-
_set_short_options!(a, args)
|
30
|
-
else
|
31
|
-
terminate(2, OUT_UNKNOWN_OPTION % a)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
options.each { |o|
|
36
|
-
o.set_default!
|
37
|
-
o.on_first_error {|msg| terminate(2, msg)}
|
38
|
-
}
|
39
|
-
|
40
|
-
options.each { |o|
|
41
|
-
terminate(2, OUT_INVALID_OPTION % o.name) unless o.validate!(self)
|
42
|
-
}
|
43
|
-
|
44
|
-
self
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def _set_argument!(a)
|
50
|
-
if (input = inputs.find{|i| !i.value || i.multiple})
|
51
|
-
input.set_value(a)
|
52
|
-
else
|
53
|
-
terminate(2, OUT_UNEXPECTED_ARGUMENT % a)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def _set_long_option!(a, tail)
|
58
|
-
a = a[2..-1]
|
59
|
-
if a.size > 1 && (option = self[a]) && !option.input
|
60
|
-
if option.argument
|
61
|
-
terminate(2, OUT_OPTION_ARGUMENT_EXPECTED % a) if tail.empty?
|
62
|
-
option.set_value(tail.shift)
|
63
|
-
else
|
64
|
-
option.set_value(nil)
|
65
|
-
end
|
66
|
-
else
|
67
|
-
terminate(2, OUT_UNKNOWN_OPTION % $1)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def _set_short_options!(a, tail)
|
72
|
-
(a = a[1..-1]).chars.each_with_index do |char, index|
|
73
|
-
unless (option = self[char]) && !option.input
|
74
|
-
terminate(2, OUT_UNKNOWN_OPTION % char)
|
75
|
-
end
|
76
|
-
if option.argument
|
77
|
-
if a.size-1 == index
|
78
|
-
terminate(2, OUT_OPTION_ARGUMENT_EXPECTED % char) if tail.empty?
|
79
|
-
option.set_value(tail.shift)
|
80
|
-
else
|
81
|
-
option.set_value(a[index+1..-1])
|
82
|
-
break
|
83
|
-
end
|
84
|
-
else
|
85
|
-
option.set_value(nil)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def _check_manifest!
|
91
|
-
{:program => program, :version => version}.each do |k, v|
|
92
|
-
terminate(2, OUT_MANIFEST_EXPECTED % k) if !v || v.to_s.strip.empty?
|
93
|
-
end
|
94
|
-
|
95
|
-
is = inputs
|
96
|
-
is.each_with_index do |i, index|
|
97
|
-
if index < is.length-1 && i.multiple
|
98
|
-
terminate(2, OUT_MULTIPLE_INPUTS % i.name)
|
99
|
-
elsif i.names.size > 1
|
100
|
-
terminate(2, OUT_MULTIPLE_NAMES % i.name)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
opt = is.index{|i| !i.required} || is.size
|
104
|
-
req = is.rindex{|i| i.required} || 0
|
105
|
-
terminate(2, OUT_REQUIRED % is[req].name) if req > opt
|
106
|
-
|
107
|
-
names = {}
|
108
|
-
options.each do |option|
|
109
|
-
option.names.each do |name|
|
110
|
-
terminate(2, OUT_UNIQUE_NAME % name) if names.has_key?(name)
|
111
|
-
names[name] = option
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|