acclaim 0.0.2 → 0.0.3
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/.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
|