qualitysmith_extensions 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.
Files changed (47) hide show
  1. data/Readme +72 -0
  2. data/lib/qualitysmith_extensions/all.rb +2 -0
  3. data/lib/qualitysmith_extensions/array/all.rb +2 -0
  4. data/lib/qualitysmith_extensions/array/average.rb +42 -0
  5. data/lib/qualitysmith_extensions/array/expand_ranges.rb +48 -0
  6. data/lib/qualitysmith_extensions/array/group_by.rb +112 -0
  7. data/lib/qualitysmith_extensions/array/sequence.rb +64 -0
  8. data/lib/qualitysmith_extensions/array/shell_escape.rb +34 -0
  9. data/lib/qualitysmith_extensions/array/to_a_recursive.rb +39 -0
  10. data/lib/qualitysmith_extensions/array/to_query_string.rb +94 -0
  11. data/lib/qualitysmith_extensions/collection_extensions_for_cgi.rb +2 -0
  12. data/lib/qualitysmith_extensions/console/command.facets.1.8.51.rb +749 -0
  13. data/lib/qualitysmith_extensions/console/command.rb +940 -0
  14. data/lib/qualitysmith_extensions/date/all.rb +2 -0
  15. data/lib/qualitysmith_extensions/date/deprecated.rb +38 -0
  16. data/lib/qualitysmith_extensions/date/iso8601.rb +29 -0
  17. data/lib/qualitysmith_extensions/date/month_ranges.rb +120 -0
  18. data/lib/qualitysmith_extensions/enumerable/enum.rb +72 -0
  19. data/lib/qualitysmith_extensions/file/exact_match_regexp.rb +32 -0
  20. data/lib/qualitysmith_extensions/file_test/binary_file.rb +108 -0
  21. data/lib/qualitysmith_extensions/filter_output.rb +107 -0
  22. data/lib/qualitysmith_extensions/global_variable_set.rb +151 -0
  23. data/lib/qualitysmith_extensions/hash/all.rb +2 -0
  24. data/lib/qualitysmith_extensions/hash/to_date.rb +32 -0
  25. data/lib/qualitysmith_extensions/hash/to_query_string.rb +119 -0
  26. data/lib/qualitysmith_extensions/kernel/all.rb +2 -0
  27. data/lib/qualitysmith_extensions/kernel/backtrace.rb +69 -0
  28. data/lib/qualitysmith_extensions/kernel/capture_output.rb +113 -0
  29. data/lib/qualitysmith_extensions/kernel/die.rb +34 -0
  30. data/lib/qualitysmith_extensions/kernel/require_all.rb +118 -0
  31. data/lib/qualitysmith_extensions/kernel/require_once.rb +16 -0
  32. data/lib/qualitysmith_extensions/month.rb +62 -0
  33. data/lib/qualitysmith_extensions/object/singleton.rb +95 -0
  34. data/lib/qualitysmith_extensions/simulate_input.rb +51 -0
  35. data/lib/qualitysmith_extensions/string/all.rb +2 -0
  36. data/lib/qualitysmith_extensions/string/digits_only.rb +25 -0
  37. data/lib/qualitysmith_extensions/string/md5.rb +27 -0
  38. data/lib/qualitysmith_extensions/string/shell_escape.rb +41 -0
  39. data/lib/qualitysmith_extensions/string/to_underscored_label.rb +35 -0
  40. data/lib/qualitysmith_extensions/test/assert_changed.rb +64 -0
  41. data/lib/qualitysmith_extensions/test/assert_exception.rb +63 -0
  42. data/lib/qualitysmith_extensions/test/assert_includes.rb +34 -0
  43. data/lib/qualitysmith_extensions/test/assert_user_error.rb +34 -0
  44. data/lib/qualitysmith_extensions/time/all.rb +2 -0
  45. data/lib/qualitysmith_extensions/time/deprecated.rb +29 -0
  46. data/test/all.rb +16 -0
  47. metadata +94 -0
