argparser 1.0.0 → 1.0.1

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.
data/.travis.yml ADDED
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.8.7"
4
+ - "1.9.3"
5
+ - "2.0.0"
6
+ - "2.1" # latest 2.1.x
7
+ - "2.2" # latest 2.2.x
8
+ - "jruby-18mode"
9
+ - "jruby-19mode"
10
+ script:
11
+ CODECLIMATE_REPO_TOKEN=3a1b48c656a57cdd02824699a1352f8a0648e9b41f3df18323b8c48bab22b5ce bundle exec rake test
12
+ branches:
13
+ - "master"
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+ gem 'codeclimate-test-reporter', :group => :development,
4
+ :require => nil,
5
+ :platforms => [:ruby_20]
data/HISTORY.md ADDED
@@ -0,0 +1,10 @@
1
+ # 1.0.1
2
+ 2015-04-14
3
+
4
+ * Some minor fixes and refactorings
5
+ * MRI-1.8, jruby-1.8-mode support
6
+
7
+ # 1.0.0
8
+ 2015-04-03
9
+
10
+ * First public release
data/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # argparser
2
- Command line argument parser library, trying to follow POSIX and GNU guidelines.
2
+ Yet another ruby command line argument parser library.
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)
3
5
 
4
6
  ## Installation
5
- `gem install argparser`, as usual.
7
+ `gem install argparser` as usual.
6
8
 
7
9
  ## Usage by example
8
10
  Suppose there's a file named `example.rb` like this:
@@ -41,6 +43,8 @@ puts args['mode'].value.inspect # So we could use our options...
41
43
  puts args['file'].value # Prints contents of a file
42
44
  ````
43
45
 
46
+ This file located here: `lib/argparser/examples/example.rb`.
47
+
44
48
  Now, let's look at the output of example given in various cases.
45
49
 
46
50
  `$ ruby example.rb` is unsufficient:
@@ -82,12 +86,12 @@ Unknown option: a
82
86
  ````
83
87
 
84
88
  ## Consider more rules
85
- * `--help` and `--version` options provided for free unless specified.
86
- * printed synopsys provided for free unless specified
89
+ * `--help` and `--version` options provided for free unless specified
90
+ * printed synopsis provided for free unless specified
87
91
  * `:default` used if option has :argument and no value given, lowest priority
88
92
  * `:env => 'ENV_VAR'` to pick default value from ENV, high priority
89
93
  * `:eval => 'ruby expr'` to pick default from eval(...), useful for read defaults from config files, so it has low priority
90
- * `--`-argument honored
94
+ * `--` argument honored
91
95
 
92
96
  ## Documentation
93
97
  This README is all i could say in a rush. No other documentation provided at this moment, see the sources.
@@ -97,6 +101,3 @@ Don't hesistate to leave a report.
97
101
 
98
102
  ## License
99
103
  MIT for now.
100
-
101
- ## TODO
102
- * Go steal milk for the hazards applied.
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env rake
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new :test do |t|
5
+ # t.test_files = FileList['spec/*_spec.rb']
6
+ t.pattern = 'spec/**/*_spec.rb'
7
+ t.libs.push 'spec'
8
+ end
9
+
10
+ task :build do
11
+ system('gem build argparser.gemspec')
12
+ # gem push argparser-1.0.0.gem
13
+ # gem uninstall argparser
14
+ # gem install argparser-1.0.0.gem
15
+ end
16
+
17
+ task :default => :test
data/argparser.gemspec CHANGED
@@ -1,19 +1,22 @@
1
1
  # coding: utf-8
