challah 0.3.4 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
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
+