acclaim 0.0.1.alpha1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []