quality_extensions 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/Readme +54 -0
  2. data/lib/qualitysmith_extensions/all.rb +4 -0
  3. data/lib/qualitysmith_extensions/array/all.rb +2 -0
  4. data/lib/qualitysmith_extensions/array/average.rb +44 -0
  5. data/lib/qualitysmith_extensions/array/classify.rb +97 -0
  6. data/lib/qualitysmith_extensions/array/expand_ranges.rb +52 -0
  7. data/lib/qualitysmith_extensions/array/group_by.rb +134 -0
  8. data/lib/qualitysmith_extensions/array/sequence.rb +66 -0
  9. data/lib/qualitysmith_extensions/array/shell_escape.rb +36 -0
  10. data/lib/qualitysmith_extensions/array/to_a_recursive.rb +41 -0
  11. data/lib/qualitysmith_extensions/array/to_query_string.rb +96 -0
  12. data/lib/qualitysmith_extensions/collection_extensions_for_cgi.rb +2 -0
  13. data/lib/qualitysmith_extensions/colored/toggleability.rb +62 -0
  14. data/lib/qualitysmith_extensions/console/command.facets.1.8.51.rb +749 -0
  15. data/lib/qualitysmith_extensions/console/command.facets.1.8.54.rb +748 -0
  16. data/lib/qualitysmith_extensions/console/command.rb +944 -0
  17. data/lib/qualitysmith_extensions/date/all.rb +2 -0
  18. data/lib/qualitysmith_extensions/date/deprecated.rb +40 -0
  19. data/lib/qualitysmith_extensions/date/iso8601.rb +31 -0
  20. data/lib/qualitysmith_extensions/date/month_ranges.rb +122 -0
  21. data/lib/qualitysmith_extensions/dir/each_child.rb +58 -0
  22. data/lib/qualitysmith_extensions/enumerable/enum.rb +69 -0
  23. data/lib/qualitysmith_extensions/enumerable/select_until.rb +4 -0
  24. data/lib/qualitysmith_extensions/enumerable/select_while.rb +109 -0
  25. data/lib/qualitysmith_extensions/exception/inspect_with_backtrace.rb +65 -0
  26. data/lib/qualitysmith_extensions/file/exact_match_regexp.rb +34 -0
  27. data/lib/qualitysmith_extensions/file_test/binary_file.rb +110 -0
  28. data/lib/qualitysmith_extensions/find/select.rb +68 -0
  29. data/lib/qualitysmith_extensions/global_variable_set.rb +153 -0
  30. data/lib/qualitysmith_extensions/hash/all.rb +2 -0
  31. data/lib/qualitysmith_extensions/hash/to_date.rb +34 -0
  32. data/lib/qualitysmith_extensions/hash/to_query_string.rb +121 -0
  33. data/lib/qualitysmith_extensions/kernel/all.rb +2 -0
  34. data/lib/qualitysmith_extensions/kernel/autoreload.rb +128 -0
  35. data/lib/qualitysmith_extensions/kernel/backtrace.rb +71 -0
  36. data/lib/qualitysmith_extensions/kernel/capture_output.rb +115 -0
  37. data/lib/qualitysmith_extensions/kernel/die.rb +49 -0
  38. data/lib/qualitysmith_extensions/kernel/example_printer.rb +81 -0
  39. data/lib/qualitysmith_extensions/kernel/filter_output.rb +108 -0
  40. data/lib/qualitysmith_extensions/kernel/remove_const.rb +178 -0
  41. data/lib/qualitysmith_extensions/kernel/remove_module.rb +127 -0
  42. data/lib/qualitysmith_extensions/kernel/require_all.rb +186 -0
  43. data/lib/qualitysmith_extensions/kernel/require_local_all.rb +4 -0
  44. data/lib/qualitysmith_extensions/kernel/require_once.rb +18 -0
  45. data/lib/qualitysmith_extensions/kernel/simulate_input.rb +52 -0
  46. data/lib/qualitysmith_extensions/kernel/trap_chain.rb +61 -0
  47. data/lib/qualitysmith_extensions/kernel/windows_platform.rb +46 -0
  48. data/lib/qualitysmith_extensions/module/alias_method.rb +6 -0
  49. data/lib/qualitysmith_extensions/module/alias_method_chain.rb +165 -0
  50. data/lib/qualitysmith_extensions/module/ancestry_of_instance_method.rb +43 -0
  51. data/lib/qualitysmith_extensions/module/attribute_accessors.rb +49 -0
  52. data/lib/qualitysmith_extensions/module/basename.rb +76 -0
  53. data/lib/qualitysmith_extensions/module/bool_attr_accessor.rb +497 -0
  54. data/lib/qualitysmith_extensions/module/class_methods.rb +87 -0
  55. data/lib/qualitysmith_extensions/module/create.rb +315 -0
  56. data/lib/qualitysmith_extensions/module/create_setter.rb +9 -0
  57. data/lib/qualitysmith_extensions/module/dirname.rb +4 -0
  58. data/lib/qualitysmith_extensions/module/guard_method.rb +312 -0
  59. data/lib/qualitysmith_extensions/module/includable_once.rb +10 -0
  60. data/lib/qualitysmith_extensions/module/join.rb +66 -0
  61. data/lib/qualitysmith_extensions/module/malias_method_chain.rb +92 -0
  62. data/lib/qualitysmith_extensions/module/module_methods.rb +4 -0
  63. data/lib/qualitysmith_extensions/module/namespace.rb +112 -0
  64. data/lib/qualitysmith_extensions/module/parents.rb +61 -0
  65. data/lib/qualitysmith_extensions/module/remove_const.rb +117 -0
  66. data/lib/qualitysmith_extensions/module/split.rb +55 -0
  67. data/lib/qualitysmith_extensions/month.rb +66 -0
  68. data/lib/qualitysmith_extensions/mutex/if_available.rb +75 -0
  69. data/lib/qualitysmith_extensions/object/ancestry_of_method.rb +257 -0
  70. data/lib/qualitysmith_extensions/object/default.rb +69 -0
  71. data/lib/qualitysmith_extensions/object/if_else.rb +157 -0
  72. data/lib/qualitysmith_extensions/object/ignore_access.rb +84 -0
  73. data/lib/qualitysmith_extensions/object/mcall.rb +92 -0
  74. data/lib/qualitysmith_extensions/object/methods.rb +63 -0
  75. data/lib/qualitysmith_extensions/object/send_if.rb +151 -0
  76. data/lib/qualitysmith_extensions/object/send_if_not_nil.rb +35 -0
  77. data/lib/qualitysmith_extensions/object/singleton_send.rb +129 -0
  78. data/lib/qualitysmith_extensions/regexp/join.rb +111 -0
  79. data/lib/qualitysmith_extensions/string/all.rb +2 -0
  80. data/lib/qualitysmith_extensions/string/constantize.rb +4 -0
  81. data/lib/qualitysmith_extensions/string/digits_only.rb +27 -0
  82. data/lib/qualitysmith_extensions/string/each_char_with_index.rb +41 -0
  83. data/lib/qualitysmith_extensions/string/md5.rb +29 -0
  84. data/lib/qualitysmith_extensions/string/shell_escape.rb +43 -0
  85. data/lib/qualitysmith_extensions/string/to_underscored_label.rb +37 -0
  86. data/lib/qualitysmith_extensions/string/with_knowledge_of_color.rb +64 -0
  87. data/lib/qualitysmith_extensions/symbol/constantize.rb +69 -0
  88. data/lib/qualitysmith_extensions/symbol/match.rb +157 -0
  89. data/lib/qualitysmith_extensions/template.rb +33 -0
  90. data/lib/qualitysmith_extensions/test/all.rb +2 -0
  91. data/lib/qualitysmith_extensions/test/assert_anything.rb +93 -0
  92. data/lib/qualitysmith_extensions/test/assert_changed.rb +66 -0
  93. data/lib/qualitysmith_extensions/test/assert_exception.rb +66 -0
  94. data/lib/qualitysmith_extensions/test/assert_includes.rb +36 -0
  95. data/lib/qualitysmith_extensions/test/assert_user_error.rb +37 -0
  96. data/lib/qualitysmith_extensions/test/difference_highlighting.rb +323 -0
  97. data/lib/qualitysmith_extensions/time/all.rb +2 -0
  98. data/lib/qualitysmith_extensions/time/deprecated.rb +31 -0
  99. data/test/all.rb +16 -0
  100. metadata +148 -0