2
- Gem::Specification.new do |spec|
3
- spec.name = 'argparser'
4
- spec.version = '1.0.0'
5
- spec.authors = ['sinm']
6
- spec.email = 'sinm.sinm@gmail.com'
7
- spec.summary = 'Command line argument parser'
8
- spec.description = '== Trying to follow POSIX and GNU guidelines'
9
- spec.homepage = 'https://github.com/sinm/argparser'
10
- spec.license = 'MIT'
11
- spec.files = `git ls-files -z`.split("\x0")
12
- spec.require_paths = ['lib']
13
-
14
- #spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
15
- #spec.test_files = spec.files.grep(%r{^test/})
16
- #spec.add_development_dependency "bundler", "~> 1.7"
17
- #spec.add_development_dependency "rake", "~> 10.0"
18
- #spec.add_development_dependency "minitest" if RUBY_VERSION > '1.8'
2
+ Gem::Specification.new do |s|
3
+ s.name = 'argparser'
4
+ s.version = '1.0.1'
5
+ s.authors = ['sinm']
6
+ s.email = 'sinm.sinm@gmail.com'
7
+ s.summary = 'Yet another ruby command line argument parser library'
8
+ s.description = '== Yet another ruby command line argument parser library'
9
+ s.homepage = 'https://github.com/sinm/argparser'
10
+ 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.require_paths = ['lib']
14
+ # s.extra_rdoc_files = 'README.md'
15
+ # s.rdoc_options << '--title' << 'argparser' <<
16
+ # '--main' << 'README' <<
17
+ # '--markup' << 'markdown' <<
18
+ # '--line-numbers'
19
+ s.add_development_dependency 'bundler', '~> 1'
20
+ s.add_development_dependency 'rake', '~> 10'
21
+ s.add_development_dependency 'minitest', '~> 4'
19
22
  end
@@ -0,0 +1,27 @@
1
+ {
2
+ "folders":
3
+ [
4
+ {
5
+ "path": "."
6
+ }
7
+ ],
8
+ "build_systems":
9
+ [
10
+ {
11
+ "name": "Run tests",
12
+ "cmd": ["bundle", "exec", "rake", "test"],
13
+ "selector": "source.rb"
14
+ }
15
+ ],
16
+ "settings":
17
+ {
18
+ "ensure_newline_at_eof_on_save": true,
19
+ "rulers":
20
+ [
21
+ 80
22
+ ],
23
+ "tab_size": 2,
24
+ "translate_tabs_to_spaces": true,
25
+ "trim_trailing_white_space_on_save": true
26
+ }
27
+ }
data/lib/argparser.rb CHANGED
@@ -1,28 +1,16 @@
1
- #__END__
2
- module Tulz
3
- def hash2vars!(hash)
4
- hash.each do |k, v|
5
- next unless self.respond_to?(k)
6
- instance_variable_set("@#{k}", v)
7
- end
8
- end
1
+ # coding: utf-8
2
+ require 'stringio'
9
3
 
10
- def to_hash
11
- instance_variables.reduce({}) { |hash, var|
12
- hash[var[1..-1]] = instance_variable_get(var)
13
- hash }
14
- end
15
-
16
- def safe_return(str)
17
- eval(str)
18
- rescue Exception
19
- # intentionally left blank
20
- end
21
- end
4
+ require 'argparser/tools'
5
+ require 'argparser/option'
6
+ require 'argparser/default_parser'
22
7
 
23
8
  class ArgParser
24
- include Tulz
25
- OUT_VERSION = '%s %s %s'
9
+ include Tools
10
+ include DefaultParser
11
+
12
+ # Output messages used in the #terminate method
13
+ OUT_VERSION = '%s%s %s'
26
14
  OUT_COPYRIGHT = 'Copyright (C) %s'
27
15
  OUT_LICENSE = 'License: %s'
28
16
  OUT_HOMEPAGE = '%s home page: %s'
@@ -40,7 +28,7 @@ class ArgParser
40
28
  OUT_OPTION_EXPECTED = 'Expected option: %s'
41
29
  OUT_ARGUMENT_EXPECTED = 'Expected argument: %s'
42
30
  OUT_UNIQUE_NAME = 'Option name should be unique: %s'
43
- INVALID_OPTION = 'Invalid option: %s'
31
+ OUT_INVALID_OPTION = 'Invalid value for option: %s'
44
32
  OPT_ENOUGH = '--'
45
33
 
46
34
  # These options don't display their synopsis and given for free unless
