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 CHANGED
@@ -1 +1,2 @@
1
1
  *.lock
2
+ doc/
@@ -1 +1,11 @@
1
- %w(command option option/parser options version).each { |file| require file.prepend 'acclaim/' }
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/' }
@@ -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 instantiated in the following form:
20
+ # A command can be created in the following form:
18
21
  #
19
22
  # class App::Command < Acclaim::Command
20
- # opt :verbose, names: %w(-v --verbose), description: 'Run verbosely'
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, names: %w(-W --what), description: 'Do what?', arity: [1, 0], required: true
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? ? 'yes' : 'no'}"
29
- # puts "Doing #{options.what} with #{arguments.inspect} now!"
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
- # Subcommands inherit their parent's option processing:
40
+ # See it in action:
38
41
  #
39
- # $ app --verbose -W test do arg1 arg2
42
+ # $ app --verbose do --what testing acclaim safeguard
40
43
  # Verbose? yes
41
- # Doing test with ["arg1", "arg2"] now!
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 if 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
- arg_separator = args.find { |arg| arg =~ Option::Parser::ARGUMENT_SEPARATOR }
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
@@ -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]
@@ -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
- # Regular expression for any kind of option switch.
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
- instance = build_options_instance! unless options.nil?
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
- letters = multiples.sub!(/^-/, '').split(//)
79
- letters.each { |letter| letter.prepend '-' }.tap do |options|
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 build_options_instance!
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
- options_instance[key] = if arity.only? 1
118
- values.first
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 m
28
+ m = method.to_s.chop!.to_sym
29
+ case method
27
30
  when /=$/
28
- self[:"#{m.chop!}"] = args.first
31
+ self[m] = args.first
29
32
  when /\?$/
30
- self[:"#{m.chop!}"] ? true : false
33
+ self[m] ? true : false
31
34
  else
32
35
  self[method]
33
36
  end
@@ -3,7 +3,7 @@ module Acclaim
3
3
 
4
4
  MAJOR = 0
5
5
  MINOR = 0
6
- PATCH = 2
6
+ PATCH = 3
7
7
  BUILD = nil
8
8
 
9
9
  STRING = [ MAJOR, MINOR, PATCH, BUILD ].compact.join '.'
@@ -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
- let!(:args) do
9
- %w(cmd -a subcmd -b PARAM1 -cdef PARAM2 --long --parameters PARAM3 PARAM4 PARAM5 -- FILE1 FILE2)
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
- subject { Acclaim::Option::Parser.new(args) }
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
- it 'should split multiple short options' do
15
- new_argv = args.dup.tap do |args|
16
- args[args.index('-cdef')] = %w[-c -d -e -f]
17
- end.flatten!
18
- subject.parse!
19
- args.should == new_argv
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 not given an array of options' do
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
- it 'should not return an options instance' do
25
- subject.parse!.should be_nil
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
- end
48
+ context 'but with a parameter list that consists of three commas' do
49
+ let!(:args) { %w(--empty=,,,) }
29
50
 
30
- context 'when given an array of options' do
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
- let(:options) do
33
- [].tap do |opts|
34
- ('a'..'f').each do |c|
35
- hash = {}
36
- hash[:arity] = [1, 0] if c == 'b' or c == 'f'
37
- hash[:required] = true if c == 'd'
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
- subject { Acclaim::Option::Parser.new(args, options) }
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
- it 'should return an options instance' do
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
- it 'should parse arguments correctly' do
52
- subject.parse!.instance_eval do
53
- a?.should be_true
54
- b.should == 'PARAM1'
55
- c?.should be_true
56
- d?.should be_true
57
- e?.should be_true
58
- f.should == 'PARAM2'
59
- long?.should be_true
60
- params.should == %w(PARAM3 PARAM4)
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
- it 'should leave unparsed arguments in argv' do
65
- subject.parse!
66
- args.should == %w(cmd subcmd PARAM5 -- FILE1 FILE2)
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 'but not given a required parameter' do
109
+ context 'containing an option with a required parameter' do
110
+ let(:options) { [ Acclaim::Option.new(:option, '-o', arity: [1,0]) ] }
70
111
 
71
- let!(:args) { %w(-db) }
112
+ context "and not given the option's switch" do
113
+ let!(:args) { [] }
72
114
 
73
- it 'should raise an error' do
74
- expect { subject.parse! }.to raise_error Acclaim::Option::Parser::Error,
75
- /number of arguments/
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 'but not passed a required option' do
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
- let!(:args) { [] }
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
- it 'should raise an error' do
85
- expect { subject.parse! }.to raise_error Acclaim::Option::Parser::Error,
86
- /required/
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
- end
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.2
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-18 00:00:00.000000000 Z
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: &7597280 !ruby/object:Gem::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: *7597280
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