optout 0.0.1 → 0.0.2

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.
Files changed (4) hide show
  1. data/README.rdoc +31 -8
  2. data/lib/optout.rb +238 -62
  3. data/spec/optout_spec.rb +369 -217
  4. metadata +9 -9
@@ -1,155 +1,225 @@
1
- require "optout"
2
- require "tempfile"
3
- require "fileutils"
4
- require "rbconfig"
5
-
6
- def create_optout(options = {})
7
- Optout.options(options) do
8
- on :x, "-x"
9
- on :y, "-y"
10
- end
11
- end
1
+ require "spec_helper"
12
2
 
13
- def optout_option(*options)
14
- Optout.options { on :x, "-x", *options }
15
- end
16
-
17
- class Optout
18
- class Option
19
- def unix?
20
- true
3
+ shared_examples_for "a validator" do
4
+ context "when the option is nil" do
5
+ it "should not be validated" do
6
+ lambda { subject.argv(:x => nil) }.should_not raise_exception
21
7
  end
22
8
  end
23
9
  end
24
10
 
25
11
  shared_examples_for "something that validates files" do
26
- before(:all) { @tmpdir = Dir.mktmpdir }
27
- after(:all) { FileUtils.rm_rf(@tmpdir) }
28
-
29
- def options
30
- { :x => @file }
31
- end
32
-
33
- it "should not raise an exception if a file does not exist but its directory does" do
34
- file = File.join(@tmpdir, "__bad__")
35
- optout = optout_option(@validator)
36
- proc { optout.argv(:x => file) }.should_not raise_exception
12
+ context "when the file does not exist but its directory does" do
13
+ it "should not raise an exception" do
14
+ no_file = File.join(File.dirname(file), "does_not_exist")
15
+ optout = optout_option(described_class)
16
+ lambda { optout.argv(:x => no_file) }.should_not raise_exception
17
+ end
37
18
  end
38
19
 
39
- describe "permissions" do
40
- # Only some chmod() modes work on Win
41
- if RbConfig::CONFIG["host_os"] !~ /mswin|mingw/i
42
- it "should raise an exception when user permissions don't match" do
43
- FileUtils.chmod(0100, @file)
44
- optout = optout_option(@validator.permissions("r"))
45
- proc { optout.argv(options) }.should raise_exception(Optout::OptionInvalid, /user permission/)
46
- end
47
-
48
- it "should not raise an exception when user permissions match" do
49
- checker = proc do |validator|
50
- proc { optout_option(validator).argv(options) }.should_not raise_exception
51
- end
52
-
53
- FileUtils.chmod(0100, @file)
54
- checker.call(@validator.permissions("x"))
55
-
56
- FileUtils.chmod(0200, @file)
57
- checker.call(@validator.permissions("w"))
58
-
59
- FileUtils.chmod(0400, @file)
60
- checker.call(@validator.permissions("r"))
61
-
62
- FileUtils.chmod(0700, @file)
63
- checker.call(@validator.permissions("rwx"))
20
+ # Only some chmod() modes work on Win
21
+ describe "#permissions", :skip_on_windows => true do
22
+ context "when the specified user permissions aren't set" do
23
+ it "should raise an OptionInvalid exception" do
24
+ FileUtils.chmod(0100, file)
25
+ optout = optout_option(described_class.permissions("r"))
26
+ lambda { optout.argv(options) }.should raise_exception(Optout::OptionInvalid, /user permission/)
64
27
  end
65
28
  end
66
- end
67
29
 
68
- describe "exists" do
69
- it "should raise an exception if the file does not exist" do
70
- optout = optout_option(@validator.exists)
71
- proc { optout.argv(:x => @file + "no_file") }.should raise_exception(Optout::OptionInvalid, /does not exist/)
72
- proc { optout.argv(options) }.should_not raise_exception
73
- end
74
- end
30
+ def chmod_and_check(mode, validator)
31
+ FileUtils.chmod(mode, file)
32
+ lambda { optout_option(validator).argv(options) }.should_not raise_exception
33
+ end
75
34
 
