acclaim 0.0.1.alpha1

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.
@@ -0,0 +1 @@
1
+ *.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
@@ -0,0 +1,14 @@
1
+ Copyright © 2011 Matheus Afonso Martins Moreira
2
+
3
+ This program is free software: you can redistribute it and/or modify
4
+ it under the terms of the GNU General Public License as published by
5
+ the Free Software Foundation, either version 3 of the License, or
6
+ (at your option) any later version.
7
+
8
+ This program is distributed in the hope that it will be useful,
9
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ GNU General Public License for more details.
12
+
13
+ You should have received a copy of the GNU General Public License
14
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,5 @@
1
+ # Acclaim
2
+
3
+ Command-line option parsing and command interface.
4
+
5
+ Extracted from [Safeguard](https://github.com/matheusmoreira/safeguard).
@@ -0,0 +1,44 @@
1
+ this_dir = File.expand_path('..', __FILE__)
2
+ gem_dir = File.join this_dir, 'gem'
3
+ spec_file = File.join this_dir, 'acclaim.gemspec'
4
+
5
+ spec = Gem::Specification.load spec_file
6
+
7
+ task :mkdir do
8
+ FileUtils.mkdir_p gem_dir
9
+ end
10
+
11
+ task :gem => :mkdir do
12
+ gem_file = File.join this_dir, Gem::Builder.new(spec).build
13
+ FileUtils.mv gem_file, gem_dir
14
+ end
15
+
16
+ namespace :gem do
17
+
18
+ task :build => :gem
19
+
20
+ gem_file = File.join gem_dir, "#{spec.name}-#{spec.version}.gem"
21
+
22
+ task :push => :gem do
23
+ sh "gem push #{gem_file}"
24
+ end
25
+
26
+ task :install => :gem do
27
+ sh "gem install #{gem_file}"
28
+ end
29
+
30
+ task :uninstall do
31
+ sh "gem uninstall #{spec.name}"
32
+ end
33
+
34
+ task :clean do
35
+ FileUtils.rm_rf gem_dir
36
+ end
37
+
38
+ end
39
+
40
+ task :clean => 'gem:clean'
41
+
42
+ task :setup => [ 'gem:install', :clean ]
43
+
44
+ task :default => :setup
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env gem build
2
+ # encoding: utf-8
3
+ $:.unshift File.expand_path('../lib', __FILE__)
4
+
5
+ require 'acclaim/version'
6
+
7
+ Gem::Specification.new('acclaim') do |gem|
8
+
9
+ gem.version = Acclaim::Version::STRING
10
+ gem.summary = 'Command-line option parser and command interface.'
11
+ gem.description = gem.summary
12
+
13
+ gem.author = 'Matheus Afonso Martins Moreira'
14
+ gem.email = 'matheus.a.m.moreira@gmail.com'
15
+
16
+ gem.files = `git ls-files`.split "\n"
17
+
18
+ gem.add_development_dependency 'rspec'
19
+
20
+ end
@@ -0,0 +1 @@
1
+ %w(command option option/parser options version).each { |file| require file.prepend 'acclaim/' }
@@ -0,0 +1,68 @@
1
+ require 'acclaim/option'
2
+ require 'acclaim/option/parser'
3
+
4
+ module Acclaim
5
+
6
+ # A command is a single word whose meaning the program understands. It calls
7
+ # upon a function of the program, which may be fine-tuned with options and
8
+ # given arguments.
9
+ #
10
+ # app --global-option do --option
11
+ #
12
+ # A subcommand benefits from its parent's option processing.
13
+ #
14
+ # app do something --option --option-for-something
15
+ #
16
+ # A command can be instantiated in the following form:
17
+ #
18
+ # cmd = Command.new :foo do
19
+ # opt :verbose, short: '-v', long: '--verbose',
20
+ # description: 'Run verbosely', default: false
21
+ # end
22
+ #
23
+ # TODO: make these class methods instead of instance methods, with one class
24
+ # per command
25
+ class Command
26
+
27
+ attr_accessor :name, :action
28
+
29
+ # Initializes a command with a name and evalutes the block if one is given.
30
+ def initialize(name, &block)
31
+ self.name = name.to_s
32
+ instance_eval &block if block
33
+ end
34
+
35
+ # The options this command can take.
36
+ def options
37
+ @options ||= []
38
+ end
39
+
40
+ def option(name, args)
41
+ args.merge!(:name => name) { |key, old, new| new }
42
+ options << Option.new(args)
43
+ end
44
+
45
+ alias :opt :option
46
+
47
+ # Executes the command with the given options and arguments.
48
+ def execute(options, *args)
49
+ action.call options, *args
50
+ end
51
+
52
+ def parse_options!(*args)
53
+ end
54
+
55
+ alias :call :execute
56
+
57
+ # The commands that may be given to this command.
58
+ def subcommands
59
+ @subcommands ||= []
60
+ end
61
+
62
+ def subcommand(*args, &block)
63
+ subcommands << Command.new(*args, &block)
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -0,0 +1,31 @@
1
+ module Acclaim
2
+
3
+ # Represents a command-line option.
4
+ class Option
5
+
6
+ attributes = %w(name short long description arity default).map!(&:to_sym).freeze
7
+
8
+ attr_accessor *attributes
9
+
10
+ def initialize(args = {})
11
+ args.each do |attribute, value|
12
+ instance_variable_set :"@#{attribute}", value
13
+ end
14
+ yield self if block_given?
15
+ end
16
+
17
+ def =~(str)
18
+ str = str.strip
19
+ long == str or short == str
20
+ end
21
+
22
+ def flag?
23
+ not arity or arity.empty? or arity == [0, 0]
24
+ end
25
+
26
+ alias :bool? :flag?
27
+ alias :boolean? :flag?
28
+ alias :switch? :flag?
29
+
30
+ end
31
+ end
@@ -0,0 +1,99 @@
1
+ require 'acclaim/options'
2
+
3
+ module Acclaim
4
+ class Option
5
+
6
+ # Parses arrays of strings and returns an Options instance containing data.
7
+ class Parser
8
+
9
+ class Error < StandardError; end
10
+
11
+ attr_accessor :argv, :options
12
+
13
+ # Initializes a new parser, with the given argument array and set of
14
+ # options. If no option array is given, the argument array will be
15
+ # preprocessed only.
16
+ def initialize(argv, options = nil)
17
+ self.argv = argv
18
+ self.options = options
19
+ end
20
+
21
+ def parse!
22
+ preprocess_argv!
23
+ instance = build_options_instance! unless options.nil?
24
+ end
25
+
26
+ private
27
+
28
+ # Argument array preprocessing. Does not touch
29
+ def preprocess_argv!
30
+ split_multiple_short_options!
31
+ # TODO: normalize parameter formats?
32
+ # --switch=PARAM1[,PARAM2,PARAM3] - split on =, then split on comma,
33
+ # then reinsert them into argv
34
+ # -sPARAM1[,PARAM2,PARAM3...] - possibly incompatible with split_multiple_short_options!
35
+ argv.compact!
36
+ end
37
+
38
+ def split_multiple_short_options!
39
+ argv.find_all { |arg| arg =~ /^-\w{2,}/ }.each do |multiples|
40
+ multiples_index = argv.index multiples
41
+ argv.delete multiples
42
+ options, *parameters = multiples.split /\s+/
43
+ separated_options = options.sub!(/^-/, '').split(//).map! { |option| option.prepend '-' }
44
+ separated_options.each_index do |option_index|
45
+ argv.insert multiples_index + option_index, separated_options[option_index]
46
+ end
47
+ last_option_index = argv.index separated_options.last
48
+ parameters.each_index do |parameter_index|
49
+ argv.insert last_option_index + paramter_index + 1,
50
+ parameters[parameter_index]
51
+ end
52
+ end
53
+ end
54
+
55
+ def build_options_instance!
56
+ Options.new.tap do |options_instance|
57
+ options.each do |option|
58
+ key = option.name.to_s.to_sym
59
+ options_instance[key] = option.default
60
+ args = argv.find_all { |arg| option =~ arg }
61
+ if args.any?
62
+ if option.flag?
63
+ options_instance[key] = true
64
+ else
65
+ minimum, optional = option.arity
66
+ args.each do |arg|
67
+ arg_index = argv.index arg
68
+ len = if optional >= 0
69
+ arg_index + minimum + optional
70
+ else
71
+ argv.length - 1
72
+ end
73
+ params = argv[arg_index + 1, len]
74
+ values = []
75
+ params.each do |param|
76
+ break if param.nil? or param =~ /^-{1,2}/ or param =~ /^-{2,}$/
77
+ values << param
78
+ end
79
+ count = values.count
80
+ if count < minimum
81
+ raise Error, "Wrong number of arguments (%d for %d)" % [count, minimum]
82
+ end
83
+ options_instance[key] = if minimum == 1 and optional.zero?
84
+ values.first
85
+ else
86
+ values
87
+ end
88
+ values.each { |value| argv.delete value }
89
+ end
90
+ end
91
+ args.each { |arg| argv.delete arg }
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,41 @@
1
+ module Acclaim
2
+
3
+ # Represents a set of options.
4
+ class Options
5
+
6
+ def [](key)
7
+ data[key]
8
+ end
9
+
10
+ def []=(key, value)
11
+ data[key] = value
12
+ end
13
+
14
+ # Handles the following cases:
15
+ #
16
+ # options.method = value
17
+ # options.method?
18
+ # options.method
19
+ def method_missing(method, *args, &block)
20
+ m = method.to_s
21
+ case m
22
+ when /=$/
23
+ self[:"#{m.chop!}"] = args.first
24
+ when /\?$/
25
+ self[:"#{m.chop!}"] ? true : false
26
+ else
27
+ self[method]
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ # The option values
34
+ def data
35
+ @options ||= {}
36
+ end
37
+
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,12 @@
1
+ module Acclaim
2
+ module Version
3
+
4
+ MAJOR = 0
5
+ MINOR = 0
6
+ PATCH = 1
7
+ BUILD = 'alpha1'
8
+
9
+ STRING = [ MAJOR, MINOR, PATCH, BUILD ].compact.join '.'
10
+
11
+ end
12
+ end
@@ -0,0 +1,73 @@
1
+ require 'acclaim/option'
2
+ require 'acclaim/option/parser'
3
+
4
+ describe Acclaim::Option::Parser do
5
+
6
+ describe '#parse!' do
7
+
8
+ let!(:args) do
9
+ %w(cmd subcmd -a -b PARAM1 -cdef PARAM2 --long --parameters PARAM3 PARAM4 -- FILE1 FILE2)
10
+ end
11
+
12
+ subject { Acclaim::Option::Parser.new(args) }
13
+
14
+ it 'should split multiple short options' do
15
+ new_argv = args.dup.tap do |args|
16
+ args[args.index('-cdef')] = %w[-c -d -e -f]
17
+ end.flatten!
18
+ subject.parse!
19
+ args.should == new_argv
20
+ end
21
+
22
+ context 'when not given an array of options' do
23
+
24
+ it 'should not return an options instance' do
25
+ subject.parse!.should be_nil
26
+ end
27
+
28
+ end
29
+
30
+ context 'when given an array of options' do
31
+
32
+ let(:options) do
33
+ [].tap do |opts|
34
+ ('a'..'f').each do |c|
35
+ hash = { name: c, short: "-#{c}" }
36
+ hash[:arity] = [1, 0] if c == 'b' or c == 'f'
37
+ opts << Acclaim::Option.new(hash)
38
+ end
39
+ opts << Acclaim::Option.new(name: 'long', long: '--long')
40
+ opts << Acclaim::Option.new(name: 'params', long: '--parameters',
41
+ arity: [1, -1], default: [])
42
+ end
43
+ end
44
+
45
+ subject { Acclaim::Option::Parser.new(args, options) }
46
+
47
+ it 'should return an options instance' do
48
+ subject.parse!.should_not be_nil
49
+ end
50
+
51
+ it 'should parse arguments correctly' do
52
+ subject.parse!.instance_eval do
53
+ a?.should be_true
54
+ b.should == 'PARAM1'
55
+ c?.should be_true
56
+ d?.should be_true
57
+ e?.should be_true
58
+ f.should == 'PARAM2'
59
+ long?.should be_true
60
+ params.should == %w(PARAM3 PARAM4)
61
+ end
62
+ end
63
+
64
+ it 'should leave unparsed arguments in argv' do
65
+ subject.parse!
66
+ args.should == %w(cmd subcmd -- FILE1 FILE2)
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+
73
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acclaim
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.alpha1
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Matheus Afonso Martins Moreira
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &14045320 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *14045320
25
+ description: Command-line option parser and command interface.
26
+ email: matheus.a.m.moreira@gmail.com
27
+ executables: []
28
+ extensions: []
29
+ extra_rdoc_files: []
30
+ files:
31
+ - .gitignore
32
+ - Gemfile
33
+ - LICENSE.GPLv3
34
+ - README.markdown
35
+ - Rakefile
36
+ - acclaim.gemspec
37
+ - lib/acclaim.rb
38
+ - lib/acclaim/command.rb
39
+ - lib/acclaim/option.rb
40
+ - lib/acclaim/option/parser.rb
41
+ - lib/acclaim/options.rb
42
+ - lib/acclaim/version.rb
43
+ - spec/acclaim/option/parser_spec.rb
44
+ homepage:
45
+ licenses: []
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>'
60
+ - !ruby/object:Gem::Version
61
+ version: 1.3.1
62
+ requirements: []
63
+ rubyforge_project:
64
+ rubygems_version: 1.8.10
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: Command-line option parser and command interface.
68
+ test_files: []