acclaim 0.0.1.alpha1 → 0.0.1.alpha2
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/.rvmrc +1 -0
- data/acclaim.gemspec +1 -0
- data/lib/acclaim/command.rb +93 -32
- data/lib/acclaim/option.rb +14 -3
- data/lib/acclaim/option/parser.rb +21 -7
- data/lib/acclaim/options.rb +5 -1
- data/lib/acclaim/version.rb +1 -1
- data/spec/acclaim/option/parser_spec.rb +29 -6
- metadata +6 -5
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm --create 1.9.3@acclaim
|
data/acclaim.gemspec
CHANGED
@@ -9,6 +9,7 @@ Gem::Specification.new('acclaim') do |gem|
|
|
9
9
|
gem.version = Acclaim::Version::STRING
|
10
10
|
gem.summary = 'Command-line option parser and command interface.'
|
11
11
|
gem.description = gem.summary
|
12
|
+
gem.homepage = 'https://github.com/matheusmoreira/acclaim'
|
12
13
|
|
13
14
|
gem.author = 'Matheus Afonso Martins Moreira'
|
14
15
|
gem.email = 'matheus.a.m.moreira@gmail.com'
|
data/lib/acclaim/command.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'acclaim/option'
|
2
2
|
require 'acclaim/option/parser'
|
3
|
+
require 'acclaim/options'
|
3
4
|
|
4
5
|
module Acclaim
|
5
6
|
|
@@ -15,52 +16,112 @@ module Acclaim
|
|
15
16
|
#
|
16
17
|
# A command can be instantiated in the following form:
|
17
18
|
#
|
18
|
-
#
|
19
|
-
# opt :verbose,
|
20
|
-
# description: 'Run verbosely', default: false
|
19
|
+
# class App::Command < Acclaim::Command
|
20
|
+
# opt :verbose, names: %w(-v --verbose), description: 'Run verbosely'
|
21
21
|
# end
|
22
22
|
#
|
23
|
-
#
|
24
|
-
#
|
23
|
+
# A subcommand can be created by inheriting from another command:
|
24
|
+
#
|
25
|
+
# class App::Command::Do < App::Command
|
26
|
+
# opt :what, names: %w(-W --what), description: 'Do what?', arity: [1, 0], required: true
|
27
|
+
# when_called do |options, arguments|
|
28
|
+
# puts "Verbose? #{options.verbose? ? 'yes' : 'no'}"
|
29
|
+
# puts "Doing #{options.what} with #{arguments.inspect} now!"
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# Then, in your application's binary, you may simply write:
|
34
|
+
#
|
35
|
+
# App::Command.run *ARGV
|
36
|
+
#
|
37
|
+
# Subcommands inherit their parent's option processing:
|
38
|
+
#
|
39
|
+
# $ app --verbose -W test do arg1 arg2
|
40
|
+
# Verbose? yes
|
41
|
+
# Doing test with ["arg1", "arg2"] now!
|
25
42
|
class Command
|
26
43
|
|
27
|
-
|
44
|
+
# Module containing the class methods every command class should inherit.
|
45
|
+
module ClassMethods
|
28
46
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
47
|
+
# String which calls this command.
|
48
|
+
def line(value = nil)
|
49
|
+
@line = value if value
|
50
|
+
@line
|
51
|
+
end
|
34
52
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
53
|
+
# Commands which may be given to this command.
|
54
|
+
def subcommands
|
55
|
+
@subcommands ||= []
|
56
|
+
end
|
39
57
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
58
|
+
# The options this command can take.
|
59
|
+
def options
|
60
|
+
@options ||= []
|
61
|
+
end
|
44
62
|
|
45
|
-
|
63
|
+
# Adds an option to this command.
|
64
|
+
def option(key, args = {})
|
65
|
+
args.merge!(key: key)
|
66
|
+
options << Option.new(args)
|
67
|
+
end
|
46
68
|
|
47
|
-
|
48
|
-
def execute(options, *args)
|
49
|
-
action.call options, *args
|
50
|
-
end
|
69
|
+
alias :opt :option
|
51
70
|
|
52
|
-
|
53
|
-
|
71
|
+
# The block which is executed when this command is called. It is given 2
|
72
|
+
# parameters; the first is an Options instance which can be queried for
|
73
|
+
# settings information; the second is the remaining command line.
|
74
|
+
def action(&block)
|
75
|
+
@action = block
|
76
|
+
end
|
77
|
+
|
78
|
+
alias :when_called :action
|
79
|
+
|
80
|
+
# Parses the argument array using this command's set of options.
|
81
|
+
def parse_options!(args)
|
82
|
+
Option::Parser.new(args, options).parse!
|
83
|
+
end
|
84
|
+
|
85
|
+
# Invokes this command with a fresh set of options.
|
86
|
+
def run(*args)
|
87
|
+
invoke Options.new, args
|
88
|
+
rescue Option::Parser::Error => e
|
89
|
+
puts e.message
|
90
|
+
end
|
91
|
+
|
92
|
+
# Parses the argument array. If the first element of the argument array
|
93
|
+
# corresponds to a subcommand, it will be invoked with said array and
|
94
|
+
# with this command's parsed options. This command will be executed
|
95
|
+
# otherwise.
|
96
|
+
def invoke(opts, args = [])
|
97
|
+
opts.merge! parse_options!(args)
|
98
|
+
subcommands.find do |subcommand|
|
99
|
+
subcommand.line == args.first
|
100
|
+
end.tap do |subcommand|
|
101
|
+
if subcommand
|
102
|
+
args.delete subcommand.line
|
103
|
+
subcommand.invoke(opts, args)
|
104
|
+
else
|
105
|
+
execute(opts, args)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Calls this command's action block with the given options and arguments.
|
111
|
+
def execute(opts, args)
|
112
|
+
@action.call opts, args
|
113
|
+
end
|
54
114
|
|
55
|
-
|
115
|
+
alias :call :execute
|
56
116
|
|
57
|
-
# The commands that may be given to this command.
|
58
|
-
def subcommands
|
59
|
-
@subcommands ||= []
|
60
117
|
end
|
61
118
|
|
62
|
-
|
63
|
-
|
119
|
+
# Add the class methods to the subclass and add it to this command's list of
|
120
|
+
# subcommands.
|
121
|
+
def self.inherited(sub)
|
122
|
+
sub.extend ClassMethods
|
123
|
+
sub.line sub.name.gsub(/^.*::/, '').downcase
|
124
|
+
subcommands << sub if respond_to? :subcommands
|
64
125
|
end
|
65
126
|
|
66
127
|
end
|
data/lib/acclaim/option.rb
CHANGED
@@ -3,7 +3,7 @@ module Acclaim
|
|
3
3
|
# Represents a command-line option.
|
4
4
|
class Option
|
5
5
|
|
6
|
-
attributes = %w(
|
6
|
+
attributes = %w(key names description arity default).map!(&:to_sym).freeze
|
7
7
|
|
8
8
|
attr_accessor *attributes
|
9
9
|
|
@@ -15,8 +15,19 @@ module Acclaim
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def =~(str)
|
18
|
-
|
19
|
-
|
18
|
+
names.include? str.strip
|
19
|
+
end
|
20
|
+
|
21
|
+
def required?
|
22
|
+
@required
|
23
|
+
end
|
24
|
+
|
25
|
+
def required=(value)
|
26
|
+
@required = value
|
27
|
+
end
|
28
|
+
|
29
|
+
def require
|
30
|
+
self.required = true
|
20
31
|
end
|
21
32
|
|
22
33
|
def flag?
|
@@ -6,7 +6,17 @@ module Acclaim
|
|
6
6
|
# Parses arrays of strings and returns an Options instance containing data.
|
7
7
|
class Parser
|
8
8
|
|
9
|
-
class Error < StandardError
|
9
|
+
class Error < StandardError
|
10
|
+
|
11
|
+
def self.raise_wrong_arg_number(actual, minimum, optional)
|
12
|
+
raise self, "Wrong number of arguments (#{actual} for #{minimum})"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.raise_missing_arg(arg)
|
16
|
+
raise self, "Missing required argument (#{arg})"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
10
20
|
|
11
21
|
attr_accessor :argv, :options
|
12
22
|
|
@@ -55,7 +65,7 @@ module Acclaim
|
|
55
65
|
def build_options_instance!
|
56
66
|
Options.new.tap do |options_instance|
|
57
67
|
options.each do |option|
|
58
|
-
key = option.
|
68
|
+
key = option.key.to_sym
|
59
69
|
options_instance[key] = option.default
|
60
70
|
args = argv.find_all { |arg| option =~ arg }
|
61
71
|
if args.any?
|
@@ -73,13 +83,15 @@ module Acclaim
|
|
73
83
|
params = argv[arg_index + 1, len]
|
74
84
|
values = []
|
75
85
|
params.each do |param|
|
76
|
-
|
77
|
-
|
86
|
+
case param
|
87
|
+
when nil, /^-{1,2}/, /^-{2,}$/ then break
|
88
|
+
else
|
89
|
+
break if optional >= 0 and values.count >= minimum + optional
|
90
|
+
values << param
|
91
|
+
end
|
78
92
|
end
|
79
93
|
count = values.count
|
80
|
-
if count < minimum
|
81
|
-
raise Error, "Wrong number of arguments (%d for %d)" % [count, minimum]
|
82
|
-
end
|
94
|
+
Error.raise_wrong_arg_number count, *option.arity if count < minimum
|
83
95
|
options_instance[key] = if minimum == 1 and optional.zero?
|
84
96
|
values.first
|
85
97
|
else
|
@@ -89,6 +101,8 @@ module Acclaim
|
|
89
101
|
end
|
90
102
|
end
|
91
103
|
args.each { |arg| argv.delete arg }
|
104
|
+
else
|
105
|
+
Error.raise_missing_arg(option.names.join ' | ') if option.required?
|
92
106
|
end
|
93
107
|
end
|
94
108
|
end
|
data/lib/acclaim/options.rb
CHANGED
@@ -11,6 +11,10 @@ module Acclaim
|
|
11
11
|
data[key] = value
|
12
12
|
end
|
13
13
|
|
14
|
+
def merge!(other, &block)
|
15
|
+
data.merge! other.data, &block
|
16
|
+
end
|
17
|
+
|
14
18
|
# Handles the following cases:
|
15
19
|
#
|
16
20
|
# options.method = value
|
@@ -28,7 +32,7 @@ module Acclaim
|
|
28
32
|
end
|
29
33
|
end
|
30
34
|
|
31
|
-
|
35
|
+
protected
|
32
36
|
|
33
37
|
# The option values
|
34
38
|
def data
|
data/lib/acclaim/version.rb
CHANGED
@@ -6,7 +6,7 @@ describe Acclaim::Option::Parser do
|
|
6
6
|
describe '#parse!' do
|
7
7
|
|
8
8
|
let!(:args) do
|
9
|
-
%w(cmd
|
9
|
+
%w(cmd -a subcmd -b PARAM1 -cdef PARAM2 --long --parameters PARAM3 PARAM4 PARAM5 -- FILE1 FILE2)
|
10
10
|
end
|
11
11
|
|
12
12
|
subject { Acclaim::Option::Parser.new(args) }
|
@@ -32,13 +32,14 @@ describe Acclaim::Option::Parser do
|
|
32
32
|
let(:options) do
|
33
33
|
[].tap do |opts|
|
34
34
|
('a'..'f').each do |c|
|
35
|
-
hash = {
|
35
|
+
hash = { key: c, names: ["-#{c}"] }
|
36
36
|
hash[:arity] = [1, 0] if c == 'b' or c == 'f'
|
37
|
+
hash[:required] = true if c == 'd'
|
37
38
|
opts << Acclaim::Option.new(hash)
|
38
39
|
end
|
39
|
-
opts << Acclaim::Option.new(
|
40
|
-
opts << Acclaim::Option.new(
|
41
|
-
arity: [1,
|
40
|
+
opts << Acclaim::Option.new(key: 'long', names: ['--long'])
|
41
|
+
opts << Acclaim::Option.new(key: 'params', names: ['--parameters'],
|
42
|
+
arity: [1, 1], default: [])
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
@@ -63,7 +64,29 @@ describe Acclaim::Option::Parser do
|
|
63
64
|
|
64
65
|
it 'should leave unparsed arguments in argv' do
|
65
66
|
subject.parse!
|
66
|
-
args.should == %w(cmd subcmd -- FILE1 FILE2)
|
67
|
+
args.should == %w(cmd subcmd PARAM5 -- FILE1 FILE2)
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'but not given a required parameter' do
|
71
|
+
|
72
|
+
let!(:args) { %w(-db) }
|
73
|
+
|
74
|
+
it 'should raise an error' do
|
75
|
+
expect { subject.parse! }.to raise_error Acclaim::Option::Parser::Error,
|
76
|
+
/number of arguments/
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'but not passed a required option' do
|
82
|
+
|
83
|
+
let!(:args) { [] }
|
84
|
+
|
85
|
+
it 'should raise an error' do
|
86
|
+
expect { subject.parse! }.to raise_error Acclaim::Option::Parser::Error,
|
87
|
+
/required/
|
88
|
+
end
|
89
|
+
|
67
90
|
end
|
68
91
|
|
69
92
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acclaim
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1.
|
4
|
+
version: 0.0.1.alpha2
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-12-
|
12
|
+
date: 2011-12-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &7316860 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *7316860
|
25
25
|
description: Command-line option parser and command interface.
|
26
26
|
email: matheus.a.m.moreira@gmail.com
|
27
27
|
executables: []
|
@@ -29,6 +29,7 @@ extensions: []
|
|
29
29
|
extra_rdoc_files: []
|
30
30
|
files:
|
31
31
|
- .gitignore
|
32
|
+
- .rvmrc
|
32
33
|
- Gemfile
|
33
34
|
- LICENSE.GPLv3
|
34
35
|
- README.markdown
|
@@ -41,7 +42,7 @@ files:
|
|
41
42
|
- lib/acclaim/options.rb
|
42
43
|
- lib/acclaim/version.rb
|
43
44
|
- spec/acclaim/option/parser_spec.rb
|
44
|
-
homepage:
|
45
|
+
homepage: https://github.com/matheusmoreira/acclaim
|
45
46
|
licenses: []
|
46
47
|
post_install_message:
|
47
48
|
rdoc_options: []
|