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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8989066f5d2d643a0c693f7446b86cf179328017
4
- data.tar.gz: 87f9acd817c83aaa29037f0e662cca0aff4c3bf9
3
+ metadata.gz: 58593645fe6bb77546efd38e6426d090f297e947
4
+ data.tar.gz: 09097746fde57e2adfe85f103fa1ae59e3b19cfa
5
5
  SHA512:
6
- metadata.gz: 3a5558cf118a3bb6e8be1ad5ba2e4e4c3a8009561afadb0a060b36adc1f434059662fbdda8d06d6c253885b1cfe6cb28cd314123bb0cc0fcce383545efb7c8cc
7
- data.tar.gz: 706d455288bc26ed3a4a8e7e81fdf4c7d30c72cee3b51383188252ec38e278065ff7d38694a57fbfea0249bc86b6b9325fade9c326edea58acb657c87985afca
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
- [![Build Status](https://travis-ci.org/sinm/argparser.svg?branch=master)](https://travis-ci.org/sinm/argparser) [![Gem Version](https://badge.fury.io/rb/argparser.svg)](http://badge.fury.io/rb/argparser) [![Dependency Status](https://gemnasium.com/sinm/argparser.svg)](https://gemnasium.com/sinm/argparser) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/sinm/argparser/master/LICENSE.txt) [![Code Climate](https://codeclimate.com/github/sinm/argparser/badges/gpa.svg)](https://codeclimate.com/github/sinm/argparser) [![Test Coverage](https://codeclimate.com/github/sinm/argparser/badges/coverage.svg)](https://codeclimate.com/github/sinm/argparser) [![Inline docs](http://inch-ci.org/github/sinm/argparser.svg?branch=master)](http://inch-ci.org/github/sinm/argparser)
4
+ [![Build Status](https://travis-ci.org/sinm/argparser.svg?branch=master)](https://travis-ci.org/sinm/argparser) [![Gem Version](https://badge.fury.io/rb/argparser.svg)](http://badge.fury.io/rb/argparser) [![Dependency Status](https://gemnasium.com/sinm/argparser.svg)](https://gemnasium.com/sinm/argparser) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/sinm/argparser/master/LICENSE.txt) [![Code Climate](https://codeclimate.com/github/sinm/argparser/badges/gpa.svg)](https://codeclimate.com/github/sinm/argparser) [![Test Coverage](https://codeclimate.com/github/sinm/argparser/badges/coverage.svg)](https://codeclimate.com/github/sinm/argparser) [![Inline docs](http://inch-ci.org/github/sinm/argparser.svg?branch=master)](http://inch-ci.org/github/sinm/argparser) [![security](https://hakiri.io/github/sinm/argparser/master.svg)](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 for free unless specified
90
- * printed synopsis provided for free unless specified
91
- * `:default` used if option has :argument and no value given, lowest priority
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 = '1.0.1'
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 -z`.split("\x0")
12
- s.test_files = `git ls-files -z spec/`.split("\0")
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/default_parser'
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
@@ -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
- # Returns most lengthy name as a 'default' name of this option
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
- # Constructs option from Hash of properties (see attr_readers)
27
- def initialize(o_manifest)
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
- if input
55
- s = name.dup
56
- s << '...' if multiple
57
- s = "[#{s}]" if !required
58
- s
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
@@ -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!(hash)
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, packing all objects to hashes
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
@@ -0,0 +1,5 @@
1
+ # coding: utf-8
2
+
3
+ class ArgParser
4
+ VERSION = '2.0.0.RC1'
5
+ end
@@ -2,20 +2,26 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  a_manifest = {
5
- :program => 'a_example', # Use additional properties like these:
6
- :version => '1.0', # :info, :copyright, :license,
7
- :options => [{ # :package, :bugs, :homepage
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
- :argument => 'first|second|third',
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.argument.split('|')
20
+ possible = this.param.split('|')
15
21
  this.value.select{|v| possible.include?(v)}.size == this.value.size })
16
- }, {
17
- :names => 'file',
18
- :input => true,
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!(%w[-])).must_be_instance_of(ArgParser)
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
- args = ArgParser.new(b_manifest)
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!([]).must_be_instance_of(ArgParser)
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!(%w[any --othelp --version])
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!(%w[any --oth --help])
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!(%w[any --1287])
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!(%w[--help])
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!(%w[-- --mode])['mode']
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!(%w[--mode second -m first -msecond -- -])
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!(%w[--mode second -m foo -msecond -])
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!(%w[--mode second -abm foo -m first -- -])
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!(%w[-- file]) }.must_raise(ExitStub)
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!(%w[-rrrrr --legacy-required --required -r -- file])
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!(%w[--mode first -rmsecond])
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
- :options => (a_manifest[:options] + [{
159
- :names => %w[file2],
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!(%w[--file2 --]) }.must_raise(ExitStub)
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!(%w[file2])
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', "\n aaaaa \t "],
203
+ :names => ['a', 'aaaaa'],
182
204
  :multiple => true,
183
- :argument => 'arg'
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!(%w[--aaaaa foo])
212
+ @args.parse(%w[--aaaaa foo])
197
213
  "#{@args['aaaaa']}".must_equal('foo')
198
- @args.parse!(%w[--aaaaa foo -abar])
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!(@hash)
20
+ obj1 = StubClass.new.hash2vars(@hash)
21
21
  hash = obj1.to_hash
22
- obj2 = StubClass.new.hash2vars!(hash)
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: 1.0.1
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-14 00:00:00.000000000 Z
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/default_parser.rb
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: '0'
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