@@ -0,0 +1,36 @@
1
+ #--
2
+ # Author:: Tyler Rick
3
+ # Copyright:: Copyright (c) 2007 QualitySmith, Inc.
4
+ # License:: Ruby License
5
+ # Submit to Facets?:: Yes.
6
+ #++
7
+
8
+ require 'rubygems'
9
+ require 'facets/core/symbol/to_proc' unless Symbol.method_defined?(:to_proc)
10
+ require 'facets/core/kernel/require_local'
11
+ require_local '../string/shell_escape.rb'
12
+
13
+ class Array
14
+ def shell_escape
15
+ self.map(&:shell_escape)
16
+ end
17
+ end
18
+
19
+ # _____ _
20
+ # |_ _|__ ___| |_
21
+ # | |/ _ \/ __| __|
22
+ # | | __/\__ \ |_
23
+ # |_|\___||___/\__|
24
+ #
25
+ =begin test
26
+ require 'test/unit'
27
+ class TheTest < Test::Unit::TestCase
28
+ def test_1
29
+ assert_equal ['a'], ['a'].shell_escape
30
+ end
31
+ def test_2
32
+ assert_equal ["arg1", "'multiple words for single argument'"],
33
+ ['arg1', 'multiple words for single argument'].shell_escape
34
+ end
35
+ end
36
+ =end
@@ -0,0 +1,41 @@
1
+ #--
2
+ # Author:: Tyler Rick
3
+ # Copyright:: Copyright (c) 2007 QualitySmith, Inc.
4
+ # License:: Ruby License
5
+ # Submit to Facets?:: Yes.
6
+ #++
7
+
8
+ class Array
9
+
10
+ # A lot like array.flatten, except that it will also "flatten" ranges (by converting range to range.to_a) and any other objects that respond to to_a contained in the array in addition to arrays contained in the array.
11
+ # Compare with Array#expand_ranges
12
+ def to_a_recursive
13
+ map do |item|
14
+ # Assume that all arrays contain only elements that do not respond to to_a_recursive or arrays.
15
+ if item.respond_to? :to_a_recursive
16
+ item.to_a_recursive
17
+ else
18
+ item.to_a
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ # _____ _
25
+ # |_ _|__ ___| |_
26
+ # | |/ _ \/ __| __|
27
+ # | | __/\__ \ |_
28
+ # |_|\___||___/\__|
29
+ #
30
+ =begin test
31
+ require 'test/unit'
32
+ class TheTest < Test::Unit::TestCase
33
+ def test_1
34
+ assert_equal [[1, 2, 3], [5, 6, 7]], [1..3, 5..7].to_a_recursive
35
+ end
36
+ def test_2
37
+ assert_equal [[1, 2, 3], [ [5, 6, 7], [9, 10] ] ], [1..3, [5..7, 9..10]].to_a_recursive
38
+ end
39
+ end
40
+ =end
41
+
@@ -0,0 +1,96 @@
1
+ #--
2
+ # Author:: Anthony Kaufman, Tyler Rick
3
+ # Copyright:: Copyright (c) 2007 QualitySmith, Inc.
4
+ # License:: Ruby License
5
+ # Submit to Facets?:: Yes.
6
+ #++
7
+
8
+ autoload :CGI, 'cgi'
9
+ class Array
10
+
11
+ =begin rdoc
12
+ Converts into a string that can be used as the {query string}[http://en.wikipedia.org/wiki/Query_string] of a URL (for example, <tt>?key[]=val1&key[]=val2</tt>).
13
+
14
+ Example:
15
+ [
16
+ 'Fred',
17
+ 'Sam'
18
+ ].to_query_string('names')
19
+ ==> "names[]=Fred&names[]=Sam"
20
+
21
+ <tt>key</tt> is the name of the key in params that will receive the array when you load the page. So, for example, if you go to page with this query string (key = "names"): <tt>?names[]=Fred&names[]=Sam</tt>, params will be have a key "names", like so: <tt>{"names"=>["Fred", "Sam"]}</tt>.
22
+ =end
23
+ def to_query_string(key)
24
+ elements = []
25
+
26
+ self.each do |value|
27
+ _key = key + '[]'
28
+ if value.is_a? Array
29
+ raise "Array#to_query_string only works on flat (1-dimensional) arrays."
30
+ elsif value.respond_to? :to_query_string
31
+ value = value.to_query_string(_key)
32
+ else
33
+ value = CGI.escape value.to_s
34
+ end
35
+ elements << _key + '=' + value
36
+ end
37
+
38
+ elements.join('&')
39
+ end
40
+ end
41
+
42
+ # _____ _
43
+ # |_ _|__ ___| |_
44
+ # | |/ _ \/ __| __|
45
+ # | | __/\__ \ |_
46
+ # |_|\___||___/\__|
47
+ #
48
+ =begin test
49
+ class TheTest < Test::Unit::TestCase
50
+ #-------------------------------------------------------------------------------------------------
51
+ # Array:
52
+
53
+ def test_array_to_query_string_trivial
54
+ data = []
55
+ assert_equal '', data.to_query_string('names')
56
+ end
57
+
58
+ def test_array_to_query_string_basic
59
+ data = [
60
+ 'Fred',
61
+ 'Sam'
62
+ ]
63
+ assert_equal 'names[]=Fred&names[]=Sam', data.to_query_string('names')
64
+ end
65
+
66
+ def test_array_to_query_string_encoding
67
+ data = ['f&r', 'a w$']
68
+
69
+ assert_equal 'foo[]=f%26r&foo[]=a+w%24', data.to_query_string('foo')
70
+ end
71
+
72
+ def test_array_to_query_string_nesting
73
+ data = [
74
+ [
75
+ 'Wilma',
76
+ 'Sara'
77
+ ],
78
+ [
79
+ 'Fred',
80
+ 'Sam'
81
+ ]
82
+ ]
83
+ assert_raise RuntimeError do data.to_query_string('names') end
84
+ end
85
+ end
86
+ =end
87
+
88
+ =begin examples
89
+ require 'irb/xmp'
90
+ xmp <<End
91
+ [
92
+ 'Fred',
93
+ 'Sam'
94
+ ].to_query_string('names')
95
+ End
96
+ =end
@@ -0,0 +1,2 @@
1
+ require File.dirname(__FILE__) + '/hash/to_query_string.rb'
2
+ require File.dirname(__FILE__) + '/array/to_query_string.rb'
@@ -0,0 +1,62 @@
1
+ #--
2
+ # Author:: Tyler Rick
3
+ # Copyright:: Copyright (c) 2007 QualitySmith, Inc.
4
+ # License:: Ruby License
5
+ # Submit to Facets?::
6
+ # Developer notes::
7
+ #++
8
+
9
+ require 'rubygems'
10
+ gem 'colored'
11
+ require 'colored'
12
+ gem 'facets'
13
+ require 'facets/core/module/alias_method_chain'
14
+ gem 'qualitysmith_extensions'
15
+ require 'qualitysmith_extensions/module/guard_method'
16
+
17
+ class String
18
+ @@colorize_enabled = true # Default to enabled
19
+ def colorize_with_toggleability(string, options = {})
20
+ if @@colorize_enabled
21
+ colorize_without_toggleability(string, options)
22
+ else
23
+ string
24
+ end
25
+ end
26
+ alias_method_chain :colorize, :toggleability
27
+ mguard_method :color_on!, :@@colorize_enabled
28
+
29
+ end unless String.instance_methods.include?('colorize_with_toggleability')
30
+
31
+
32
+ # _____ _
33
+ # |_ _|__ ___| |_
34
+ # | |/ _ \/ __| __|
35
+ # | | __/\__ \ |_
36
+ # |_|\___||___/\__|
37
+ #
38
+ =begin test
39
+ require 'test/unit'
40
+
41
+ class TheTest < Test::Unit::TestCase
42
+ def test_color_on
43
+ String.color_on!
44
+ assert_equal "\e[31mfoo\e[0m", 'foo'.red
45
+ end
46
+ def test_color_off
47
+ String.color_on! false
48
+ assert_equal "foo", 'foo'.red
49
+ end
50
+ def test_color_off_with_block
51
+ String.color_on!
52
+ assert_equal "\e[31mfoo\e[0m", 'foo'.red
53
+ String.color_on! false do
54
+ assert_equal "foo", 'foo'.red
55
+ end
56
+ assert_equal "\e[31mfoo\e[0m", 'foo'.red
57
+ end
58
+
59
+ end
60
+ =end
61
+
62
+
@@ -0,0 +1,749 @@
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
+
34
+ # = Console
35
+ #
36
+ # Console namespace for use by tools specifically designed
37
+ # for command line interfaces.
38
+
39
+ module Console ; end
40
+
41
+ # = Console Command
42
+ #
43
+ # Console::Command provides a clean and easy way
44
+ # to create a command line interface for your program.
45
+ # The unique technique utlizes a Commandline to Object
46
+ # Mapping (COM) to make it quick and easy.
47
+ #
48
+ # == Synopsis
49
+ #
50
+ # Let's make an executable called 'mycmd'.
51
+ #
52
+ # #!/usr/bin/env ruby
53
+ #
54
+ # require 'facets'
55
+ # require 'command'
56
+ #
57
+ # MyCmd << Console::Command
58
+ #
59
+ # def _v
60
+ # $VERBOSE = true
61
+ # end
62
+ #
63
+ # def jump
64
+ # if $VERBOSE
65
+ # puts "JUMP! JUMP! JUMP!"
66
+ # else
67
+ # puts "Jump"
68
+ # end
69
+ # end
70
+ #
71
+ # end
72
+ #
73
+ # MyCmd.execute
74
+ #
75
+ # Then on the command line:
76
+ #
77
+ # % mycmd jump
78
+ # Jump
79
+ #
80
+ # % mycmd -v jump
81
+ # JUMP! JUMP! JUMP!
82
+ #
83
+ # == Subcommands
84
+ #
85
+ # Commands can take subcommand and suboptions. To do this
86
+ # simply add a module to your class with the same name
87
+ # as the subcommand, in which the suboption methods are defined.
88
+ #
89
+ # MyCmd << Console::Command
90
+ #
91
+ # def initialize
92
+ # @height = 1
93
+ # end
94
+ #
95
+ # def _v
96
+ # $VERBOSE = true
97
+ # end
98
+ #
99
+ # def jump
100
+ # if $VERBOSE
101
+ # puts "JUMP!" * @height
102
+ # else
103
+ # puts "Jump" * @height
104
+ # end
105
+ # end
106
+ #
107
+ # module Jump
108
+ # def __height(h)
109
+ # @height = h.to_i
110
+ # end
111
+ # end
112
+ #
113
+ # end
114
+ #
115
+ # MyCmd.start
116
+ #
117
+ # Then on the command line:
118
+ #
119
+ # % mycmd jump -h 2
120
+ # Jump Jump
121
+ #
122
+ # % mycmd -v jump -h 3
123
+ # JUMP! JUMP! JUMP!
124
+ #
125
+ # Another thing to notice about this example is that #start is an alias
126
+ # for #execute.
127
+ #
128
+ # == Missing Subcommands
129
+ #
130
+ # You can use #method_missing to catch missing subcommand calls.
131
+ #
132
+ # == Main and Default
133
+ #
134
+ # If your command does not take subcommands then simply define
135
+ # a #main method to dispatch action. All options will be treated globablly
136
+ # in this case and any remaining comman-line arguments will be passed
137
+ # to #main.
138
+ #
139
+ # If on the other hand your command does take subcommands but none is given,
140
+ # the #default method will be called, if defined. If not defined
141
+ # an error will be raised (but only reported if $DEBUG is true).
142
+ #
143
+ # == Global Options
144
+ #
145
+ # You can define <i>global options</i> which are options that will be
146
+ # processed no matter where they occur in the command line. In the above
147
+ # examples only the options occuring before the subcommand are processed
148
+ # globally. Anything occuring after the subcommand belonds strictly to
149
+ # the subcommand. For instance, if we had added the following to the above
150
+ # example:
151
+ #
152
+ # global_option :_v
153
+ #
154
+ # Then -v could appear anywhere in the command line, even on the end,
155
+ # and still work as expected.
156
+ #
157
+ # % mycmd jump -h 3 -v
158
+ #
159
+ # == Missing Options
160
+ #
161
+ # You can use #option_missing to catch any options that are not explicility
162
+ # defined.
163
+ #
164
+ # The method signature should look like:
165
+ #
166
+ # option_missing(option_name, args)
167
+ #
168
+ # Example:
169
+ # def option_missing(option_name, args)
170
+ # p args if $debug
171
+ # case option_name
172
+ # when 'p'
173
+ # @a = args[0].to_i
174
+ # @b = args[1].to_i
175
+ # 2
176
+ # else
177
+ # raise InvalidOptionError(option_name, args)
178
+ # end
179
+ # end
180
+ #
181
+ # Its return value should be the effective "arity" of that options -- that is,
182
+ # how many arguments it consumed ("-p a b", for example, would consume 2 args:
183
+ # "a" and "b"). An arity of 1 is assumed if nil or false is returned.
184
+ #
185
+ # Be aware that when using subcommand modules, the same option_missing
186
+ # method will catch missing options for global options and subcommand
187
+ # options too unless an option_missing method is also defined in the
188
+ # subcommand module.
189
+ #
190
+ #--
191
+ #
192
+ # == Help Documentation
193
+ #
194
+ # You can also add help information quite easily. If the following code
195
+ # is saved as 'foo' for instance.
196
+ #
197
+ # MyCmd << Console::Command
198
+ #
199
+ # help "Dispays the word JUMP!"
200
+ #
201
+ # def jump
202
+ # if $VERBOSE
203
+ # puts "JUMP! JUMP! JUMP!"
204
+ # else
205
+ # puts "Jump"
206
+ # end
207
+ # end
208
+ #
209
+ # end
210
+ #
211
+ # MyCmd.execute
212
+ #
213
+ # then by running 'foo help' on the command line, standard help information
214
+ # will be displayed.
215
+ #
216
+ # foo
217
+ #
218
+ # jump Displays the word JUMP!
219
+ #
220
+ #++
221
+
222
+ class Console::Command
223
+
224
+ class << self
225
+ # Starts the command execution.
226
+
227
+ def execute( *args )
228
+ new(global_options).execute( *args )
229
+ end
230
+
231
+ # Alias for #execute.
232
+
233
+ alias_method :start, :execute
234
+
235
+ # Change the option mode.
236
+
237
+ def global_option( *names )
238
+ names.each{ |name| global_options << name.to_sym }
239
+ end
240
+
241
+ def global_options
242
+ @global_options ||= []
243
+ end
244
+ end
245
+
246
+ # Do not let this pass through to
247
+ # any included module.
248
+
249
+ def initialize(global_options)
250
+ @global_options = global_options
251
+ end
252
+
253
+ # Execute the command.
254
+
255
+ def execute( line=nil )
256
+ case line
257
+ when String
258
+ arguments = Shellwords.shellwords(line)
259
+ when Array
260
+ arguments = line
261
+ else
262
+ arguments = ARGV
263
+ end
264
+
265
+ # duplicate arguments to work on them in-place.
266
+
267
+ argv = arguments.dup
268
+
269
+ # Split single letter option groupings into separate options.
270
+ # ie. -xyz => -x -y -z
271
+
272
+ argv = argv.collect { |arg|
273
+ if md = /^-(\w{2,})/.match( arg )
274
+ md[1].split(//).collect { |c| "-#{c}" }
275
+ else
276
+ arg
277
+ end
278
+ }.flatten
279
+
280
+ # process global options
281
+ global_options.each do |name|
282
+ o = name.to_s.sub('__','--').sub('_','-')
283
+ m = method(name)
284
+ c = m.arity
285
+ while i = argv.index(o)
286
+ args = argv.slice!(i,c+1)
287
+ args.shift
288
+ m.call(*args)
289
+ end
290
+ end
291
+
292
+ # Does this command take subcommands?
293
+ subcommand = !respond_to?(:main)
294
+
295
+ # process primary options
296
+ argv = execute_options( argv, subcommand )
297
+
298
+ # If this command doesn't take subcommands, then the remaining arguments are arguments for main().
299
+ return send(:main, *argv) unless subcommand
300
+
301
+ # What to do if there is nothing else?
302
+ if argv.empty?
303
+ if respond_to?(:default)
304
+ return __send__(:default)
305
+ else
306
+ $stderr << "Nothing to do."
307
+ return
308
+ end
309
+ end
310
+
311
+ # Remaining arguments are subcommand and suboptions.
312
+
313
+ subcmd = argv.shift.gsub('-','_')
314
+ #puts "subcmd = #{subcmd}"
315
+
316
+ # Extend subcommand option module
317
+ subconst = subcmd.gsub(/\W/,'_').capitalize
318
+ #puts self.class.name
319
+ if self.class.const_defined?(subconst)
320
+ puts "Extending self (#{self.class}) with subcommand module #{subconst}" if $debug
321
+ submod = self.class.const_get(subconst)
322
+ self.extend submod
323
+ end
324
+
325
+ # process subcommand options
326
+ #puts "Treating the rest of the args as subcommand options:"
327
+ #p argv
328
+ argv = execute_options( argv )
329
+
330
+ # This is a little tricky. The method has to be defined by a subclass.
331
+ if self.respond_to?( subcmd ) and not Console::Command.public_instance_methods.include?( subcmd.to_s )
332
+ puts "Calling #{subcmd}(#{argv.inspect})" if $debug
333
+ __send__(subcmd, *argv)
334
+ else
335
+ #begin
336
+ puts "Calling method_missing with #{subcmd}, #{argv.inspect}" if $debug
337
+ method_missing(subcmd, *argv)
338
+ #rescue NoMethodError => e
339
+ #if self.private_methods.include?( "no_command_error" )
340
+ # no_command_error( *args )
341
+ #else
342
+ # $stderr << "Non-applicable command -- #{argv.join(' ')}\n"
343
+ # exit -1
344
+ #end
345
+ #end
346
+ end
347
+
348
+ # rescue => err
349
+ # if $DEBUG
350
+ # raise err
351
+ # else
352
+ # msg = err.message.chomp('.') + '.'
353
+ # msg[0,1] = msg[0,1].capitalize
354
+ # msg << " (#{err.class})" if $VERBOSE
355
+ # $stderr << msg
356
+ # end
357
+ end
358
+
359
+ private
360
+
361
+ #
362
+
363
+ def global_options
364
+ @global_options
365
+ end
366
+
367
+ #
368
+
369
+ def execute_options( argv, subcmd=false )
370
+ puts "in execute_options:" if $debug
371
+ argv = argv.dup
372
+ args_to_return = []
373
+ until argv.empty?
374
+ arg = argv.first
375
+ if arg[0,1] == '-'
376
+ puts "'#{arg}' -- is an option" if $debug
377
+
378
+ name = arg.gsub('-','_')
379
+ puts " responds_to(#{name})?" if $debug
380
+ if respond_to?(name)
381
+ m = method(name)
382
+ arity = m.arity
383
+ #puts "{argv before slice: #{argv.inspect}" if $debug
384
+ args_for_current_option = argv.slice!(0, arity+1)
385
+ #puts "}argv after slice: #{argv.inspect}" if $debug
386
+ #puts "{args_for_current_option before shift: #{args_for_current_option.inspect}" if $debug
387
+ args_for_current_option.shift
388
+ #puts "}args_for_current_option after shift: #{args_for_current_option.inspect}" if $debug
389
+ #puts " arity=#{m.arity}" if $debug
390
+ #puts " calling #{name} with #{args_for_current_option.inspect}" if $debug
391
+ m.call(*args_for_current_option)
392
+ elsif respond_to?(:option_missing)
393
+ puts " option_missing(#{argv.inspect})" if $debug
394
+ arity = option_missing(arg.gsub(/^[-]+/,''), argv[1..-1]) || 1
395
+ puts " arity == #{arity}" if $debug
396
+ argv.slice!(0, arity)
397
+ argv.shift # Get rid of the *name* of the option
398
+ else
399
+ $stderr << "Unknown option '#{arg}'.\n"
400
+ exit -1
401
+ end
402
+ else
403
+ puts "'#{arg}' -- not an option. Adding to args_to_return..." if $debug
404
+ if subcmd
405
+ args_to_return = argv
406
+ #puts "subcommand. args_to_return=#{args_to_return.inspect}" if $debug
407
+ break
408
+ else
409
+ args_to_return << argv.shift
410
+ puts "args_to_return=#{args_to_return.inspect}" if $debug
411
+ end
412
+ end
413
+ end
414
+ puts "Returning #{args_to_return.inspect}" if $debug
415
+ return args_to_return
416
+ end
417
+
418
+ public
419
+
420
+ =begin
421
+ # We include a module here so you can define your own help
422
+ # command and call #super to utilize this one.
423
+
424
+ module Help
425
+
426
+ def help
427
+ opts = help_options
428
+ s = ""
429
+ s << "#{File.basename($0)}\n\n"
430
+ unless opts.empty?
431
+ s << "OPTIONS\n"
432
+ s << help_options
433
+ s << "\n"
434
+ end
435
+ s << "COMMANDS\n"
436
+ s << help_commands
437
+ puts s
438
+ end
439
+
440
+ private
441
+
442
+ def help_commands
443
+ help = self.class.help
444
+ bufs = help.keys.collect{ |a| a.to_s.size }.max + 3
445
+ lines = []
446
+ help.each { |cmd, str|
447
+ cmd = cmd.to_s
448
+ if cmd !~ /^_/
449
+ lines << " " + cmd + (" " * (bufs - cmd.size)) + str
450
+ end
451
+ }
452
+ lines.join("\n")
453
+ end
454
+
455
+ def help_options
456
+ help = self.class.help
457
+ bufs = help.keys.collect{ |a| a.to_s.size }.max + 3
458
+ lines = []
459
+ help.each { |cmd, str|
460
+ cmd = cmd.to_s
461
+ if cmd =~ /^_/
462
+ lines << " " + cmd.gsub(/_/,'-') + (" " * (bufs - cmd.size)) + str
463
+ end
464
+ }
465
+ lines.join("\n")
466
+ end
467
+
468
+ module ClassMethods
469
+
470
+ def help( str=nil )
471
+ return (@help ||= {}) unless str
472
+ @current_help = str
473
+ end
474
+
475
+ def method_added( meth )
476
+ if @current_help
477
+ @help ||= {}
478
+ @help[meth] = @current_help
479
+ @current_help = nil
480
+ end
481
+ end
482
+
483
+ end
484
+
485
+ end
486
+
487
+ include Help
488
+ extend Help::ClassMethods
489
+ =end
490
+
491
+ end
492
+
493
+
494
+
495
+ # _____ _
496
+ # |_ _|__ ___| |_
497
+ # | |/ _ \/ __| __|
498
+ # | | __/\__ \ |_
499
+ # |_|\___||___/\__|
500
+ #
501
+
502
+ =begin test
503
+
504
+ require 'test/unit'
505
+ require 'stringio'
506
+
507
+ class TestCommand < Test::Unit::TestCase
508
+ def setup
509
+ $output = nil
510
+ $stderr = StringIO.new
511
+ end
512
+
513
+ #
514
+
515
+ class SimpleCommand < Console::Command
516
+ def __here ; @here = true ; end
517
+
518
+ def main(*args)
519
+ $output = [@here] | args
520
+ end
521
+ end
522
+
523
+ def test_SimpleCommand
524
+ SimpleCommand.execute( '--here file1 file2' )
525
+ assert_equal( [true, 'file1', 'file2'], $output )
526
+ end
527
+
528
+ #
529
+
530
+ class CommandWithMethodMissingSubcommand < Console::Command
531
+ def __here ; @here = true ; end
532
+
533
+ def method_missing(subcommand, *args)
534
+ $output = [@here, subcommand] | args
535
+ end
536
+ end
537
+
538
+ def test_CommandWithMethodMissingSubcommand
539
+ CommandWithMethodMissingSubcommand.execute( '--here go file1' )
540
+ assert_equal( [true, 'go', 'file1'], $output )
541
+ end
542
+
543
+ #
544
+
545
+ class CommandWithSimpleSubcommand < Console::Command
546
+ def __here ; @here = true ; end
547
+
548
+ # subcommand
549
+
550
+ module Go
551
+ def _p(n)
552
+ @p = n.to_i
553
+ end
554
+ end
555
+
556
+ def go ; $output = [@here, @p] ; end
557
+ end
558
+
559
+ def test_CommandWithSimpleSubcommand
560
+ CommandWithSimpleSubcommand.execute( '--here go -p 1' )
561
+ assert_equal( [true, 1], $output )
562
+ end
563
+
564
+ #
565
+
566
+ # Global options can be anywhere, right? Even after subcommands? Let's find out.
567
+ class CommandWithGlobalOptionsAfterSubcommand < Console::Command
568
+ def _x ; @x = true ; end
569
+ global_option :_x
570
+
571
+ def go ; $output = [@x, @p] ; end
572
+
573
+ module Go
574
+ def _p(n)
575
+ @p = n.to_i
576
+ end
577
+ end
578
+ end
579
+
580
+ def test_CommandWithGlobalOptionsAfterSubcommand
581
+ CommandWithGlobalOptionsAfterSubcommand.execute( 'go -x -p 1' )
582
+ assert_equal( [true, 1], $output )
583
+
584
+ CommandWithGlobalOptionsAfterSubcommand.execute( 'go -p 1 -x' )
585
+ assert_equal( [true, 1], $output )
586
+ end
587
+
588
+ #
589
+
590
+ class GivingUnrecognizedOptions < Console::Command
591
+ def _x ; @x = true ; end
592
+ def go ; $output = [@x, @p] ; end
593
+ end
594
+
595
+ def test_GivingUnrecognizedOptions
596
+ assert_raise(SystemExit) do
597
+ GivingUnrecognizedOptions.execute( '--an-option-that-wont-be-recognized -x go' )
598
+ end
599
+ assert_equal "Unknown option '--an-option-that-wont-be-recognized'.\n", $stderr.string
600
+ assert_equal( nil, $output )
601
+ end
602
+ #
603
+
604
+ class PassingMultipleSingleCharOptionsAsOneOption < Console::Command
605
+ def _x ; @x = true ; end
606
+ def _y ; @y = true ; end
607
+ def _z(n) ; @z = n ; end
608
+
609
+ global_option :_x
610
+
611
+ def go ; $output = [@x, @y, @z, @p] ; end
612
+
613
+ module Go
614
+ def _p(n)
615
+ @p = n.to_i
616
+ end
617
+ end
618
+ end
619
+
620
+ def test_PassingMultipleSingleCharOptionsAsOneOption
621
+ PassingMultipleSingleCharOptionsAsOneOption.execute( '-xy -z HERE go -p 1' )
622
+ assert_equal( [true, true, 'HERE', 1], $output )
623
+ end
624
+
625
+ #
626
+
627
+ class CommandWithOptionUsingEquals < Console::Command
628
+ module Go
629
+ def __mode(mode) ; @mode = mode ; end
630
+ end
631
+ def go ; $output = [@mode] ; end
632
+ end
633
+
634
+ def test_CommandWithOptionUsingEquals
635
+ CommandWithOptionUsingEquals.execute( 'go --mode smart' )
636
+ assert_equal( ['smart'], $output )
637
+
638
+ # I would expect this to work too, but currently it doesn't.
639
+ #assert_nothing_raised { CommandWithOptionUsingEquals.execute( 'go --mode=smart' ) }
640
+ #assert_equal( ['smart'], $output )
641
+ end
642
+
643
+ #
644
+
645
+ class CommandWithSubcommandThatTakesArgs < Console::Command
646
+ def go(arg1, *args) ; $output = [arg1] | args ; end
647
+ end
648
+
649
+ def test_CommandWithSubcommandThatTakesArgs
650
+ CommandWithSubcommandThatTakesArgs.execute( 'go file1 file2 file3' )
651
+ assert_equal( ['file1', 'file2', 'file3'], $output )
652
+ end
653
+
654
+ #
655
+
656
+ class CommandWith2OptionalArgs < Console::Command
657
+ def __here ; @here = true ; end
658
+
659
+ module Go
660
+ def _p(n)
661
+ @p = n.to_i
662
+ end
663
+ end
664
+
665
+ def go(required1 = nil, optional2 = nil) ; $output = [@here, @p, required1, optional2 ] ; end
666
+ end
667
+
668
+ def test_CommandWith2OptionalArgs
669
+ CommandWith2OptionalArgs.execute( '--here go -p 1 to' )
670
+ assert_equal( [true, 1, 'to', nil], $output )
671
+ end
672
+
673
+ #
674
+
675
+ class CommandWithVariableArgs < Console::Command
676
+ def __here ; @here = true ; end
677
+
678
+ module Go
679
+ def _p(n)
680
+ @p = n.to_i
681
+ end
682
+ end
683
+
684
+ def go(*args) ; $output = [@here, @p] | args ; end
685
+ end
686
+
687
+ def test_CommandWithVariableArgs
688
+ CommandWithVariableArgs.execute( '--here go -p 1 to bed' )
689
+ assert_equal( [true, 1, 'to', 'bed'], $output )
690
+ end
691
+
692
+ #
693
+
694
+ class CommandWithOptionMissing < Console::Command
695
+ def __here ; @here = true ; end
696
+
697
+ module Go
698
+ def option_missing(option_name, args)
699
+ p args if $debug
700
+ case option_name
701
+ when 'p'
702
+ @p = args[0].to_i
703
+ 1
704
+ else
705
+ raise InvalidOptionError(option_name, args)
706
+ end
707
+ end
708
+ end
709
+
710
+ def go(*args) ; $output = [@here, @p] | args ; end
711
+ end
712
+
713
+ def test_CommandWithOptionMissing
714
+ CommandWithOptionMissing.execute( '--here go -p 1 to bed right now' )
715
+ assert_equal( [true, 1, 'to', 'bed', 'right', 'now'], $output )
716
+ end
717
+
718
+ #
719
+
720
+ class CommandWithOptionMissingArityOf2 < Console::Command
721
+ def __here ; @here = true ; end
722
+
723
+ module Go
724
+ def option_missing(option_name, args)
725
+ p args if $debug
726
+ case option_name
727
+ when 'p'
728
+ @p1 = args[0].to_i
729
+ @p2 = args[1].to_i
730
+ 2
731
+ when 'q'
732
+ @q = args[0].to_i
733
+ nil # Test default arity
734
+ else
735
+ raise InvalidOptionError(option_name, args)
736
+ end
737
+ end
738
+ end
739
+
740
+ def go(*args) ; $output = [@here, @p1, @p2, @q] | args ; end
741
+ end
742
+
743
+ def test_CommandWithOptionMissingArityOf2
744
+ CommandWithOptionMissingArityOf2.execute( '--here go -p 1 2 -q 3 to bed right now' )
745
+ assert_equal( [true, 1, 2, 3, 'to', 'bed', 'right', 'now'], $output )
746
+ end
747
+ end
748
+
749
+ =end