@@ -49,76 +37,6 @@ class ArgParser
49
37
  OPT_VERSION = 'version'
50
38
  OPTS_RESERVED = [OPT_HELP, OPT_VERSION]
51
39
 
52
- class Option
53
- include Tulz
54
- attr_reader :names # Names of an option (short, long, etc.)
55
- attr_reader :argument # Name of an argument, if present
56
- attr_reader :help # Help string for an option
57
- attr_reader :validate # Lambda(option, parser) to validate an option
58
- attr_reader :default # Default value for an option
59
- attr_reader :input # Option is an input argument
60
- attr_reader :required # Option required
61
- attr_reader :multiple # Option may occure multiple times
62
- attr_reader :count # Option occucences
63
- attr_reader :env # Default option set by this ENV VAR, if any
64
- attr_reader :eval # Default option set by this eval,
65
- # superseded by :env, if any
66
- # So, in order: value - env - eval - default
67
- attr_accessor :value # Values of an option, Array if multiple
68
-
69
- def name
70
- names.first
71
- end
72
-
73
- def initialize(o_manifest)
74
- hash2vars!(o_manifest)
75
- @names = Array(names).map(&:to_s).sort{|n1, n2| n1.size <=> n2.size}
76
- @value = [] if multiple
77
- @count = 0
78
- end
79
-
80
- def set_value(v)
81
- @count += 1
82
- multiple ? (@value << v).flatten! : @value = v
83
- end
84
-
85
- def value?
86
- multiple ? !value.compact.empty? : !!value
87
- end
88
-
89
- def to_s
90
- str = ''
91
- str += (count ? count.to_s : ' ')
92
- str += (argument ? 'A' : ' ')
93
- str += (validate ? 'V' : ' ')
94
- str += (default ? 'D' : ' ')
95
- str += (input ? 'I' : ' ')
96
- str += (required ? 'R' : ' ')
97
- str += (multiple ? 'M' : ' ')
98
- str += " #{names.inspect}"
99
- str += " #{value.inspect}" if value
100
- end
101
-
102
- def synopsis
103
- if input
104
- s = name.dup
105
- s << '...' if multiple
106
- s = "[#{s}]" if !required
107
- return s
108
- else
109
- s = names.map{|n| n.size == 1 ? "-#{n}" : "--#{n}"}.join(', ')
110
- s << " #{argument}" if argument
111
- s = "[#{s}]" if !required
112
- s << '...' if multiple
113
- return s
114
- end
115
- end
116
-
117
- def validate!(parser)
118
- !validate || validate.call(self, parser)
119
- end
120
- end
121
-
122
40
  attr_reader :program # Program name, REQUIRED
123
41
  attr_reader :package # Set to nil if there's no package
124
42
  attr_reader :version # Follow semantic versioning rules, REQUIRED
@@ -129,9 +47,10 @@ class ArgParser
129
47
  attr_reader :homepage # Package or, if absent, program's home page
130
48
  attr_reader :synopsis # Print this if present or construct from options
131
49
  attr_reader :help # Print this if present or construct from options
132
- attr_reader :options # Options in a hash,
133
- # see ArgParser::Option class' attr_reader
50
+ attr_reader :options # Array of options,
51
+ # see ArgParser::Option class' attr_readers
134
52
 
53
+ # Returns option by any of its names given
135
54
  def [](name)
136
55
  options.find{|o| o.names.include?(name)}
137
56
  end
@@ -142,13 +61,10 @@ class ArgParser
142
61
  end
143
62
 
144
63
  def initialize(manifest)
145
- manifest = {
146
- :options => []
147
- }.merge(safe_return('$config.manifest') || {}).merge(manifest)
64
+ manifest = (safe_return('$config.manifest') || {}).merge(manifest)
148
65
  hash2vars!(manifest)
149
- @options = (manifest[:options] || manifest['options'] || {}).map do |o|
150
- Option.new(o)
151
- end
66
+ @options = (manifest[:options] || manifest['options'] || []).
67
+ map {|o| o.kind_of?(Option) ? o : Option.new(o)}
152
68
  if !self['help']
