argparser 1.0.1 → 2.0.0.RC1

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: 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