acclaim 0.0.6 → 0.0.7
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/lib/acclaim/command.rb +7 -7
- data/lib/acclaim/option.rb +2 -1
- data/lib/acclaim/option/parser.rb +39 -27
- data/lib/acclaim/option/parser/regexp.rb +2 -25
- data/lib/acclaim/version.rb +1 -1
- data/spec/acclaim/command_spec.rb +69 -0
- data/spec/acclaim/option/parser_spec.rb +45 -2
- metadata +11 -10
data/lib/acclaim/command.rb
CHANGED
@@ -49,9 +49,9 @@ module Acclaim
|
|
49
49
|
module ClassMethods
|
50
50
|
|
51
51
|
# String which calls this command.
|
52
|
-
def line(
|
53
|
-
@line =
|
54
|
-
@line ||= name.gsub(/^.*::/, '').downcase
|
52
|
+
def line(*args)
|
53
|
+
@line = args.first unless args.empty?
|
54
|
+
@line ||= (name.gsub(/^.*::/, '').downcase rescue nil)
|
55
55
|
end
|
56
56
|
|
57
57
|
# Commands which may be given to this command.
|
@@ -72,8 +72,8 @@ module Acclaim
|
|
72
72
|
alias :opt :option
|
73
73
|
|
74
74
|
# The block which is executed when this command is called. It is given 2
|
75
|
-
# parameters; the first is an Ribbon
|
76
|
-
#
|
75
|
+
# parameters; the first is an Ribbon instance which can be queried for
|
76
|
+
# settings information; the second is the remaining command line.
|
77
77
|
def action(&block)
|
78
78
|
@action = block
|
79
79
|
end
|
@@ -102,7 +102,7 @@ module Acclaim
|
|
102
102
|
|
103
103
|
# Invokes this command with a fresh set of option values.
|
104
104
|
def run(*args)
|
105
|
-
invoke Ribbon
|
105
|
+
invoke Ribbon.new, args
|
106
106
|
rescue Option::Parser::Error => e
|
107
107
|
puts e.message
|
108
108
|
end
|
@@ -116,7 +116,7 @@ module Acclaim
|
|
116
116
|
# All argument separators will be deleted from the argument array before a
|
117
117
|
# command is executed.
|
118
118
|
def invoke(opts, args = [])
|
119
|
-
Ribbon
|
119
|
+
Ribbon.merge! opts, parse_options!(args)
|
120
120
|
handle_special_options! opts, args
|
121
121
|
if subcommand = parse_subcommands!(args)
|
122
122
|
subcommand.invoke(opts, args)
|
data/lib/acclaim/option.rb
CHANGED
@@ -7,7 +7,7 @@ module Acclaim
|
|
7
7
|
# Represents a command-line option.
|
8
8
|
class Option
|
9
9
|
|
10
|
-
attr_accessor :key, :names, :description, :type, :default, :handler
|
10
|
+
attr_accessor :key, :names, :description, :type, :default, :handler, :on_multiple
|
11
11
|
|
12
12
|
# Initializes a command line option. The +key+ is the object used to
|
13
13
|
# associate this option with a value. The other arguments may be:
|
@@ -45,6 +45,7 @@ module Acclaim
|
|
45
45
|
self.key = key
|
46
46
|
self.names = matches.fetch true, []
|
47
47
|
self.description = matches.fetch(false, []).first
|
48
|
+
self.on_multiple = options.fetch :on_multiple, :replace
|
48
49
|
self.arity = options[:arity]
|
49
50
|
self.default = options[:default]
|
50
51
|
self.required = options[:required]
|
@@ -14,16 +14,25 @@ module Acclaim
|
|
14
14
|
|
15
15
|
# Raises an Error with the following error message:
|
16
16
|
#
|
17
|
-
# Wrong number of arguments (actual for minimum)
|
17
|
+
# Wrong number of arguments (#{actual} for #{minimum})
|
18
18
|
def self.raise_wrong_arg_number(actual, minimum, optional)
|
19
19
|
raise self, "Wrong number of arguments (#{actual} for #{minimum})"
|
20
20
|
end
|
21
21
|
|
22
22
|
# Raises an Error with the following error message:
|
23
23
|
#
|
24
|
-
# Missing required argument (
|
25
|
-
def self.
|
26
|
-
|
24
|
+
# Missing required argument (#{option.names.join '|'})
|
25
|
+
def self.raise_missing_required(option)
|
26
|
+
names = option.names.join '|'
|
27
|
+
raise self, "Missing required argument (#{names})"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Raises an error with the following error message:
|
31
|
+
#
|
32
|
+
# Multiple instances of [#{options.names.join ''}] encountered
|
33
|
+
def self.raise_multiple(option)
|
34
|
+
names = option.names.join '|'
|
35
|
+
raise self, "Multiple instances of [#{names}] encountered"
|
27
36
|
end
|
28
37
|
|
29
38
|
end
|
@@ -34,8 +43,8 @@ module Acclaim
|
|
34
43
|
# options. If no option array is given, the argument array will be
|
35
44
|
# preprocessed only.
|
36
45
|
def initialize(argv, options = nil)
|
37
|
-
self.argv = argv
|
38
|
-
self.options = options
|
46
|
+
self.argv = argv || []
|
47
|
+
self.options = options || []
|
39
48
|
end
|
40
49
|
|
41
50
|
# Parses the meaning of the options given to this parser. If none were
|
@@ -51,13 +60,13 @@ module Acclaim
|
|
51
60
|
# options << Option.new(:verbose, '--verbose')
|
52
61
|
#
|
53
62
|
# Option::Parser.new(args, options).parse!
|
54
|
-
# => {
|
63
|
+
# => {Ribbon file: "log.txt", verbose: true}
|
55
64
|
#
|
56
65
|
# args
|
57
66
|
# => ["arg1", "arg2"]
|
58
67
|
def parse!
|
59
68
|
preprocess_argv!
|
60
|
-
parse_values!
|
69
|
+
parse_values!
|
61
70
|
end
|
62
71
|
|
63
72
|
private
|
@@ -98,25 +107,23 @@ module Acclaim
|
|
98
107
|
end
|
99
108
|
|
100
109
|
# Parses the options and their arguments, associating that information
|
101
|
-
# with a Ribbon
|
110
|
+
# with a Ribbon instance.
|
102
111
|
def parse_values!
|
103
|
-
ribbon = Ribbon
|
112
|
+
ribbon = Ribbon.new
|
104
113
|
options.each do |option|
|
105
114
|
key = option.key
|
106
|
-
ribbon[key] = option.default unless ribbon
|
115
|
+
ribbon[key] = option.default unless Ribbon[ribbon].has_key? key
|
107
116
|
switches = argv.find_all { |switch| option =~ switch }
|
108
|
-
if switches.
|
117
|
+
Error.raise_missing_required option if option.required? and switches.empty?
|
118
|
+
Error.raise_multiple option if option.on_multiple == :raise and switches.count > 1
|
119
|
+
switches.each do |switch|
|
109
120
|
if option.flag?
|
110
121
|
found_boolean option, ribbon
|
111
|
-
argv.
|
122
|
+
argv.delete_at argv.index(switch)
|
112
123
|
else
|
113
|
-
|
114
|
-
|
115
|
-
found_params_for option, ribbon, params
|
116
|
-
end
|
124
|
+
params = extract_parameters_of! option, switch
|
125
|
+
found_params_for option, params, ribbon
|
117
126
|
end
|
118
|
-
else
|
119
|
-
Error.raise_missing_arg(option.names.join ' | ') if option.required?
|
120
127
|
end
|
121
128
|
end
|
122
129
|
ribbon
|
@@ -150,8 +157,8 @@ module Acclaim
|
|
150
157
|
end
|
151
158
|
count = values.count
|
152
159
|
Error.raise_wrong_arg_number count, *arity if count < arity.required
|
153
|
-
argv.
|
154
|
-
values
|
160
|
+
argv.slice! switch_index..(switch_index + count)
|
161
|
+
values
|
155
162
|
end
|
156
163
|
|
157
164
|
# If the option has an custom handler associated, it will be called with
|
@@ -160,22 +167,27 @@ module Acclaim
|
|
160
167
|
# <tt>params.first</tt>, if the option takes only one argument, or to
|
161
168
|
# +params+ if it takes more.
|
162
169
|
#
|
170
|
+
# Appends +params+ to the current values of the option if the it specifies
|
171
|
+
# so. In this case, the value of the option will always be an array.
|
172
|
+
#
|
163
173
|
# The parameters will be converted according to the option's type.
|
164
|
-
def found_params_for(option,
|
174
|
+
def found_params_for(option, params = [], ribbon = Ribbon.new)
|
165
175
|
params = option.convert_parameters *params
|
166
|
-
if handler = option.handler then handler.call
|
176
|
+
if handler = option.handler then handler.call ribbon, params
|
167
177
|
else
|
178
|
+
key = option.key.to_sym
|
168
179
|
value = option.arity.total == 1 ? params.first : params
|
169
|
-
|
180
|
+
value = [*ribbon[key], *value] if option.on_multiple == :append
|
181
|
+
ribbon[option.key.to_sym] = value unless params.empty?
|
170
182
|
end
|
171
183
|
end
|
172
184
|
|
173
185
|
# If the option has an custom handler associated, it will be called with
|
174
186
|
# only the option values as the first argument. Otherwise, the value will
|
175
187
|
# be set to <tt>true</tt>.
|
176
|
-
def found_boolean(option,
|
177
|
-
if handler = option.handler then handler.call
|
178
|
-
else
|
188
|
+
def found_boolean(option, ribbon = Ribbon.new)
|
189
|
+
if handler = option.handler then handler.call ribbon
|
190
|
+
else ribbon[option.key.to_sym] = true end
|
179
191
|
end
|
180
192
|
|
181
193
|
end
|
@@ -7,9 +7,6 @@ module Acclaim
|
|
7
7
|
|
8
8
|
# Regular expression for a short option switch.
|
9
9
|
#
|
10
|
-
# Matches strings that begin with a single dash and contains only one
|
11
|
-
# word character before the end of the string.
|
12
|
-
#
|
13
10
|
# Examples: <tt>-s; -_</tt>
|
14
11
|
#
|
15
12
|
# <tt>'-mult'</tt> should match MULTIPLE_SHORT_SWITCHES, and will be
|
@@ -19,11 +16,6 @@ module Acclaim
|
|
19
16
|
|
20
17
|
# Regular expression for a long option switch.
|
21
18
|
#
|
22
|
-
# Matches strings that begin with a double dash, contain one or more
|
23
|
-
# word character or digit, and can be followed by either nothing or a
|
24
|
-
# single dash. The latter must be followed by one or more word character
|
25
|
-
# or digit.
|
26
|
-
#
|
27
19
|
# Examples: <tt>--long; --no-feature; --with_underscore;
|
28
20
|
# --_private-option; --1-1</tt>
|
29
21
|
LONG_SWITCH = /\A--[\w\d]+(-[\w\d]+)*\Z/
|
@@ -31,24 +23,12 @@ module Acclaim
|
|
31
23
|
# Regular expression for multiple short options in a single "short"
|
32
24
|
# switch.
|
33
25
|
#
|
34
|
-
#
|
35
|
-
# more word characters, among which is the underscore but not the dash
|
36
|
-
# character.
|
37
|
-
#
|
38
|
-
# Examples: -xvf, -abc, -de_f
|
26
|
+
# Examples: <tt>-xvf; -abc; -de_f</tt>
|
39
27
|
MULTIPLE_SHORT_SWITCHES = /\A-\w{2,}\Z/
|
40
28
|
|
41
29
|
# Regular expression for a long switch connected to its parameters with
|
42
30
|
# an equal sign. Multiple parameters are be separated by commas.
|
43
31
|
#
|
44
|
-
# Matches strings that begin with a double dash, are followed by one or
|
45
|
-
# more word character or digit and may be followed by one dash and one
|
46
|
-
# or more word character or digit.
|
47
|
-
#
|
48
|
-
# After that, there must be an equals sign, which must be followed by
|
49
|
-
# either nothing, any number of commmas or any number of word characters
|
50
|
-
# or digits.
|
51
|
-
#
|
52
32
|
# Examples:
|
53
33
|
# <tt>
|
54
34
|
# --switch=PARAM; --files=f1,f2,f3; --weird=,PARAM2; --empty=,,; --none=
|
@@ -63,15 +43,12 @@ module Acclaim
|
|
63
43
|
SWITCH_PARAM_EQUALS = /\A--[\w\d]+(-[\w\d]+)*=(,*[\w\d]*)*\Z/
|
64
44
|
|
65
45
|
# Regular expression for any kind of option switch.
|
66
|
-
#
|
67
|
-
# Matches anything that matches any of the other switch regular
|
68
|
-
# expressions.
|
69
46
|
SWITCH = /(#{SHORT_SWITCH})|(#{LONG_SWITCH})|(#{MULTIPLE_SHORT_SWITCHES})|(#{SWITCH_PARAM_EQUALS})/
|
70
47
|
|
71
48
|
# Regular expression for the string that separates options and their
|
72
49
|
# parameters from arguments like filenames.
|
73
50
|
#
|
74
|
-
#
|
51
|
+
# Examples: <tt>--; ---</tt>
|
75
52
|
ARGUMENT_SEPARATOR = /\A-{2,}\Z/
|
76
53
|
|
77
54
|
end
|
data/lib/acclaim/version.rb
CHANGED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'acclaim/command'
|
2
|
+
|
3
|
+
module Test; end
|
4
|
+
class Test::Command < Acclaim::Command; end
|
5
|
+
class Test::Command::Subcommand < Test::Command; end
|
6
|
+
|
7
|
+
describe Acclaim::Command do
|
8
|
+
|
9
|
+
describe Test::Command do
|
10
|
+
subject { Test::Command }
|
11
|
+
|
12
|
+
describe '::line' do
|
13
|
+
context 'when not given a parameter' do
|
14
|
+
it 'should return the default command name of the class' do
|
15
|
+
subject.line.should == 'command'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'when given a parameter' do
|
20
|
+
let(:param) { 'test2' }
|
21
|
+
before(:all) { subject.line param }
|
22
|
+
after(:all) { subject.line nil } # Reset command name.
|
23
|
+
|
24
|
+
it 'should set the command name of the class' do
|
25
|
+
subject.line.should == param
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '::subcommands' do
|
31
|
+
it 'should not be empty' do
|
32
|
+
subject.subcommands.should_not be_empty
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should include the subcommand' do
|
36
|
+
subject.subcommands.should include(Test::Command::Subcommand)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe Test::Command::Subcommand do
|
42
|
+
subject { Test::Command::Subcommand }
|
43
|
+
|
44
|
+
describe '::line' do
|
45
|
+
context 'when not given a parameter' do
|
46
|
+
it 'should return the default command name of the class' do
|
47
|
+
subject.line.should == 'subcommand'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when given a parameter' do
|
52
|
+
let(:param) { 'subcommand2' }
|
53
|
+
before(:all) { subject.line param }
|
54
|
+
after(:all) { subject.line nil } # Reset command name.
|
55
|
+
|
56
|
+
it 'should set the command name of the class' do
|
57
|
+
subject.line.should == param
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '::subcommands' do
|
63
|
+
it 'should be empty' do
|
64
|
+
subject.subcommands.should be_empty
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -74,8 +74,8 @@ describe Acclaim::Option::Parser do
|
|
74
74
|
end
|
75
75
|
|
76
76
|
context 'when not given an array of options' do
|
77
|
-
it 'should
|
78
|
-
subject.parse!.
|
77
|
+
it 'should return an empty ribbon' do
|
78
|
+
subject.parse!.should_not be_nil
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
@@ -200,6 +200,49 @@ describe Acclaim::Option::Parser do
|
|
200
200
|
end
|
201
201
|
end
|
202
202
|
end
|
203
|
+
|
204
|
+
context 'containing an option which' do
|
205
|
+
let!(:options) { [ Acclaim::Option.new(:files, '-f', arity: [1,0], on_multiple: on_multiple, &block) ] }
|
206
|
+
let!(:args) { %w(-f f1 -f f2 -f f3) }
|
207
|
+
let(:block) { nil }
|
208
|
+
|
209
|
+
context 'replaces the previously found value' do
|
210
|
+
let(:on_multiple) { :replace }
|
211
|
+
|
212
|
+
it 'should replace the value with the last argument found' do
|
213
|
+
subject.parse!.files.should == 'f3'
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
context 'appends to the previously found values' do
|
218
|
+
let(:on_multiple) { :append }
|
219
|
+
|
220
|
+
it 'should return all arguments' do
|
221
|
+
subject.parse!.files.should == %w(f1 f2 f3)
|
222
|
+
end
|
223
|
+
|
224
|
+
context 'but that was passed a handler block' do
|
225
|
+
let(:block) { proc { |values| values.files = :block } }
|
226
|
+
|
227
|
+
it 'should parse all options and arguments' do
|
228
|
+
subject.parse!
|
229
|
+
args.should be_empty
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should use the handler to obtain the option's value" do
|
233
|
+
subject.parse!.files.should == :block
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
context 'raises an error if encountered multiple times' do
|
239
|
+
let(:on_multiple) { :raise }
|
240
|
+
|
241
|
+
it 'should raise a parser error' do
|
242
|
+
expect { subject.parse! }.to raise_error Acclaim::Option::Parser::Error, /multiple/i
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
203
246
|
end
|
204
247
|
end
|
205
248
|
|
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.
|
4
|
+
version: 0.0.7
|
5
5
|
prerelease:
|
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:
|
12
|
+
date: 2012-01-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ribbon
|
16
|
-
requirement: &
|
16
|
+
requirement: &17514000 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *17514000
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &17513440 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *17513440
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rookie
|
38
|
-
requirement: &
|
38
|
+
requirement: &17513000 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *17513000
|
47
47
|
description: Command-line option parser and command interface.
|
48
48
|
email: matheus.a.m.moreira@gmail.com
|
49
49
|
executables: []
|
@@ -76,6 +76,7 @@ files:
|
|
76
76
|
- lib/acclaim/option/type/time.rb
|
77
77
|
- lib/acclaim/option/type/uri.rb
|
78
78
|
- lib/acclaim/version.rb
|
79
|
+
- spec/acclaim/command_spec.rb
|
79
80
|
- spec/acclaim/option/arity_spec.rb
|
80
81
|
- spec/acclaim/option/parser_spec.rb
|
81
82
|
- spec/acclaim/option_spec.rb
|
@@ -93,7 +94,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
94
|
version: '0'
|
94
95
|
segments:
|
95
96
|
- 0
|
96
|
-
hash:
|
97
|
+
hash: 1879446143954765838
|
97
98
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
99
|
none: false
|
99
100
|
requirements:
|
@@ -102,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
103
|
version: '0'
|
103
104
|
segments:
|
104
105
|
- 0
|
105
|
-
hash:
|
106
|
+
hash: 1879446143954765838
|
106
107
|
requirements: []
|
107
108
|
rubyforge_project:
|
108
109
|
rubygems_version: 1.8.10
|