76
- describe "under a directory" do
77
- it "should raise an exception if not under the given directory" do
78
- optout = optout_option(@validator.under(File.join("wrong", "path")))
79
- proc { optout.argv(options) }.should raise_exception(Optout::OptionInvalid, /must be under/)
80
-
81
- optout = optout_option(@validator.under(@tmpdir))
82
- proc { optout.argv(options) }.should_not raise_exception
35
+ context "when user read is set" do
36
+ it "should not raise an exception" do
37
+ chmod_and_check(0400, described_class.permissions("r"))
38
+ end
83
39
  end
84
-
85
- it "should raise an exception if the parent directory does not match the given pattern" do
86
- # We need to respect the @file's type to ensure other validation rules implicitly applied by the @validator pass.
87
- # First create parent dirs to validate against
88
- tmp = File.join(@tmpdir, "a1", "b1")
89
- FileUtils.mkdir_p(tmp)
90
40
 
91
- # Then copy the target of the validation (file or directory) under the parent dir.
92
- FileUtils.cp_r(@file, tmp)
41
+ context "when user write is set" do
42
+ it "should not raise an exception" do
43
+ chmod_and_check(0200, described_class.permissions("w"))
44
+ end
45
+ end
93
46
 
94
- # And create the option's value
95
- tmp = File.join(tmp, File.basename(@file))
96
- options = { :x => tmp }
47
+ context "when user execute is set" do
48
+ it "should not raise an exception" do
49
+ chmod_and_check(0100, described_class.permissions("x"))
50
+ end
51
+ end
97
52
 
98
- optout = optout_option(@validator.under(/X$/))
99
- proc { optout.argv(options) }.should raise_exception(Optout::OptionInvalid, /must be under/)
53
+ context "when all permissions are set" do
54
+ it "should not raise an exception" do
55
+ chmod_and_check(0700, described_class.permissions("rwx"))
56
+ end
57
+ end
58
+ end
100
59
 
101
- [ %r|(/[a-z]\d){2}|, %r|[a-z]\d$| ].each do |r|
102
- optout = optout_option(@validator.under(r))
103
- proc { optout.argv(options) }.should_not raise_exception
60
+ describe "#exists" do
61
+ subject { optout_option(described_class.exists) }
62
+
63
+ context "when the file exists" do
64
+ it "does not raise an exception" do
65
+ lambda { subject.argv(options) }.should_not raise_exception
66
+ end
67
+ end
68
+
69
+ context "when the file does not exist" do
70
+ it "raises an OptionInvalid exception" do
71
+ lambda { subject.argv(:x => file + "no_file") }.should raise_exception(Optout::OptionInvalid, /does not exist/)
104
72
  end
105
73
  end
106
74
  end
107
75
 
