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 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