methadone-rehab 1.9.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 (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +11 -0
  6. data/CHANGES.md +66 -0
  7. data/Gemfile +6 -0
  8. data/LICENSE.txt +201 -0
  9. data/README.rdoc +179 -0
  10. data/Rakefile +98 -0
  11. data/TODO.md +3 -0
  12. data/bin/methadone +157 -0
  13. data/features/bootstrap.feature +169 -0
  14. data/features/license.feature +43 -0
  15. data/features/multilevel_commands.feature +125 -0
  16. data/features/readme.feature +26 -0
  17. data/features/rspec_support.feature +27 -0
  18. data/features/step_definitions/bootstrap_steps.rb +47 -0
  19. data/features/step_definitions/license_steps.rb +30 -0
  20. data/features/step_definitions/readme_steps.rb +26 -0
  21. data/features/step_definitions/version_steps.rb +4 -0
  22. data/features/support/env.rb +26 -0
  23. data/features/version.feature +17 -0
  24. data/lib/methadone.rb +15 -0
  25. data/lib/methadone/argv_parser.rb +50 -0
  26. data/lib/methadone/cli.rb +124 -0
  27. data/lib/methadone/cli_logger.rb +133 -0
  28. data/lib/methadone/cli_logging.rb +138 -0
  29. data/lib/methadone/cucumber.rb +174 -0
  30. data/lib/methadone/error.rb +32 -0
  31. data/lib/methadone/execution_strategy/base.rb +34 -0
  32. data/lib/methadone/execution_strategy/jvm.rb +37 -0
  33. data/lib/methadone/execution_strategy/mri.rb +16 -0
  34. data/lib/methadone/execution_strategy/open_3.rb +16 -0
  35. data/lib/methadone/execution_strategy/open_4.rb +22 -0
  36. data/lib/methadone/execution_strategy/rbx_open_4.rb +12 -0
  37. data/lib/methadone/exit_now.rb +40 -0
  38. data/lib/methadone/main.rb +1039 -0
  39. data/lib/methadone/process_status.rb +45 -0
  40. data/lib/methadone/sh.rb +223 -0
  41. data/lib/methadone/version.rb +3 -0
  42. data/methadone-rehab.gemspec +32 -0
  43. data/templates/full/.gitignore.erb +4 -0
  44. data/templates/full/README.rdoc.erb +25 -0
  45. data/templates/full/Rakefile.erb +74 -0
  46. data/templates/full/_license_head.txt.erb +2 -0
  47. data/templates/full/apache_LICENSE.txt.erb +203 -0
  48. data/templates/full/bin/executable.erb +47 -0
  49. data/templates/full/custom_LICENSE.txt.erb +0 -0
  50. data/templates/full/features/executable.feature.erb +13 -0
  51. data/templates/full/features/step_definitions/executable_steps.rb.erb +1 -0
  52. data/templates/full/features/support/env.rb.erb +16 -0
  53. data/templates/full/gplv2_LICENSE.txt.erb +14 -0
  54. data/templates/full/gplv3_LICENSE.txt.erb +15 -0
  55. data/templates/full/mit_LICENSE.txt.erb +7 -0
  56. data/templates/multicommand/bin/executable.erb +52 -0
  57. data/templates/multicommand/lib/command.rb.erb +40 -0
  58. data/templates/multicommand/lib/commands.rb.erb +7 -0
  59. data/templates/rspec/spec/something_spec.rb.erb +5 -0
  60. data/templates/test_unit/test/tc_something.rb.erb +7 -0
  61. data/test/base_test.rb +20 -0
  62. data/test/command_for_tests.sh +7 -0
  63. data/test/execution_strategy/test_base.rb +24 -0
  64. data/test/execution_strategy/test_jvm.rb +77 -0
  65. data/test/execution_strategy/test_mri.rb +32 -0
  66. data/test/execution_strategy/test_open_3.rb +70 -0
  67. data/test/execution_strategy/test_open_4.rb +86 -0
  68. data/test/execution_strategy/test_rbx_open_4.rb +25 -0
  69. data/test/test_cli_logger.rb +219 -0
  70. data/test/test_cli_logging.rb +243 -0
  71. data/test/test_exit_now.rb +37 -0
  72. data/test/test_main.rb +1213 -0
  73. data/test/test_multi.rb +405 -0
  74. data/test/test_sh.rb +404 -0
  75. metadata +321 -0
@@ -0,0 +1,37 @@
1
+ require 'base_test'
2
+ require 'methadone'
3
+ require 'stringio'
4
+
5
+ class TestExitNow < BaseTest
6
+ include Methadone
7
+ include Methadone::ExitNow
8
+
9
+ test_that "exit_now raises the proper error" do
10
+ Given {
11
+ @exit_code = any_int :min => 1
12
+ @message = any_string
13
+ }
14
+ When {
15
+ @code = lambda { exit_now!(@exit_code,@message) }
16
+ }
17
+ Then {
18
+ exception = assert_raises(Methadone::Error,&@code)
19
+ exception.exit_code.should == @exit_code
20
+ exception.message.should == @message
21
+ }
22
+ end
23
+
24
+ test_that "exit_now without an exit code uses 1 as the exit code" do
25
+ Given {
26
+ @message = any_string
27
+ }
28
+ When {
29
+ @code = lambda { exit_now!(@message) }
30
+ }
31
+ Then {
32
+ exception = assert_raises(Methadone::Error,&@code)
33
+ exception.exit_code.should == 1
34
+ exception.message.should == @message
35
+ }
36
+ end
37
+ end
@@ -0,0 +1,1213 @@
1
+ require 'base_test'
2
+ require 'methadone'
3
+ require 'stringio'
4
+ require 'fileutils'
5
+
6
+ class TestMain < BaseTest
7
+ include Methadone::Main
8
+
9
+ def setup
10
+ @original_argv = ARGV.clone
11
+ ARGV.clear
12
+ @old_stdout = $stdout
13
+ $stdout = StringIO.new
14
+ @logged = StringIO.new
15
+ @custom_logger = Logger.new(@logged)
16
+
17
+ @original_home = ENV['HOME']
18
+ fake_home = '/tmp/fake-home'
19
+ FileUtils.rm_rf(fake_home)
20
+ FileUtils.mkdir(fake_home)
21
+ ENV['HOME'] = fake_home
22
+ end
23
+
24
+ # Override the built-in logger so we can capture it
25
+ def logger
26
+ @custom_logger
27
+ end
28
+
29
+ def teardown
30
+ set_argv @original_argv
31
+ ENV.delete('DEBUG')
32
+ ENV.delete('APP_OPTS')
33
+ $stdout = @old_stdout
34
+ ENV['HOME'] = @original_home
35
+ end
36
+
37
+ test_that "my main block gets called by run and has access to CLILogging" do
38
+ Given {
39
+ @called = false
40
+ main do
41
+ begin
42
+ logger.debug "debug"
43
+ logger.info "info"
44
+ logger.warn "warn"
45
+ logger.error "error"
46
+ logger.fatal "fatal"
47
+ @called = true
48
+ rescue => ex
49
+ puts ex.message
50
+ end
51
+ end
52
+ }
53
+ When run_go_safely
54
+ Then main_shouldve_been_called
55
+ end
56
+
57
+ test_that "my main block does not get command-line parameters that were not specified by args, but they are still available in ARGV" do
58
+ Given {
59
+ @params = []
60
+ @argv = []
61
+ main do |param1,param2,param3|
62
+ @params << param1
63
+ @params << param2
64
+ @params << param3
65
+ @argv = ::ARGV
66
+ end
67
+ set_argv %w(one two three)
68
+ }
69
+ When run_go_safely
70
+ Then {
71
+ @params.should == [nil,nil,nil]
72
+ @argv.should == %w(one two three)
73
+ }
74
+ end
75
+
76
+ test_that "my main block can freely ignore arguments given" do
77
+ Given {
78
+ @called = false
79
+ main do
80
+ @called = true
81
+ end
82
+ set_argv %w(one two three)
83
+ }
84
+ When run_go_safely
85
+ Then main_shouldve_been_called
86
+ end
87
+
88
+ test_that "my main block can ask for arguments that it might not receive" do
89
+ Given {
90
+ @params = []
91
+ @argv = []
92
+ arg 'param1'
93
+ main do |param1,param2,param3|
94
+ @params << param1
95
+ @params << param2
96
+ @params << param3
97
+ @argv = ::ARGV
98
+ end
99
+ set_argv %w(one two)
100
+ }
101
+ When run_go_safely
102
+ Then {
103
+ @params.should == ['one',nil,nil]
104
+ @argv.should == ['two']
105
+ }
106
+ end
107
+
108
+ test_that "go exits zero when main evaluates to nil or some other non number" do
109
+ [nil,'some string',Object.new,[],4.5].each do |non_number|
110
+ Given main_that_exits non_number
111
+ Then {
112
+ assert_exits(0,"for value #{non_number}") { When run_go! }
113
+ }
114
+ end
115
+ end
116
+
117
+ test_that "go exits with the numeric value that main evaluated to" do
118
+ [0,1,2,3].each do |exit_status|
119
+ Given main_that_exits exit_status
120
+ Then {
121
+ assert_exits(exit_status) { When run_go! }
122
+ }
123
+ end
124
+ end
125
+
126
+ test_that "go exits with 70, which is the Linux sysexits.h code for this sort of thing, if there's an exception" do
127
+ Given {
128
+ leak_exceptions false
129
+ main do
130
+ raise "oh noes"
131
+ end
132
+ }
133
+ Then {
134
+ assert_exits(70) { When run_go! }
135
+ assert_logged_at_error "oh noes"
136
+ }
137
+ end
138
+
139
+ test_that "go allows the exception raised to leak through if DEBUG is set in the environment" do
140
+ Given {
141
+ ENV['DEBUG'] = 'true'
142
+ main do
143
+ raise ArgumentError,"oh noes"
144
+ end
145
+ }
146
+ Then {
147
+ assert_raises ArgumentError do
148
+ When run_go!
149
+ end
150
+ }
151
+ end
152
+
153
+ test_that "An exception that's not a StandardError causes the excepteion to break through and raise" do
154
+ Given {
155
+ main do
156
+ raise Exception,"oh noes"
157
+ end
158
+ }
159
+ Then {
160
+ ex = assert_raises Exception do
161
+ When run_go!
162
+ end
163
+ assert_equal "oh noes",ex.message
164
+ }
165
+ end
166
+
167
+ test_that "Non-methadone exceptions leak through if we configure it that way" do
168
+ Given {
169
+ main do
170
+ raise StandardError,"oh noes"
171
+ end
172
+ leak_exceptions true
173
+ }
174
+ Then {
175
+ ex = assert_raises StandardError do
176
+ When run_go!
177
+ end
178
+ assert_equal "oh noes",ex.message
179
+ }
180
+ end
181
+
182
+ test_that "go exits with the exit status included in the special-purpose excepiton" do
183
+ Given {
184
+ main do
185
+ raise Methadone::Error.new(4,"oh noes")
186
+ end
187
+ }
188
+ Then {
189
+ assert_exits(4) { When run_go! }
190
+ assert_logged_at_error "oh noes"
191
+ }
192
+ end
193
+
194
+ test_that "go allows the special methadone exception to leak through if DEBUG is set in the environment" do
195
+ Given {
196
+ ENV['DEBUG'] = 'true'
197
+ main do
198
+ raise Methadone::Error.new(4,"oh noes")
199
+ end
200
+ }
201
+ Then {
202
+ assert_raises Methadone::Error do
203
+ When run_go!
204
+ end
205
+ }
206
+ end
207
+
208
+ test_that "can exit with a specific status by using the helper method instead of making a new exception" do
209
+ Given {
210
+ main do
211
+ exit_now!(4,"oh noes")
212
+ end
213
+ }
214
+ Then {
215
+ assert_exits(4) { When run_go! }
216
+ assert_logged_at_error "oh noes"
217
+ }
218
+ end
219
+
220
+ test_that "when we help_now! we exit and show help" do
221
+ Given {
222
+ @message = any_sentence
223
+ main do
224
+ help_now!(@message)
225
+ end
226
+
227
+ opts.on("--switch") { options[:switch] = true }
228
+ opts.on("--flag FLAG") { |value| options[:flag] = value }
229
+
230
+ set_argv []
231
+ }
232
+
233
+ Then {
234
+ assert_exits(64) { When run_go! }
235
+ assert $stdout.string.include?(opts.to_s),"Expected #{$stdout.string} to contain #{opts.to_s}"
236
+ assert_logged_at_error @message
237
+ }
238
+ end
239
+
240
+ test_that "opts allows us to more expediently set up OptionParser" do
241
+ Given {
242
+ @switch = nil
243
+ @flag = nil
244
+ main do
245
+ @switch = options[:switch]
246
+ @flag = options[:flag]
247
+ end
248
+
249
+ opts.on("--switch") { options[:switch] = true }
250
+ opts.on("--flag FLAG") { |value| options[:flag] = value }
251
+
252
+ set_argv %w(--switch --flag value)
253
+ }
254
+
255
+ When run_go_safely
256
+
257
+ Then {
258
+ @switch.should be true
259
+ @flag.should == 'value'
260
+ }
261
+ end
262
+
263
+ test_that "when the command line is invalid, we exit with 64 and print the CLI help" do
264
+ Given {
265
+ main do
266
+ end
267
+
268
+ opts.on("--switch") { options[:switch] = true }
269
+ opts.on("--flag FLAG") { |value| options[:flag] = value }
270
+
271
+ set_argv %w(--invalid --flag value)
272
+ }
273
+
274
+ Then {
275
+ assert_exits(64) { When run_go! }
276
+ assert $stdout.string.include?(opts.to_s),"Expected #{$stdout.string} to contain #{opts.to_s}"
277
+ }
278
+ end
279
+
280
+ test_that "when setting defaults they get copied to strings/symbols as well" do
281
+ Given {
282
+ @flag_with_string_key_defalt = nil
283
+ @flag_with_symbol_key_defalt = nil
284
+ reset!
285
+ main do
286
+ @flag_with_string_key_defalt = options[:foo]
287
+ @flag_with_symbol_key_defalt = options['bar']
288
+ end
289
+ options['foo'] = 'FOO'
290
+ options[:bar] = 'BAR'
291
+ on("--foo")
292
+ on("--bar")
293
+ }
294
+ When run_go_safely
295
+ Then {
296
+ assert_equal 'FOO',@flag_with_string_key_defalt
297
+ assert_equal 'BAR',@flag_with_symbol_key_defalt
298
+ }
299
+ end
300
+
301
+ test_that "passing non-strings wont' break automagic stuff" do
302
+ Given {
303
+ @foo = nil
304
+ @bar = nil
305
+ main do
306
+ @foo = options[:foo]
307
+ @bar = options[:bar]
308
+ end
309
+ on("--foo ARG",OptionParser::DecimalInteger)
310
+ on("--bar ARG",/^\d/)
311
+ set_argv %w(--foo 88 --bar 4)
312
+ }
313
+ When run_go_safely
314
+ Then {
315
+ assert_equal 88,@foo,@logged.string + $stdout.string
316
+ assert_equal '4',@bar,@logged.string + $stdout.string
317
+ }
318
+ end
319
+
320
+ test_that "omitting the block to opts simply sets the value in the options hash and returns itself" do
321
+ Given {
322
+ @switch = nil
323
+ @negatable = nil
324
+ @flag = nil
325
+ @f = nil
326
+ @other = nil
327
+ @some_other = nil
328
+ @with_dashes = nil
329
+ main do
330
+ @switch = [options[:switch],options['switch']]
331
+ @flag = [options[:flag],options['flag']]
332
+ @f = [options[:f],options['f']]
333
+ @negatable = [options[:negatable],options['negatable']]
334
+ @other = [options[:other],options['other']]
335
+ @some_other = [options[:some_other],options['some_other']]
336
+ @with_dashes = [options[:'flag-with-dashes'],options['flag-with-dashes']]
337
+ end
338
+
339
+ on("--switch")
340
+ on("--[no-]negatable")
341
+ on("--flag FLAG","-f","Some documentation string")
342
+ on("--flag-with-dashes FOO")
343
+ on("--other") do
344
+ options[:some_other] = true
345
+ end
346
+
347
+ set_argv %w(--switch --flag value --negatable --other --flag-with-dashes=BAR)
348
+ }
349
+
350
+ When run_go_safely
351
+
352
+ Then {
353
+ @switch[0].should be true
354
+ @some_other[0].should be true
355
+ @other[0].should_not be true
356
+ @flag[0].should == 'value'
357
+ @f[0].should == 'value'
358
+ @with_dashes[0].should == 'BAR'
359
+
360
+ @switch[1].should be true
361
+ @some_other[1].should be nil # ** this is set manually
362
+ @other[1].should_not be true
363
+ @flag[1].should == 'value'
364
+ @f[1].should == 'value'
365
+ @with_dashes[1].should == 'BAR'
366
+
367
+ opts.to_s.should match /Some documentation string/
368
+ }
369
+ end
370
+
371
+ test_that "without specifying options, options in brackets doesn't show up in our banner" do
372
+ Given {
373
+ main {}
374
+ }
375
+
376
+ Then {
377
+ opts.banner.should_not match /\[options\]/
378
+ }
379
+ end
380
+
381
+ test_that "when specifying an option, [options] shows up in the banner" do
382
+ Given {
383
+ main {}
384
+ on("-s")
385
+ }
386
+
387
+ Then {
388
+ opts.banner.should match /\[options\]/
389
+ }
390
+
391
+ end
392
+
393
+ test_that "I can specify which arguments my app takes and if they are required as well as document them" do
394
+ Given {
395
+ main {}
396
+ @db_name_desc = any_string
397
+ @user_desc = any_string
398
+ @password_desc = any_string
399
+
400
+ arg :db_name, @db_name_desc
401
+ arg :user, :required, @user_desc
402
+ arg :password, :optional, @password_desc
403
+ }
404
+ When run_go_safely
405
+ Then {
406
+ opts.banner.should match /db_name user \[password\]$/
407
+ opts.to_s.should match /#{@db_name_desc}/
408
+ opts.to_s.should match /#{@user_desc}/
409
+ opts.to_s.should match /#{@password_desc}/
410
+ }
411
+ end
412
+
413
+ test_that "I can specify which arguments my app takes and if they are singular or plural" do
414
+ Given {
415
+ main {}
416
+
417
+ arg :db_name
418
+ arg :user, :required, :one
419
+ arg :tables, :many
420
+ }
421
+
422
+ Then {
423
+ opts.banner.should match /db_name user tables...$/
424
+ }
425
+ end
426
+
427
+ test_that "I can specify which arguments my app takes and if they are singular or optional plural" do
428
+ Given {
429
+ main {}
430
+
431
+ arg :db_name
432
+ arg :user, :required, :one
433
+ arg :tables, :any
434
+ }
435
+
436
+ Then {
437
+ opts.banner.should match /db_name user \[tables...\]$/
438
+ }
439
+ end
440
+
441
+ test_that "I can set a description for my app" do
442
+ Given {
443
+ main {}
444
+ description "An app of total awesome"
445
+
446
+ }
447
+ Then {
448
+ opts.banner.should match /^An app of total awesome$/
449
+ }
450
+ end
451
+
452
+ test_that "when I override the banner, we don't automatically do anything" do
453
+ Given {
454
+ main {}
455
+ opts.banner = "FOOBAR"
456
+
457
+ on("-s")
458
+ }
459
+ When {opts}
460
+ Then {
461
+ opts.banner.should == 'FOOBAR'
462
+ }
463
+ end
464
+
465
+ test_that "when I say an argument is required and its omitted, I get an error" do
466
+ Given {
467
+ main {}
468
+ arg :foo
469
+ arg :bar
470
+
471
+ set_argv %w(blah)
472
+ }
473
+
474
+ Then {
475
+ assert_exits(64) { When run_go! }
476
+ assert_logged_at_error("parse error: 'bar' is required")
477
+ }
478
+ end
479
+
480
+ test_that "when I say an argument is many and its omitted, I get an error" do
481
+ Given {
482
+ main {}
483
+ arg :foo
484
+ arg :bar, :many
485
+
486
+ set_argv %w(blah)
487
+ }
488
+
489
+ Then {
490
+ assert_exits(64) { When run_go! }
491
+ assert_logged_at_error("parse error: at least one 'bar' is required")
492
+ }
493
+ end
494
+
495
+ test_that "when I specify a version, it shows up in the banner" do
496
+ Given {
497
+ main{}
498
+ version "0.0.1"
499
+ }
500
+
501
+ Then {
502
+ opts.banner.should match /^v0.0.1/m
503
+ }
504
+ end
505
+
506
+ test_that "when I specify a version, I can get help via --version" do
507
+ Given {
508
+ main{}
509
+ version "0.0.1"
510
+ set_argv(['--version'])
511
+ }
512
+ Then run_go_safely
513
+ And {
514
+ opts.to_s.should match /Show help\/version info/m
515
+ $stdout.string.should match /^#{@version}$/
516
+ }
517
+ end
518
+
519
+ test_that "when I specify a version with custom help, it shows up" do
520
+ @version_message = "SHOW ME VERSIONS"
521
+ Given {
522
+ main{}
523
+ version "0.0.1",@version_message
524
+ set_argv(['--verison'])
525
+ }
526
+ Then run_go_safely
527
+ And {
528
+ opts.to_s.should match /#{@version_message}/
529
+ }
530
+ end
531
+
532
+ test_that "when I specify a version with :basic format, only the command and version show up and the help is 'show version info'" do
533
+ @version = "2.0.9.pre"
534
+ Given {
535
+ version @version, :basic
536
+ set_argv %w(--version)
537
+ }
538
+ When run_go_safely
539
+ Then {
540
+ opts.to_s.should match /^ *--version.*Show version info$/
541
+ $stdout.string.should match /^#{::File.basename($0)} version #{@version}$/
542
+ }
543
+ end
544
+
545
+ test_that "when I specify a version with :terse format, only the version show up and the help is 'show version'" do
546
+ @version = "2.0.9.pre"
547
+ Given {
548
+ version @version, :terse
549
+ set_argv %w(--version)
550
+ }
551
+ When run_go_safely
552
+ Then {
553
+ opts.to_s.should match /^ *--version.*Show version$/
554
+ $stdout.string.should match /^#{@version}$/
555
+ }
556
+ end
557
+
558
+ test_that "default values for boolean options are put into the docstring" do
559
+ Given {
560
+ main {}
561
+ options[:foo] = false
562
+ options[:bar] = true
563
+ on("--foo","Do the foo")
564
+ on("--[no-]bar", "Bar it like crazy")
565
+ }
566
+ When {
567
+ @help_string = opts.to_s
568
+ }
569
+ When {
570
+ @help_string.should match /--foo.*\n.*\(default: false\)/
571
+ @help_string.should match /--\[no-\]bar.*\n.*\(default: true\)/
572
+ }
573
+ end
574
+
575
+ test_that "default values for options are put into the docstring" do
576
+ Given {
577
+ main {}
578
+ options[:foo] = "bar"
579
+ on("--foo ARG","Indicate the type of foo")
580
+ }
581
+ When {
582
+ @help_string = opts.to_s
583
+ }
584
+ When {
585
+ @help_string.should match /\(default: bar\)/
586
+ }
587
+
588
+ end
589
+
590
+ test_that "default values for options with several names are put into the docstring" do
591
+ Given {
592
+ main {}
593
+ options[:foo] = "bar"
594
+ on("-f ARG","--foo","Indicate the type of foo")
595
+ }
596
+ When {
597
+ @help_string = opts.to_s
598
+ }
599
+ When {
600
+ @help_string.should match /\(default: bar\)/
601
+ }
602
+ end
603
+
604
+ test_that "when getting defaults from an environment variable, show it in the help output" do
605
+ Given app_to_use_environment
606
+ When run_go_safely
607
+ And {
608
+ @help_string = opts.to_s
609
+ }
610
+ Then {
611
+ @help_string.should match /Default values can be placed in the APP_OPTS environment variable/
612
+ }
613
+ end
614
+
615
+ test_that "when we want to get opts from the environment, we can" do
616
+ Given app_to_use_environment
617
+ And {
618
+ @flag_value = '56'
619
+ @some_arg = any_string
620
+ set_argv([])
621
+ ENV['APP_OPTS'] = "--switch --flag=#{@flag_value} #{@some_arg}"
622
+ }
623
+ When {
624
+ @code = lambda { go! }
625
+ }
626
+ Then {
627
+ assert_exits(0,'',&@code)
628
+ @switch.should == true
629
+ @flag.should == @flag_value
630
+ @args.should == [@some_arg]
631
+ }
632
+ end
633
+
634
+ test_that "environment args are overridden by the command line" do
635
+ Given app_to_use_environment
636
+ And {
637
+ @flag_value = any_string
638
+ ENV['APP_OPTS'] = "--switch --flag=#{any_string}"
639
+ set_argv(['--flag',@flag_value])
640
+ }
641
+ When {
642
+ @code = lambda { go! }
643
+ }
644
+ Then {
645
+ assert_exits(0,'',&@code)
646
+ @switch.should == true
647
+ @flag.should == @flag_value
648
+ }
649
+ end
650
+
651
+ test_that "environment args correctly handle spaces" do
652
+ Given app_to_use_environment
653
+ And {
654
+ @flag_value = any_string + ' ' + any_string
655
+ ENV['APP_OPTS'] = "--switch --flag='#{@flag_value}'"
656
+ }
657
+ When {
658
+ @code = lambda { go! }
659
+ }
660
+ Then {
661
+ assert_exits(0,'',&@code)
662
+ @switch.should == true
663
+ @flag.should == @flag_value
664
+ }
665
+ end
666
+
667
+ test_that "environment args correctly handle spaces via backslash stuff" do
668
+ Given app_to_use_environment
669
+ And {
670
+ cli_flag_value = any_string(:max => 4) + "\\ " + any_string(:max => 4)
671
+ @flag_value = cli_flag_value.gsub("\\ "," ")
672
+ ENV['APP_OPTS'] = "--switch --flag=#{cli_flag_value}"
673
+ }
674
+ When {
675
+ @code = lambda { go! }
676
+ }
677
+ Then {
678
+ assert_exits(0,'',&@code)
679
+ @switch.should == true
680
+ @flag.should == @flag_value
681
+ }
682
+ end
683
+
684
+ test_that "we can get defaults from a YAML config file if it's specified" do
685
+ Given app_to_use_rc_file
686
+ And {
687
+ @flag_value = any_string
688
+ rc_file = File.join(ENV['HOME'],'.my_app.rc')
689
+ File.open(rc_file,'w') do |file|
690
+ file.puts({
691
+ 'switch' => true,
692
+ 'flag' => @flag_value,
693
+ }.to_yaml)
694
+ end
695
+ }
696
+ When {
697
+ @code = lambda { go! }
698
+ }
699
+ Then {
700
+ assert_exits(0,&@code)
701
+ @switch.should == true
702
+ @flag.should == @flag_value
703
+ }
704
+
705
+ end
706
+
707
+ test_that "we can get defaults from an absolute config filename" do
708
+ tempfile = Tempfile.new('methadone_test.rc')
709
+ Given app_to_use_rc_file(tempfile.path)
710
+ And {
711
+ @flag_value = any_string
712
+ rc_file = File.join(tempfile.path)
713
+ File.open(rc_file,'w') do |file|
714
+ file.puts({
715
+ 'switch' => true,
716
+ 'flag' => @flag_value,
717
+ }.to_yaml)
718
+ end
719
+ }
720
+ When {
721
+ @code = lambda { go! }
722
+ }
723
+ Then {
724
+ assert_exits(0,&@code)
725
+ @switch.should == true
726
+ @flag.should == @flag_value
727
+ }
728
+
729
+ end
730
+
731
+
732
+ test_that "we can specify an rc file even if it doesn't exist" do
733
+ Given app_to_use_rc_file
734
+ And {
735
+ @flag_value = any_string
736
+ rc_file = File.join(ENV['HOME'],'.my_app.rc')
737
+ raise "Something's wrong, expection rc file not to exist" if File.exists?(rc_file)
738
+ }
739
+ When {
740
+ @code = lambda { go! }
741
+ }
742
+ Then {
743
+ assert_exits(0,&@code)
744
+ @switch.should == nil
745
+ @flag.should == nil
746
+ }
747
+ end
748
+
749
+ test_that "we can use a simpler, text format for the rc file" do
750
+ Given app_to_use_rc_file
751
+ And {
752
+ @flag_value = any_string
753
+ rc_file = File.join(ENV['HOME'],'.my_app.rc')
754
+ File.open(rc_file,'w') do |file|
755
+ file.puts "--switch --flag=#{@flag_value}"
756
+ end
757
+ }
758
+ When {
759
+ @code = lambda { go! }
760
+ }
761
+ Then {
762
+ assert_exits(0,&@code)
763
+ @switch.should == true
764
+ @flag.should == @flag_value
765
+ }
766
+
767
+ end
768
+
769
+ test_that "the text format for the rc file attempts to respect quoted arguments" do
770
+ Given app_to_use_rc_file
771
+ And {
772
+ @flag_value = any_string(:max => 10) + " " + any_string(:max => 10)
773
+ rc_file = File.join(ENV['HOME'],'.my_app.rc')
774
+ File.open(rc_file,'w') do |file|
775
+ file.puts "--switch --flag='#{@flag_value}'"
776
+ end
777
+ }
778
+ When {
779
+ @code = lambda { go! }
780
+ }
781
+ Then {
782
+ assert_exits(0,&@code)
783
+ @switch.should == true
784
+ @flag.should == @flag_value
785
+ }
786
+ end
787
+
788
+ test_that "with an ill-formed rc file, we get a reasonable error message" do
789
+ Given app_to_use_rc_file
790
+ And {
791
+ @flag_value = any_string
792
+ rc_file = File.join(ENV['HOME'],'.my_app.rc')
793
+ File.open(rc_file,'w') do |file|
794
+ file.puts OpenStruct.new(:foo => :bar).to_yaml
795
+ end
796
+ }
797
+ When {
798
+ @code = lambda { go! }
799
+ }
800
+ Then {
801
+ assert_exits(64,&@code)
802
+ }
803
+
804
+ end
805
+
806
+ test_that "we can call methods prior to calling main without a problem" do
807
+ Given {
808
+ description "An app of total awesome"
809
+ opts.on("--switch") { options[:switch] = true }
810
+ main {}
811
+ }
812
+ Then {
813
+ opts.banner.should match /^An app of total awesome$/
814
+ opts.to_s.should match /--switch/
815
+ }
816
+ end
817
+
818
+ test_that "usage is displayed if called with no arguments given when help_if_bare specified" do
819
+ Given {
820
+ help_if_bare
821
+ on("--required VALUE")
822
+ main {puts 'main called'}
823
+ }
824
+ When {
825
+ @code = lambda {go!}
826
+ }
827
+ Then {
828
+ assert_exits(64,&@code)
829
+ $stdout.string.should_not match /main called/
830
+ $stdout.string.should match /Usage:.*--required VALUE/m
831
+ }
832
+ end
833
+
834
+ test_that "arguments are filled according to their need" do
835
+ Given {
836
+ @can_use_one = nil
837
+ @any_will_do = nil
838
+ @need_1 = nil
839
+ @at_least_one = nil
840
+ @if_youve_got_one = nil
841
+ @any_left = nil
842
+ @last_one = nil
843
+ arg 'can_use_one', :optional, Integer
844
+ arg 'any_will_do', :any
845
+ arg 'need_1', :required
846
+ arg 'at_least_one', :many
847
+ arg 'if_youve_got_one', :optional
848
+ arg 'any_left', :any
849
+ arg 'last_one', :required
850
+ main do |can_use_one,any_will_do,need_1,at_least_one,if_youve_got_one,any_left,last_one|
851
+ @can_use_one = can_use_one
852
+ @any_will_do = any_will_do
853
+ @need_1 = need_1
854
+ @at_least_one = at_least_one
855
+ @if_youve_got_one = if_youve_got_one
856
+ @any_left = any_left
857
+ @last_one = last_one
858
+ end
859
+ set_argv %w(1 2 3 4 5 6 7 8 9 10)
860
+ }
861
+ When run_go_safely
862
+ Then {
863
+ @can_use_one.should == '1'
864
+ @any_will_do.should == %w(2 3 4 5 6 7)
865
+ @need_1.should == '8'
866
+ @at_least_one.should == ['9']
867
+ @if_youve_got_one.should == nil
868
+ @any_left.should == []
869
+ @last_one.should == '10'
870
+ }
871
+ end
872
+
873
+ test_that "arguments can be given valid values" do
874
+ Given {
875
+ arg "foo", %w(one two three)
876
+ @foo = nil
877
+ main do |foo|
878
+ @foo = foo
879
+ end
880
+ set_argv %w(two)
881
+ }
882
+ When run_go_safely
883
+ Then {
884
+ @foo = 'two'
885
+ }
886
+ end
887
+
888
+ test_that "arguments can be given valid values, and raise errors when they don't match" do
889
+ Given {
890
+ arg "foo", %w(one two three)
891
+ @foo = nil
892
+ main do |foo|
893
+ @foo = foo
894
+ end
895
+ set_argv %w(four)
896
+ }
897
+ When run_go_safely
898
+ Then {
899
+ assert_logged_at_error("parse error: foo: 'four' is invalid")
900
+ @foo = nil
901
+ }
902
+ end
903
+
904
+ test_that "arguments that take multiple values can be given valid values" do
905
+ Given {
906
+ arg "foo", :many, %w(one two three)
907
+ @foo = nil
908
+ main do |foo|
909
+ @foo = foo
910
+ end
911
+ set_argv %w(two one two)
912
+ }
913
+ When run_go_safely
914
+ Then {
915
+ @foo = %w(two one two)
916
+ }
917
+ end
918
+
919
+ test_that "arguments that take multiple values can be given valid values, and raise errors when they don't match" do
920
+ Given {
921
+ arg "foo", :many, %w(one two three)
922
+ @foo = nil
923
+ main do |foo|
924
+ @foo = foo
925
+ end
926
+ set_argv %w(four one two seven)
927
+ }
928
+ When run_go_safely
929
+ Then {
930
+ assert_logged_at_error("parse error: foo: The following value(s) were invalid: 'four seven'")
931
+ @foo = nil
932
+ }
933
+ end
934
+ test_that "arguments can be given regexp filters" do
935
+ Given {
936
+ arg "foo", /^th/
937
+ @foo = nil
938
+ main do |foo|
939
+ @foo = foo
940
+ end
941
+ set_argv %w(this)
942
+ }
943
+ When run_go_safely
944
+ Then {
945
+ @foo = 'this'
946
+ }
947
+ end
948
+
949
+ test_that "arguments can be given regexp filters, and raise errors when they don't match" do
950
+ Given {
951
+ arg "foo", :optional, /^th/
952
+ @foo = nil
953
+ main do |foo|
954
+ @foo = foo
955
+ end
956
+ set_argv %w(width)
957
+ }
958
+ When run_go_safely
959
+ Then {
960
+ assert_logged_at_error("parse error: foo: 'width' is invalid")
961
+ @foo = nil
962
+ }
963
+ end
964
+
965
+ test_that "arguments that take multiple values can be given regexp filters" do
966
+ Given {
967
+ arg "foo", :many, /^th/
968
+ @foo = nil
969
+ main do |foo|
970
+ @foo = foo
971
+ end
972
+ set_argv %w(this that the\ other)
973
+ }
974
+ When run_go_safely
975
+ Then {
976
+ @foo = %w(this that the\ other)
977
+ }
978
+ end
979
+
980
+ test_that "arguments that take multiple values can be given regexp filters, and raise errors when they don't match" do
981
+ Given {
982
+ arg "foo", :any, /^th/
983
+ @foo = nil
984
+ main do |foo|
985
+ @foo = foo
986
+ end
987
+ set_argv %w(there is this theory on things that grow)
988
+ }
989
+ When run_go_safely
990
+ Then {
991
+ assert_logged_at_error("parse error: foo: The following value(s) were invalid: 'is on grow'")
992
+ @foo = nil
993
+ }
994
+ end
995
+
996
+ test_that "arguments can be given multiple filters, requiring only one be accepted" do
997
+ Given {
998
+ arg "foo", :any, /^....$/, ['one', 'two', 'six'], /\A..?e.?e.?\z/
999
+ @foo = nil
1000
+ main do |foo|
1001
+ @foo = foo
1002
+ end
1003
+ set_argv %w(one two three four five six seven)
1004
+ }
1005
+ When run_go_safely
1006
+ Then {
1007
+ @foo = %w(one two three four five six seven)
1008
+ }
1009
+ end
1010
+
1011
+ test_that "options can be mutually exclusive" do
1012
+ Given {
1013
+ @first = nil
1014
+ @second = nil
1015
+ reset!
1016
+ main do
1017
+ @first = options[:f]
1018
+ @second = options[:s]
1019
+ end
1020
+
1021
+ on("-f", "--first", "First Option")
1022
+ on("-s", "Second Option", :excludes => :f)
1023
+ }
1024
+ When {
1025
+ set_argv %w(-f)
1026
+ @code = lambda { go! }
1027
+ }
1028
+ Then {
1029
+ assert_exits(0,&@code)
1030
+ @first.should be_truthy
1031
+ @second.should be_nil
1032
+ }
1033
+ end
1034
+
1035
+ test_that "options can be mutually exclusive, which raise an error if both are used" do
1036
+ Given {
1037
+
1038
+ @first = nil
1039
+ @second = nil
1040
+ reset!
1041
+ main do
1042
+ @first = options[:f]
1043
+ @second = options[:s]
1044
+ end
1045
+ on("-f", "--first", "First Option")
1046
+ on("-s", "Second Option", :excludes => :f)
1047
+ }
1048
+ When {
1049
+ set_argv %w(-f -s)
1050
+ @code = lambda { go! }
1051
+ }
1052
+ Then {
1053
+ assert_exits(64,&@code)
1054
+ assert_logged_at_error("parse error: -s cannot be used if already using -f|--first")
1055
+ $stdout.string.should match /Usage:/
1056
+ }
1057
+ end
1058
+
1059
+ test_that "options can be mutually exclusive, which raise an error if both are used - order doesn't matter" do
1060
+ Given {
1061
+
1062
+ @first = nil
1063
+ @second = nil
1064
+ reset!
1065
+ main do
1066
+ @first = options[:f]
1067
+ @second = options[:s]
1068
+ end
1069
+ on("-f", "--first", "First Option")
1070
+ on("-s", "Second Option", :excludes => :f)
1071
+ }
1072
+ When {
1073
+ set_argv %w(-s -f)
1074
+ @code = lambda { go! }
1075
+ }
1076
+ Then {
1077
+ assert_exits(64,&@code)
1078
+ assert_logged_at_error("parse error: -s cannot be used if already using -f|--first")
1079
+ $stdout.string.should match /Usage:/
1080
+ }
1081
+ end
1082
+
1083
+ test_that "options can be mutually exclusive, the enemy of my enemy is my friend" do
1084
+ Given {
1085
+
1086
+ @first = nil
1087
+ @second = nil
1088
+ @third = nil
1089
+ reset!
1090
+ main do
1091
+ @first = options[:f]
1092
+ @second = options[:s]
1093
+ @third = options[:third]
1094
+ end
1095
+ on("-f", "--first", "First Option")
1096
+ on("--another-one", "Second Option", :excludes => :f)
1097
+ on("--third TEST", "Second Option", :excludes => "another-one")
1098
+ }
1099
+ When {
1100
+ set_argv %w(-f --third value)
1101
+ @code = lambda { go! }
1102
+ }
1103
+ Then {
1104
+ assert_exits(0,&@code)
1105
+ @first.should be_truthy
1106
+ @second.should be_nil
1107
+ @third.should == 'value'
1108
+ }
1109
+ end
1110
+
1111
+ test_that "options can be options can require other options to be present" do
1112
+ Given {
1113
+
1114
+ @first = nil
1115
+ @second = nil
1116
+ @third = nil
1117
+ reset!
1118
+ main do
1119
+ @first = options[:f]
1120
+ @second = options[:s]
1121
+ @third = options[:third]
1122
+ end
1123
+ on("-f", "--first", "First Option")
1124
+ on("-a","--another-one", "Second Option")
1125
+ on("--third TEST", "Second Option", :requires => [:first,'another-one'])
1126
+ }
1127
+ When {
1128
+ set_argv %w(-f --third value)
1129
+ @code = lambda { go! }
1130
+ }
1131
+ Then {
1132
+ assert_exits(64,&@code)
1133
+ assert_logged_at_error("parse error: Missing option -a|--another-one required by option --third TEST")
1134
+ $stdout.string.should match /Usage:/
1135
+ }
1136
+ end
1137
+
1138
+ private
1139
+
1140
+ def app_to_use_rc_file(rc_file = '.my_app.rc')
1141
+ lambda {
1142
+ reset!
1143
+ @switch = nil
1144
+ @flag = nil
1145
+ @args = nil
1146
+ main do |*args|
1147
+ @switch = options[:switch]
1148
+ @flag = options[:flag]
1149
+ @args = args
1150
+ end
1151
+
1152
+ defaults_from_config_file rc_file
1153
+
1154
+ on('--switch','Some Switch')
1155
+ on('--flag FOO','Some Flag')
1156
+ }
1157
+ end
1158
+
1159
+ def main_that_exits(exit_status)
1160
+ proc { main { exit_status } }
1161
+ end
1162
+
1163
+ def app_to_use_environment
1164
+ lambda {
1165
+ @switch = nil
1166
+ @flag = nil
1167
+ @args = nil
1168
+ arg :args, :any
1169
+ main do |args|
1170
+ @switch = options[:switch]
1171
+ @flag = options[:flag]
1172
+ @args = args
1173
+ end
1174
+
1175
+ defaults_from_env_var 'APP_OPTS'
1176
+
1177
+ on('--switch','Some Switch')
1178
+ on('--flag FOO','Some Flag')
1179
+ }
1180
+ end
1181
+
1182
+ def main_shouldve_been_called
1183
+ Proc.new { assert @called,"Main block wasn't called?!" }
1184
+ end
1185
+
1186
+ def run_go_safely
1187
+ Proc.new { safe_go! }
1188
+ end
1189
+
1190
+ # Calls go!, but traps the exit
1191
+ def safe_go!
1192
+ go!
1193
+ rescue SystemExit
1194
+ end
1195
+
1196
+ def run_go!; proc { go! }; end
1197
+
1198
+ def assert_logged_at_error(expected_message)
1199
+ @logged.string.should include expected_message
1200
+ end
1201
+
1202
+ def assert_exits(exit_code,message='',&block)
1203
+ block.call
1204
+ fail "Expected an exit of #{exit_code}, but we didn't even exit!"
1205
+ rescue SystemExit => ex
1206
+ assert_equal exit_code,ex.status,@logged.string
1207
+ end
1208
+
1209
+ def set_argv(args)
1210
+ ARGV.clear
1211
+ args.each { |arg| ARGV << arg }
1212
+ end
1213
+ end