108
- describe "basename" do
109
- it "should raise an exception if it does not equal the given value" do
110
- optout = optout_option(@validator.named("__bad__"))
111
- proc { optout.argv(options) }.should raise_exception(Optout::OptionInvalid, /name must match/)
112
-
113
- optout = optout_option(@validator.named(File.basename(@file)))
114
- proc { optout.argv(options) }.should_not raise_exception
76
+ describe "#under" do
77
+ context "when not under the given directory" do
78
+ it "should raise an OptionInvalid exception" do
79
+ optout = optout_option(described_class.under(File.join("wrong", "path")))
80
+ lambda { optout.argv(options) }.should raise_exception(Optout::OptionInvalid, /must be under/)
81
+ end
82
+ end
83
+
84
+ context "when under the given directory" do
85
+ it "should not raise an exception" do
86
+ optout = optout_option(described_class.under(File.dirname(file)))
87
+ lambda { optout.argv(options) }.should_not raise_exception
88
+ end
89
+ end
90
+
91
+ context "when the parent directory does not match the given regex" do
92
+ it "should raise an OptionInvalid exception" do
93
+ optout = optout_option(described_class.under(/_{20},#{Time.now.to_i}$/))
94
+ lambda { optout.argv(options) }.should raise_exception(Optout::OptionInvalid, /must be under/)
95
+ end
96
+ end
97
+
98
+ context "when the parent directory matches the given regex" do
99
+ it "should not raise an exception" do
100
+ ends_with = File.dirname(file)[-1,1]
101
+ optout = optout_option(described_class.under(/#{Regexp.quote(ends_with)}$/))
102
+ lambda { optout.argv(options) }.should_not raise_exception
103
+ end
104
+ end
105
+ end
106
+
107
+ describe "#named" do
108
+ context "when the basename matches the regex" do
109
+ it "should not raise an exception" do
110
+ ends_with = File.basename(subject)[-1,1]
111
+ optout = optout_option(described_class.named(/#{Regexp.quote(ends_with)}$/))
112
+ lambda { optout.argv(options) }.should_not raise_exception
113
+ end
114
+ end
115
+
116
+ context "when the basename does not match the regex" do
117
+ it "should raise an OptionInvalid exception" do
118
+ optout = optout_option(described_class.named(/\A#{Time.now.to_i}/))
119
+ lambda { optout.argv(options) }.should raise_exception(Optout::OptionInvalid, /name must match/)
120
+ end
115
121
  end
116
122
 
117
- it "should raise an exception if it does not match the given pattern" do
118
- optout = optout_option(@validator.named(/\A-_-_-_/))
119
- proc { optout.argv(options) }.should raise_exception(Optout::OptionInvalid, /name must match/)
123
+ context "when the basename is not equal" do
124
+ it "should raise an OptionInvalid exception" do
125
+ optout = optout_option(described_class.named(Time.now.to_s))
126
+ lambda { optout.argv(options) }.should raise_exception(Optout::OptionInvalid, /name must match/)
127
+ end
128
+ end
120
129
 
121
- ends_with = File.basename(@file)[/.{2}\z/]
122
- optout = optout_option(@validator.named(/#{Regexp.quote(ends_with)}\z/))
123
- proc { optout.argv(options) }.should_not raise_exception
130
+ context "when the basename is equal" do
131
+ it "should not raise an exception" do
132
+ optout = optout_option(described_class.named(File.basename(file)))
133
+ lambda { optout.argv(options) }.should_not raise_exception
134
+ end
124
135
  end
125
136
  end
126
137
  end
127
138
 
128
139
  describe Optout do
129
- describe "defining options" do
140
+ describe "#on" do
130
141
  before(:each) { @optout = Optout.new }
131
142
 
132
- it "should require the option's key" do
133
- proc { @optout.on }.should raise_exception(ArgumentError, /option key required/)
134
- proc { Optout.options { on } }.should raise_exception(ArgumentError, /option key required/)
143
+ it "requires the option's key" do
144
+ lambda { @optout.on }.should raise_exception(ArgumentError, /option key required/)
135
145
  end
136
-
146
+
137
147
  it "should not allow an option to be defined twice" do
138
148
  @optout.on :x
139
- proc { @optout.on :x }.should raise_exception(ArgumentError, /already defined/)
140
- proc do
141
- Optout.options do
142
- on :x
143
- on :x
149
+ [:x, "x"].each do |opt|
150
+ lambda { @optout.on opt }.should raise_exception(ArgumentError, /already defined/)
151
+ end
152
+ end
153
+
154
+ describe "the :required option" do
155
+ context "when true" do
156
+ subject { optout_option(:required => true) }
157
+
158
+ it "should raise an OptionRequired exception if the option is missing" do
159
+ lambda { subject.argv }.should raise_exception(Optout::OptionRequired, /'x'/)
160
+ lambda { subject.argv(:x => nil) }.should raise_exception(Optout::OptionRequired, /'x'/)
161
+ end
162
+
163
+ it "should not raise an exception if the option is not missing" do
164
+ lambda { subject.argv(:x => "x") }.should_not raise_exception
165
+ end
166
+ end
167
+
168
+ context "when false" do
169
+ it "should not raise an exception if the option is missing" do
170
+ optout = optout_option(:required => false)
171
+ lambda { optout.argv }.should_not raise_exception
172
+ end
173
+ end
174
+ end
175
+
176
+ describe "the :multiple option" do
177
+ let(:collection) do
178
+ [ :x => %w|a b|,
179
+ :x => { :a => "a", :b => "b" } ]
180
+ end
181
+
182
+ context "when false" do
183
+ subject { optout_option(:multiple => false) }
184
+
185
+ it "should raise an OptionInvalid exception if an option contains multiple values" do
186
+ collection.each do |options|
187
+ lambda { subject.argv(options) }.should raise_exception(Optout::OptionInvalid, /multiple values/)
188
+ end
189
+ end
190
+
191
+ it "should not raise an exception if an option contains a collection with only 1 element" do
192
+ [ :x => %w|a|,
193
+ :x => { :a => "a" } ].each do |options|
194
+ lambda { subject.argv(options) }.should_not raise_exception
195
+ end
196
+ end
197
+
198
+ it "should not raise an exception if an option contains a single value" do
199
+ lambda { subject.argv(:x => "x") }.should_not raise_exception
144
200
  end
145
- end.should raise_exception(ArgumentError, /already defined/)
201
+ end
202
+
203
+ context "when true" do
204
+ subject { optout_option(:multiple => true) }
205
+
206
+ it "should not raise an OptionInvalid exception if an option contains multiple values" do
207
+ collection.each do |options|
208
+ lambda { subject.argv(options) }.should_not raise_exception
209
+ end
210
+ end
211
+
212
+ it "should not raise an OptionInvalid exception if an option contains a single value" do
213
+ lambda { subject.argv(:x => "x") }.should_not raise_exception
214
+ end
215
+ end
146
216
  end
147
217
  end
148
218
 
149
219
  describe "creating options" do
150
220
  before(:each) { @optout = create_optout }
151
221
 
152
- context "as a string" do
222
+ context "as a string" do
153
223
  it "should only output the option's value if there's no switch" do
154
224
  optout = Optout.options { on :x }
155
225
  optout.shell(:x => "x").should eql("'x'")
@@ -159,45 +229,53 @@ describe Optout do
159
229
  @optout.shell({}).should be_empty
160
230
  end
161
231
 
162
- it "should only output the option's switch if its value if true" do
232
+ it "should raise an ArgumentError if the options are not a Hash" do
233
+ lambda { @optout.shell("optionz") }.should raise_exception(ArgumentError)
234
+ end
235
+
236
+ it "should only output the option's switch if its value if true" do
163
237
  @optout.shell(:x => true, :y => true).should eql("-x -y")
164
238
  end
165
239
 
166
- it "should not output the option if its value is false" do
240
+ it "should not output the option if its value is false" do
167
241
  @optout.shell(:x => false, :y => true).should eql("-y")
168
242
  end
169
243
 
170
- it "should only output the options that have a value" do
244
+ it "should only output the options that have a value" do
171
245
  @optout.shell(:x => "x", :y => nil).should eql("-x 'x'")
172
246
  end
173
-
174
- it "should output all of the options" do
247
+
248
+ it "should output all of the options" do
175
249
  @optout.shell(:x => "x", :y => "y").should eql("-x 'x' -y 'y'")
176
250
  end
177
251
 
178
- it "should escape the single quote char" do
252
+ it "should escape the single quote char" do
179
253
  @optout.shell(:x => "' a'b'c '").should eql(%q|-x ''\'' a'\''b'\''c '\'''|)
180
254
  end
181
255
 
182
- it "should not separate switches from their value" do
256
+ it "should not separate switches from their value" do
183
257
  optout = create_optout(:arg_separator => "")
184
258
  optout.shell(:x => "x", :y => "y").should eql("-x'x' -y'y'")
185
259
  end
186
-
260
+
187
261
  it "should seperate all switches from their value with a '='" do
188
262
  optout = create_optout(:arg_separator => "=")
189
263
  optout.shell(:x => "x", :y => "y").should eql("-x='x' -y='y'")
190
- end
264
+ end
191
265
 
192
- it "should join all options with multiple values on a delimiter" do
266
+ it "should join all options with multiple values on a delimiter" do
193
267
  optout = create_optout(:multiple => true)
194
268
  optout.shell(:x => %w|a b c|, :y => "y").should eql("-x 'a,b,c' -y 'y'")
195
269
  end
196
270
 
197
- it "should join all options with multiple values on a ':'" do
271
+ it "should join all options with multiple values on a ':'" do
198
272
  optout = create_optout(:multiple => ":")
199
273
  optout.shell(:x => %w|a b c|, :y => "y").should eql("-x 'a:b:c' -y 'y'")
200
- end
274
+ end
275
+
276
+ it "should not differentiate between a String key and a Symbol key" do
277
+ @optout.shell("x" => "x").should eql(@optout.shell(:x => "x"))
278
+ end
201
279
  end
202
280
 
203
281
  context "as an array" do
@@ -206,6 +284,10 @@ describe Optout do
206
284
  optout.argv(:x => "x").should eql(["x"])
207
285
  end
208
286
 
287
+ it "should raise an ArgumentError if the options are not a Hash" do
288
+ lambda { @optout.argv("optionz") }.should raise_exception(ArgumentError)
289
+ end
290
+
209
291
  it "should output an empty array if the option hash is empty" do
210
292
  @optout.argv({}).should be_empty
211
293
  end
@@ -218,7 +300,7 @@ describe Optout do
218
300
  @optout.argv(:x => false, :y => true).should eql(["-y"])
219
301
  end
220
302
 
221
- it "should only output the options that have a value" do
303
+ it "should only output the options that have a value" do
222
304
  @optout.argv(:x => "x", :y => nil).should eql(["-x", "x"])
223
305
  end
224
306
 
@@ -226,155 +308,225 @@ describe Optout do
226
308
  @optout.argv(:x => "x", :y => "y").should eql(["-x", "x", "-y", "y"])
227
309
  end
228
310
 
229
- it "should not escape the single quote char" do
311
+ it "should not escape the single quote char" do
230
312
  @optout.argv(:x => "' a'b'c '").should eql(["-x", "' a'b'c '"])
231
313
  end
232
314
 
233
- it "should not separate switches from their value" do
315
+ it "should not separate switches from their value" do
234
316
  optout = create_optout(:arg_separator => "")
235
317
  optout.argv(:x => "x", :y => "y").should eql(["-xx", "-yy"])
236
318
  end
237
319
 
238
320
  it "should seperate all of switches from their value with a '='" do
239
- optout = create_optout(:arg_separator => "=")
321
+ optout = create_optout(:arg_separator => "=")
240
322
  optout.argv(:x => "x", :y => "y").should eql(["-x=x", "-y=y"])
241
323
  end
242
324
 
243
- it "should join all options with multiple values on a delimiter" do
325
+ it "should join all options with multiple values on a delimiter" do
244
326
  optout = create_optout(:multiple => true)
245
327
  optout.argv(:x => %w|a b c|, :y => "y").should eql(["-x", "a,b,c", "-y", "y"])
246
328
  end
247
329
 
248
- it "should join all options with multiple values on a ':'" do
330
+ it "should join all options with multiple values on a ':'" do
249
331
  optout = create_optout(:multiple => ":")
250
332
  optout.argv(:x => %w|a b c|, :y => "y").should eql(["-x", "a:b:c", "-y", "y"])
251
- end
333
+ end
334
+
335
+ it "should not differentiate between a String key and a Symbol key" do
336
+ @optout.argv("x" => "x").should eql(@optout.argv(:x => "x"))
337
+ end
252
338
  end
253
339
  end
254
340
 
255
341
  # TODO: Check exception.key
256
- describe "validation rules" do
257
- it "should raise an exception if the option hash contains an unknown key" do
258
- optout = create_optout
259
- proc { optout.argv(:bad => 123) }.should raise_exception(Optout::OptionUnknown)
260
- end
261
-
262
- it "should not raise an exception if the option hash contains an unknown key" do
263
- optout = create_optout(:check_keys => false)
264
- proc { optout.argv(:bad => 123) }.should_not raise_exception
265
- end
342
+ describe "validation rules" do
343
+ describe "the :check_keys option" do
344
+ context "when true" do
345
+ it "raises an exception if the option hash contains an unknown key" do
346
+ optout = create_optout
347
+ lambda { optout.argv(:bad => 123) }.should raise_exception(Optout::OptionUnknown, /bad/)
348
+ end
349
+ end
266
350
 
267
- it "should raise an exception if an option is missing" do
268
- optout = create_optout(:required => true)
269
- proc { optout.argv(:x => 123) }.should raise_exception(Optout::OptionRequired, /'y'/)
351
+ context "when false" do
352
+ it "does not raise an exception if the option hash contains an unknown key" do
353
+ optout = create_optout(:check_keys => false)
354
+ lambda { optout.argv(:bad => 123) }.should_not raise_exception
355
+ end
356
+ end
270
357
  end
271
358
 
272
- it "should raise an exception if a required option is missing" do
273
- optout = Optout.options do
274
- on :x
275
- on :y, :required => true
359
+ describe "type checking" do
360
+ it_should_behave_like "a validator"
361
+ subject { optout_option(Float) }
362
+
363
+ context "when the type is correct" do
364
+ it "should not raise an exception" do
365
+ lambda { subject.argv(:x => 123.0) }.should_not raise_exception
366
+ end
276
367
  end
277
-
278
- [ { :x => 123 }, { :x => 123, :y => false } ].each do |options|
279
- proc { optout.argv(options) }.should raise_exception(Optout::OptionRequired, /'y'/)
368
+
369
+ context "when the type is incorrect" do
370
+ it "should raise an OptionInvalid exception" do
371
+ lambda { subject.argv(:x => 123) }.should raise_exception(Optout::OptionInvalid, /type Float/)
372
+ end
280
373
  end
281
374
  end
282
375
 
283
- it "should raise an exception if any option contains multiple values" do
284
- optout = create_optout(:multiple => false)
376
+ describe "restricting the option to a set of values " do
377
+ it_should_behave_like "a validator"
378
+ subject { optout_option(%w|sshaw skye|) }
285
379
 
286
- [ { :x => 123, :y => %w|a b c| },
287
- { :x => 123, :y => { :a => "b", :b => "c" }} ].each do |options|
288
- proc { optout.argv(options) }.should raise_exception(Optout::OptionInvalid)
380
+ context "when a value is included in the set" do
381
+ it "should not raise an exception" do
382
+ lambda { subject.argv(:x => "skye") }.should_not raise_exception
383
+ end
289
384
  end
290
385
 
291
- # An Array with 1 value is OK
292
- proc { optout.argv(:x => 123, :y => %w|a|) }.should_not raise_exception(Optout::OptionInvalid)
386
+ context "when a value is not included in the set" do
387
+ it "should raise an OptionInvalid exception" do
388
+ lambda { subject.argv(:x => "jay_kat") }.should raise_exception(Optout::OptionInvalid, /must be one of/)
389
+ end
390
+ end
293
391
  end
294
392
 
295
- it "should raise an exception if a single value option contains multiple values" do
296
- optout = Optout.options do
297
- on :x
298
- on :y, :multiple => false
393
+ describe "pattern matching" do
394
+ it_should_behave_like "a validator"
395
+ subject { optout_option(/X\d{2}/) }
396
+
397
+ context "when it matches" do
398
+ it "should not raise an exception" do
399
+ lambda { subject.argv(:x => "X21") }.should_not raise_exception
400
+ end
299
401
  end
300
402
 
301
- proc { optout.argv(:x => "x", :y => %w|a b c|) }.should raise_exception(Optout::OptionInvalid, /\by\b/)
302
- end
303
-
304
- it "should check the option's type" do
305
- optout = optout_option(Float)
306
- proc { optout.argv(:x => 123) }.should raise_exception(Optout::OptionInvalid, /type Float/)
307
- proc { optout.argv(:x => 123.0) }.should_not raise_exception(Optout::OptionInvalid)
403
+ context "when it does not match" do
404
+ it "should raise an OptionInvalid exception" do
405
+ lambda { subject.argv(:x => "X7") }.should raise_exception(Optout::OptionInvalid, /match pattern/)
406
+ end
407
+ end
308
408
  end
309
409
 
310
- it "should raise an exception if the option's value is not in the given set" do
311
- optout = optout_option(%w|sshaw skye|, :multiple => true)
410
+ describe Optout::Boolean do
411
+ it_should_behave_like "a validator"
412
+ subject { optout_option(Optout::Boolean) }
312
413
 
313
- [ "bob", [ "jack", "jill" ] ].each do |v|
314
- proc { optout.argv(:x => v) }.should raise_exception(Optout::OptionInvalid)
414
+ context "when the option's a boolean" do
415
+ it "should not raise an exception" do
416
+ [ false, true ].each do |v|
417
+ lambda { subject.argv(:x => v) }.should_not raise_exception
418
+ end
419
+ end
315
420
  end
316
421
 
317
- [ "sshaw", [ "sshaw", "skye" ] ].each do |v|
318
- proc { optout.argv(:x => v) }.should_not raise_exception
422
+ context "when the option's not a boolean" do
423
+ it "should raise an OptionInvalid exception" do
424
+ lambda { subject.argv(:x => "x") }.should raise_exception(Optout::OptionInvalid, /does not accept/)
425
+ end
426
+ end
427
+ end
428
+ end
429
+
430
+ describe "a custom validator" do
431
+ context "when it responds to :validate!" do
432
+ it "should be called" do
433
+ klass = mock("validator")
434
+ klass.should_receive(:validate!)
435
+ optout = optout_option(klass)
436
+ optout.argv(:x => "x")
319
437
  end
320
438
  end
321
-
322
- it "should raise an exception if the option's value does not match the given pattern" do
323
- optout = optout_option(/X\d{2}/)
324
- proc { optout.argv(:x => "X7") }.should raise_exception(Optout::OptionInvalid, /match pattern/)
325
- proc { optout.argv(:x => "X21") }.should_not raise_exception
439
+
440
+ context "when it does not respond to :validate!" do
441
+ it "should raise an ArgumentError" do
442
+ optout = optout_option(Class.new.new)
443
+ lambda { optout.argv(:x => "x") }.should raise_exception(ArgumentError, /don't know how to validate/)
444
+ end
445
+ end
446
+ end
447
+
448
+ describe "unknown validation rules" do
449
+ it "should raise an ArgumentError" do
450
+ optout = optout_option("whaaaaa")
451
+ lambda { optout.argv(:x => "x") }.should raise_exception(ArgumentError, /don't know how to validate/)
326
452
  end
327
-
328
- it "should raise an exception if the option has a non-boolean value" do
329
- optout = optout_option(Optout::Boolean)
330
- proc { optout.argv(:x => "x") }.should raise_exception(Optout::OptionInvalid, /does not accept/)
331
- [ false, true, nil ].each do |v|
332
- proc { optout.argv(:x => v) }.should_not raise_exception
453
+ end
454
+
455
+ describe "#required" do
456
+ before :all do
457
+ @optout = Optout.new
458
+ @optout.required do
459
+ on :x, "-x"
460
+ on :y, "-y"
333
461
  end
334
462
  end
335
-
336
- it "should call a custom validator" do
337
- klass = Class.new do
338
- def validate!(opt)
339
- raise "raise up!"
340
- end
463
+
464
+ context "when options are missing" do
465
+ it "should raise an OptionRequired exception" do
466
+ lambda { @optout.argv }.should raise_exception(Optout::OptionRequired, /'x|y'/)
467
+ lambda { @optout.argv :x => "x" }.should raise_exception(Optout::OptionRequired, /'y'/)
468
+ lambda { @optout.argv :y => "y" }.should raise_exception(Optout::OptionRequired, /'x'/)
341
469
  end
470
+ end
342
471
 
343
- optout = optout_option(klass.new)
344
- proc { optout.argv(:x => "x") }.should raise_exception(RuntimeError, "raise up!")
472
+ context "when no options are missing" do
473
+ it "should not raise an exception" do
474
+ lambda { @optout.argv :x => "x", :y => "y" }.should_not raise_exception
475
+ end
345
476
  end
477
+ end
346
478
 
347
- it "should raise an exception if an unknown validation rule is used" do
348
- optout = optout_option("whaaaaa")
349
- proc { optout.argv(:x => "x") }.should raise_exception(ArgumentError, /don't know how to validate/)
479
+ describe "#optional" do
480
+ before :all do
481
+ @optout = Optout.new
482
+ @optout.optional do
483
+ on :x, "-x"
484
+ on :y, "-y"
485
+ end
486
+ end
487
+
488
+ it "should not raise an exception if any options are missing" do
489
+ lambda { @optout.argv }.should_not raise_exception(Optout::OptionRequired)
490
+ lambda { @optout.argv(:x => "x", :y => "y") }.should_not raise_exception(Optout::OptionRequired)
350
491
  end
492
+ end
493
+ end
351
494
 
352
- context "when validating a file" do
353
- it_should_behave_like "something that validates files"
495
+ shared_context "file validation" do
496
+ before(:all) { @tmpdir = Dir.mktmpdir }
497
+ after(:all) { FileUtils.rm_rf(@tmpdir) }
498
+ end
354
499
 
355
- before(:all) do
356
- @file = Tempfile.new("", @tmpdir).path
357
- @validator = Optout::File
358
- end
500
+ describe Optout::File do
501
+ include_context "file validation"
502
+ it_should_behave_like "something that validates files"
359
503
 
360
- it "should raise an exception if it's not a file" do
361
- optout = optout_option(@validator)
362
- proc { optout.argv(:x => @tmpdir) }.should raise_exception(Optout::OptionInvalid, /can't create a file/)
363
- end
364
- end
504
+ before(:all) { @file = Tempfile.new("", @tmpdir) }
505
+ subject { @file.path }
365
506
 
366
- context "when validating a directory" do
367
- it_should_behave_like "something that validates files"
507
+ let(:file) { @file.path }
508
+ let(:options) { { :x => file } }
368
509
 
369
- before(:all) do
370
- @file = Dir.mktmpdir(nil, @tmpdir)
371
- @validator = Optout::Dir
372
- end
510
+ context "when the option is not a file" do
511
+ it "raises an OptionInvalid exception" do
512
+ optout = optout_option(described_class)
513
+ lambda { optout.argv(:x => @tmpdir) }.should raise_exception(Optout::OptionInvalid, /can't create a file/)
514
+ end
515
+ end
516
+ end
517
+
518
+ describe Optout::Dir do
519
+ include_context "file validation"
520
+ it_should_behave_like "something that validates files"
373
521
 
374
- it "should raise an exception if it's not a directory" do
375
- optout = optout_option(@validator)
376
- proc { optout.argv(:x => Tempfile.new("", @tmpdir).path) }.should raise_exception(Optout::OptionInvalid)
377
- end
522
+ subject { @tmpdir }
523
+ let(:file) { @tmpdir }
524
+ let(:options) { {:x => file} }
525
+
526
+ context "when the option is not a directory" do
527
+ it "raises an OptionInvalid exception" do
528
+ optout = optout_option(described_class)
529
+ lambda { optout.argv(:x => Tempfile.new("", @tmpdir).path) }.should raise_exception(Optout::OptionInvalid)
378
530
  end
379
531
  end
380
532
  end