153
69
  options << Option.new(:names => OPT_HELP,
154
70
  :help => 'Print this help and exit.',
@@ -165,190 +81,50 @@ class ArgParser
165
81
  end
166
82
  end
167
83
 
168
- def parse!(arguments = ARGV)
169
- {:program => @program, :version => @version}.each do |k, v|
170
- if !v || v.to_s.strip.empty?
171
- terminate(2, OUT_MANIFEST_EXPECTED % k)
172
- end
173
- end
174
-
175
- is = inputs
176
- is.each_with_index do |i, index|
177
- if index < is.length-1 and i.multiple
178
- terminate(2, OUT_MULTIPLE_INPUTS % i.name)
179
- end
180
- if i.names.size > 1
181
- terminate(2, OUT_MULTIPLE_NAMES % i.name)
182
- end
183
- end
184
- first_optional = is.index{|i| !i.required} || is.size
185
- last_required = is.rindex{|i| i.required} || 0
186
- if last_required > first_optional
187
- terminate(2, OUT_REQUIRED % is[last_required].name)
188
- end
189
-
190
- names = {}
191
- options.each do |option|
192
- option.names.each do |name|
193
- if name.strip.empty?
194
- terminate(2, OUT_OPTION_NULL)
195
- end
196
- if names.has_key?(name)
197
- terminate(2, OUT_UNIQUE_NAME % name)
198
- end
199
- names[name] = option
200
- end
201
- if option.required && option.default
202
- terminate(2, OUT_REQUIRED_DEFAULT % option.name)
203
- end
204
- end
205
-
206
- OPTS_RESERVED.each { |o|
207
- next unless arguments.include?("--#{o}")
208
- o = self[o]
209
- o.set_value(nil)
210
- o.validate!(self)
211
- }
212
-
213
- args = arguments.dup
214
- enough = false
215
- while (a = args.shift)
216
- if a == OPT_ENOUGH
217
- enough = true
218
- elsif enough || (a =~ /^[^-]/) || (a == '-') # input argument
219
- if (input = inputs.find{|i| !i.value || i.multiple})
220
- input.set_value(a)
221
- else
222
- terminate(2, OUT_UNEXPECTED_ARGUMENT % a)
223
- end
224
- elsif a =~ /^--(.+)/ # long option
225
- if $1.size > 1 && option = self[$1]
226
- if option.argument
227
- if args.empty?
228
- terminate(2, $stderr.puts(OUT_OPTION_ARGUMENT_EXPECTED % a))
229
- end
230
- option.set_value(args.shift)
231
- else
232
- option.set_value(nil)
233
- end
234
- else
235
- terminate(2, OUT_UNKNOWN_OPTION % $1)
236
- end
237
- elsif a =~ /^-([^-].*)/ # short option, may combine and has an arg at end
238
- (opts = $1).chars.to_a.each_with_index do |char, index|
239
- if (option = self[char])
240
- if option.argument
241
- if opts.size-1 == index
242
- if args.empty?
243
- terminate(2, OUT_OPTION_ARGUMENT_EXPECTED % a)
244
- else
245
- option.set_value(args.shift)
246
- end
247
- else
248
- option.set_value(opts[index+1..-1])
249
- break
250
- end
251
- else
252
- option.set_value(nil)
253
- end
254
- else
255
- terminate(2, OUT_UNKNOWN_OPTION % char)
256
- end
257
- end
258
- else
259
- terminate(2, OUT_UNKNOWN_OPTION % a)
260
- end
261
- end
262
-
263
- options.each do |option|
264
- if !option.value? && (option.argument || option.input) &&
265
- ((option.env && (e = ENV[option.env])) ||
266
- (option.eval && (e = safe_return(option.eval))) ||
267
- (!!option.default && (e = option.default)))
268
- option.set_value(e)
269
- end
270
- if !option.multiple && option.count > 1
271
- terminate(2, OUT_SINGLE_OPTION % option.name)
272
- elsif option.required && option.count < 1
273
- if option.input
274
- terminate(2, OUT_ARGUMENT_EXPECTED % option.name)
275
- else
276
- terminate(2, OUT_OPTION_EXPECTED % option.name)
277
- end
278
- end
279
- end
280
-
281
- options.each { |o|
282
- next if o.validate!(self)
283
- terminate(2, INVALID_OPTION % o.name)
284
- }
285
- self
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)
286
89
  end
