solutious-rudy 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. data/CHANGES.txt +19 -1
  2. data/README.rdoc +66 -35
  3. data/Rakefile +1 -0
  4. data/Rudyfile +97 -6
  5. data/bin/ird +7 -4
  6. data/bin/rudy +120 -212
  7. data/bin/rudy-ec2 +240 -0
  8. data/bin/rudy-s3 +76 -0
  9. data/bin/rudy-sdb +67 -0
  10. data/lib/annoy.rb +61 -18
  11. data/lib/console.rb +10 -5
  12. data/lib/rudy/aws/ec2/address.rb +76 -40
  13. data/lib/rudy/aws/ec2/group.rb +138 -78
  14. data/lib/rudy/aws/ec2/image.rb +134 -12
  15. data/lib/rudy/aws/ec2/instance.rb +116 -89
  16. data/lib/rudy/aws/ec2/keypair.rb +26 -14
  17. data/lib/rudy/aws/ec2/snapshot.rb +23 -12
  18. data/lib/rudy/aws/ec2/volume.rb +198 -202
  19. data/lib/rudy/aws/ec2/zone.rb +77 -0
  20. data/lib/rudy/aws/ec2.rb +56 -25
  21. data/lib/rudy/aws/s3.rb +54 -0
  22. data/lib/rudy/aws/sdb/error.rb +46 -0
  23. data/lib/rudy/aws/sdb.rb +298 -0
  24. data/lib/rudy/aws.rb +29 -57
  25. data/lib/rudy/{metadata/backup.rb → backup.rb} +8 -8
  26. data/lib/rudy/cli/aws/ec2/addresses.rb +105 -0
  27. data/lib/rudy/cli/aws/ec2/candy.rb +208 -0
  28. data/lib/rudy/cli/aws/ec2/groups.rb +121 -0
  29. data/lib/rudy/cli/aws/ec2/images.rb +196 -0
  30. data/lib/rudy/cli/aws/ec2/instances.rb +194 -0
  31. data/lib/rudy/cli/aws/ec2/keypairs.rb +53 -0
  32. data/lib/rudy/cli/aws/ec2/snapshots.rb +49 -0
  33. data/lib/rudy/cli/aws/ec2/volumes.rb +104 -0
  34. data/lib/rudy/cli/aws/ec2/zones.rb +22 -0
  35. data/lib/rudy/cli/aws/s3/buckets.rb +50 -0
  36. data/lib/rudy/cli/aws/s3/store.rb +22 -0
  37. data/lib/rudy/cli/aws/sdb/domains.rb +41 -0
  38. data/lib/rudy/cli/{deploy.rb → candy.rb} +2 -6
  39. data/lib/rudy/cli/config.rb +25 -20
  40. data/lib/rudy/cli/disks.rb +18 -108
  41. data/lib/rudy/cli/machines.rb +94 -0
  42. data/lib/rudy/cli/routines.rb +47 -70
  43. data/lib/rudy/cli.rb +104 -89
  44. data/lib/rudy/config/objects.rb +5 -43
  45. data/lib/rudy/config.rb +8 -24
  46. data/lib/rudy/disks.rb +248 -0
  47. data/lib/rudy/global.rb +121 -0
  48. data/lib/rudy/huxtable.rb +232 -147
  49. data/lib/rudy/machines.rb +245 -0
  50. data/lib/rudy/metadata.rb +136 -0
  51. data/lib/rudy/routines/helpers/diskhelper.rb +101 -0
  52. data/lib/rudy/routines/helpers/scripthelper.rb +91 -0
  53. data/lib/rudy/routines/release.rb +27 -8
  54. data/lib/rudy/routines/shutdown.rb +47 -32
  55. data/lib/rudy/routines/startup.rb +47 -37
  56. data/lib/rudy/routines.rb +30 -37
  57. data/lib/rudy/scm/svn.rb +1 -1
  58. data/lib/rudy/utils.rb +262 -4
  59. data/lib/rudy.rb +76 -248
  60. data/lib/storable.rb +19 -16
  61. data/lib/sysinfo.rb +1 -1
  62. data/rudy.gemspec +88 -68
  63. data/support/rudy-ec2-startup +5 -5
  64. data/test/05_config/00_setup_test.rb +3 -7
  65. data/test/20_sdb/00_setup_test.rb +2 -17
  66. data/test/20_sdb/10_domains_test.rb +18 -16
  67. data/test/25_ec2/00_setup_test.rb +5 -10
  68. data/test/25_ec2/10_keypairs_test.rb +13 -5
  69. data/test/25_ec2/20_groups_test.rb +48 -56
  70. data/test/25_ec2/30_addresses_test.rb +13 -10
  71. data/test/25_ec2/40_volumes_test.rb +11 -8
  72. data/test/25_ec2/50_snapshots_test.rb +17 -12
  73. data/test/26_ec2_instances/00_setup_test.rb +3 -8
  74. data/test/26_ec2_instances/10_instances_test.rb +21 -19
  75. data/test/30_sdb_metadata/00_setup_test.rb +2 -9
  76. data/test/30_sdb_metadata/10_disks_test.rb +47 -37
  77. data/test/30_sdb_metadata/20_backups_test.rb +9 -9
  78. data/test/helper.rb +5 -3
  79. data/vendor/highline-1.5.1/CHANGELOG +222 -0
  80. data/vendor/highline-1.5.1/INSTALL +35 -0
  81. data/vendor/highline-1.5.1/LICENSE +7 -0
  82. data/vendor/highline-1.5.1/README +63 -0
  83. data/vendor/highline-1.5.1/Rakefile +82 -0
  84. data/vendor/highline-1.5.1/TODO +6 -0
  85. data/vendor/highline-1.5.1/examples/ansi_colors.rb +38 -0
  86. data/vendor/highline-1.5.1/examples/asking_for_arrays.rb +18 -0
  87. data/vendor/highline-1.5.1/examples/basic_usage.rb +75 -0
  88. data/vendor/highline-1.5.1/examples/color_scheme.rb +32 -0
  89. data/vendor/highline-1.5.1/examples/limit.rb +12 -0
  90. data/vendor/highline-1.5.1/examples/menus.rb +65 -0
  91. data/vendor/highline-1.5.1/examples/overwrite.rb +19 -0
  92. data/vendor/highline-1.5.1/examples/page_and_wrap.rb +322 -0
  93. data/vendor/highline-1.5.1/examples/password.rb +7 -0
  94. data/vendor/highline-1.5.1/examples/trapping_eof.rb +22 -0
  95. data/vendor/highline-1.5.1/examples/using_readline.rb +17 -0
  96. data/vendor/highline-1.5.1/lib/highline/color_scheme.rb +120 -0
  97. data/vendor/highline-1.5.1/lib/highline/compatibility.rb +17 -0
  98. data/vendor/highline-1.5.1/lib/highline/import.rb +43 -0
  99. data/vendor/highline-1.5.1/lib/highline/menu.rb +395 -0
  100. data/vendor/highline-1.5.1/lib/highline/question.rb +463 -0
  101. data/vendor/highline-1.5.1/lib/highline/system_extensions.rb +193 -0
  102. data/vendor/highline-1.5.1/lib/highline.rb +758 -0
  103. data/vendor/highline-1.5.1/setup.rb +1360 -0
  104. data/vendor/highline-1.5.1/test/tc_color_scheme.rb +56 -0
  105. data/vendor/highline-1.5.1/test/tc_highline.rb +823 -0
  106. data/vendor/highline-1.5.1/test/tc_import.rb +54 -0
  107. data/vendor/highline-1.5.1/test/tc_menu.rb +429 -0
  108. data/vendor/highline-1.5.1/test/ts_all.rb +15 -0
  109. metadata +81 -69
  110. data/lib/aws_sdb/error.rb +0 -42
  111. data/lib/aws_sdb/service.rb +0 -215
  112. data/lib/aws_sdb.rb +0 -3
  113. data/lib/rudy/aws/simpledb.rb +0 -71
  114. data/lib/rudy/cli/addresses.rb +0 -85
  115. data/lib/rudy/cli/backups.rb +0 -175
  116. data/lib/rudy/cli/domains.rb +0 -17
  117. data/lib/rudy/cli/groups.rb +0 -77
  118. data/lib/rudy/cli/images.rb +0 -111
  119. data/lib/rudy/cli/instances.rb +0 -142
  120. data/lib/rudy/cli/keypairs.rb +0 -47
  121. data/lib/rudy/cli/manager.rb +0 -51
  122. data/lib/rudy/cli/release.rb +0 -174
  123. data/lib/rudy/cli/volumes.rb +0 -121
  124. data/lib/rudy/command/addresses.rb +0 -69
  125. data/lib/rudy/command/backups.rb +0 -65
  126. data/lib/rudy/command/disks-old.rb +0 -322
  127. data/lib/rudy/command/disks.rb +0 -9
  128. data/lib/rudy/command/domains.rb +0 -34
  129. data/lib/rudy/command/groups.rb +0 -118
  130. data/lib/rudy/command/instances.rb +0 -278
  131. data/lib/rudy/command/keypairs.rb +0 -149
  132. data/lib/rudy/command/manager.rb +0 -65
  133. data/lib/rudy/command/volumes.rb +0 -127
  134. data/lib/rudy/metadata/disk.rb +0 -149
  135. data/lib/rudy/metadata/machine.rb +0 -34
  136. data/lib/rudy/routines/disk_handler.rb +0 -190
  137. data/lib/rudy/routines/script_runner.rb +0 -65
  138. data/test/50_commands/00_setup_test.rb +0 -11
  139. data/test/50_commands/10_keypairs_test.rb +0 -79
  140. data/test/50_commands/20_groups_test.rb +0 -77
  141. data/test/50_commands/40_volumes_test.rb +0 -55
  142. data/test/50_commands/50_instances_test.rb +0 -110
  143. data/tryouts/console_tryout.rb +0 -91
  144. data/tryouts/disks.rb +0 -55
  145. data/tryouts/nested_methods.rb +0 -36
  146. data/tryouts/session_tryout.rb +0 -46