@@ -0,0 +1,940 @@
1
+ # = command.rb
2
+ #
3
+ # == Copyright (c) 2005 Thomas Sawyer
4
+ #
5
+ # Ruby License
6
+ #
7
+ # This module is free software. You may use, modify, and/or
8
+ # redistribute this software under the same terms as Ruby.
9
+ #
10
+ # This program is distributed in the hope that it will be
11
+ # useful, but WITHOUT ANY WARRANTY; without even the implied
12
+ # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13
+ # PURPOSE.
14
+ #
15
+ # == Author(s)
16
+ #
17
+ # CREDIT Thomas Sawyer
18
+ # CREDIT Tyler Rick
19
+ #
20
+ # == Developer Notes
21
+ #
22
+ # TODO Add help/documentation features.
23
+ #
24
+ # TODO Move to console/command.rb, but I'm not sure yet if
25
+ # adding subdirectories to more/ is a good idea.
26
+ #
27
+
28
+ # Author:: Thomas Sawyer, Tyler Rick
29
+ # Copyright:: Copyright (c) 2005-2007
30
+ # License:: Ruby License
31
+
32
+ require 'shellwords'
33
+ require 'rubygems'
34
+ require 'facet/string/modulize'
35
+ require 'escape' # http://www.a-k-r.org/escape/
36
+
37
+ # TODO Test
38
+ class String
39
+ def option_demethodize
40
+ self.sub('__','--').sub('_','-')
41
+ end
42
+ def option_methodize
43
+ self.gsub('-','_')
44
+ end
45
+ end
46
+
47
+ # = Console
48
+ #
49
+ # Console namespace for use by tools specifically designed
50
+ # for command line interfaces.
51
+
52
+ module Console ; end
53
+
54
+ # = Console Command
55
+ #
56
+ # Console::Command provides a clean and easy way
57
+ # to create a command line interface for your program.
58
+ # The unique technique utlizes a Commandline to Object
59
+ # Mapping (COM) to make it quick and easy.
60
+ #
61
+ # == Synopsis
62
+ #
63
+ # Let's make an executable called 'mycmd'.
64
+ #
65
+ # #!/usr/bin/env ruby
66
+ #
67
+ # require 'facets'
68
+ # require 'command'
69
+ #
70
+ # MyCmd << Console::Command
71
+ #
72
+ # def _v
73
+ # $VERBOSE = true
74
+ # end
75
+ #
76
+ # def jump
77
+ # if $VERBOSE
78
+ # puts "JUMP! JUMP! JUMP!"
79
+ # else
80
+ # puts "Jump"
81
+ # end
82
+ # end
83
+ #
84
+ # end
85
+ #
86
+ # MyCmd.execute
87
+ #
88
+ # Then on the command line:
89
+ #
90
+ # % mycmd jump
91
+ # Jump
92
+ #
93
+ # % mycmd -v jump
94
+ # JUMP! JUMP! JUMP!
95
+ #
96
+ # == Subcommands
97
+ #
98
+ # Commands can take subcommand and suboptions. To do this
99
+ # simply add a module to your class with the same name
100
+ # as the subcommand, in which the suboption methods are defined.
101
+ #
102
+ # MyCmd << Console::Command
103
+ #
104
+ # def initialize
105
+ # @height = 1
106
+ # end
107
+ #
108
+ # def _v
109
+ # $VERBOSE = true
110
+ # end
111
+ #
112
+ # def jump
113
+ # if $VERBOSE
114
+ # puts "JUMP!" * @height
115
+ # else
116
+ # puts "Jump" * @height
117
+ # end
118
+ # end
119
+ #
120
+ # module Jump
121
+ # def __height(h)
122
+ # @height = h.to_i
123
+ # end
124
+ # end
125
+ #
126
+ # end
127
+ #
128
+ # MyCmd.start
129
+ #
130
+ # Then on the command line:
131
+ #
132
+ # % mycmd jump -h 2
133
+ # Jump Jump
134
+ #
135
+ # % mycmd -v jump -h 3
136
+ # JUMP! JUMP! JUMP!
137
+ #
138
+ # Another thing to notice about this example is that #start is an alias
139
+ # for #execute.
140
+ #
141
+ # == Missing Subcommands
142
+ #
143
+ # You can use #method_missing to catch missing subcommand calls.
144
+ #
145
+ # == Aliasing Subcommands
146
+ #
147
+ # You can use alias_subcommand to create an alias for a subcommand. For
148
+ # example:
149
+ #
150
+ # alias_subcommand :st, :status
151
+ #
152
+ # When the user attempts to call the 'st' subcommand, the Status subcommand
153
+ # module will be mixed in and the status subcommand method called.
154
+ #
155
+ # == Main and Default
156
+ #
157
+ # If your command does not take subcommands then simply define
158
+ # a #main method to dispatch action. All options will be treated globablly
159
+ # in this case and any remaining comman-line arguments will be passed
160
+ # to #main.
161
+ #
162
+ # If on the other hand your command does take subcommands but none is given,
163
+ # the #default method will be called, if defined. If not defined
164
+ # an error will be raised (but only reported if $DEBUG is true).
165
+ #
166
+ # == Global Options
167
+ #
168
+ # You can define <i>global options</i> which are options that will be
169
+ # processed no matter where they occur in the command line. In the above
170
+ # examples only the options occuring before the subcommand are processed
171
+ # globally. Anything occuring after the subcommand belonds strictly to
172
+ # the subcommand. For instance, if we had added the following to the above
173
+ # example:
174
+ #
175
+ # global_option :_v
176
+ #
177
+ # Then -v could appear anywhere in the command line, even on the end,
178
+ # and still work as expected.
179
+ #
180
+ # % mycmd jump -h 3 -v
181
+ #
182
+ # == Missing Options
183
+ #
184
+ # You can use #option_missing to catch any options that are not explicility
185
+ # defined.
186
+ #
187
+ # The method signature should look like:
188
+ #
189
+ # option_missing(option_name, args)
190
+ #
191
+ # Example:
192
+ # def option_missing(option_name, args)
193
+ # p args if $debug
194
+ # case option_name
195
+ # when 'p'
196
+ # @a = args[0].to_i
197
+ # @b = args[1].to_i
198
+ # 2
199
+ # else
200
+ # raise Console::Command::UnknownOptionError.new(option_name, args)
201
+ # end
202
+ # end
203
+ #
204
+ # Its return value should be the effective "arity" of that options -- that is,
205
+ # how many arguments it consumed ("-p a b", for example, would consume 2 args:
206
+ # "a" and "b"). An arity of 1 is assumed if nil or false is returned.
207
+ #
208
+ # Be aware that when using subcommand modules, the same option_missing
209
+ # method will catch missing options for global options and subcommand
210
+ # options too unless an option_missing method is also defined in the
211
+ # subcommand module.
212
+ #
213
+ #--
214
+ #
215
+ # == Help Documentation
216
+ #
217
+ # You can also add help information quite easily. If the following code
218
+ # is saved as 'foo' for instance.
219
+ #
220
+ # MyCmd << Console::Command
221
+ #
222
+ # help "Dispays the word JUMP!"
223
+ #
224
+ # def jump
225
+ # if $VERBOSE
226
+ # puts "JUMP! JUMP! JUMP!"
227
+ # else
228
+ # puts "Jump"
229
+ # end
230
+ # end
231
+ #
232
+ # end
233
+ #
234
+ # MyCmd.execute
235
+ #
236
+ # then by running 'foo help' on the command line, standard help information
237
+ # will be displayed.
238
+ #
239
+ # foo
240
+ #
241
+ # jump Displays the word JUMP!
242
+ #
243
+ #++
244
+
245
+ class Console::Command
246
+
247
+ class << self
248
+ # Starts the command execution.
249
+ def execute( *args )
250
+ new(global_options, @subcommand_aliases || {}).execute( *args )
251
+ end
252
+
253
+ # Alias for #execute.
254
+ alias_method :start, :execute
255
+
256
+ # Change the option mode.
257
+ def global_option( *names )
258
+ names.each{ |name| global_options << name.to_sym }
259
+ end
260
+
261
+ def global_options
262
+ @global_options ||= []
263
+ end
264
+
265
+ # This is to be called from your Subcommand module to specify which options should simply be "passed on" to some wrapped command that you will later call.
266
+ # Options that are collected by the option methods that this generates will be stored in @passthrough_options (so remember to append that array to your wrapped command!).
267
+ #
268
+ # module Status
269
+ # Console::Command.pass_through({
270
+ # [:_q, :__quiet] => 0,
271
+ # [:_N, :__non_recursive] => 0,
272
+ # [:__no_ignore] => 0,
273
+ # }, self)
274
+ # end
275
+ #
276
+ # Development notes:
277
+ # * Currently requires you to pass the subcommand module's "self" to this method. I didn't know of a better way to cause it to create the instance methods in *that* module rather than here in Console::Command.
278
+ # * Possible alternatives:
279
+ # * Binding.of_caller() (http://facets.rubyforge.org/src/doc/rdoc/core/classes/Binding.html) -- wary of using it if it depends on Continuations, which I understand are deprecated
280
+ # * copy the pass_through class method to each subcommand module so that calls will be in the module's context...
281
+ def pass_through(options, mod)
282
+ options.each do |method_names, arity|
283
+ method_names.each do |method_name|
284
+ if method_name == :_u
285
+ #puts "Defining method #{method_name}(with arity #{arity}) in #{mod.name}"
286
+ #puts "#{mod.name} has #{(mod.methods - Object.methods).inspect}"
287
+ end
288
+ option_name = method_name.to_s.option_demethodize
289
+ mod.send(:define_method, method_name.to_sym) do |*args|
290
+ @passthrough_options << option_name
291
+ args_for_current_option = Escape.shell_command(args.slice(0, arity))
292
+ @passthrough_options << args_for_current_option unless args_for_current_option == ''
293
+ #p args_for_current_option
294
+ #puts "in #{method_name}: Passing through #{arity} options: #{@passthrough_options.inspect}" #(why does @passthrough_options show up as nil? even when later on it's *not* nil...)
295
+ arity
296
+ end
297
+
298
+
299
+
300
+ # mod.instance_eval %Q{
301
+ # def #{method_name}(*args)
302
+ # @passthrough_options << '#{option_name}'
303
+ # args_for_current_option = Escape.shell_command(args.slice(0, #{arity}))
304
+ # @passthrough_options << args_for_current_option unless args_for_current_option == ''
305
+ # #p args_for_current_option
306
+ # #puts "in #{method_name}: Passing through #{arity} options: #{@passthrough_options.inspect}" #(why does @passthrough_options show up as nil? even when later on it's *not* nil...)
307
+ # #{arity}
308
+ # end
309
+ # }
310
+
311
+ end
312
+ end
313
+ end
314
+
315
+ def alias_subcommand(hash)
316
+ (@subcommand_aliases ||= {}).merge! hash
317
+ end
318
+
319
+ end
320
+
321
+ # Do not let this pass through to
322
+ # any included module.
323
+
324
+ def initialize(global_options, subcommand_aliases)
325
+ @global_options, @subcommand_aliases =
326
+ global_options, subcommand_aliases
327
+ end
328
+
329
+ # Execute the command.
330
+
331
+ def execute( line=nil )
332
+ begin
333
+ case line
334
+ when String
335
+ arguments = Shellwords.shellwords(line)
336
+ when Array
337
+ arguments = line
338
+ else
339
+ arguments = ARGV
340
+ end
341
+
342
+ # Duplicate arguments to work on them in-place.
343
+ argv = arguments.dup
344
+
345
+ # Split single letter option groupings into separate options.
346
+ # ie. -xyz => -x -y -z
347
+ argv = argv.collect { |arg|
348
+ if md = /^-(\w{2,})/.match( arg )
349
+ md[1].split(//).collect { |c| "-#{c}" }
350
+ else
351
+ arg
352
+ end
353
+ }.flatten
354
+
355
+ # Process global options
356
+ global_options.each do |name|
357
+ o = name.to_s.option_demethodize
358
+ m = method(name)
359
+ c = m.arity
360
+ while i = argv.index(o)
361
+ args = argv.slice!(i,c+1)
362
+ args.shift
363
+ m.call(*args)
364
+ end
365
+ end
366
+
367
+ # Does this command take subcommands?
368
+ takes_subcommands = !respond_to?(:main)
369
+
370
+ # Process primary options
371
+ argv = execute_options( argv, takes_subcommands )
372
+
373
+ # If this command doesn't take subcommands, then the remaining arguments are arguments for main().
374
+ return send(:main, *argv) unless takes_subcommands
375
+
376
+ # What to do if there is nothing else?
377
+ if argv.empty?
378
+ if respond_to?(:default)
379
+ return __send__(:default)
380
+ else
381
+ $stderr << "Nothing to do."
382
+ puts '' # :fix: This seems to be necessary or else I don't see the $stderr output at all! --Tyler
383
+ return
384
+ end
385
+ end
386
+
387
+ # Remaining arguments are subcommand and suboptions.
388
+
389
+ @subcommand = argv.shift.gsub('-','_')
390
+ @subcommand = (subcommand_aliases[@subcommand.to_sym] || @subcommand).to_s
391
+ puts "@subcommand = #{@subcommand}" if $debug
392
+
393
+ # Extend subcommand option module
394
+ #subconst = subcommand.gsub(/\W/,'_').capitalize
395
+ subconst = @subcommand.modulize
396
+ #p self.class.constants if $debug
397
+ if self.class.const_defined?(subconst)
398
+ puts "Extending self (#{self.class}) with subcommand module #{subconst}" if $debug
399
+ submod = self.class.const_get(subconst)
400
+ #puts "... which has these **module** methods (should be instance methods): #{(submod.methods - submod.instance_methods - Object.methods).sort.inspect}"
401
+ self.extend submod
402
+ #puts "... and now self has: #{(self.methods - Object.methods).sort.inspect}"
403
+ end
404
+
405
+ # Is the subcommand defined?
406
+ # This is a little tricky. The method has to be defined by a *subclass*.
407
+ @subcommand_is_defined = self.respond_to?( @subcommand ) and
408
+ !Console::Command.public_instance_methods.include?( @subcommand.to_s )
409
+
410
+ # The rest of the args will be interpreted as options for this particular subcommand options.
411
+ argv = execute_options( argv, false )
412
+
413
+ # Actually call the subcommand (or method_missing if the subcommand method isn't defined)
414
+ if @subcommand_is_defined
415
+ puts "Calling #{@subcommand}(#{argv.inspect})" if $debug
416
+ __send__(@subcommand, *argv)
417
+ else
418
+ #begin
419
+ puts "Calling method_missing with #{@subcommand}, #{argv.inspect}" if $debug
420
+ method_missing(@subcommand, *argv)
421
+ #rescue NoMethodError => e
422
+ #if self.private_methods.include?( "no_command_error" )
423
+ # no_command_error( *args )
424
+ #else
425
+ # $stderr << "Non-applicable command -- #{argv.join(' ')}\n"
426
+ # exit -1
427
+ #end
428
+ #end
429
+ end
430
+
431
+ rescue UnknownOptionError => exception
432
+ $stderr << exception.message << "\n"
433
+ exit -1
434
+ end
435
+
436
+ # rescue => err
437
+ # if $DEBUG
438
+ # raise err
439
+ # else
440
+ # msg = err.message.chomp('.') + '.'
441
+ # msg[0,1] = msg[0,1].capitalize
442
+ # msg << " (#{err.class})" if $VERBOSE
443
+ # $stderr << msg
444
+ # end
445
+ end # def execute
446
+
447
+ private
448
+
449
+ #
450
+
451
+ attr_accessor :global_options
452
+ attr_accessor :subcommand_aliases
453
+
454
+ def subcommand_aliases_list(main_subcommand_name)
455
+ # If subcommand_aliases returns {:edit_ext=>:edit_externals, :ee=>:edit_externals}, then
456
+ # subcommand_aliases_list(:edit_externals) ought to return [:edit_ext, :ee]
457
+ subcommand_aliases.select {|k, v| v == main_subcommand_name}.
458
+ map {|k, v| k if v == main_subcommand_name}
459
+ end
460
+
461
+ #
462
+
463
+ def execute_options( argv, break_when_hit_subcommand = false )
464
+ argv = argv.dup
465
+ args_to_return = []
466
+ until argv.empty?
467
+ arg = argv.first
468
+ if arg[0,1] == '-'
469
+ puts "'#{arg}' -- is an option" if $debug
470
+ method_name = arg.option_methodize
471
+ #puts "Methods: #{(methods - Object.methods).inspect}" if $debug
472
+ if respond_to?(method_name)
473
+ m = method(method_name)
474
+ puts "Method named #{method_name} exists and has an arity of #{m.arity}" if $debug
475
+ if m.arity == -1
476
+ # Implemented the same as for option_missing, except that we don't pass the *name* of the option
477
+ arity = m.call(*argv[1..-1]) || 1
478
+ puts "#{method_name} returned an arity of #{arity}" if $debug
479
+ if !arity.is_a?(Fixnum)
480
+ raise "Expected #{method_name} to return a valid arity, but it didn't"
481
+ end
482
+ #puts "argv before: #{argv.inspect}"
483
+ argv.shift # Get rid of the *name* of the option
484
+ argv.slice!(0, arity) # Then discard as many arguments as that option claimed it used up
485
+ #puts "argv after: #{argv.inspect}"
486
+ else
487
+ args_for_current_option = argv.slice!(0, m.arity+1) # The +1 is so that we also remove the option name from argv
488
+ args_for_current_option.shift # Remove the option name from args_for_current_option as well
489
+ m.call(*args_for_current_option)
490
+ end
491
+ elsif respond_to?(:option_missing)
492
+ puts "No method named #{method_name} exists -- calling option_missing(#{arg}, #{argv[1..-1].inspect})" if $debug
493
+ # Old: arity = option_missing(arg.gsub(/^[-]+/,''), argv[1..-1]) || 1
494
+ arity = option_missing(arg, argv[1..-1]) || 1
495
+ argv.shift # Get rid of the *name* of the option
496
+ argv.slice!(0, arity) # Then discard as many arguments as that option claimed it used up
497
+ else
498
+ raise UnknownOptionError.new(arg)
499
+ end
500
+ else
501
+ puts "'#{arg}' -- not an option. Adding to args_to_return..." if $debug
502
+ if break_when_hit_subcommand
503
+ # If we are parsing options for the *main* command and we are allowing subcommands, then we want to stop as soon as we
504
+ # get to the first non-option, because that non-option will be the name of our subcommand and all options that follow
505
+ # should be parsed later when we handle the subcommand (after we've extended the subcommand module, for instance).
506
+ args_to_return = argv
507
+ break
508
+ else
509
+ args_to_return << argv.shift
510
+ end
511
+ end
512
+ end
513
+ puts "Returning #{args_to_return.inspect}" if $debug
514
+ return args_to_return
515
+ end
516
+
517
+ public
518
+
519
+ =begin
520
+ # We include a module here so you can define your own help
521
+ # command and call #super to utilize this one.
522
+
523
+ module Help
524
+
525
+ def help
526
+ opts = help_options
527
+ s = ""
528
+ s << "#{File.basename($0)}\n\n"
529
+ unless opts.empty?
530
+ s << "OPTIONS\n"
531
+ s << help_options
532
+ s << "\n"
533
+ end
534
+ s << "COMMANDS\n"
535
+ s << help_commands
536
+ puts s
537
+ end
538
+
539
+ private
540
+
541
+ def help_commands
542
+ help = self.class.help
543
+ bufs = help.keys.collect{ |a| a.to_s.size }.max + 3
544
+ lines = []
545
+ help.each { |cmd, str|
546
+ cmd = cmd.to_s
547
+ if cmd !~ /^_/
548
+ lines << " " + cmd + (" " * (bufs - cmd.size)) + str
549
+ end
550
+ }
551
+ lines.join("\n")
552
+ end
553
+
554
+ def help_options
555
+ help = self.class.help
556
+ bufs = help.keys.collect{ |a| a.to_s.size }.max + 3
557
+ lines = []
558
+ help.each { |cmd, str|
559
+ cmd = cmd.to_s
560
+ if cmd =~ /^_/
561
+ lines << " " + cmd.gsub(/_/,'-') + (" " * (bufs - cmd.size)) + str
562
+ end
563
+ }
564
+ lines.join("\n")
565
+ end
566
+
567
+ module ClassMethods
568
+
569
+ def help( str=nil )
570
+ return (@help ||= {}) unless str
571
+ @current_help = str
572
+ end
573
+
574
+ def method_added( meth )
575
+ if @current_help
576
+ @help ||= {}
577
+ @help[meth] = @current_help
578
+ @current_help = nil
579
+ end
580
+ end
581
+
582
+ end
583
+
584
+ end
585
+
586
+ include Help
587
+ extend Help::ClassMethods
588
+ =end
589
+
590
+ class UnknownOptionError < StandardError
591
+ def initialize(option_name)
592
+ @option_name = option_name
593
+ end
594
+ def message
595
+ "Unknown option '#{@option_name}'."
596
+ end
597
+ end
598
+ end
599
+
600
+
601
+
602
+ # _____ _
603
+ # |_ _|__ ___| |_
604
+ # | |/ _ \/ __| __|
605
+ # | | __/\__ \ |_
606
+ # |_|\___||___/\__|
607
+ #
608
+
609
+ =begin test
610
+
611
+ require 'test/unit'
612
+ require 'stringio'
613
+ require 'qualitysmith_extensions/kernel/capture_output'
614
+
615
+ class TestCommand < Test::Unit::TestCase
616
+ def setup
617
+ $output = nil
618
+ end
619
+
620
+ #-----------------------------------------------------------------------------------------------------------------------------
621
+
622
+ class SimpleCommand < Console::Command
623
+ def __here ; @here = true ; end
624
+
625
+ def main(*args)
626
+ $output = [@here] | args
627
+ end
628
+ end
629
+
630
+ def test_SimpleCommand
631
+ SimpleCommand.execute( '--here file1 file2' )
632
+ assert_equal( [true, 'file1', 'file2'], $output )
633
+ end
634
+
635
+ #-----------------------------------------------------------------------------------------------------------------------------
636
+
637
+ class MethodMissingSubcommand < Console::Command
638
+ def __here ; @here = true ; end
639
+
640
+ def method_missing(subcommand, *args)
641
+ $output = [@here, subcommand] | args
642
+ end
643
+ end
644
+
645
+ def test_MethodMissingSubcommand
646
+ MethodMissingSubcommand.execute( '--here go file1' )
647
+ assert_equal( [true, 'go', 'file1'], $output )
648
+ end
649
+
650
+ #-----------------------------------------------------------------------------------------------------------------------------
651
+
652
+ class SimpleSubcommand < Console::Command
653
+ def __here ; @here = true ; end
654
+
655
+ # subcommand
656
+
657
+ module Go
658
+ def _p(n)
659
+ @p = n.to_i
660
+ end
661
+ end
662
+
663
+ def go ; $output = [@here, @p] ; end
664
+ end
665
+
666
+ def test_SimpleSubcommand
667
+ SimpleSubcommand.execute( '--here go -p 1' )
668
+ assert_equal( [true, 1], $output )
669
+ end
670
+
671
+ #-----------------------------------------------------------------------------------------------------------------------------
672
+
673
+ # Global options can be anywhere, right? Even after subcommands? Let's find out.
674
+ class GlobalOptionsAfterSubcommand < Console::Command
675
+ def _x ; @x = true ; end
676
+ global_option :_x
677
+
678
+ def go ; $output = [@x, @p] ; end
679
+
680
+ module Go
681
+ def _p(n)
682
+ @p = n.to_i
683
+ end
684
+ end
685
+ end
686
+
687
+ def test_GlobalOptionsAfterSubcommand
688
+ GlobalOptionsAfterSubcommand.execute( 'go -x -p 1' )
689
+ assert_equal( [true, 1], $output )
690
+
691
+ GlobalOptionsAfterSubcommand.execute( 'go -p 1 -x' )
692
+ assert_equal( [true, 1], $output )
693
+ end
694
+
695
+ #-----------------------------------------------------------------------------------------------------------------------------
696
+
697
+ class GivingUnrecognizedOptions < Console::Command
698
+ def _x ; @x = true ; end
699
+ def go ; $output = [@x, @p] ; end
700
+ end
701
+
702
+ def test_GivingUnrecognizedOptions
703
+ stderr = capture_output $stderr do
704
+ assert_raise(SystemExit) do
705
+ GivingUnrecognizedOptions.execute( '--an-option-that-wont-be-recognized -x go' )
706
+ end
707
+ end
708
+ assert_equal "Unknown option '--an-option-that-wont-be-recognized'.\n", stderr
709
+ assert_equal( nil, $output )
710
+ end
711
+
712
+ #-----------------------------------------------------------------------------------------------------------------------------
713
+
714
+ class PassingMultipleSingleCharOptionsAsOneOption < Console::Command
715
+ def _x ; @x = true ; end
716
+ def _y ; @y = true ; end
717
+ def _z(n) ; @z = n ; end
718
+
719
+ global_option :_x
720
+
721
+ def go ; $output = [@x, @y, @z, @p] ; end
722
+
723
+ module Go
724
+ def _p(n)
725
+ @p = n.to_i
726
+ end
727
+ end
728
+ end
729
+
730
+ def test_PassingMultipleSingleCharOptionsAsOneOption
731
+ PassingMultipleSingleCharOptionsAsOneOption.execute( '-xy -z HERE go -p 1' )
732
+ assert_equal( [true, true, 'HERE', 1], $output )
733
+ end
734
+
735
+ #-----------------------------------------------------------------------------------------------------------------------------
736
+
737
+ class OptionUsingEquals < Console::Command
738
+ module Go
739
+ def __mode(mode) ; @mode = mode ; end
740
+ end
741
+ def go ; $output = [@mode] ; end
742
+ end
743
+
744
+ def test_OptionUsingEquals
745
+ OptionUsingEquals.execute( 'go --mode smart' )
746
+ assert_equal( ['smart'], $output )
747
+
748
+ # I would expect this to work too, but currently it doesn't.
749
+ #assert_nothing_raised { OptionUsingEquals.execute( 'go --mode=smart' ) }
750
+ #assert_equal( ['smart'], $output )
751
+ end
752
+
753
+ #-----------------------------------------------------------------------------------------------------------------------------
754
+
755
+ class SubcommandThatTakesArgs < Console::Command
756
+ def go(arg1, *args) ; $output = [arg1] | args ; end
757
+ end
758
+
759
+ def test_SubcommandThatTakesArgs
760
+ SubcommandThatTakesArgs.execute( 'go file1 file2 file3' )
761
+ assert_equal( ['file1', 'file2', 'file3'], $output )
762
+ end
763
+
764
+ #-----------------------------------------------------------------------------------------------------------------------------
765
+
766
+ class With2OptionalArgs < Console::Command
767
+ module Go
768
+ def _p(n)
769
+ @p = n.to_i
770
+ end
771
+ end
772
+
773
+ def go(optional1 = nil, optional2 = nil) ; $output = [@p, optional1, optional2 ] ; end
774
+ end
775
+
776
+ def test_With2OptionalArgs
777
+ With2OptionalArgs.execute( 'go -p 1 to' )
778
+ assert_equal( [1, 'to', nil], $output )
779
+ end
780
+
781
+ #-----------------------------------------------------------------------------------------------------------------------------
782
+
783
+ class VariableArgs < Console::Command
784
+ module Go
785
+ def _p(n)
786
+ @p = n.to_i
787
+ end
788
+ end
789
+
790
+ def go(*args) ; $output = [@p] | args ; end
791
+ end
792
+
793
+ def test_VariableArgs
794
+ VariableArgs.execute( 'go -p 1 to bed' )
795
+ assert_equal( [1, 'to', 'bed'], $output )
796
+ end
797
+
798
+ #-----------------------------------------------------------------------------------------------------------------------------
799
+
800
+ class OptionMissing < Console::Command
801
+ module Go
802
+ def option_missing(option_name, args)
803
+ p args if $debug
804
+ case option_name
805
+ when '-p'
806
+ @p = args[0].to_i
807
+ 1
808
+ else
809
+ raise Console::Command::UnknownOptionError.new(option_name)
810
+ end
811
+ end
812
+ end
813
+
814
+ def go(*args) ; $output = [@p] | args ; end
815
+ end
816
+
817
+ def test_OptionMissing
818
+ OptionMissing.execute( 'go -p 1 to bed right now' )
819
+ assert_equal( [1, 'to', 'bed', 'right', 'now'], $output )
820
+ end
821
+
822
+ #-----------------------------------------------------------------------------------------------------------------------------
823
+
824
+ class OptionWith0Arity < Console::Command
825
+ module Go
826
+ def _p()
827
+ @p = 13
828
+ end
829
+ end
830
+
831
+ def go(arg1) ; $output = [@p, arg1] ; end
832
+ end
833
+
834
+ def test_OptionWith0Arity
835
+ OptionWith0Arity.execute( 'go -p away' )
836
+ assert_equal( [13, 'away'], $output )
837
+ end
838
+
839
+ #-----------------------------------------------------------------------------------------------------------------------------
840
+
841
+ class OptionWithVariableArity < Console::Command
842
+ module Go
843
+ def _p(*args)
844
+ #puts "_p received #{args.size} args: #{args.inspect}"
845
+ @p = args.reject {|arg| arg.to_i.to_s != arg } # TODO: this should be extracted to reusable String#is_numeric? if one doesn't exist
846
+ #puts "_p accepting #{@p.size} args: #{@p.inspect}"
847
+ @p.size
848
+ end
849
+ end
850
+
851
+ def go(arg1) ; $output = @p | [arg1] ; end
852
+ end
853
+
854
+ def test_OptionWithVariableArity
855
+ OptionWithVariableArity.execute( 'go -p 1 2 3 4 away' )
856
+ assert_equal( ['1', '2', '3', '4', 'away'], $output )
857
+ end
858
+
859
+ #-----------------------------------------------------------------------------------------------------------------------------
860
+
861
+ class OptionMissingArityOf2 < Console::Command
862
+ module Go
863
+ def option_missing(option_name, args)
864
+ case option_name
865
+ when '-p'
866
+ @p1 = args[0].to_i
867
+ @p2 = args[1].to_i
868
+ 2
869
+ when '-q'
870
+ @q = args[0].to_i
871
+ nil # Test default arity
872
+ else
873
+ raise Console::Command::UnknownOptionError.new.new(option_name, args)
874
+ end
875
+ end
876
+ end
877
+
878
+ def go(*args) ; $output = [@p1, @p2, @q] | args ; end
879
+ end
880
+
881
+ def test_OptionMissingArityOf2
882
+ OptionMissingArityOf2.execute( 'go -p 1 2 -q 3 to bed right now' )
883
+ assert_equal( [1, 2, 3, 'to', 'bed', 'right', 'now'], $output )
884
+ end
885
+
886
+ #-----------------------------------------------------------------------------------------------------------------------------
887
+
888
+ class OptionMissingReceivesShortAndLongOptionsDifferently < Console::Command
889
+ module Go
890
+ def option_missing(option_name, args)
891
+ case option_name
892
+ when '-s'
893
+ @s = "-s #{args[0]}"
894
+ 1
895
+ when '--long'
896
+ @long = "--long #{args[0]}"
897
+ 1
898
+ else
899
+ raise Console::Command::UnknownOptionError.new(option_name, args)
900
+ end
901
+ end
902
+ end
903
+
904
+ def go(*args) ; $output = [@s, @long] ; end
905
+ end
906
+
907
+ def test_OptionMissingReceivesShortAndLongOptionsDifferently
908
+ OptionMissingReceivesShortAndLongOptionsDifferently.execute( 'go -s 1 --long long' )
909
+ assert_equal( ['-s 1', '--long long'], $output )
910
+ end
911
+
912
+ #-----------------------------------------------------------------------------------------------------------------------------
913
+
914
+ class AliasSubcommand < Console::Command
915
+ alias_subcommand :g => :go
916
+ module Go
917
+ def option_missing(option_name, args)
918
+ case option_name
919
+ when '-s'
920
+ @s = "-s #{args[0]}"
921
+ 1
922
+ when '--long'
923
+ @long = "--long #{args[0]}"
924
+ 1
925
+ else
926
+ raise Console::Command::UnknownOptionError.new(option_name, args)
927
+ end
928
+ end
929
+ end
930
+
931
+ def go(*args) ; $output = [@s, @long] ; end
932
+ end
933
+
934
+ def test_AliasSubcommand
935
+ AliasSubcommand.execute( 'g -s 1 --long long' )
936
+ assert_equal( ['-s 1', '--long long'], $output )
937
+ end
938
+ end
939
+
940
+ =end