287
90
 
288
- def terminate(code, str = nil)
289
- stream = code == 0 ? $stdout : $stderr
290
- stream.puts(printed_synopsis) if code != 0
291
- if str
292
- stream.print(str)
293
- stream.puts() unless str[-1] == "\n"
294
- end
295
- exit!(code)
91
+ def on_exit(code, message)
92
+ (code == 0 ? $stdout : $stderr).print(message)
93
+ exit(code)
296
94
  end
297
95
 
298
96
  def printed_version
299
- pk = (pk = @package) ? "(#{pk})" : ''
300
- str = ''
301
- str << (OUT_VERSION % [@program, pk, @version]) + "\n"
302
- str << (OUT_COPYRIGHT % @copyright) + "\n"
303
- str << (OUT_LICENSE % @license) + "\n"
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
304
103
  end
305
104
 
306
105
  def printed_help
307
- str = printed_synopsis + "\n"
308
- str << info + "\n\n" if info
106
+ str = StringIO.new
107
+ str.puts(printed_synopsis)
108
+ str.puts(info) if info
309
109
  if help
310
- str << help + "\n"
110
+ str.puts(help)
311
111
  else
312
- opts = []
313
- options.select{|o| !o.input}.each do |o|
314
- next unless h = o.help
315
- h << "\n\tDefaults to: #{o.default}" if o.default
316
- opts << [o.synopsis, h]
317
- end
318
- inputs.each do |i|
319
- next unless i.help
320
- help = i.help
321
- help << "\n\tDefaults to: #{i.default}" if i.default
322
- opts << [i.synopsis, help]
323
- end
324
- opts.each do |o|
325
- str << "%s\n\t%s\n" % [o.first, o.last]
112
+ (options.select{|o| !o.input} + inputs).each do |o|
113
+ str.puts(o.printed_help)
326
114
  end
327
115
  # term_width = ENV['COLUMNS'] || `tput columns` || 80
328
- # name_width = opts.reduce(0){|max, o| (sz = o.first.size) > max ? sz : max}
329
- # help_width = term_width - (name_width += 1)
116
+ # width = opts.reduce(0){|max, o| (sz = o.first.size) > max ? sz : max}
117
+ # help_width = term_width - (width += 1)
330
118
  # if help_width < 32...
331
119
  end
332
- str << (OUT_BUGS % @bugs) + "\n" if bugs
333
- what = @package || @program
334
- str << (OUT_HOMEPAGE % [what, @homepage]) + "\n" if homepage
335
- str
120
+ str.puts(OUT_BUGS % bugs) if bugs
121
+ str.puts(OUT_HOMEPAGE % [(package||program), homepage]) if homepage
122
+ str.string
336
123
  end
337
124
 
338
125
  def printed_synopsis
339
126
  s = synopsis ||
340
- (options.select{|o| !o.input} + inputs).map{|o|
341
- OPTS_RESERVED.include?(o.name) ? nil : o.synopsis}.compact.join(' ')
342
- "#{program} #{s}"
343
- end
344
-
345
- if __FILE__ == $0 # Some selftests... while hakin in an editor
346
- $stdout.sync = true
347
- $stderr.sync = true
348
- ARGV = %w[--abcm first]
349
- example = File.read('argparser/examples/example.rb')
350
- example = example.split("\n")[1..-1].join("\n") # Cut 'require' string
351
- eval(example) # Last line
127
+ (options.select{|o| !o.input} + inputs).map{|o| o.synopsis}.join(' ')
128
+ "Usage: #{program} #{s}"
352
129
  end
353
-
354
- end # the very end
130
+ end