acclaim 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/lib/acclaim.rb +11 -1
- data/lib/acclaim/command.rb +44 -13
- data/lib/acclaim/command/help.rb +51 -0
- data/lib/acclaim/command/version.rb +30 -0
- data/lib/acclaim/option.rb +3 -5
- data/lib/acclaim/option/arity.rb +4 -0
- data/lib/acclaim/option/parser.rb +23 -40
- data/lib/acclaim/option/parser/regexp.rb +79 -0
- data/lib/acclaim/option/values.rb +10 -7
- data/lib/acclaim/version.rb +1 -1
- data/spec/acclaim/option/arity_spec.rb +236 -0
- data/spec/acclaim/option/parser_spec.rb +161 -50
- metadata +8 -4
data/.gitignore
CHANGED
data/lib/acclaim.rb
CHANGED
@@ -1 +1,11 @@
|
|
1
|
-
%w(
|
1
|
+
%w(
|
2
|
+
|
3
|
+
command
|
4
|
+
option
|
5
|
+
option/arity
|
6
|
+
option/parser
|
7
|
+
option/parser/regexp
|
8
|
+
option/values
|
9
|
+
version
|
10
|
+
|
11
|
+
).each { |file| require file.prepend 'acclaim/' }
|
data/lib/acclaim/command.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
require 'acclaim/command/help'
|
2
|
+
require 'acclaim/command/version'
|
1
3
|
require 'acclaim/option'
|
2
4
|
require 'acclaim/option/parser'
|
5
|
+
require 'acclaim/option/parser/regexp'
|
3
6
|
require 'acclaim/option/values'
|
4
7
|
|
5
8
|
module Acclaim
|
@@ -12,21 +15,21 @@ module Acclaim
|
|
12
15
|
#
|
13
16
|
# A subcommand benefits from its parent's option processing.
|
14
17
|
#
|
15
|
-
# app do something --option --option-for-something
|
18
|
+
# app --global-option do something --option-for-do --option-for-something
|
16
19
|
#
|
17
|
-
# A command can be
|
20
|
+
# A command can be created in the following form:
|
18
21
|
#
|
19
22
|
# class App::Command < Acclaim::Command
|
20
|
-
#
|
23
|
+
# option :verbose, '-v', '--verbose', 'Run verbosely'
|
21
24
|
# end
|
22
25
|
#
|
23
26
|
# A subcommand can be created by inheriting from another command:
|
24
27
|
#
|
25
28
|
# class App::Command::Do < App::Command
|
26
|
-
# opt :what,
|
29
|
+
# opt :what, '-W', '--what', 'What to do', arity: [1, 0], required: true
|
27
30
|
# when_called do |options, arguments|
|
28
|
-
# puts "Verbose? #{options.verbose? ?
|
29
|
-
# puts "Doing #{options.what} with #{arguments.
|
31
|
+
# puts "Verbose? #{options.verbose? ? :yes : :no}"
|
32
|
+
# puts "Doing #{options.what} with #{arguments.join ' and ')}!"
|
30
33
|
# end
|
31
34
|
# end
|
32
35
|
#
|
@@ -34,11 +37,11 @@ module Acclaim
|
|
34
37
|
#
|
35
38
|
# App::Command.run *ARGV
|
36
39
|
#
|
37
|
-
#
|
40
|
+
# See it in action:
|
38
41
|
#
|
39
|
-
# $ app --verbose
|
42
|
+
# $ app --verbose do --what testing acclaim safeguard
|
40
43
|
# Verbose? yes
|
41
|
-
# Doing
|
44
|
+
# Doing testing with acclaim and safeguard!
|
42
45
|
class Command
|
43
46
|
|
44
47
|
# Module containing the class methods every command class should inherit.
|
@@ -46,8 +49,8 @@ module Acclaim
|
|
46
49
|
|
47
50
|
# String which calls this command.
|
48
51
|
def line(value = nil)
|
49
|
-
@line = value
|
50
|
-
@line
|
52
|
+
@line = value
|
53
|
+
@line ||= name.gsub(/^.*::/, '').downcase
|
51
54
|
end
|
52
55
|
|
53
56
|
# Commands which may be given to this command.
|
@@ -76,6 +79,14 @@ module Acclaim
|
|
76
79
|
|
77
80
|
alias :when_called :action
|
78
81
|
|
82
|
+
def help(opts = {})
|
83
|
+
subcommands << Help.create(self, opts)
|
84
|
+
end
|
85
|
+
|
86
|
+
def version(version_string, opts = {})
|
87
|
+
subcommands << Version.create(self, version_string, opts)
|
88
|
+
end
|
89
|
+
|
79
90
|
# Parses the argument array using this command's set of options.
|
80
91
|
def parse_options!(args)
|
81
92
|
Option::Parser.new(args, options).parse!
|
@@ -94,7 +105,10 @@ module Acclaim
|
|
94
105
|
# otherwise.
|
95
106
|
def invoke(opts, args = [])
|
96
107
|
opts.merge! parse_options!(args)
|
97
|
-
|
108
|
+
handle_special_options! opts, args
|
109
|
+
arg_separator = args.find do |arg|
|
110
|
+
arg =~ Option::Parser::Regexp::ARGUMENT_SEPARATOR
|
111
|
+
end
|
98
112
|
separator_index = args.index arg_separator
|
99
113
|
subcommands.find do |subcommand|
|
100
114
|
index = args.index subcommand.line
|
@@ -118,13 +132,30 @@ module Acclaim
|
|
118
132
|
|
119
133
|
alias :call :execute
|
120
134
|
|
135
|
+
def root?
|
136
|
+
superclass == Acclaim::Command
|
137
|
+
end
|
138
|
+
|
139
|
+
def root
|
140
|
+
command = self
|
141
|
+
command = command.superclass until command.root?
|
142
|
+
command
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
# Handles special options such as <tt>--help</tt> or <tt>--version</tt>.
|
148
|
+
def handle_special_options!(opts, args)
|
149
|
+
const_get(:Help).execute opts, args if opts.acclaim_help?
|
150
|
+
const_get(:Version).execute opts, args if opts.acclaim_version?
|
151
|
+
end
|
152
|
+
|
121
153
|
end
|
122
154
|
|
123
155
|
# Add the class methods to the subclass and add it to this command's list of
|
124
156
|
# subcommands.
|
125
157
|
def self.inherited(sub)
|
126
158
|
sub.extend ClassMethods
|
127
|
-
sub.line sub.name.gsub(/^.*::/, '').downcase
|
128
159
|
subcommands << sub if respond_to? :subcommands
|
129
160
|
end
|
130
161
|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Acclaim
|
2
|
+
class Command
|
3
|
+
|
4
|
+
# Module which adds help support to a command.
|
5
|
+
module Help
|
6
|
+
|
7
|
+
def self.add_options_to!(command, opts = {})
|
8
|
+
switches = opts.fetch :switches, %w(-h --help)
|
9
|
+
description = opts.fetch :description, 'Show usage information and exit.'
|
10
|
+
command.option :acclaim_help, *switches, description
|
11
|
+
end
|
12
|
+
|
13
|
+
private_class_method :add_options_to!
|
14
|
+
|
15
|
+
def self.create(base, opts = {})
|
16
|
+
if opts.fetch :options, true
|
17
|
+
add_options_to! base, opts
|
18
|
+
end
|
19
|
+
base.const_set(:Help, Class.new(base)).tap do |help_command|
|
20
|
+
help_command.when_called do |options, args|
|
21
|
+
display_help_for base.root
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Displays a very simple help screen for the given command and all its
|
28
|
+
# subcommands.
|
29
|
+
def self.display_help_for(command)
|
30
|
+
# TODO rewrite this VERY CRUDE implementation.
|
31
|
+
# Look into how to code a text formatter later.
|
32
|
+
help_string = ''
|
33
|
+
command.options.tap do |options|
|
34
|
+
if options.any?
|
35
|
+
help_string << "\nCommand '#{command.line}':\n\n" unless command.root?
|
36
|
+
max = options.map { |option| option.names.join(', ').length }.max
|
37
|
+
options.each do |option|
|
38
|
+
switches = option.names.join ', '
|
39
|
+
help_string << ' ' * 4 << switches << ' ' * (4 + max - switches.length)
|
40
|
+
help_string << option.description << "\n"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
puts help_string unless help_string.empty?
|
45
|
+
command.subcommands.each { |subcommand| display_help_for subcommand }
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Acclaim
|
2
|
+
class Command
|
3
|
+
|
4
|
+
# Module which adds version query support to a command.
|
5
|
+
module Version
|
6
|
+
|
7
|
+
def self.add_options_to!(command, opts = {})
|
8
|
+
switches = opts.fetch :switches, %w(-v --version)
|
9
|
+
description = opts.fetch :description, 'Show version and exit.'
|
10
|
+
command.option :acclaim_version, *switches, description
|
11
|
+
end
|
12
|
+
|
13
|
+
private_class_method :add_options_to!
|
14
|
+
|
15
|
+
def self.create(base, version_string, opts = {})
|
16
|
+
if opts.fetch :options, true
|
17
|
+
add_options_to! base, opts
|
18
|
+
end
|
19
|
+
base.const_set(:Version, Class.new(base)).tap do |version_command|
|
20
|
+
version_command.when_called do |options, args|
|
21
|
+
puts version_string
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
data/lib/acclaim/option.rb
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
require 'acclaim/option/arity'
|
2
|
-
require 'acclaim/option/parser'
|
2
|
+
require 'acclaim/option/parser/regexp'
|
3
3
|
|
4
4
|
module Acclaim
|
5
5
|
|
6
6
|
# Represents a command-line option.
|
7
7
|
class Option
|
8
8
|
|
9
|
-
attributes = %w().map!(&:to_sym).freeze
|
10
|
-
|
11
9
|
attr_accessor :key, :names, :description, :type, :default
|
12
10
|
|
13
11
|
def initialize(key, *args)
|
14
12
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
15
13
|
self.key = key
|
16
|
-
self.names = args.find_all { |arg| arg =~ Parser::SWITCH }
|
17
|
-
self.description = args.find { |arg| arg !~ Parser::SWITCH }
|
14
|
+
self.names = args.find_all { |arg| arg =~ Parser::Regexp::SWITCH }
|
15
|
+
self.description = args.find { |arg| arg !~ Parser::Regexp::SWITCH }
|
18
16
|
self.type = args.find { |arg| arg.is_a? Class }
|
19
17
|
self.arity = options[:arity]
|
20
18
|
self.default = options[:default]
|
data/lib/acclaim/option/arity.rb
CHANGED
@@ -8,6 +8,10 @@ module Acclaim
|
|
8
8
|
attr_accessor :minimum, :optional
|
9
9
|
alias :required :minimum
|
10
10
|
|
11
|
+
# Initializes this arity with a number of required parameters and a number
|
12
|
+
# of optional parameters. If the latter is less than zero, then it means
|
13
|
+
# the option may take infinite parameters, as long as it takes at least
|
14
|
+
# +minimum+ parameters.
|
11
15
|
def initialize(minimum = 0, optional = 0)
|
12
16
|
@minimum, @optional = minimum, optional
|
13
17
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'acclaim/option/parser/regexp'
|
1
2
|
require 'acclaim/option/values'
|
2
3
|
|
3
4
|
module Acclaim
|
@@ -6,31 +7,7 @@ module Acclaim
|
|
6
7
|
# Parses arrays of strings and returns an Options instance containing data.
|
7
8
|
class Parser
|
8
9
|
|
9
|
-
|
10
|
-
#
|
11
|
-
# Matches strings that begin with 1 or 2 dashes, are followed by at least
|
12
|
-
# one word character or number, and may be followed by any other word
|
13
|
-
# character, number or dash.
|
14
|
-
#
|
15
|
-
# Examples: -s, --long, -multiple, -1, --no-feature, --with_underscore,
|
16
|
-
# --_private_option, etc.
|
17
|
-
SWITCH = /^-{1,2}[\w\d]+[\w\d-]*$/
|
18
|
-
|
19
|
-
# Regular expression for multiple short options in a single "short"
|
20
|
-
# switch.
|
21
|
-
#
|
22
|
-
# Matches strings that begin with a single dash and are followed by 2 or
|
23
|
-
# more word characters, among which is the underscore but not the dash
|
24
|
-
# character.
|
25
|
-
#
|
26
|
-
# Examples: -xvf, -abc, -de_f
|
27
|
-
MULTIPLE_SHORT_SWITCHES = /^-\w{2,}$/
|
28
|
-
|
29
|
-
# Regular expression for the string that separates options and their
|
30
|
-
# parameters from arguments like filenames.
|
31
|
-
#
|
32
|
-
# Matches strings made up of 2 or more dashes.
|
33
|
-
ARGUMENT_SEPARATOR = /^-{2,}$/
|
10
|
+
include Parser::Regexp
|
34
11
|
|
35
12
|
class Error < StandardError
|
36
13
|
|
@@ -54,9 +31,13 @@ module Acclaim
|
|
54
31
|
self.options = options
|
55
32
|
end
|
56
33
|
|
34
|
+
# Parses the meaning of the options given to this parser. If none were
|
35
|
+
# given, the argument array will only be preprocessed. Any parsed options
|
36
|
+
# and arguments will be removed from the argument array, so pass in a
|
37
|
+
# duplicate if you need the original.
|
57
38
|
def parse!
|
58
39
|
preprocess_argv!
|
59
|
-
|
40
|
+
parse_values! unless options.nil?
|
60
41
|
end
|
61
42
|
|
62
43
|
private
|
@@ -64,9 +45,8 @@ module Acclaim
|
|
64
45
|
# Argument array preprocessing.
|
65
46
|
def preprocess_argv!
|
66
47
|
split_multiple_short_options!
|
48
|
+
normalize_parameters!
|
67
49
|
# TODO: normalize parameter formats?
|
68
|
-
# --switch=PARAM1[,PARAM2,PARAM3] - split on =, then split on comma,
|
69
|
-
# then reinsert them into argv
|
70
50
|
# -sPARAM1[,PARAM2,PARAM3...] - possibly incompatible with split_multiple_short_options!
|
71
51
|
argv.compact!
|
72
52
|
end
|
@@ -75,16 +55,22 @@ module Acclaim
|
|
75
55
|
argv.find_all { |arg| arg =~ MULTIPLE_SHORT_SWITCHES }.each do |multiples|
|
76
56
|
multiples_index = argv.index multiples
|
77
57
|
argv.delete multiples
|
78
|
-
|
79
|
-
|
80
|
-
options.each_index do |option_index|
|
81
|
-
argv.insert multiples_index + option_index, options[option_index]
|
82
|
-
end
|
83
|
-
end
|
58
|
+
switches = multiples.sub!(/^-/, '').split(//).each { |letter| letter.prepend '-' }
|
59
|
+
argv.insert multiples_index, *switches
|
84
60
|
end
|
85
61
|
end
|
86
62
|
|
87
|
-
def
|
63
|
+
def normalize_parameters!
|
64
|
+
argv.find_all { |arg| arg =~ SWITCH_PARAM_EQUALS }.each do |switch|
|
65
|
+
switch_index = argv.index switch
|
66
|
+
argv.delete switch
|
67
|
+
switch, params = switch.split /\=/
|
68
|
+
params = (params or '').split /,/
|
69
|
+
argv.insert switch_index, *[ switch, *params ]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def parse_values!
|
88
74
|
Values.new.tap do |options_instance|
|
89
75
|
options.each do |option|
|
90
76
|
key = option.key.to_sym
|
@@ -114,11 +100,8 @@ module Acclaim
|
|
114
100
|
end
|
115
101
|
count = values.count
|
116
102
|
Error.raise_wrong_arg_number count, *option.arity if count < arity.required
|
117
|
-
|
118
|
-
|
119
|
-
else
|
120
|
-
values
|
121
|
-
end
|
103
|
+
value = if arity.total == 1 then values.first else values end
|
104
|
+
options_instance[key] = value unless values.empty?
|
122
105
|
values.each { |value| argv.delete value }
|
123
106
|
end
|
124
107
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Acclaim
|
2
|
+
class Option
|
3
|
+
class Parser
|
4
|
+
|
5
|
+
# Contains all regular expressions used by the parser.
|
6
|
+
module Regexp
|
7
|
+
|
8
|
+
# Regular expression for a short option switch.
|
9
|
+
#
|
10
|
+
# Matches strings that begin with a single dash and contain only word
|
11
|
+
# characters or digits until the end of the string.
|
12
|
+
#
|
13
|
+
# Examples: <tt>-s; -mult; -5; -_</tt>
|
14
|
+
#
|
15
|
+
# <tt>'-mult'</tt> will be split into <tt>%w(-m -u -l -t)</tt>.
|
16
|
+
SHORT_SWITCH = /\A-[\w\d]+\Z/
|
17
|
+
|
18
|
+
# Regular expression for a long option switch.
|
19
|
+
#
|
20
|
+
# Matches strings that begin with a double dash, contain one or more
|
21
|
+
# word character or digit, and can be followed by either nothing or a
|
22
|
+
# single dash. The latter must be followed by one or more word character
|
23
|
+
# or digit.
|
24
|
+
#
|
25
|
+
# Examples: <tt>--long; --no-feature; --with_underscore;
|
26
|
+
# --_private-option; --1-1</tt>
|
27
|
+
LONG_SWITCH = /\A--[\w\d]+(-[\w\d]+)*\Z/
|
28
|
+
|
29
|
+
# Regular expression for any kind of option switch.
|
30
|
+
#
|
31
|
+
# Matches either a SHORT_SWITCH or a LONG_SWITCH. See their descriptions
|
32
|
+
# for details.
|
33
|
+
SWITCH = /(#{SHORT_SWITCH})|(#{LONG_SWITCH})/
|
34
|
+
|
35
|
+
# Regular expression for multiple short options in a single "short"
|
36
|
+
# switch.
|
37
|
+
#
|
38
|
+
# Matches strings that begin with a single dash and are followed by 2 or
|
39
|
+
# more word characters, among which is the underscore but not the dash
|
40
|
+
# character.
|
41
|
+
#
|
42
|
+
# Examples: -xvf, -abc, -de_f
|
43
|
+
MULTIPLE_SHORT_SWITCHES = /\A-\w{2,}\Z/
|
44
|
+
|
45
|
+
# Regular expression for a long switch connected to its parameters with
|
46
|
+
# an equal sign. Multiple parameters are be separated by commas.
|
47
|
+
#
|
48
|
+
# Matches strings that begin with a double dash, are followed by one or
|
49
|
+
# more word character or digit and may be followed by one dash and one
|
50
|
+
# or more word character or digit.
|
51
|
+
#
|
52
|
+
# After that, there must be an equals sign, which must be followed by
|
53
|
+
# either nothing, any number of commmas or any number of word characters
|
54
|
+
# or digits.
|
55
|
+
#
|
56
|
+
# Examples:
|
57
|
+
# <tt>
|
58
|
+
# --switch=PARAM; --files=f1,f2,f3; --weird=,PARAM2; --empty=,,; --none=
|
59
|
+
# </tt>
|
60
|
+
#
|
61
|
+
# The reason something like <tt>'--none='</tt> is allowed is because it
|
62
|
+
# will become <tt>['--none']</tt> when it is split up.
|
63
|
+
# <tt>'--empty=,,'</tt> will become <tt>['--empty']</tt>
|
64
|
+
# <tt>'--weird=,PARAM2'</tt> will become
|
65
|
+
# <tt>['--weird', '', 'PARAM2']</tt> when it is split up. What to make
|
66
|
+
# of those isn't a decision for a preprocessor.
|
67
|
+
SWITCH_PARAM_EQUALS = /\A--[\w\d]+(-?[\w\d]+)*=(,*[\w\d]*)*\Z/
|
68
|
+
|
69
|
+
# Regular expression for the string that separates options and their
|
70
|
+
# parameters from arguments like filenames.
|
71
|
+
#
|
72
|
+
# Matches strings made up of 2 or more dashes.
|
73
|
+
ARGUMENT_SEPARATOR = /\A-{2,}\Z/
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -4,30 +4,33 @@ module Acclaim
|
|
4
4
|
# Represents a set of option values.
|
5
5
|
class Values
|
6
6
|
|
7
|
+
# Gets a value by key.
|
7
8
|
def [](key)
|
8
9
|
data[key]
|
9
10
|
end
|
10
11
|
|
12
|
+
# Sets a value by key.
|
11
13
|
def []=(key, value)
|
12
14
|
data[key] = value
|
13
15
|
end
|
14
16
|
|
17
|
+
# Merge these values with the others.
|
15
18
|
def merge!(other, &block)
|
16
19
|
data.merge! other.data, &block
|
17
20
|
end
|
18
21
|
|
19
22
|
# Handles the following cases:
|
20
23
|
#
|
21
|
-
# options.method = value
|
22
|
-
# options.method?
|
23
|
-
# options.method
|
24
|
+
# options.method = value => options[method] = value
|
25
|
+
# options.method? => options[method] ? true : false
|
26
|
+
# options.method => options[method]
|
24
27
|
def method_missing(method, *args, &block)
|
25
|
-
m = method.to_s
|
26
|
-
case
|
28
|
+
m = method.to_s.chop!.to_sym
|
29
|
+
case method
|
27
30
|
when /=$/
|
28
|
-
self[
|
31
|
+
self[m] = args.first
|
29
32
|
when /\?$/
|
30
|
-
self[
|
33
|
+
self[m] ? true : false
|
31
34
|
else
|
32
35
|
self[method]
|
33
36
|
end
|
data/lib/acclaim/version.rb
CHANGED
@@ -0,0 +1,236 @@
|
|
1
|
+
require 'acclaim/option/arity'
|
2
|
+
|
3
|
+
describe Acclaim::Option::Arity do
|
4
|
+
|
5
|
+
subject { Acclaim::Option::Arity.new(required, optional) }
|
6
|
+
|
7
|
+
context 'an arity that does not require any parameters' do
|
8
|
+
let(:required) { 0 }
|
9
|
+
let(:optional) { 0 }
|
10
|
+
|
11
|
+
describe '#only?' do
|
12
|
+
context 'when given zero' do
|
13
|
+
it 'should return true' do
|
14
|
+
subject.only?(0).should be_true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when given a non-zero number' do
|
19
|
+
let(:number) { 1 }
|
20
|
+
|
21
|
+
it 'should return false' do
|
22
|
+
subject.only?(number).should be_false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#unlimited?' do
|
28
|
+
it 'should return false' do
|
29
|
+
subject.unlimited?.should be_false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#bound?' do
|
34
|
+
it 'should return true' do
|
35
|
+
subject.bound?.should be_true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#total' do
|
40
|
+
it 'should equal zero' do
|
41
|
+
subject.total.should == 0
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'but allows five optional parameters' do
|
46
|
+
let(:optional) { 5 }
|
47
|
+
|
48
|
+
describe '#only?' do
|
49
|
+
context 'when given the number of required parameters' do
|
50
|
+
it 'should return false' do
|
51
|
+
subject.only?(required).should be_false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when given a number other than the required number of parameters' do
|
56
|
+
let(:number) { required + 10 }
|
57
|
+
|
58
|
+
it 'should return false' do
|
59
|
+
subject.only?(number).should be_false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#unlimited?' do
|
65
|
+
it 'should return false' do
|
66
|
+
subject.unlimited?.should be_false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#bound?' do
|
71
|
+
it 'should return true' do
|
72
|
+
subject.bound?.should be_true
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#total' do
|
77
|
+
it 'should equal five' do
|
78
|
+
subject.total.should == 5
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'but allows for unlimited additional parameters' do
|
84
|
+
let(:optional) { -1 }
|
85
|
+
|
86
|
+
describe '#only?' do
|
87
|
+
context 'when given the number of required parameters' do
|
88
|
+
it 'should return false' do
|
89
|
+
subject.only?(required).should be_false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when given a number other than the required number of parameters' do
|
94
|
+
let(:number) { required + 10 }
|
95
|
+
|
96
|
+
it 'should return false' do
|
97
|
+
subject.only?(number).should be_false
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe '#unlimited?' do
|
103
|
+
it 'should return true' do
|
104
|
+
subject.unlimited?.should be_true
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '#bound?' do
|
109
|
+
it 'should return false' do
|
110
|
+
subject.bound?.should be_false
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe '#total' do
|
115
|
+
it 'should return nil' do
|
116
|
+
subject.total.should be_nil
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'an arity which requires one parameter' do
|
123
|
+
let(:required) { 1 }
|
124
|
+
let(:optional) { 0 }
|
125
|
+
|
126
|
+
describe '#only?' do
|
127
|
+
context 'when given one' do
|
128
|
+
it 'should return true' do
|
129
|
+
subject.only?(required).should be_true
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'when given a number other than the required number of parameters' do
|
134
|
+
let(:number) { required + 10 }
|
135
|
+
it 'should return false' do
|
136
|
+
subject.only?(number).should be_false
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe '#unlimited?' do
|
142
|
+
it 'should return false' do
|
143
|
+
subject.unlimited?.should be_false
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe '#bound?' do
|
148
|
+
it 'should return true' do
|
149
|
+
subject.bound?.should be_true
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe '#total' do
|
154
|
+
it 'should equal one' do
|
155
|
+
subject.total.should == 1
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context 'and allows for 4 additional parameters' do
|
160
|
+
let(:optional) { 4 }
|
161
|
+
|
162
|
+
describe '#only?' do
|
163
|
+
context 'when given the number of required parameters' do
|
164
|
+
it 'should return false' do
|
165
|
+
subject.only?(required).should be_false
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'when given a number other than the required number of parameters' do
|
170
|
+
let(:number) { required + 10 }
|
171
|
+
|
172
|
+
it 'should return false' do
|
173
|
+
subject.only?(number).should be_false
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe '#unlimited?' do
|
179
|
+
it 'should return false' do
|
180
|
+
subject.unlimited?.should be_false
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe '#bound?' do
|
185
|
+
it 'should return true' do
|
186
|
+
subject.bound?.should be_true
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe '#total' do
|
191
|
+
it 'should equal five' do
|
192
|
+
subject.total.should == 5
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'and allows for unlimited additional parameters' do
|
198
|
+
let(:optional) { -1 }
|
199
|
+
|
200
|
+
describe '#only?' do
|
201
|
+
context 'when given the number of required parameters' do
|
202
|
+
it 'should return false' do
|
203
|
+
subject.only?(required).should be_false
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
context 'when given a number other than the required number of parameters' do
|
208
|
+
let(:number) { required + 10 }
|
209
|
+
|
210
|
+
it 'should return false' do
|
211
|
+
subject.only?(number).should be_false
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe '#unlimited?' do
|
217
|
+
it 'should return true' do
|
218
|
+
subject.unlimited?.should be_true
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
describe '#bound?' do
|
223
|
+
it 'should return false' do
|
224
|
+
subject.bound?.should be_false
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
describe '#total' do
|
229
|
+
it 'should return nil' do
|
230
|
+
subject.total.should be_nil
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
@@ -3,93 +3,204 @@ require 'acclaim/option/parser'
|
|
3
3
|
|
4
4
|
describe Acclaim::Option::Parser do
|
5
5
|
|
6
|
+
let!(:args) { [] }
|
7
|
+
let(:options) { nil }
|
8
|
+
subject { Acclaim::Option::Parser.new(args, options) }
|
9
|
+
|
6
10
|
describe '#parse!' do
|
11
|
+
context 'when given a long switch with a parameter separated by an equals sign' do
|
12
|
+
let!(:args) { %w(--switch=PARAM) }
|
7
13
|
|
8
|
-
|
9
|
-
|
14
|
+
it 'should separate the switch from the single parameter' do
|
15
|
+
subject.parse!
|
16
|
+
args.should == %w(--switch PARAM)
|
17
|
+
end
|
10
18
|
end
|
11
19
|
|
12
|
-
|
20
|
+
context 'when given a long switch with multiple parameters separated by an equals sign' do
|
21
|
+
let!(:args) { %w(--files FILE1 FILE2 FILE3) }
|
13
22
|
|
14
|
-
|
15
|
-
|
16
|
-
args
|
17
|
-
end
|
18
|
-
|
19
|
-
|
23
|
+
it 'should separate the switch and the parameters' do
|
24
|
+
subject.parse!
|
25
|
+
args.should == %w(--files FILE1 FILE2 FILE3)
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'but the parameter list starts with a comma' do
|
29
|
+
let!(:args) { %w(--files=,FILE2,FILE3) }
|
30
|
+
|
31
|
+
it 'should treat the first parameter as if it was an empty string' do
|
32
|
+
subject.parse!
|
33
|
+
args.should == ['--files', '', 'FILE2', 'FILE3']
|
34
|
+
end
|
35
|
+
end
|
20
36
|
end
|
21
37
|
|
22
|
-
context 'when
|
38
|
+
context 'when given a long siwtch with an equals sign' do
|
39
|
+
context 'but no parameters' do
|
40
|
+
let!(:args) { %w(--none=) }
|
23
41
|
|
24
|
-
|
25
|
-
|
42
|
+
it 'should separate the switch from the empty parameter' do
|
43
|
+
subject.parse!
|
44
|
+
args.should == %w(--none)
|
45
|
+
end
|
26
46
|
end
|
27
47
|
|
28
|
-
|
48
|
+
context 'but with a parameter list that consists of three commas' do
|
49
|
+
let!(:args) { %w(--empty=,,,) }
|
29
50
|
|
30
|
-
|
51
|
+
it 'should treat the parameters as if they were not there' do
|
52
|
+
subject.parse!
|
53
|
+
args.should == %w(--empty)
|
54
|
+
end
|
31
55
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
opts << Acclaim::Option.new(c.to_sym, "-#{c}", hash)
|
56
|
+
context 'and ends with a parameter' do
|
57
|
+
let!(:args) { %w(--not-pretty=,,,PARAM4) }
|
58
|
+
|
59
|
+
it 'should treat the first three parameters as if they were empty strings' do
|
60
|
+
subject.parse!
|
61
|
+
args.should == ['--not-pretty', '', '', '', 'PARAM4']
|
39
62
|
end
|
40
|
-
opts << Acclaim::Option.new(:long, '--long')
|
41
|
-
opts << Acclaim::Option.new(:params, '--parameters', arity: [1, 1], default: [])
|
42
63
|
end
|
43
64
|
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'when given multiple combined short options' do
|
68
|
+
let!(:args) { %w(-abcd) }
|
69
|
+
|
70
|
+
it 'should split the combined option into multiple short options' do
|
71
|
+
subject.parse!
|
72
|
+
args.should == %w(-a -b -c -d)
|
73
|
+
end
|
74
|
+
end
|
44
75
|
|
45
|
-
|
76
|
+
context 'when not given an array of options' do
|
77
|
+
it 'should not return any option values' do
|
78
|
+
subject.parse!.should be_nil
|
79
|
+
end
|
80
|
+
end
|
46
81
|
|
47
|
-
|
82
|
+
context 'when given an array of options' do
|
83
|
+
let(:options) { [ Acclaim::Option.new(:option) ] }
|
84
|
+
|
85
|
+
it 'should return option values' do
|
48
86
|
subject.parse!.should_not be_nil
|
49
87
|
end
|
50
88
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
params.should == %w(PARAM3 PARAM4)
|
89
|
+
context 'containing a required option' do
|
90
|
+
let(:options) { [ Acclaim::Option.new(:option, '-o', required: true) ] }
|
91
|
+
|
92
|
+
context 'but not given the required switch' do
|
93
|
+
let!(:args) { [] }
|
94
|
+
|
95
|
+
it 'should raise an error' do
|
96
|
+
expect { subject.parse! }.to raise_error Acclaim::Option::Parser::Error, /required/
|
97
|
+
end
|
61
98
|
end
|
62
|
-
end
|
63
99
|
|
64
|
-
|
65
|
-
|
66
|
-
|
100
|
+
context 'and given the required switch' do
|
101
|
+
let!(:args) { %w(-o) }
|
102
|
+
|
103
|
+
it 'parse the value of the option correctly' do
|
104
|
+
subject.parse!.option?.should be_true
|
105
|
+
end
|
106
|
+
end
|
67
107
|
end
|
68
108
|
|
69
|
-
context '
|
109
|
+
context 'containing an option with a required parameter' do
|
110
|
+
let(:options) { [ Acclaim::Option.new(:option, '-o', arity: [1,0]) ] }
|
70
111
|
|
71
|
-
|
112
|
+
context "and not given the option's switch" do
|
113
|
+
let!(:args) { [] }
|
72
114
|
|
73
|
-
|
74
|
-
|
75
|
-
|
115
|
+
it 'should not raise an error' do
|
116
|
+
expect { subject.parse! }.to_not raise_error Acclaim::Option::Parser::Error, /arguments/
|
117
|
+
end
|
76
118
|
end
|
77
119
|
|
120
|
+
context "and given the option's switch" do
|
121
|
+
context 'with a parameter' do
|
122
|
+
let!(:args) { %w(-o PARAM) }
|
123
|
+
|
124
|
+
it 'should not raise an error' do
|
125
|
+
expect { subject.parse! }.to_not raise_error Acclaim::Option::Parser::Error, /arguments/
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should parse the value of the parameter correctly' do
|
129
|
+
subject.parse!.option.should == 'PARAM'
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'without a parameter' do
|
134
|
+
let!(:args) { %w(-o) }
|
135
|
+
|
136
|
+
it 'should raise an error' do
|
137
|
+
expect { subject.parse! }.to raise_error Acclaim::Option::Parser::Error, /arguments/
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
78
141
|
end
|
79
142
|
|
80
|
-
context '
|
143
|
+
context 'containing an option with an optional argument' do
|
144
|
+
let(:options) { [ Acclaim::Option.new(:volume, '-V', arity: [0,1], default: 2) ] }
|
145
|
+
|
146
|
+
context 'and not given the option a parameter' do
|
147
|
+
let!(:args) { %w(-V) }
|
81
148
|
|
82
|
-
|
149
|
+
it 'should not raise an error' do
|
150
|
+
expect { subject.parse! }.to_not raise_error Acclaim::Option::Parser::Error, /arguments/
|
151
|
+
end
|
83
152
|
|
84
|
-
|
85
|
-
|
86
|
-
|
153
|
+
it 'should initialize the option to its default' do
|
154
|
+
subject.parse!.volume.should == 2
|
155
|
+
end
|
87
156
|
end
|
88
157
|
|
158
|
+
context 'and given the option a parameter' do
|
159
|
+
let!(:args) { %w(-V 5) }
|
160
|
+
|
161
|
+
it 'should not raise an error' do
|
162
|
+
expect { subject.parse! }.to_not raise_error Acclaim::Option::Parser::Error, /arguments/
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should initialize the option to the value given' do
|
166
|
+
subject.parse!.volume.should == '5'
|
167
|
+
end
|
168
|
+
end
|
89
169
|
end
|
90
170
|
|
91
|
-
|
171
|
+
context 'containing an option that requires at least one argument' do
|
172
|
+
let(:options) { [ Acclaim::Option.new(:files, '--files', arity: [1,-1]) ] }
|
173
|
+
|
174
|
+
context 'and not given the switch any arguments' do
|
175
|
+
let!(:args) { %w(--files) }
|
176
|
+
|
177
|
+
it 'should raise an error' do
|
178
|
+
expect { subject.parse! }.to raise_error Acclaim::Option::Parser::Error, /arguments/
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'and given the switch three arguments' do
|
183
|
+
let!(:args) { %w(--files FILE1 FILE2 FILE3) }
|
184
|
+
|
185
|
+
it 'should not raise an error' do
|
186
|
+
expect { subject.parse! }.to_not raise_error Acclaim::Option::Parser::Error, /arguments/
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'should initialize the option to the values given' do
|
190
|
+
subject.parse!.files.should == %w(FILE1 FILE2 FILE3)
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'among other arguments and commands' do
|
194
|
+
let!(:args) { %w(cmd subcmd --files FILE1 FILE2 -- ARG1 ARG2) }
|
92
195
|
|
196
|
+
it 'should ignore the other arguments and leave them where they are' do
|
197
|
+
subject.parse!
|
198
|
+
args.should == %w(cmd subcmd -- ARG1 ARG2)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
93
204
|
end
|
94
205
|
|
95
206
|
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.
|
4
|
+
version: 0.0.3
|
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: 2011-12-
|
12
|
+
date: 2011-12-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &13323500 !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: *13323500
|
25
25
|
description: Command-line option parser and command interface.
|
26
26
|
email: matheus.a.m.moreira@gmail.com
|
27
27
|
executables: []
|
@@ -37,11 +37,15 @@ files:
|
|
37
37
|
- acclaim.gemspec
|
38
38
|
- lib/acclaim.rb
|
39
39
|
- lib/acclaim/command.rb
|
40
|
+
- lib/acclaim/command/help.rb
|
41
|
+
- lib/acclaim/command/version.rb
|
40
42
|
- lib/acclaim/option.rb
|
41
43
|
- lib/acclaim/option/arity.rb
|
42
44
|
- lib/acclaim/option/parser.rb
|
45
|
+
- lib/acclaim/option/parser/regexp.rb
|
43
46
|
- lib/acclaim/option/values.rb
|
44
47
|
- lib/acclaim/version.rb
|
48
|
+
- spec/acclaim/option/arity_spec.rb
|
45
49
|
- spec/acclaim/option/parser_spec.rb
|
46
50
|
- spec/acclaim/option_spec.rb
|
47
51
|
homepage: https://github.com/matheusmoreira/acclaim
|