choosy 0.1.0
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/Gemfile +7 -0
- data/Gemfile.lock +25 -0
- data/LICENSE +23 -0
- data/README.markdown +393 -0
- data/Rakefile +57 -0
- data/lib/VERSION +1 -0
- data/lib/choosy/base_command.rb +59 -0
- data/lib/choosy/command.rb +32 -0
- data/lib/choosy/converter.rb +115 -0
- data/lib/choosy/dsl/base_command_builder.rb +172 -0
- data/lib/choosy/dsl/command_builder.rb +43 -0
- data/lib/choosy/dsl/option_builder.rb +155 -0
- data/lib/choosy/dsl/super_command_builder.rb +41 -0
- data/lib/choosy/errors.rb +11 -0
- data/lib/choosy/option.rb +22 -0
- data/lib/choosy/parse_result.rb +64 -0
- data/lib/choosy/parser.rb +184 -0
- data/lib/choosy/printing/color.rb +101 -0
- data/lib/choosy/printing/erb_printer.rb +23 -0
- data/lib/choosy/printing/help_printer.rb +174 -0
- data/lib/choosy/super_command.rb +77 -0
- data/lib/choosy/super_parser.rb +81 -0
- data/lib/choosy/verifier.rb +62 -0
- data/lib/choosy/version.rb +12 -0
- data/lib/choosy.rb +16 -0
- data/spec/choosy/base_command_spec.rb +11 -0
- data/spec/choosy/command_spec.rb +51 -0
- data/spec/choosy/converter_spec.rb +145 -0
- data/spec/choosy/dsl/base_command_builder_spec.rb +328 -0
- data/spec/choosy/dsl/commmand_builder_spec.rb +80 -0
- data/spec/choosy/dsl/option_builder_spec.rb +386 -0
- data/spec/choosy/dsl/super_command_builder_spec.rb +83 -0
- data/spec/choosy/parser_spec.rb +275 -0
- data/spec/choosy/printing/color_spec.rb +74 -0
- data/spec/choosy/printing/help_printer_spec.rb +117 -0
- data/spec/choosy/super_command_spec.rb +80 -0
- data/spec/choosy/super_parser_spec.rb +106 -0
- data/spec/choosy/verifier_spec.rb +180 -0
- data/spec/integration/command-A_spec.rb +37 -0
- data/spec/integration/supercommand-A_spec.rb +61 -0
- data/spec/spec_helpers.rb +30 -0
- metadata +150 -0
@@ -0,0 +1,275 @@
|
|
1
|
+
require 'spec_helpers'
|
2
|
+
require 'choosy/parser'
|
3
|
+
require 'choosy/command'
|
4
|
+
require 'choosy/dsl/command_builder'
|
5
|
+
|
6
|
+
module Choosy
|
7
|
+
class ParserBuilder
|
8
|
+
def initialize
|
9
|
+
@terminals = nil
|
10
|
+
@lazy = false
|
11
|
+
@command = Choosy::Command.new(:parser)
|
12
|
+
end
|
13
|
+
|
14
|
+
def lazy!
|
15
|
+
@lazy = true
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse!(*args)
|
20
|
+
parser = build
|
21
|
+
parser.parse!(args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def terminals(*terms)
|
25
|
+
@terminals = terms
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def boolean(sym, default=nil)
|
30
|
+
default ||= false
|
31
|
+
@command.alter do |c|
|
32
|
+
c.boolean(sym, sym.to_s, :default => default)
|
33
|
+
end
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def single(sym)
|
38
|
+
@command.alter do |c|
|
39
|
+
c.single(sym, sym.to_s)
|
40
|
+
end
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def multiple(sym, min=nil, max=nil)
|
45
|
+
min ||= 1
|
46
|
+
max ||= 1000
|
47
|
+
@command.alter do |c|
|
48
|
+
c.multiple(sym, sym.to_s) do |m|
|
49
|
+
m.count :at_least => min, :at_most => max
|
50
|
+
end
|
51
|
+
end
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def build
|
56
|
+
Parser.new(@command, @lazy, @terminals)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe Parser do
|
61
|
+
before :each do
|
62
|
+
@pb = ParserBuilder.new
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "with no options" do
|
66
|
+
it "should handle everyting after the '--'" do
|
67
|
+
@pb.parse!('--', '-a', 'not an option').args.should have(2).items
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should be able to parse a list of unremarvable objects" do
|
71
|
+
@pb.parse!('a', 'b', 'c').args.should have(3).items
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should stop when it encounters a terminal" do
|
75
|
+
@pb.terminals('a', 'b')
|
76
|
+
res = @pb.parse!('c', 'n', 'b', 'q')
|
77
|
+
res.args.should eql(['c', 'n'])
|
78
|
+
res.unparsed.should eql(['b', 'q'])
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should capture even if the first item is a terminal" do
|
82
|
+
res = @pb.terminals('a').parse!('a', 'b')
|
83
|
+
res.unparsed.should eql(['a', 'b'])
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "when being built" do
|
88
|
+
it "should fail when duplicate short options are given" do
|
89
|
+
@pb.boolean :opt
|
90
|
+
@pb.boolean :opt2
|
91
|
+
attempting {
|
92
|
+
@pb.build
|
93
|
+
}.should raise_error(Choosy::ConfigurationError, /Duplicate option: '-o'/)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should fail when duplicate long options are given" do
|
97
|
+
@pb.boolean :opt
|
98
|
+
@pb.boolean :Opt
|
99
|
+
attempting {
|
100
|
+
@pb.build
|
101
|
+
}.should raise_error(Choosy::ConfigurationError, /Duplicate option: '--opt'/)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "while unsuccessfully parsing arguments" do
|
106
|
+
it "should fail with an unassociated '-'" do
|
107
|
+
attempting {
|
108
|
+
@pb.parse!('a', '-')
|
109
|
+
}.should raise_error(Choosy::ParseError, /'-'/)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should fail on unrecognized option" do
|
113
|
+
attempting {
|
114
|
+
@pb.parse!('a', '-l')
|
115
|
+
}.should raise_error(Choosy::ParseError, "Unrecognized option: '-l'")
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should fail when a boolean argument attaches an argument" do
|
119
|
+
attempting {
|
120
|
+
@pb.boolean(:o).parse!('-o=blah')
|
121
|
+
}.should raise_error(Choosy::ParseError, "Argument given to boolean flag: '-o=blah'")
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should fail when a required argument is missing" do
|
125
|
+
attempting {
|
126
|
+
@pb.single(:option).parse!('-o')
|
127
|
+
}.should raise_error(Choosy::ParseError, "Argument missing for option: '-o'")
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "of multiple arity" do
|
131
|
+
it "should fail when not enough arguments are provided after the '='" do
|
132
|
+
attempting {
|
133
|
+
@pb.multiple(:option, 2).parse!('-o=Opt')
|
134
|
+
}.should raise_error(Choosy::ParseError, "The '-o' flag requires at least 2 arguments")
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should fail when not enough arguments are provided" do
|
138
|
+
attempting {
|
139
|
+
puts @pb.multiple(:option, 3).parse!('-o', 'Opt', 'OO').options
|
140
|
+
}.should raise_error(Choosy::ParseError, "The '-o' flag requires at least 3 arguments")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe "while successfully parsing options" do
|
146
|
+
it "should handle a single short boolean argument" do
|
147
|
+
res = @pb.boolean(:o).parse!('-o')
|
148
|
+
res.args.should be_empty
|
149
|
+
res.options.should eql({:o => true})
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should handle a boolean flag whose default is true" do
|
153
|
+
res = @pb.boolean(:opt, true).parse!('-o')
|
154
|
+
res.options.should eql({:opt => false})
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should handle a flag with an arity of 1" do
|
158
|
+
res = @pb.single(:option).parse!('-o', 'Opt')
|
159
|
+
res.args.should be_empty
|
160
|
+
res.options.should eql({:option => 'Opt'})
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should handle a flag of arity 1 with an '=' sign" do
|
164
|
+
res = @pb.single(:option).parse!('-o=Opt')
|
165
|
+
res.args.should be_empty
|
166
|
+
res.options.should eql({:option => 'Opt'})
|
167
|
+
end
|
168
|
+
|
169
|
+
describe "of multiple arity" do
|
170
|
+
it "should parse a single argument" do
|
171
|
+
res = @pb.multiple(:option).parse!('-o', 'Opt')
|
172
|
+
res.args.should be_empty
|
173
|
+
res.options.should eql({:option => ['Opt']})
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should leave args at the end if limited in size" do
|
177
|
+
res = @pb.multiple(:option, 1, 2).parse!('-o', '1', '2', '3')
|
178
|
+
res.args.should eql(['3'])
|
179
|
+
res.options.should eql({:option => ['1', '2']})
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should handle the '-' part to end argument parsing to options" do
|
183
|
+
res = @pb.multiple(:option).parse!('-o', '1', '2', '-', '3')
|
184
|
+
res.args.should eql(['3'])
|
185
|
+
res.options.should eql({:option => ['1', '2']})
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe "in combination" do
|
190
|
+
it "should handle multiple boolean flags" do
|
191
|
+
@pb.boolean(:abs)
|
192
|
+
@pb.boolean(:not)
|
193
|
+
res = @pb.parse!('-a', '-n', 'q')
|
194
|
+
res.args.should eql(['q'])
|
195
|
+
res.options.should eql({:abs => true, :not => true})
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should be able to hantld multi-arg options and booleans" do
|
199
|
+
@pb.boolean(:abs)
|
200
|
+
@pb.multiple(:mult)
|
201
|
+
res = @pb.parse!('a', '-m', 'b', 'c', '-a', 'c')
|
202
|
+
res.args.should eql(['a', 'c'])
|
203
|
+
res.options.should eql({:abs => true, :mult => ['b', 'c']})
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should be able to handle multiple single arg options" do
|
207
|
+
@pb.single(:sub)
|
208
|
+
@pb.single(:add)
|
209
|
+
res = @pb.parse!('-s', '1', '3', '-a', '2', '4')
|
210
|
+
res.args.should eql(['3', '4'])
|
211
|
+
res.options.should eql({:sub => '1', :add => '2'})
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe "while being lazy" do
|
217
|
+
it "should keep extra arguments as unparsed" do
|
218
|
+
res = @pb.lazy!.parse!('a', 'b')
|
219
|
+
res.unparsed.should eql(['a', 'b'])
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should simply retain the '-' character" do
|
223
|
+
res = @pb.lazy!.parse!('a', '-', 'b')
|
224
|
+
res.unparsed.should eql(['a', '-', 'b'])
|
225
|
+
end
|
226
|
+
|
227
|
+
it "should feed args after '--'" do
|
228
|
+
res = @pb.lazy!.parse!('a', '--', 'z')
|
229
|
+
res.unparsed.should eql(['a', '--', 'z'])
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should skip options it doesn't capture" do
|
233
|
+
res = @pb.lazy!.parse!('-a', 'b')
|
234
|
+
res.unparsed.should eql(['-a', 'b'])
|
235
|
+
res.options.should eql({})
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should skip irrelevant boolean args" do
|
239
|
+
res = @pb.lazy!.boolean(:opt).parse!('a', '-b', '--opt', 'c')
|
240
|
+
res.unparsed.should eql(['a', '-b', 'c'])
|
241
|
+
end
|
242
|
+
|
243
|
+
it "should capture relevant boolean args" do
|
244
|
+
res = @pb.lazy!.boolean(:opt).parse!('a', '-b', '--opt', 'c')
|
245
|
+
res.options.should eql({:opt => true})
|
246
|
+
end
|
247
|
+
|
248
|
+
it "should capture relevant 1 arity args" do
|
249
|
+
res = @pb.lazy!.single(:opt).parse!('-a', 'n', '-o', 'i', 'q')
|
250
|
+
res.options.should eql({:opt => 'i'})
|
251
|
+
end
|
252
|
+
|
253
|
+
it "should skip uncaptured 1 arity args" do
|
254
|
+
res = @pb.lazy!.single(:opt).parse!('-a', 'n', '-o', 'i', 'q')
|
255
|
+
res.unparsed.should eql(['-a', 'n', 'q'])
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should capture multipe argument items" do
|
259
|
+
res = @pb.lazy!.multiple(:opt).parse!('-a', '-o', 'r', 's', 't', '-', 'q')
|
260
|
+
res.options.should eql({:opt => ['r', 's', 't']})
|
261
|
+
end
|
262
|
+
|
263
|
+
it "should skip anything stopped by the '-' in a multi-arg parse" do
|
264
|
+
res = @pb.lazy!.multiple(:opt).parse!('-a', '-o', 'r', 's', 't', '-', 'q')
|
265
|
+
res.unparsed.should eql(['-a', 'q'])
|
266
|
+
end
|
267
|
+
|
268
|
+
it "should include the terminals in the unparsed part" do
|
269
|
+
res = @pb.lazy!.boolean(:a).terminals('b', 'c').parse!('q', '-a', 'b', 'c')
|
270
|
+
res.unparsed.should eql(['q', 'b', 'c'])
|
271
|
+
end
|
272
|
+
end#lazy
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helpers'
|
2
|
+
require 'choosy/printing/color'
|
3
|
+
|
4
|
+
module Choosy::Printing
|
5
|
+
describe Color do
|
6
|
+
before :each do
|
7
|
+
@c = Color.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should be enabled when run from the command line" do
|
11
|
+
@c.disabled?.should be(false)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should not respond to random colors" do
|
15
|
+
attempting {
|
16
|
+
@c.non_color
|
17
|
+
}.should raise_error(NoMethodError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should respond to colors" do
|
21
|
+
Color::COLORS.each_key do |k|
|
22
|
+
@c.respond_to?(k).should be(true)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should respond to effects" do
|
27
|
+
Color::EFFECTS.each_key do |k|
|
28
|
+
@c.respond_to?(k).should be(true)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return the string itself if not enabled" do
|
33
|
+
@c.disable!
|
34
|
+
@c.red("this").should eql("this")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should return an empty string if not enabled" do
|
38
|
+
@c.disable!
|
39
|
+
@c.red.should eql("")
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should fail on too many arguments" do
|
43
|
+
attempting {
|
44
|
+
@c.red("string", :foreground, "extra")
|
45
|
+
}.should raise_error(ArgumentError, /Color#red/)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should fail when setting a style not foreground or background" do
|
49
|
+
attempting {
|
50
|
+
@c.red("string", :no_method)
|
51
|
+
}.should raise_error(ArgumentError, /:foreground or :background/)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should return a formatted string for just the color code when no args are given" do
|
55
|
+
@c.red.should eql("e31[m")
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should return a formatted string with a reset if a full string is supplied" do
|
59
|
+
@c.red("this").should eql("e31[mthise0[m")
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should recognize when the foreground is being set" do
|
63
|
+
@c.red("this", :foreground).should eql("e31[mthise0[m")
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should recognize when the background is being set" do
|
67
|
+
@c.red("this", :background).should eql("e41[mthise0[m")
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should also handle effects" do
|
71
|
+
@c.blink("this").should eql("e5[mthise0[m")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'spec_helpers'
|
2
|
+
require 'choosy/command'
|
3
|
+
require 'choosy/printing/help_printer'
|
4
|
+
|
5
|
+
module Choosy::Printing
|
6
|
+
describe HelpPrinter do
|
7
|
+
before :each do
|
8
|
+
@h = HelpPrinter.new
|
9
|
+
@c = Choosy::Command.new :foo do |foo|
|
10
|
+
foo.summary "This is a summary of a command."
|
11
|
+
foo.desc <<EOF
|
12
|
+
This is a description of this command that should span
|
13
|
+
|
14
|
+
multiple lines and carry itself beyond the average line length when actually called out from the unit tests itself so that we can correctly guage the line wrapping.
|
15
|
+
EOF
|
16
|
+
foo.separator
|
17
|
+
foo.boolean :evaluate, "The evaluation"
|
18
|
+
foo.integer :count, "The count"
|
19
|
+
foo.separator
|
20
|
+
|
21
|
+
foo.separator 'Indifferent options:'
|
22
|
+
foo.boolean :debug, "Debug output"
|
23
|
+
foo.version "1.2"
|
24
|
+
foo.help
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should know the width of the screen, if possible, or set a default [COULD BREAK ON YOUR MACHINE]" do
|
29
|
+
@h.columns.should satisfy {|c| c >= HelpPrinter::DEFAULT_COLUMN_COUNT }
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should know the lenght the screen, if possible, or set a default [COULD BREAK ON YOUR MACHINE]" do
|
33
|
+
@h.lines.should satisfy {|l| l >= HelpPrinter::DEFAULT_LINE_COUNT }
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should now how to format a usage string" do
|
37
|
+
o = capture :stdout do
|
38
|
+
@h.print_usage(@c)
|
39
|
+
end
|
40
|
+
|
41
|
+
o.should eql("USAGE: foo [OPTIONS]\n")
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should show the usage string if it accepts extra arguments" do
|
45
|
+
@c.alter do |foo|
|
46
|
+
foo.arguments do |args|
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
o = capture :stdout do
|
51
|
+
@h.print_usage(@c)
|
52
|
+
end
|
53
|
+
|
54
|
+
o.should eql("USAGE: foo [OPTIONS] [ARGS]\n")
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should print a newline on an empty separator" do
|
58
|
+
o = capture :stdout do
|
59
|
+
@h.print_separator("")
|
60
|
+
end
|
61
|
+
|
62
|
+
o.should eql("\n\n")
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should print a line with separator text" do
|
66
|
+
o = capture :stdout do
|
67
|
+
@h.print_separator("this line")
|
68
|
+
end
|
69
|
+
|
70
|
+
o.should eql("\nthis line\n")
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should print out a command" do
|
74
|
+
o = capture :stdout do
|
75
|
+
@h.print_command(@c)
|
76
|
+
end
|
77
|
+
|
78
|
+
o.should eql(" foo\tThis is a summary of a command.\n")
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should print the summary, if present" do
|
82
|
+
o = capture :stdout do
|
83
|
+
@h.print_summary(@c.summary)
|
84
|
+
end
|
85
|
+
|
86
|
+
o.should eql(" This is a summary of a command.\n")
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should print out an option on 2 lines." do
|
90
|
+
o = capture :stdout do
|
91
|
+
@h.print_option(@c.listing[2])
|
92
|
+
end
|
93
|
+
|
94
|
+
o.should eql(" -c, --count COUNT\n The count\n")
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should print out any commands that are present"
|
98
|
+
|
99
|
+
it "should print out the description" do
|
100
|
+
@h.columns = 80
|
101
|
+
o = capture :stdout do
|
102
|
+
@h.print_description(@c.description)
|
103
|
+
end
|
104
|
+
|
105
|
+
o.should eql(<<EOF
|
106
|
+
|
107
|
+
DESCRIPTION
|
108
|
+
This is a description of this command that should span
|
109
|
+
|
110
|
+
multiple lines and carry itself beyond the average line length when actually
|
111
|
+
called out from the unit tests itself so that we can correctly guage the line
|
112
|
+
wrapping.
|
113
|
+
EOF
|
114
|
+
)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helpers'
|
2
|
+
require 'choosy/super_command'
|
3
|
+
|
4
|
+
module Choosy
|
5
|
+
describe SuperCommand do
|
6
|
+
before :each do
|
7
|
+
@c = SuperCommand.new :superfoo
|
8
|
+
end
|
9
|
+
|
10
|
+
describe :parse! do
|
11
|
+
it "should be able to print out the version number" do
|
12
|
+
@c.alter do |c|
|
13
|
+
c.version "superblah"
|
14
|
+
end
|
15
|
+
|
16
|
+
o = capture :stdout do
|
17
|
+
@c.parse!(['--version'])
|
18
|
+
end
|
19
|
+
|
20
|
+
o.should eql("superblah\n")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should print out the supercommand help message" do
|
24
|
+
@c.alter do |c|
|
25
|
+
c.help
|
26
|
+
end
|
27
|
+
|
28
|
+
o = capture :stdout do
|
29
|
+
@c.parse!([])
|
30
|
+
end
|
31
|
+
|
32
|
+
o.should match(/USAGE:/)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should print out a subcommand help message" do
|
36
|
+
@c.alter do |c|
|
37
|
+
c.help
|
38
|
+
c.command :bar do |bar|
|
39
|
+
bar.boolean :count, "The count"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
o = capture :stdout do
|
44
|
+
@c.parse!(['help', 'bar'])
|
45
|
+
end
|
46
|
+
|
47
|
+
o.should match(/--count/)
|
48
|
+
end
|
49
|
+
end#parse!
|
50
|
+
|
51
|
+
describe :execute! do
|
52
|
+
it "should fail when no executor is present" do
|
53
|
+
@c.alter do |c|
|
54
|
+
c.command :bar do |bar|
|
55
|
+
bar.boolean :count, "The count"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
attempting {
|
60
|
+
@c.execute!(['bar', '--count'])
|
61
|
+
}.should raise_error(Choosy::ConfigurationError, /No executor/)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should call the executors" do
|
65
|
+
count = 0
|
66
|
+
@c.alter do |c|
|
67
|
+
c.command :bar do |bar|
|
68
|
+
bar.integer :count, "The count"
|
69
|
+
bar.executor do |opts, args|
|
70
|
+
count = opts[:count]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
@c.execute!(['bar', '--count', '5'])
|
76
|
+
count.should eql(5)
|
77
|
+
end
|
78
|
+
end#execute!
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'spec_helpers'
|
2
|
+
require 'choosy/super_parser'
|
3
|
+
require 'choosy/super_command'
|
4
|
+
|
5
|
+
module Choosy
|
6
|
+
class SuperParserBuilder
|
7
|
+
attr_reader :super
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@super = Choosy::SuperCommand.new :super
|
11
|
+
@parsimonious = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def command(name)
|
15
|
+
@super.alter do |a|
|
16
|
+
a.command name do |c|
|
17
|
+
yield c if block_given?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def single(name)
|
24
|
+
@super.alter do |a|
|
25
|
+
a.single name, name.to_s
|
26
|
+
end
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse!(*args)
|
31
|
+
parser = build
|
32
|
+
parser.parse!(args)
|
33
|
+
end
|
34
|
+
|
35
|
+
def parsimonious!
|
36
|
+
@parsimonious = true
|
37
|
+
end
|
38
|
+
|
39
|
+
def build
|
40
|
+
SuperParser.new(@super, @parsimonious)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe SuperParser do
|
45
|
+
before :each do
|
46
|
+
@p = SuperParserBuilder.new
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "without any subcommands" do
|
50
|
+
it "should fail with a regular global options" do
|
51
|
+
attempting {
|
52
|
+
@p.single(:count).parse!('--count', '5')
|
53
|
+
}.should raise_error(Choosy::SuperParseError, /requires a command/)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should fail on unrecognized subcommands" do
|
57
|
+
attempting {
|
58
|
+
@p.single(:count).parse!('baz')
|
59
|
+
}.should raise_error(Choosy::SuperParseError, /unrecognized command: 'baz'/)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should fail on unrecognized options" do
|
63
|
+
attempting {
|
64
|
+
@p.parse!('--here')
|
65
|
+
}.should raise_error(Choosy::SuperParseError, /unrecognized option: '--here'/)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should raise a HelpCalled error when a help command is defined" do
|
69
|
+
@p.super.builder.help
|
70
|
+
attempting {
|
71
|
+
@p.parse!()
|
72
|
+
}.should raise_error(Choosy::HelpCalled, :super)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "with subcommands" do
|
77
|
+
describe "being liberal" do
|
78
|
+
it "should be able to parse a single subcommand" do
|
79
|
+
@p.command(:bar).parse!('bar').subresults.should have(1).item
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should merge parent option with child commands" do
|
83
|
+
@p.single(:count).command(:bar).parse!('bar').subresults[0].options.should eql({:count => nil})
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should merge parent option value with child" do
|
87
|
+
@p.single(:count).command(:bar).parse!('bar', '--count', '3').subresults[0].options.should eql({:count => '3'})
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should collect other names of commands as arguments" do
|
91
|
+
@p.command(:bar).command(:baz).parse!('bar', 'baz').subresults.should have(1).item
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "being parsimonious" do
|
96
|
+
before :each do
|
97
|
+
@p.parsimonious!
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should parse separate commands" do
|
101
|
+
@p.command(:bar).command(:baz).parse!('bar', 'baz').subresults.should have(2).items
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|