challah 0.3.4 → 0.3.5

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 (48) hide show
  1. data/CHANGELOG.md +4 -0
  2. data/lib/challah/version.rb +1 -1
  3. data/lib/tasks/crud.rake +23 -53
  4. data/vendor/bundle/cache/highline-1.6.11.gem +0 -0
  5. data/vendor/bundle/gems/highline-1.6.11/AUTHORS +3 -0
  6. data/vendor/bundle/gems/highline-1.6.11/CHANGELOG +304 -0
  7. data/vendor/bundle/gems/highline-1.6.11/COPYING +340 -0
  8. data/vendor/bundle/gems/highline-1.6.11/INSTALL +55 -0
  9. data/vendor/bundle/gems/highline-1.6.11/LICENSE +7 -0
  10. data/vendor/bundle/gems/highline-1.6.11/README +63 -0
  11. data/vendor/bundle/gems/highline-1.6.11/Rakefile +53 -0
  12. data/vendor/bundle/gems/highline-1.6.11/TODO +6 -0
  13. data/vendor/bundle/gems/highline-1.6.11/examples/ansi_colors.rb +38 -0
  14. data/vendor/bundle/gems/highline-1.6.11/examples/asking_for_arrays.rb +18 -0
  15. data/vendor/bundle/gems/highline-1.6.11/examples/basic_usage.rb +75 -0
  16. data/vendor/bundle/gems/highline-1.6.11/examples/color_scheme.rb +32 -0
  17. data/vendor/bundle/gems/highline-1.6.11/examples/limit.rb +12 -0
  18. data/vendor/bundle/gems/highline-1.6.11/examples/menus.rb +65 -0
  19. data/vendor/bundle/gems/highline-1.6.11/examples/overwrite.rb +19 -0
  20. data/vendor/bundle/gems/highline-1.6.11/examples/page_and_wrap.rb +322 -0
  21. data/vendor/bundle/gems/highline-1.6.11/examples/password.rb +7 -0
  22. data/vendor/bundle/gems/highline-1.6.11/examples/trapping_eof.rb +22 -0
  23. data/vendor/bundle/gems/highline-1.6.11/examples/using_readline.rb +17 -0
  24. data/vendor/bundle/gems/highline-1.6.11/highline.gemspec +36 -0
  25. data/vendor/bundle/gems/highline-1.6.11/lib/highline/color_scheme.rb +136 -0
  26. data/vendor/bundle/gems/highline-1.6.11/lib/highline/compatibility.rb +16 -0
  27. data/vendor/bundle/gems/highline-1.6.11/lib/highline/import.rb +43 -0
  28. data/vendor/bundle/gems/highline-1.6.11/lib/highline/menu.rb +398 -0
  29. data/vendor/bundle/gems/highline-1.6.11/lib/highline/question.rb +465 -0
  30. data/vendor/bundle/gems/highline-1.6.11/lib/highline/string_extensions.rb +98 -0
  31. data/vendor/bundle/gems/highline-1.6.11/lib/highline/style.rb +184 -0
  32. data/vendor/bundle/gems/highline-1.6.11/lib/highline/system_extensions.rb +180 -0
  33. data/vendor/bundle/gems/highline-1.6.11/lib/highline.rb +978 -0
  34. data/vendor/bundle/gems/highline-1.6.11/setup.rb +1360 -0
  35. data/vendor/bundle/gems/highline-1.6.11/site/highline.css +65 -0
  36. data/vendor/bundle/gems/highline-1.6.11/site/images/logo.png +0 -0
  37. data/vendor/bundle/gems/highline-1.6.11/site/index.html +58 -0
  38. data/vendor/bundle/gems/highline-1.6.11/test/string_methods.rb +34 -0
  39. data/vendor/bundle/gems/highline-1.6.11/test/tc_color_scheme.rb +98 -0
  40. data/vendor/bundle/gems/highline-1.6.11/test/tc_highline.rb +962 -0
  41. data/vendor/bundle/gems/highline-1.6.11/test/tc_import.rb +54 -0
  42. data/vendor/bundle/gems/highline-1.6.11/test/tc_menu.rb +429 -0
  43. data/vendor/bundle/gems/highline-1.6.11/test/tc_string_extension.rb +22 -0
  44. data/vendor/bundle/gems/highline-1.6.11/test/tc_string_highline.rb +40 -0
  45. data/vendor/bundle/gems/highline-1.6.11/test/tc_style.rb +569 -0
  46. data/vendor/bundle/gems/highline-1.6.11/test/ts_all.rb +18 -0
  47. data/vendor/bundle/specifications/highline-1.6.11.gemspec +29 -0
  48. metadata +63 -8