@@ -0,0 +1,463 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # question.rb
4
+ #
5
+ # Created by James Edward Gray II on 2005-04-26.
6
+ # Copyright 2005 Gray Productions. All rights reserved.
7
+ #
8
+ # This is Free Software. See LICENSE and COPYING for details.
9
+
10
+ require "optparse"
11
+ require "date"
12
+ require "pathname"
13
+
14
+ class HighLine
15
+ #
16
+ # Question objects contain all the details of a single invocation of
17
+ # HighLine.ask(). The object is initialized by the parameters passed to
18
+ # HighLine.ask() and then queried to make sure each step of the input
19
+ # process is handled according to the users wishes.
20
+ #
21
+ class Question
22
+ # An internal HighLine error. User code does not need to trap this.
23
+ class NoAutoCompleteMatch < StandardError
24
+ # do nothing, just creating a unique error type
25
+ end
26
+
27
+ #
28
+ # Create an instance of HighLine::Question. Expects a _question_ to ask
29
+ # (can be <tt>""</tt>) and an _answer_type_ to convert the answer to.
30
+ # The _answer_type_ parameter must be a type recongnized by
31
+ # Question.convert(). If given, a block is yeilded the new Question
32
+ # object to allow custom initializaion.
33
+ #
34
+ def initialize( question, answer_type )
35
+ # initialize instance data
36
+ @question = question
37
+ @answer_type = answer_type
38
+
39
+ @character = nil
40
+ @limit = nil
41
+ @echo = true
42
+ @readline = false
43
+ @whitespace = :strip
44
+ @case = nil
45
+ @default = nil
46
+ @validate = nil
47
+ @above = nil
48
+ @below = nil
49
+ @in = nil
50
+ @confirm = nil
51
+ @gather = false
52
+ @first_answer = nil
53
+ @directory = Pathname.new(File.expand_path(File.dirname($0)))
54
+ @glob = "*"
55
+ @responses = Hash.new
56
+ @overwrite = false
57
+
58
+ # allow block to override settings
59
+ yield self if block_given?
60
+
61
+ # finalize responses based on settings
62
+ build_responses
63
+ end
64
+
65
+ # The ERb template of the question to be asked.
66
+ attr_accessor :question
67
+ # The type that will be used to convert this answer.
68
+ attr_accessor :answer_type
69
+ #
70
+ # Can be set to +true+ to use HighLine's cross-platform character reader
71
+ # instead of fetching an entire line of input. (Note: HighLine's
72
+ # character reader *ONLY* supports STDIN on Windows and Unix and may not
73
+ # work correctly if STDIN is redirected.) Can also be set to <tt>:getc</tt>
74
+ # to use that method on the input stream.
75
+ #
76
+ # *WARNING*: The _echo_ and _overwrite_ attributes for a question are
77
+ # ignored when using the <tt>:getc</tt> method.
78
+ #
79
+ attr_accessor :character
80
+ #
81
+ # Allows you to set a character limit for input.
82
+ #
83
+ # *WARNING*: This option forces a character by character read.
84
+ #
85
+ attr_accessor :limit
86
+ #
87
+ # Can be set to +true+ or +false+ to control whether or not input will
88
+ # be echoed back to the user. A setting of +true+ will cause echo to
89
+ # match input, but any other true value will be treated as to String to
90
+ # echo for each character typed.
91
+ #
92
+ # This requires HighLine's character reader. See the _character_
93
+ # attribute for details.
94
+ #
95
+ # *Note*: When using HighLine to manage echo on Unix based systems, we
96
+ # recommend installing the termios gem. Without it, it's possible to type
97
+ # fast enough to have letters still show up (when reading character by
98
+ # character only).
99
+ #
100
+ attr_accessor :echo
101
+ #
102
+ # Use the Readline library to fetch input. This allows input editing as
103
+ # well as keeping a history. In addition, tab will auto-complete
104
+ # within an Array of choices or a file listing.
105
+ #
106
+ # *WARNING*: This option is incompatible with all of HighLine's
107
+ # character reading modes and it causes HighLine to ignore the
108
+ # specified _input_ stream.
109
+ #
110
+ attr_accessor :readline
111
+ #
112
+ # Used to control whitespace processing for the answer to this question.
113
+ # See HighLine::Question.remove_whitespace() for acceptable settings.
114
+ #
115
+ attr_accessor :whitespace
116
+ #
117
+ # Used to control character case processing for the answer to this question.
118
+ # See HighLine::Question.change_case() for acceptable settings.
119
+ #
120
+ attr_accessor :case
121
+ # Used to provide a default answer to this question.
122
+ attr_accessor :default
123
+ #
124
+ # If set to a Regexp, the answer must match (before type conversion).
125
+ # Can also be set to a Proc which will be called with the provided
126
+ # answer to validate with a +true+ or +false+ return.
127
+ #
128
+ attr_accessor :validate
129
+ # Used to control range checks for answer.
130
+ attr_accessor :above, :below
131
+ # If set, answer must pass an include?() check on this object.
132
+ attr_accessor :in
133
+ #
134
+ # Asks a yes or no confirmation question, to ensure a user knows what
135
+ # they have just agreed to. If set to +true+ the question will be,
136
+ # "Are you sure? " Any other true value for this attribute is assumed
137
+ # to be the question to ask. When +false+ or +nil+ (the default),
138
+ # answers are not confirmed.
139
+ #
140
+ attr_accessor :confirm
141
+ #
142
+ # When set, the user will be prompted for multiple answers which will
143
+ # be collected into an Array or Hash and returned as the final answer.
144
+ #
145
+ # You can set _gather_ to an Integer to have an Array of exactly that
146
+ # many answers collected, or a String/Regexp to match an end input which
147
+ # will not be returned in the Array.
148
+ #
149
+ # Optionally _gather_ can be set to a Hash. In this case, the question
150
+ # will be asked once for each key and the answers will be returned in a
151
+ # Hash, mapped by key. The <tt>@key</tt> variable is set before each
152
+ # question is evaluated, so you can use it in your question.
153
+ #
154
+ attr_accessor :gather
155
+ #
156
+ # When set to a non *nil* value, this will be tried as an answer to the
157
+ # question. If this answer passes validations, it will become the result
158
+ # without the user ever being prompted. Otherwise this value is discarded,
159
+ # and this Question is resolved as a normal call to HighLine.ask().
160
+ #
161
+ attr_writer :first_answer
162
+ #
163
+ # The directory from which a user will be allowed to select files, when
164
+ # File or Pathname is specified as an _answer_type_. Initially set to
165
+ # <tt>Pathname.new(File.expand_path(File.dirname($0)))</tt>.
166
+ #
167
+ attr_accessor :directory
168
+ #
169
+ # The glob pattern used to limit file selection when File or Pathname is
170
+ # specified as an _answer_type_. Initially set to <tt>"*"</tt>.
171
+ #
172
+ attr_accessor :glob
173
+ #
174
+ # A Hash that stores the various responses used by HighLine to notify
175
+ # the user. The currently used responses and their purpose are as
176
+ # follows:
177
+ #
178
+ # <tt>:ambiguous_completion</tt>:: Used to notify the user of an
179
+ # ambiguous answer the auto-completion
180
+ # system cannot resolve.
181
+ # <tt>:ask_on_error</tt>:: This is the question that will be
182
+ # redisplayed to the user in the event
183
+ # of an error. Can be set to
184
+ # <tt>:question</tt> to repeat the
185
+ # original question.
186
+ # <tt>:invalid_type</tt>:: The error message shown when a type
187
+ # conversion fails.
188
+ # <tt>:no_completion</tt>:: Used to notify the user that their
189
+ # selection does not have a valid
190
+ # auto-completion match.
191
+ # <tt>:not_in_range</tt>:: Used to notify the user that a
192
+ # provided answer did not satisfy
193
+ # the range requirement tests.
194
+ # <tt>:not_valid</tt>:: The error message shown when
195
+ # validation checks fail.
196
+ #
197
+ attr_reader :responses
198
+ #
199
+ # When set to +true+ the question is asked, but output does not progress to
200
+ # the next line. The Cursor is moved back to the beginning of the question
201
+ # line and it is cleared so that all the contents of the line disappear from
202
+ # the screen.
203
+ #
204
+ attr_accessor :overwrite
205
+
206
+ #
207
+ # Returns the provided _answer_string_ or the default answer for this
208
+ # Question if a default was set and the answer is empty.
209
+ #
210
+ def answer_or_default( answer_string )
211
+ if answer_string.length == 0 and not @default.nil?
212
+ @default
213
+ else
214
+ answer_string
215
+ end
216
+ end
217
+
218
+ #
219
+ # Called late in the initialization process to build intelligent
220
+ # responses based on the details of this Question object.
221
+ #
222
+ def build_responses( )
223
+ ### WARNING: This code is quasi-duplicated in ###
224
+ ### Menu.update_responses(). Check there too when ###
225
+ ### making changes! ###
226
+ append_default unless default.nil?
227
+ @responses = { :ambiguous_completion =>
228
+ "Ambiguous choice. " +
229
+ "Please choose one of #{@answer_type.inspect}.",
230
+ :ask_on_error =>
231
+ "? ",
232
+ :invalid_type =>
233
+ "You must enter a valid #{@answer_type}.",
234
+ :no_completion =>
235
+ "You must choose one of " +
236
+ "#{@answer_type.inspect}.",
237
+ :not_in_range =>
238
+ "Your answer isn't within the expected range " +
239
+ "(#{expected_range}).",
240
+ :not_valid =>
241
+ "Your answer isn't valid (must match " +
242
+ "#{@validate.inspect})." }.merge(@responses)
243
+ ### WARNING: This code is quasi-duplicated in ###
244
+ ### Menu.update_responses(). Check there too when ###
245
+ ### making changes! ###
246
+ end
247
+
248
+ #
249
+ # Returns the provided _answer_string_ after changing character case by
250
+ # the rules of this Question. Valid settings for whitespace are:
251
+ #
252
+ # +nil+:: Do not alter character case.
253
+ # (Default.)
254
+ # <tt>:up</tt>:: Calls upcase().
255
+ # <tt>:upcase</tt>:: Calls upcase().
256
+ # <tt>:down</tt>:: Calls downcase().
257
+ # <tt>:downcase</tt>:: Calls downcase().
258
+ # <tt>:capitalize</tt>:: Calls capitalize().
259
+ #
260
+ # An unrecognized choice (like <tt>:none</tt>) is treated as +nil+.
261
+ #
262
+ def change_case( answer_string )
263
+ if [:up, :upcase].include?(@case)
264
+ answer_string.upcase
265
+ elsif [:down, :downcase].include?(@case)
266
+ answer_string.downcase
267
+ elsif @case == :capitalize
268
+ answer_string.capitalize
269
+ else
270
+ answer_string
271
+ end
272
+ end
273
+
274
+ #
275
+ # Transforms the given _answer_string_ into the expected type for this
276
+ # Question. Currently supported conversions are:
277
+ #
278
+ # <tt>[...]</tt>:: Answer must be a member of the passed Array.
279
+ # Auto-completion is used to expand partial
280
+ # answers.
281
+ # <tt>lambda {...}</tt>:: Answer is passed to lambda for conversion.
282
+ # Date:: Date.parse() is called with answer.
283
+ # DateTime:: DateTime.parse() is called with answer.
284
+ # File:: The entered file name is auto-completed in
285
+ # terms of _directory_ + _glob_, opened, and
286
+ # returned.
287
+ # Float:: Answer is converted with Kernel.Float().
288
+ # Integer:: Answer is converted with Kernel.Integer().
289
+ # +nil+:: Answer is left in String format. (Default.)
290
+ # Pathname:: Same as File, save that a Pathname object is
291
+ # returned.
292
+ # String:: Answer is converted with Kernel.String().
293
+ # Regexp:: Answer is fed to Regexp.new().
294
+ # Symbol:: The method to_sym() is called on answer and
295
+ # the result returned.
296
+ # <i>any other Class</i>:: The answer is passed on to
297
+ # <tt>Class.parse()</tt>.
298
+ #
299
+ # This method throws ArgumentError, if the conversion cannot be
300
+ # completed for any reason.
301
+ #
302
+ def convert( answer_string )
303
+ if @answer_type.nil?
304
+ answer_string
305
+ elsif [Float, Integer, String].include?(@answer_type)
306
+ Kernel.send(@answer_type.to_s.to_sym, answer_string)
307
+ elsif @answer_type == Symbol
308
+ answer_string.to_sym
309
+ elsif @answer_type == Regexp
310
+ Regexp.new(answer_string)
311
+ elsif @answer_type.is_a?(Array) or [File, Pathname].include?(@answer_type)
312
+ # cheating, using OptionParser's Completion module
313
+ choices = selection
314
+ choices.extend(OptionParser::Completion)
315
+ answer = choices.complete(answer_string)
316
+ if answer.nil?
317
+ raise NoAutoCompleteMatch
318
+ end
319
+ if @answer_type.is_a?(Array)
320
+ answer.last
321
+ elsif @answer_type == File
322
+ File.open(File.join(@directory.to_s, answer.last))
323
+ else
324
+ Pathname.new(File.join(@directory.to_s, answer.last))
325
+ end
326
+ elsif [Date, DateTime].include?(@answer_type) or @answer_type.is_a?(Class)
327
+ @answer_type.parse(answer_string)
328
+ elsif @answer_type.is_a?(Proc)
329
+ @answer_type[answer_string]
330
+ end
331
+ end
332
+
333
+ # Returns a english explination of the current range settings.
334
+ def expected_range( )
335
+ expected = [ ]
336
+
337
+ expected << "above #{@above}" unless @above.nil?
338
+ expected << "below #{@below}" unless @below.nil?
339
+ expected << "included in #{@in.inspect}" unless @in.nil?
340
+
341
+ case expected.size
342
+ when 0 then ""
343
+ when 1 then expected.first
344
+ when 2 then expected.join(" and ")
345
+ else expected[0..-2].join(", ") + ", and #{expected.last}"
346
+ end
347
+ end
348
+
349
+ # Returns _first_answer_, which will be unset following this call.
350
+ def first_answer( )
351
+ @first_answer
352
+ ensure
353
+ @first_answer = nil
354
+ end
355
+
356
+ # Returns true if _first_answer_ is set.
357
+ def first_answer?( )
358
+ not @first_answer.nil?
359
+ end
360
+
361
+ #
362
+ # Returns +true+ if the _answer_object_ is greater than the _above_
363
+ # attribute, less than the _below_ attribute and included?()ed in the
364
+ # _in_ attribute. Otherwise, +false+ is returned. Any +nil+ attributes
365
+ # are not checked.
366
+ #
367
+ def in_range?( answer_object )
368
+ (@above.nil? or answer_object > @above) and
369
+ (@below.nil? or answer_object < @below) and
370
+ (@in.nil? or @in.include?(answer_object))
371
+ end
372
+
373
+ #
374
+ # Returns the provided _answer_string_ after processing whitespace by
375
+ # the rules of this Question. Valid settings for whitespace are:
376
+ #
377
+ # +nil+:: Do not alter whitespace.
378
+ # <tt>:strip</tt>:: Calls strip(). (Default.)
379
+ # <tt>:chomp</tt>:: Calls chomp().
380
+ # <tt>:collapse</tt>:: Collapses all whitspace runs to a
381
+ # single space.
382
+ # <tt>:strip_and_collapse</tt>:: Calls strip(), then collapses all
383
+ # whitspace runs to a single space.
384
+ # <tt>:chomp_and_collapse</tt>:: Calls chomp(), then collapses all
385
+ # whitspace runs to a single space.
386
+ # <tt>:remove</tt>:: Removes all whitespace.
387
+ #
388
+ # An unrecognized choice (like <tt>:none</tt>) is treated as +nil+.
389
+ #
390
+ # This process is skipped, for single character input.
391
+ #
392
+ def remove_whitespace( answer_string )
393
+ if @whitespace.nil?
394
+ answer_string
395
+ elsif [:strip, :chomp].include?(@whitespace)
396
+ answer_string.send(@whitespace)
397
+ elsif @whitespace == :collapse
398
+ answer_string.gsub(/\s+/, " ")
399
+ elsif [:strip_and_collapse, :chomp_and_collapse].include?(@whitespace)
400
+ result = answer_string.send(@whitespace.to_s[/^[a-z]+/])
401
+ result.gsub(/\s+/, " ")
402
+ elsif @whitespace == :remove
403
+ answer_string.gsub(/\s+/, "")
404
+ else
405
+ answer_string
406
+ end
407
+ end
408
+
409
+ #
410
+ # Returns an Array of valid answers to this question. These answers are
411
+ # only known when _answer_type_ is set to an Array of choices, File, or
412
+ # Pathname. Any other time, this method will return an empty Array.
413
+ #
414
+ def selection( )
415
+ if @answer_type.is_a?(Array)
416
+ @answer_type
417
+ elsif [File, Pathname].include?(@answer_type)
418
+ Dir[File.join(@directory.to_s, @glob)].map do |file|
419
+ File.basename(file)
420
+ end
421
+ else
422
+ [ ]
423
+ end
424
+ end
425
+
426
+ # Stringifies the question to be asked.
427
+ def to_str( )
428
+ @question
429
+ end
430
+
431
+ #
432
+ # Returns +true+ if the provided _answer_string_ is accepted by the
433
+ # _validate_ attribute or +false+ if it's not.
434
+ #
435
+ # It's important to realize that an answer is validated after whitespace
436
+ # and case handling.
437
+ #
438
+ def valid_answer?( answer_string )
439
+ @validate.nil? or
440
+ (@validate.is_a?(Regexp) and answer_string =~ @validate) or
441
+ (@validate.is_a?(Proc) and @validate[answer_string])
442
+ end
443
+
444
+ private
445
+
446
+ #
447
+ # Adds the default choice to the end of question between <tt>|...|</tt>.
448
+ # Trailing whitespace is preserved so the function of HighLine.say() is
449
+ # not affected.
450
+ #
451
+ def append_default( )
452
+ if @question =~ /([\t ]+)\Z/
453
+ @question << "|#{@default}|#{$1}"
454
+ elsif @question == ""
455
+ @question << "|#{@default}| "
456
+ elsif @question[-1, 1] == "\n"
457
+ @question[-2, 0] = " |#{@default}|"
458
+ else
459
+ @question << " |#{@default}|"
460
+ end
461
+ end
462
+ end
463
+ end
@@ -0,0 +1,193 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # system_extensions.rb
4
+ #
5
+ # Created by James Edward Gray II on 2006-06-14.
6
+ # Copyright 2006 Gray Productions. All rights reserved.
7
+ #
8
+ # This is Free Software. See LICENSE and COPYING for details.
9
+
10
+ class HighLine
11
+ module SystemExtensions
12
+ module_function
13
+
14
+ #
15
+ # This section builds character reading and terminal size functions
16
+ # to suit the proper platform we're running on. Be warned: Here be
17
+ # dragons!
18
+ #
19
+ begin
20
+ # Cygwin will look like Windows, but we want to treat it like a Posix OS:
21
+ raise LoadError, "Cygwin is a Posix OS." if RUBY_PLATFORM =~ /\bcygwin\b/i
22
+
23
+ require "Win32API" # See if we're on Windows.
24
+
25
+ CHARACTER_MODE = "Win32API" # For Debugging purposes only.
26
+
27
+ #
28
+ # Windows savvy getc().
29
+ #
30
+ #
31
+ def get_character( input = STDIN )
32
+ @stdin_handle ||= GetStdHandle(STD_INPUT_HANDLE)
33
+
34
+ begin
35
+ SetConsoleEcho(@stdin_handle, false)
36
+ input.getbyte
37
+ ensure
38
+ SetConsoleEcho(@stdin_handle, true)
39
+ end
40
+ end
41
+
42
+ # A Windows savvy method to fetch the console columns, and rows.
43
+ def terminal_size
44
+ stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE)
45
+
46
+ bufx, bufy, curx, cury, wattr, left, top, right, bottom, maxx, maxy =
47
+ GetConsoleScreenBufferInfo(stdout_handle)
48
+ return right - left + 1, bottom - top + 1
49
+ end
50
+
51
+ # windows savvy console echo toggler
52
+ def SetConsoleEcho( console_handle, on )
53
+ mode = GetConsoleMode(console_handle)
54
+
55
+ # toggle the console echo bit
56
+ if on
57
+ mode |= ENABLE_ECHO_INPUT
58
+ else
59
+ mode &= ~ENABLE_ECHO_INPUT
60
+ end
61
+
62
+ ok = SetConsoleMode(console_handle, mode)
63
+ end
64
+
65
+ # win32 console APIs
66
+
67
+ STD_INPUT_HANDLE = -10
68
+ STD_OUTPUT_HANDLE = -11
69
+ STD_ERROR_HANDLE = -12
70
+
71
+ ENABLE_PROCESSED_INPUT = 0x0001
72
+ ENABLE_LINE_INPUT = 0x0002
73
+ ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
74
+ ENABLE_ECHO_INPUT = 0x0004
75
+ ENABLE_WINDOW_INPUT = 0x0008
76
+ ENABLE_MOUSE_INPUT = 0x0010
77
+ ENABLE_INSERT_MODE = 0x0020
78
+ ENABLE_QUICK_EDIT_MODE = 0x0040
79
+
80
+ @@apiGetStdHandle = nil
81
+ @@apiGetConsoleMode = nil
82
+ @@apiSetConsoleMode = nil
83
+ @@apiGetConsoleScreenBufferInfo = nil
84
+
85
+ def GetStdHandle( handle_type )
86
+ @@apiGetStdHandle ||= Win32API.new( "kernel32", "GetStdHandle",
87
+ ['L'], 'L' )
88
+
89
+ @@apiGetStdHandle.call( handle_type )
90
+ end
91
+
92
+ def GetConsoleMode( console_handle )
93
+ @@apiGetConsoleMode ||= Win32API.new( "kernel32", "GetConsoleMode",
94
+ ['L', 'P'], 'I' )
95
+
96
+ mode = ' ' * 4
97
+ @@apiGetConsoleMode.call(console_handle, mode)
98
+ mode.unpack('L')[0]
99
+ end
100
+
101
+ def SetConsoleMode( console_handle, mode )
102
+ @@apiSetConsoleMode ||= Win32API.new( "kernel32", "SetConsoleMode",
103
+ ['L', 'L'], 'I' )
104
+
105
+ @@apiSetConsoleMode.call(console_handle, mode) != 0
106
+ end
107
+
108
+ def GetConsoleScreenBufferInfo( console_handle )
109
+ @@apiGetConsoleScreenBufferInfo ||=
110
+ Win32API.new( "kernel32", "GetConsoleScreenBufferInfo",
111
+ ['L', 'P'], 'L' )
112
+
113
+ format = 'SSSSSssssSS'
114
+ buf = ([0] * format.size).pack(format)
115
+ @@apiGetConsoleScreenBufferInfo.call(console_handle, buf)
116
+ buf.unpack(format)
117
+ end
118
+
119
+ rescue LoadError # If we're not on Windows try...
120
+ begin
121
+ require "termios" # Unix, first choice.
122
+
123
+ CHARACTER_MODE = "termios" # For Debugging purposes only.
124
+
125
+ #
126
+ # Unix savvy getc(). (First choice.)
127
+ #
128
+ # *WARNING*: This method requires the "termios" library!
129
+ #
130
+ def get_character( input = STDIN )
131
+ old_settings = Termios.getattr(input)
132
+
133
+ new_settings = old_settings.dup
134
+ new_settings.c_lflag &= ~(Termios::ECHO | Termios::ICANON)
135
+ new_settings.c_cc[Termios::VMIN] = 1
136
+
137
+ begin
138
+ Termios.setattr(input, Termios::TCSANOW, new_settings)
139
+ input.getbyte
140
+ ensure
141
+ Termios.setattr(input, Termios::TCSANOW, old_settings)
142
+ end
143
+ end
144
+ rescue LoadError # If our first choice fails, default.
145
+ CHARACTER_MODE = "stty" # For Debugging purposes only.
146
+
147
+ #
148
+ # Unix savvy getc(). (Second choice.)
149
+ #
150
+ # *WARNING*: This method requires the external "stty" program!
151
+ #
152
+ def get_character( input = STDIN )
153
+ raw_no_echo_mode
154
+
155
+ begin
156
+ input.getbyte
157
+ ensure
158
+ restore_mode
159
+ end
160
+ end
161
+
162
+ #
163
+ # Switched the input mode to raw and disables echo.
164
+ #
165
+ # *WARNING*: This method requires the external "stty" program!
166
+ #
167
+ def raw_no_echo_mode
168
+ @state = `stty -g`
169
+ system "stty raw -echo cbreak isig"
170
+ end
171
+
172
+ #
173
+ # Restores a previously saved input mode.
174
+ #
175
+ # *WARNING*: This method requires the external "stty" program!
176
+ #
177
+ def restore_mode
178
+ system "stty #{@state}"
179
+ end
180
+ end
181
+
182
+ # A Unix savvy method to fetch the console columns, and rows.
183
+ def terminal_size
184
+ if /solaris/ =~ RUBY_PLATFORM and
185
+ `stty` =~ /\brows = (\d+).*\bcolumns = (\d+)/
186
+ [$2, $1].map { |c| x.to_i }
187
+ else
188
+ `stty size`.split.map { |x| x.to_i }.reverse
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end