@@ -0,0 +1,978 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # highline.rb
4
+ #
5
+ # Created by James Edward Gray II on 2005-04-26.
6
+ # Copyright 2005 Gray Productions. All rights reserved.
7
+ #
8
+ # See HighLine for documentation.
9
+ #
10
+ # This is Free Software. See LICENSE and COPYING for details.
11
+
12
+ require "erb"
13
+ require "optparse"
14
+ require "stringio"
15
+ require "abbrev"
16
+ require "highline/system_extensions"
17
+ require "highline/question"
18
+ require "highline/menu"
19
+ require "highline/color_scheme"
20
+ require "highline/style"
21
+
22
+ #
23
+ # A HighLine object is a "high-level line oriented" shell over an input and an
24
+ # output stream. HighLine simplifies common console interaction, effectively
25
+ # replacing puts() and gets(). User code can simply specify the question to ask
26
+ # and any details about user interaction, then leave the rest of the work to
27
+ # HighLine. When HighLine.ask() returns, you'll have the answer you requested,
28
+ # even if HighLine had to ask many times, validate results, perform range
29
+ # checking, convert types, etc.
30
+ #
31
+ class HighLine
32
+ # The version of the installed library.
33
+ VERSION = "1.6.11".freeze
34
+
35
+ # An internal HighLine error. User code does not need to trap this.
36
+ class QuestionError < StandardError
37
+ # do nothing, just creating a unique error type
38
+ end
39
+
40
+ # The setting used to disable color output.
41
+ @@use_color = true
42
+
43
+ # Pass +false+ to _setting_ to turn off HighLine's color escapes.
44
+ def self.use_color=( setting )
45
+ @@use_color = setting
46
+ end
47
+
48
+ # Returns true if HighLine is currently using color escapes.
49
+ def self.use_color?
50
+ @@use_color
51
+ end
52
+
53
+ # For checking if the current version of HighLine supports RGB colors
54
+ # Usage: HighLine.supports_rgb_color? rescue false # rescue for compatibility with older versions
55
+ # Note: color usage also depends on HighLine.use_color being set
56
+ def self.supports_rgb_color?
57
+ true
58
+ end
59
+
60
+ # The setting used to disable EOF tracking.
61
+ @@track_eof = true
62
+
63
+ # Pass +false+ to _setting_ to turn off HighLine's EOF tracking.
64
+ def self.track_eof=( setting )
65
+ @@track_eof = setting
66
+ end
67
+
68
+ # Returns true if HighLine is currently tracking EOF for input.
69
+ def self.track_eof?
70
+ @@track_eof
71
+ end
72
+
73
+ # The setting used to control color schemes.
74
+ @@color_scheme = nil
75
+
76
+ # Pass ColorScheme to _setting_ to turn set a HighLine color scheme.
77
+ def self.color_scheme=( setting )
78
+ @@color_scheme = setting
79
+ end
80
+
81
+ # Returns the current color scheme.
82
+ def self.color_scheme
83
+ @@color_scheme
84
+ end
85
+
86
+ # Returns +true+ if HighLine is currently using a color scheme.
87
+ def self.using_color_scheme?
88
+ not @@color_scheme.nil?
89
+ end
90
+
91
+ #
92
+ # Embed in a String to clear all previous ANSI sequences. This *MUST* be
93
+ # done before the program exits!
94
+ #
95
+
96
+ ERASE_LINE_STYLE = Style.new(:name=>:erase_line, :builtin=>true, :code=>"\e[K") # Erase the current line of terminal output
97
+ ERASE_CHAR_STYLE = Style.new(:name=>:erase_char, :builtin=>true, :code=>"\e[P") # Erase the character under the cursor.
98
+ CLEAR_STYLE = Style.new(:name=>:clear, :builtin=>true, :code=>"\e[0m") # Clear color settings
99
+ RESET_STYLE = Style.new(:name=>:reset, :builtin=>true, :code=>"\e[0m") # Alias for CLEAR.
100
+ BOLD_STYLE = Style.new(:name=>:bold, :builtin=>true, :code=>"\e[1m") # Bold; Note: bold + a color works as you'd expect,
101
+ # for example bold black. Bold without a color displays
102
+ # the system-defined bold color (e.g. red on Mac iTerm)
103
+ DARK_STYLE = Style.new(:name=>:dark, :builtin=>true, :code=>"\e[2m") # Dark; support uncommon
104
+ UNDERLINE_STYLE = Style.new(:name=>:underline, :builtin=>true, :code=>"\e[4m") # Underline
105
+ UNDERSCORE_STYLE = Style.new(:name=>:underscore, :builtin=>true, :code=>"\e[4m") # Alias for UNDERLINE
106
+ BLINK_STYLE = Style.new(:name=>:blink, :builtin=>true, :code=>"\e[5m") # Blink; support uncommon
107
+ REVERSE_STYLE = Style.new(:name=>:reverse, :builtin=>true, :code=>"\e[7m") # Reverse foreground and background
108
+ CONCEALED_STYLE = Style.new(:name=>:concealed, :builtin=>true, :code=>"\e[8m") # Concealed; support uncommon
109
+
110
+ STYLES = %w{CLEAR RESET BOLD DARK UNDERLINE UNDERSCORE BLINK REVERSE CONCEALED}
111
+
112
+ # These RGB colors are approximate; see http://en.wikipedia.org/wiki/ANSI_escape_code
113
+ BLACK_STYLE = Style.new(:name=>:black, :builtin=>true, :code=>"\e[30m", :rgb=>[ 0, 0, 0])
114
+ RED_STYLE = Style.new(:name=>:red, :builtin=>true, :code=>"\e[31m", :rgb=>[128, 0, 0])
115
+ GREEN_STYLE = Style.new(:name=>:green, :builtin=>true, :code=>"\e[32m", :rgb=>[ 0,128, 0])
116
+ BLUE_STYLE = Style.new(:name=>:blue, :builtin=>true, :code=>"\e[34m", :rgb=>[ 0, 0,128])
117
+ YELLOW_STYLE = Style.new(:name=>:yellow, :builtin=>true, :code=>"\e[33m", :rgb=>[128,128, 0])
118
+ MAGENTA_STYLE = Style.new(:name=>:magenta, :builtin=>true, :code=>"\e[35m", :rgb=>[128, 0,128])
119
+ CYAN_STYLE = Style.new(:name=>:cyan, :builtin=>true, :code=>"\e[36m", :rgb=>[ 0,128,128])
120
+ # On Mac OSX Terminal, white is actually gray
121
+ WHITE_STYLE = Style.new(:name=>:white, :builtin=>true, :code=>"\e[37m", :rgb=>[192,192,192])
122
+ # Alias for WHITE, since WHITE is actually a light gray on Macs
123
+ GRAY_STYLE = Style.new(:name=>:gray, :builtin=>true, :code=>"\e[37m", :rgb=>[192,192,192])
124
+ # On Mac OSX Terminal, this is black foreground, or bright white background.
125
+ # Also used as base for RGB colors, if available
126
+ NONE_STYLE = Style.new(:name=>:none, :builtin=>true, :code=>"\e[38m", :rgb=>[ 0, 0, 0])
127
+
128
+ BASIC_COLORS = %w{BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE GRAY NONE}
129
+
130
+ colors = BASIC_COLORS.dup
131
+ BASIC_COLORS.each do |color|
132
+ bright_color = "BRIGHT_#{color}"
133
+ colors << bright_color
134
+ const_set bright_color+'_STYLE', const_get(color + '_STYLE').bright
135
+ end
136
+ COLORS = colors
137
+
138
+ colors.each do |color|
139
+ const_set color, const_get("#{color}_STYLE").code
140
+ const_set "ON_#{color}_STYLE", const_get("#{color}_STYLE").on
141
+ const_set "ON_#{color}", const_get("ON_#{color}_STYLE").code
142
+ end
143
+ ON_NONE_STYLE.rgb = [255,255,255] # Override; white background
144
+
145
+ STYLES.each do |style|
146
+ const_set style, const_get("#{style}_STYLE").code
147
+ end
148
+
149
+ # For RGB colors:
150
+ def self.const_missing(name)
151
+ if name.to_s =~ /^(ON_)?(RGB_)([A-F0-9]{6})(_STYLE)?$/ # RGB color
152
+ on = $1
153
+ suffix = $4
154
+ if suffix
155
+ code_name = $1.to_s + $2 + $3
156
+ else
157
+ code_name = name.to_s
158
+ end
159
+ style_name = code_name + '_STYLE'
160
+ style = Style.rgb($3)
161
+ style = style.on if on
162
+ const_set(style_name, style)
163
+ const_set(code_name, style.code)
164
+ if suffix
165
+ style
166
+ else
167
+ style.code
168
+ end
169
+ else
170
+ raise NameError, "Bad color or uninitialized constant #{name}"
171
+ end
172
+ end
173
+
174
+ #
175
+ # Create an instance of HighLine, connected to the streams _input_
176
+ # and _output_.
177
+ #
178
+ def initialize( input = $stdin, output = $stdout,
179
+ wrap_at = nil, page_at = nil )
180
+ @input = input
181
+ @output = output
182
+ if JRUBY
183
+ require 'java'
184
+ java_import 'java.io.OutputStreamWriter'
185
+ java_import 'java.nio.channels.Channels'
186
+ java_import 'jline.ConsoleReader'
187
+ java_import 'jline.Terminal'
188
+
189
+ @java_input = Channels.newInputStream($stdin.to_channel)
190
+ @java_output = OutputStreamWriter.new(Channels.newOutputStream($stdout.to_channel))
191
+ @java_terminal = Terminal.getTerminal
192
+ @java_console = ConsoleReader.new(@java_input, @java_output)
193
+ @java_console.setUseHistory(false)
194
+ @java_console.setBellEnabled(true)
195
+ @java_console.setUsePagination(false)
196
+ end
197
+
198
+ self.wrap_at = wrap_at
199
+ self.page_at = page_at
200
+
201
+ @question = nil
202
+ @answer = nil
203
+ @menu = nil
204
+ @header = nil
205
+ @prompt = nil
206
+ @gather = nil
207
+ @answers = nil
208
+ @key = nil
209
+ end
210
+
211
+ include HighLine::SystemExtensions
212
+
213
+ # The current column setting for wrapping output.
214
+ attr_reader :wrap_at
215
+ # The current row setting for paging output.
216
+ attr_reader :page_at
217
+
218
+ #
219
+ # A shortcut to HighLine.ask() a question that only accepts "yes" or "no"
220
+ # answers ("y" and "n" are allowed) and returns +true+ or +false+
221
+ # (+true+ for "yes"). If provided a +true+ value, _character_ will cause
222
+ # HighLine to fetch a single character response. A block can be provided
223
+ # to further configure the question as in HighLine.ask()
224
+ #
225
+ # Raises EOFError if input is exhausted.
226
+ #
227
+ def agree( yes_or_no_question, character = nil )
228
+ ask(yes_or_no_question, lambda { |yn| yn.downcase[0] == ?y}) do |q|
229
+ q.validate = /\Ay(?:es)?|no?\Z/i
230
+ q.responses[:not_valid] = 'Please enter "yes" or "no".'
231
+ q.responses[:ask_on_error] = :question
232
+ q.character = character
233
+
234
+ yield q if block_given?
235
+ end
236
+ end
237
+
238
+ #
239
+ # This method is the primary interface for user input. Just provide a
240
+ # _question_ to ask the user, the _answer_type_ you want returned, and
241
+ # optionally a code block setting up details of how you want the question
242
+ # handled. See HighLine.say() for details on the format of _question_, and
243
+ # HighLine::Question for more information about _answer_type_ and what's
244
+ # valid in the code block.
245
+ #
246
+ # If <tt>@question</tt> is set before ask() is called, parameters are
247
+ # ignored and that object (must be a HighLine::Question) is used to drive
248
+ # the process instead.
249
+ #
250
+ # Raises EOFError if input is exhausted.
251
+ #
252
+ def ask( question, answer_type = String, &details ) # :yields: question
253
+ @question ||= Question.new(question, answer_type, &details)
254
+
255
+ return gather if @question.gather
256
+
257
+ # readline() needs to handle it's own output, but readline only supports
258
+ # full line reading. Therefore if @question.echo is anything but true,
259
+ # the prompt will not be issued. And we have to account for that now.
260
+ say(@question) unless (@question.readline and @question.echo == true)
261
+ begin
262
+ @answer = @question.answer_or_default(get_response)
263
+ unless @question.valid_answer?(@answer)
264
+ explain_error(:not_valid)
265
+ raise QuestionError
266
+ end
267
+
268
+ @answer = @question.convert(@answer)
269
+
270
+ if @question.in_range?(@answer)
271
+ if @question.confirm
272
+ # need to add a layer of scope to ask a question inside a
273
+ # question, without destroying instance data
274
+ context_change = self.class.new(@input, @output, @wrap_at, @page_at)
275
+ if @question.confirm == true
276
+ confirm_question = "Are you sure? "
277
+ else
278
+ # evaluate ERb under initial scope, so it will have
279
+ # access to @question and @answer
280
+ template = ERB.new(@question.confirm, nil, "%")
281
+ confirm_question = template.result(binding)
282
+ end
283
+ unless context_change.agree(confirm_question)
284
+ explain_error(nil)
285
+ raise QuestionError
286
+ end
287
+ end
288
+
289
+ @answer
290
+ else
291
+ explain_error(:not_in_range)
292
+ raise QuestionError
293
+ end
294
+ rescue QuestionError
295
+ retry
296
+ rescue ArgumentError, NameError => error
297
+ raise if error.is_a?(NoMethodError)
298
+ if error.message =~ /ambiguous/
299
+ # the assumption here is that OptionParser::Completion#complete
300
+ # (used for ambiguity resolution) throws exceptions containing
301
+ # the word 'ambiguous' whenever resolution fails
302
+ explain_error(:ambiguous_completion)
303
+ else
304
+ explain_error(:invalid_type)
305
+ end
306
+ retry
307
+ rescue Question::NoAutoCompleteMatch
308
+ explain_error(:no_completion)
309
+ retry
310
+ ensure
311
+ @question = nil # Reset Question object.
312
+ end
313
+ end
314
+
315
+ #
316
+ # This method is HighLine's menu handler. For simple usage, you can just
317
+ # pass all the menu items you wish to display. At that point, choose() will
318
+ # build and display a menu, walk the user through selection, and return
319
+ # their choice amoung the provided items. You might use this in a case
320
+ # statement for quick and dirty menus.
321
+ #
322
+ # However, choose() is capable of much more. If provided, a block will be
323
+ # passed a HighLine::Menu object to configure. Using this method, you can
324
+ # customize all the details of menu handling from index display, to building
325
+ # a complete shell-like menuing system. See HighLine::Menu for all the
326
+ # methods it responds to.
327
+ #
328
+ # Raises EOFError if input is exhausted.
329
+ #
330
+ def choose( *items, &details )
331
+ @menu = @question = Menu.new(&details)
332
+ @menu.choices(*items) unless items.empty?
333
+
334
+ # Set _answer_type_ so we can double as the Question for ask().
335
+ @menu.answer_type = if @menu.shell
336
+ lambda do |command| # shell-style selection
337
+ first_word = command.to_s.split.first || ""
338
+
339
+ options = @menu.options
340
+ options.extend(OptionParser::Completion)
341
+ answer = options.complete(first_word)
342
+
343
+ if answer.nil?
344
+ raise Question::NoAutoCompleteMatch
345
+ end
346
+
347
+ [answer.last, command.sub(/^\s*#{first_word}\s*/, "")]
348
+ end
349
+ else
350
+ @menu.options # normal menu selection, by index or name
351
+ end
352
+
353
+ # Provide hooks for ERb layouts.
354
+ @header = @menu.header
355
+ @prompt = @menu.prompt
356
+
357
+ if @menu.shell
358
+ selected = ask("Ignored", @menu.answer_type)
359
+ @menu.select(self, *selected)
360
+ else
361
+ selected = ask("Ignored", @menu.answer_type)
362
+ @menu.select(self, selected)
363
+ end
364
+ end
365
+
366
+ #
367
+ # This method provides easy access to ANSI color sequences, without the user
368
+ # needing to remember to CLEAR at the end of each sequence. Just pass the
369
+ # _string_ to color, followed by a list of _colors_ you would like it to be
370
+ # affected by. The _colors_ can be HighLine class constants, or symbols
371
+ # (:blue for BLUE, for example). A CLEAR will automatically be embedded to
372
+ # the end of the returned String.
373
+ #
374
+ # This method returns the original _string_ unchanged if HighLine::use_color?
375
+ # is +false+.
376
+ #
377
+ def self.color( string, *colors )
378
+ return string unless self.use_color?
379
+ Style(*colors).color(string)
380
+ end
381
+
382
+ # In case you just want the color code, without the embedding and the CLEAR
383
+ def self.color_code(*colors)
384
+ Style(*colors).code
385
+ end
386
+
387
+ # Works as an instance method, same as the class method
388
+ def color_code(*colors)
389
+ self.class.color_code(*colors)
390
+ end
391
+
392
+ # Works as an instance method, same as the class method
393
+ def color(*args)
394
+ self.class.color(*args)
395
+ end
396
+
397
+ # Remove color codes from a string
398
+ def self.uncolor(string)
399
+ Style.uncolor(string)
400
+ end
401
+
402
+ # Works as an instance method, same as the class method
403
+ def uncolor(string)
404
+ self.class.uncolor(string)
405
+ end
406
+
407
+ #
408
+ # This method is a utility for quickly and easily laying out lists. It can
409
+ # be accessed within ERb replacements of any text that will be sent to the
410
+ # user.
411
+ #
412
+ # The only required parameter is _items_, which should be the Array of items
413
+ # to list. A specified _mode_ controls how that list is formed and _option_
414
+ # has different effects, depending on the _mode_. Recognized modes are:
415
+ #
416
+ # <tt>:columns_across</tt>:: _items_ will be placed in columns,
417
+ # flowing from left to right. If given,
418
+ # _option_ is the number of columns to be
419
+ # used. When absent, columns will be
420
+ # determined based on _wrap_at_ or a
421
+ # default of 80 characters.
422
+ # <tt>:columns_down</tt>:: Identical to <tt>:columns_across</tt>,
423
+ # save flow goes down.
424
+ # <tt>:uneven_columns_across</tt>:: Like <tt>:columns_across</tt> but each
425
+ # column is sized independently.
426
+ # <tt>:uneven_columns_down</tt>:: Like <tt>:columns_down</tt> but each
427
+ # column is sized independently.
428
+ # <tt>:inline</tt>:: All _items_ are placed on a single line.
429
+ # The last two _items_ are separated by
430
+ # _option_ or a default of " or ". All
431
+ # other _items_ are separated by ", ".
432
+ # <tt>:rows</tt>:: The default mode. Each of the _items_ is
433
+ # placed on it's own line. The _option_
434
+ # parameter is ignored in this mode.
435
+ #
436
+ # Each member of the _items_ Array is passed through ERb and thus can contain
437
+ # their own expansions. Color escape expansions do not contribute to the
438
+ # final field width.
439
+ #
440
+ def list( items, mode = :rows, option = nil )
441
+ items = items.to_ary.map do |item|
442
+ ERB.new(item, nil, "%").result(binding)
443
+ end
444
+
445
+ if items.empty?
446
+ ""
447
+ else
448
+ case mode
449
+ when :inline
450
+ option = " or " if option.nil?
451
+
452
+ if items.size == 1
453
+ items.first
454
+ else
455
+ items[0..-2].join(", ") + "#{option}#{items.last}"
456
+ end
457
+ when :columns_across, :columns_down
458
+ max_length = actual_length(
459
+ items.max { |a, b| actual_length(a) <=> actual_length(b) }
460
+ )
461
+
462
+ if option.nil?
463
+ limit = @wrap_at || 80
464
+ option = (limit + 2) / (max_length + 2)
465
+ end
466
+
467
+ items = items.map do |item|
468
+ pad = max_length + (item.to_s.length - actual_length(item))
469
+ "%-#{pad}s" % item
470
+ end
471
+ row_count = (items.size / option.to_f).ceil
472
+
473
+ if mode == :columns_across
474
+ rows = Array.new(row_count) { Array.new }
475
+ items.each_with_index do |item, index|
476
+ rows[index / option] << item
477
+ end
478
+
479
+ rows.map { |row| row.join(" ") + "\n" }.join
480
+ else
481
+ columns = Array.new(option) { Array.new }
482
+ items.each_with_index do |item, index|
483
+ columns[index / row_count] << item
484
+ end
485
+
486
+ list = ""
487
+ columns.first.size.times do |index|
488
+ list << columns.map { |column| column[index] }.
489
+ compact.join(" ") + "\n"
490
+ end
491
+ list
492
+ end
493
+ when :uneven_columns_across
494
+ if option.nil?
495
+ limit = @wrap_at || 80
496
+ items.size.downto(1) do |column_count|
497
+ row_count = (items.size / column_count.to_f).ceil
498
+ rows = Array.new(row_count) { Array.new }
499
+ items.each_with_index do |item, index|
500
+ rows[index / column_count] << item
501
+ end
502
+
503
+ widths = Array.new(column_count, 0)
504
+ rows.each do |row|
505
+ row.each_with_index do |field, column|
506
+ size = actual_length(field)
507
+ widths[column] = size if size > widths[column]
508
+ end
509
+ end
510
+
511
+ if column_count == 1 or
512
+ widths.inject(0) { |sum, n| sum + n + 2 } <= limit + 2
513
+ return rows.map { |row|
514
+ row.zip(widths).map { |field, i|
515
+ "%-#{i + (field.to_s.length - actual_length(field))}s" % field
516
+ }.join(" ") + "\n"
517
+ }.join
518
+ end
519
+ end
520
+ else
521
+ row_count = (items.size / option.to_f).ceil
522
+ rows = Array.new(row_count) { Array.new }
523
+ items.each_with_index do |item, index|
524
+ rows[index / option] << item
525
+ end
526
+
527
+ widths = Array.new(option, 0)
528
+ rows.each do |row|
529
+ row.each_with_index do |field, column|
530
+ size = actual_length(field)
531
+ widths[column] = size if size > widths[column]
532
+ end
533
+ end
534
+
535
+ return rows.map { |row|
536
+ row.zip(widths).map { |field, i|
537
+ "%-#{i + (field.to_s.length - actual_length(field))}s" % field
538
+ }.join(" ") + "\n"
539
+ }.join
540
+ end
541
+ when :uneven_columns_down
542
+ if option.nil?
543
+ limit = @wrap_at || 80
544
+ items.size.downto(1) do |column_count|
545
+ row_count = (items.size / column_count.to_f).ceil
546
+ columns = Array.new(column_count) { Array.new }
547
+ items.each_with_index do |item, index|
548
+ columns[index / row_count] << item
549
+ end
550
+
551
+ widths = Array.new(column_count, 0)
552
+ columns.each_with_index do |column, i|
553
+ column.each do |field|
554
+ size = actual_length(field)
555
+ widths[i] = size if size > widths[i]
556
+ end
557
+ end
558
+
559
+ if column_count == 1 or
560
+ widths.inject(0) { |sum, n| sum + n + 2 } <= limit + 2
561
+ list = ""
562
+ columns.first.size.times do |index|
563
+ list << columns.zip(widths).map { |column, width|
564
+ field = column[index]
565
+ "%-#{width + (field.to_s.length - actual_length(field))}s" %
566
+ field
567
+ }.compact.join(" ").strip + "\n"
568
+ end
569
+ return list
570
+ end
571
+ end
572
+ else
573
+ row_count = (items.size / option.to_f).ceil
574
+ columns = Array.new(option) { Array.new }
575
+ items.each_with_index do |item, index|
576
+ columns[index / row_count] << item
577
+ end
578
+
579
+ widths = Array.new(option, 0)
580
+ columns.each_with_index do |column, i|
581
+ column.each do |field|
582
+ size = actual_length(field)
583
+ widths[i] = size if size > widths[i]
584
+ end
585
+ end
586
+
587
+ list = ""
588
+ columns.first.size.times do |index|
589
+ list << columns.zip(widths).map { |column, width|
590
+ field = column[index]
591
+ "%-#{width + (field.to_s.length - actual_length(field))}s" % field
592
+ }.compact.join(" ").strip + "\n"
593
+ end
594
+ return list
595
+ end
596
+ else
597
+ items.map { |i| "#{i}\n" }.join
598
+ end
599
+ end
600
+ end
601
+
602
+ #
603
+ # The basic output method for HighLine objects. If the provided _statement_
604
+ # ends with a space or tab character, a newline will not be appended (output
605
+ # will be flush()ed). All other cases are passed straight to Kernel.puts().
606
+ #
607
+ # The _statement_ parameter is processed as an ERb template, supporting
608
+ # embedded Ruby code. The template is evaluated with a binding inside
609
+ # the HighLine instance, providing easy access to the ANSI color constants
610
+ # and the HighLine.color() method.
611
+ #
612
+ def say( statement )
613
+ statement = statement.to_str
614
+ return unless statement.length > 0
615
+
616
+ template = ERB.new(statement, nil, "%")
617
+ statement = template.result(binding)
618
+
619
+ statement = wrap(statement) unless @wrap_at.nil?
620
+ statement = page_print(statement) unless @page_at.nil?
621
+
622
+ if statement[-1, 1] == " " or statement[-1, 1] == "\t"
623
+ @output.print(statement)
624
+ @output.flush
625
+ else
626
+ @output.puts(statement)
627
+ end
628
+ end
629
+
630
+ #
631
+ # Set to an integer value to cause HighLine to wrap output lines at the
632
+ # indicated character limit. When +nil+, the default, no wrapping occurs. If
633
+ # set to <tt>:auto</tt>, HighLine will attempt to determing the columns
634
+ # available for the <tt>@output</tt> or use a sensible default.
635
+ #
636
+ def wrap_at=( setting )
637
+ @wrap_at = setting == :auto ? output_cols : setting
638
+ end
639
+
640
+ #
641
+ # Set to an integer value to cause HighLine to page output lines over the
642
+ # indicated line limit. When +nil+, the default, no paging occurs. If
643
+ # set to <tt>:auto</tt>, HighLine will attempt to determing the rows available
644
+ # for the <tt>@output</tt> or use a sensible default.
645
+ #
646
+ def page_at=( setting )
647
+ @page_at = setting == :auto ? output_rows - 2 : setting
648
+ end
649
+
650
+ #
651
+ # Returns the number of columns for the console, or a default it they cannot
652
+ # be determined.
653
+ #
654
+ def output_cols
655
+ return 80 unless @output.tty?
656
+ terminal_size.first
657
+ rescue
658
+ return 80
659
+ end
660
+
661
+ #
662
+ # Returns the number of rows for the console, or a default if they cannot be
663
+ # determined.
664
+ #
665
+ def output_rows
666
+ return 24 unless @output.tty?
667
+ terminal_size.last
668
+ rescue
669
+ return 24
670
+ end
671
+
672
+ private
673
+
674
+ #
675
+ # A helper method for sending the output stream and error and repeat
676
+ # of the question.
677
+ #
678
+ def explain_error( error )
679
+ say(@question.responses[error]) unless error.nil?
680
+ if @question.responses[:ask_on_error] == :question
681
+ say(@question)
682
+ elsif @question.responses[:ask_on_error]
683
+ say(@question.responses[:ask_on_error])
684
+ end
685
+ end
686
+
687
+ #
688
+ # Collects an Array/Hash full of answers as described in
689
+ # HighLine::Question.gather().
690
+ #
691
+ # Raises EOFError if input is exhausted.
692
+ #
693
+ def gather( )
694
+ @gather = @question.gather
695
+ @answers = [ ]
696
+ original_question = @question
697
+
698
+ @question.gather = false
699
+
700
+ case @gather
701
+ when Integer
702
+ @answers << ask(@question)
703
+ @gather -= 1
704
+
705
+ original_question.question = ""
706
+ until @gather.zero?
707
+ @question = original_question
708
+ @answers << ask(@question)
709
+ @gather -= 1
710
+ end
711
+ when ::String, Regexp
712
+ @answers << ask(@question)
713
+
714
+ original_question.question = ""
715
+ until (@gather.is_a?(::String) and @answers.last.to_s == @gather) or
716
+ (@gather.is_a?(Regexp) and @answers.last.to_s =~ @gather)
717
+ @question = original_question
718
+ @answers << ask(@question)
719
+ end
720
+
721
+ @answers.pop
722
+ when Hash
723
+ @answers = { }
724
+ @gather.keys.sort.each do |key|
725
+ @question = original_question
726
+ @key = key
727
+ @answers[key] = ask(@question)
728
+ end
729
+ end
730
+
731
+ @answers
732
+ end
733
+
734
+ #
735
+ # Read a line of input from the input stream and process whitespace as
736
+ # requested by the Question object.
737
+ #
738
+ # If Question's _readline_ property is set, that library will be used to
739
+ # fetch input. *WARNING*: This ignores the currently set input stream.
740
+ #
741
+ # Raises EOFError if input is exhausted.
742
+ #
743
+ def get_line( )
744
+ if @question.readline
745
+ require "readline" # load only if needed
746
+
747
+ # capture say()'s work in a String to feed to readline()
748
+ old_output = @output
749
+ @output = StringIO.new
750
+ say(@question)
751
+ question = @output.string
752
+ @output = old_output
753
+
754
+ # prep auto-completion
755
+ Readline.completion_proc = lambda do |string|
756
+ @question.selection.grep(/\A#{Regexp.escape(string)}/)
757
+ end
758
+
759
+ # work-around ugly readline() warnings
760
+ old_verbose = $VERBOSE
761
+ $VERBOSE = nil
762
+ raw_answer = Readline.readline(question, true)
763
+ if raw_answer.nil?
764
+ if @@track_eof
765
+ raise EOFError, "The input stream is exhausted."
766
+ else
767
+ raw_answer = String.new # Never return nil
768
+ end
769
+ end
770
+ answer = @question.change_case(
771
+ @question.remove_whitespace(raw_answer))
772
+ $VERBOSE = old_verbose
773
+
774
+ answer
775
+ else
776
+ if JRUBY
777
+ enable_echo_afterwards = @java_terminal.isEchoEnabled
778
+ @java_terminal.disableEcho
779
+ begin
780
+ raw_answer = @java_console.readLine(nil, nil)
781
+ ensure
782
+ @java_terminal.enableEcho if enable_echo_afterwards
783
+ end
784
+ else
785
+ raise EOFError, "The input stream is exhausted." if @@track_eof and
786
+ @input.eof?
787
+ raw_answer = @input.gets
788
+ end
789
+
790
+ @question.change_case(@question.remove_whitespace(raw_answer))
791
+ end
792
+ end
793
+
794
+ def get_single_character(is_stty)
795
+ if JRUBY
796
+ @java_console.readVirtualKey
797
+ elsif is_stty
798
+ @input.getbyte
799
+ else
800
+ get_character(@input)
801
+ end
802
+ end
803
+
804
+ #
805
+ # Return a line or character of input, as requested for this question.
806
+ # Character input will be returned as a single character String,
807
+ # not an Integer.
808
+ #
809
+ # This question's _first_answer_ will be returned instead of input, if set.
810
+ #
811
+ # Raises EOFError if input is exhausted.
812
+ #
813
+ def get_response( )
814
+ return @question.first_answer if @question.first_answer?
815
+
816
+ stty = (CHARACTER_MODE == "stty")
817
+
818
+ if @question.character.nil?
819
+ if @question.echo == true and @question.limit.nil?
820
+ get_line
821
+ else
822
+ if JRUBY
823
+ enable_echo_afterwards = @java_terminal.isEchoEnabled
824
+ @java_terminal.disableEcho
825
+ elsif stty
826
+ raw_no_echo_mode
827
+ end
828
+
829
+ line = ""
830
+ backspace_limit = 0
831
+ begin
832
+
833
+ while character = get_single_character(stty)
834
+ # honor backspace and delete
835
+ if character == 127 or character == 8
836
+ line.slice!(-1, 1)
837
+ backspace_limit -= 1
838
+ else
839
+ line << character.chr
840
+ backspace_limit = line.size
841
+ end
842
+ # looking for carriage return (decimal 13) or
843
+ # newline (decimal 10) in raw input
844
+ break if character == 13 or character == 10
845
+ if @question.echo != false
846
+ if character == 127 or character == 8
847
+ # only backspace if we have characters on the line to
848
+ # eliminate, otherwise we'll tromp over the prompt
849
+ if backspace_limit >= 0 then
850
+ @output.print("\b#{HighLine.Style(:erase_char).code}")
851
+ else
852
+ # do nothing
853
+ end
854
+ else
855
+ if @question.echo == true
856
+ @output.print(character.chr)
857
+ else
858
+ @output.print(@question.echo)
859
+ end
860
+ end
861
+ @output.flush
862
+ end
863
+ break if @question.limit and line.size == @question.limit
864
+ end
865
+ ensure
866
+ if JRUBY
867
+ @java_terminal.enableEcho if enable_echo_afterwards
868
+ elsif stty
869
+ restore_mode
870
+ end
871
+ end
872
+ if @question.overwrite
873
+ @output.print("\r#{HighLine.Style(:erase_line).code}")
874
+ @output.flush
875
+ else
876
+ say("\n")
877
+ end
878
+
879
+ @question.change_case(@question.remove_whitespace(line))
880
+ end
881
+ else
882
+ if JRUBY
883
+ enable_echo_afterwards = @java_terminal.isEchoEnabled
884
+ @java_terminal.disableEcho
885
+ end
886
+ begin
887
+ if @question.character == :getc
888
+ response = get_single_character(true).chr
889
+ else
890
+ response = get_single_character(stty).chr
891
+ if @question.overwrite
892
+ @output.print("\r#{HighLine.Style(:erase_line).code}")
893
+ @output.flush
894
+ else
895
+ echo = if @question.echo == true
896
+ response
897
+ elsif @question.echo != false
898
+ @question.echo
899
+ else
900
+ ""
901
+ end
902
+ say("#{echo}\n")
903
+ end
904
+ end
905
+ ensure
906
+ if JRUBY
907
+ @java_terminal.enableEcho if enable_echo_afterwards
908
+ end
909
+ end
910
+ @question.change_case(response)
911
+ end
912
+ end
913
+
914
+ #
915
+ # Page print a series of at most _page_at_ lines for _output_. After each
916
+ # page is printed, HighLine will pause until the user presses enter/return
917
+ # then display the next page of data.
918
+ #
919
+ # Note that the final page of _output_ is *not* printed, but returned
920
+ # instead. This is to support any special handling for the final sequence.
921
+ #
922
+ def page_print( output )
923
+ lines = output.scan(/[^\n]*\n?/)
924
+ while lines.size > @page_at
925
+ @output.puts lines.slice!(0...@page_at).join
926
+ @output.puts
927
+ # Return last line if user wants to abort paging
928
+ return (["...\n"] + lines.slice(-2,1)).join unless continue_paging?
929
+ end
930
+ return lines.join
931
+ end
932
+
933
+ #
934
+ # Ask user if they wish to continue paging output. Allows them to type "q" to
935
+ # cancel the paging process.
936
+ #
937
+ def continue_paging?
938
+ command = HighLine.new(@input, @output).ask(
939
+ "-- press enter/return to continue or q to stop -- "
940
+ ) { |q| q.character = true }
941
+ command !~ /\A[qQ]\Z/ # Only continue paging if Q was not hit.
942
+ end
943
+
944
+ #
945
+ # Wrap a sequence of _lines_ at _wrap_at_ characters per line. Existing
946
+ # newlines will not be affected by this process, but additional newlines
947
+ # may be added.
948
+ #
949
+ def wrap( text )
950
+ wrapped = [ ]
951
+ text.each_line do |line|
952
+ while line =~ /([^\n]{#{@wrap_at + 1},})/
953
+ search = $1.dup
954
+ replace = $1.dup
955
+ if index = replace.rindex(" ", @wrap_at)
956
+ replace[index, 1] = "\n"
957
+ replace.sub!(/\n[ \t]+/, "\n")
958
+ line.sub!(search, replace)
959
+ else
960
+ line[$~.begin(1) + @wrap_at, 0] = "\n"
961
+ end
962
+ end
963
+ wrapped << line
964
+ end
965
+ return wrapped.join
966
+ end
967
+
968
+ #
969
+ # Returns the length of the passed +string_with_escapes+, minus and color
970
+ # sequence escapes.
971
+ #
972
+ def actual_length( string_with_escapes )
973
+ string_with_escapes.to_s.gsub(/\e\[\d{1,2}m/, "").length
974
+ end
975
+ end
976
+
977
+ require "highline/string_extensions"
978
+