highline 2.0.0.pre.develop.2 → 2.0.0.pre.develop.4

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.
@@ -12,18 +12,26 @@ require "highline/question"
12
12
 
13
13
  class HighLine
14
14
  #
15
- # Menu objects encapsulate all the details of a call to HighLine.choose().
16
- # Using the accessors and Menu.choice() and Menu.choices(), the block passed
17
- # to HighLine.choose() can detail all aspects of menu display and control.
15
+ # Menu objects encapsulate all the details of a call to {HighLine#choose HighLine#choose}.
16
+ # Using the accessors and {Menu#choice} and {Menu#choices}, the block passed
17
+ # to {HighLine#choose} can detail all aspects of menu display and control.
18
18
  #
19
19
  class Menu < Question
20
20
  #
21
21
  # Create an instance of HighLine::Menu. All customization is done
22
- # through the passed block, which should call accessors and choice() and
23
- # choices() as needed to define the Menu. Note that Menus are also
24
- # Questions, so all that functionality is available to the block as
25
- # well.
26
- #
22
+ # through the passed block, which should call accessors, {#choice} and
23
+ # {#choices} as needed to define the Menu. Note that Menus are also
24
+ # {HighLine::Question Questions}, so all that functionality is available
25
+ # to the block as well.
26
+ #
27
+ # @example Implicit menu creation through HighLine#choose
28
+ # cli = HighLine.new
29
+ # answer = cli.choose do |menu|
30
+ # menu.prompt = "Please choose your favorite programming language? "
31
+ # menu.choice(:ruby) { say("Good choice!") }
32
+ # menu.choices(:python, :perl) { say("Not from around here, are you?") }
33
+ # end
34
+
27
35
  def initialize( )
28
36
  #
29
37
  # Initialize Question objects with ignored values, we'll
@@ -136,6 +144,23 @@ class HighLine
136
144
  # the current HighLine context before the action code is called and can
137
145
  # thus be used for adding output and the like.
138
146
  #
147
+ # @param name [#to_s] menu item title/header/name to be displayed.
148
+ # @param action [Proc] callback action to be run when the item is selected.
149
+ # @param help [String] help/hint string to be displayed.
150
+ # @return [void]
151
+ # @example (see HighLine::Menu#initialize)
152
+ # @example Use of help string on menu items
153
+ # cli = HighLine.new
154
+ # cli.choose do |menu|
155
+ # menu.shell = true
156
+ #
157
+ # menu.choice(:load, "Load a file.")
158
+ # menu.choice(:save, "Save data in file.")
159
+ # menu.choice(:quit, "Exit program.")
160
+ #
161
+ # menu.help("rules", "The rules of this system are as follows...")
162
+ # end
163
+
139
164
  def choice( name, help = nil, &action )
140
165
  @items << [name, action]
141
166
 
@@ -144,16 +169,25 @@ class HighLine
144
169
  end
145
170
 
146
171
  #
147
- # A shortcut for multiple calls to the sister method choice(). <b>Be
172
+ # A shortcut for multiple calls to the sister method {#choice}. <b>Be
148
173
  # warned:</b> An _action_ set here will apply to *all* provided
149
174
  # _names_. This is considered to be a feature, so you can easily
150
175
  # hand-off interface processing to a different chunk of code.
151
- #
176
+ # @param names [Array<#to_s>] menu item titles/headers/names to be displayed.
177
+ # @param action (see #choice)
178
+ # @return [void]
179
+ # @example (see HighLine::Menu#initialize)
152
180
  def choices( *names, &action )
153
181
  names.each { |n| choice(n, &action) }
154
182
  end
155
183
 
156
- # Identical to choice(), but the item will not be listed for the user.
184
+ # Identical to {#choice}, but the item will not be listed for the user.
185
+ # @see #choice
186
+ # @param name (see #choice)
187
+ # @param help (see #choice)
188
+ # @param action (see #choice)
189
+ # @return (see #choice)
190
+
157
191
  def hidden( name, help = nil, &action )
158
192
  @hidden_items << [name, action]
159
193
 
@@ -212,8 +246,12 @@ class HighLine
212
246
 
213
247
  #
214
248
  # Used to set help for arbitrary topics. Use the topic <tt>"help"</tt>
215
- # to override the default message.
249
+ # to override the default message. Mainly for internal use.
216
250
  #
251
+ # @param topic [String] the menu item header/title/name to be associated with
252
+ # a help message.
253
+ # @param help [String] the help message to be associated with the menu
254
+ # item/title/name.
217
255
  def help( topic, help )
218
256
  @help[topic] = help
219
257
  end
@@ -290,19 +328,22 @@ class HighLine
290
328
  #
291
329
  # This method processes the auto-completed user selection, based on the
292
330
  # rules for this Menu object. If an action was provided for the
293
- # selection, it will be executed as described in Menu.choice().
331
+ # selection, it will be executed as described in {#choice}.
294
332
  #
333
+ # @param highline_context [HighLine] a HighLine instance to be used as context.
334
+ # @param selection [String, Integer] index or title of the selected menu item.
335
+ # @param details additional parameter to be passed when in shell mode.
336
+ # @return [nil, Object] if @nil_on_handled is set it returns +nil+,
337
+ # else it returns the action return value.
295
338
  def select( highline_context, selection, details = nil )
296
339
  # add in any hidden menu commands
297
340
  @items.concat(@hidden_items)
298
341
 
299
342
  # Find the selected action.
300
- name, action = if selection =~ /^\d+$/
301
- @items[selection.to_i - 1]
343
+ name, action = if selection =~ /^\d+$/ # is a number?
344
+ get_item_by_number(selection)
302
345
  else
303
- l_index = "`"
304
- index = @items.map { "#{l_index.succ!}" }.index(selection)
305
- @items.find { |c| c.first == selection } or @items[index]
346
+ get_item_by_letter(selection)
306
347
  end
307
348
 
308
349
  # Run or return it.
@@ -322,6 +363,20 @@ class HighLine
322
363
  @items.slice!(@items.size - @hidden_items.size, @hidden_items.size)
323
364
  end
324
365
 
366
+ # Returns the menu item referenced by its index
367
+ # @param selection [Integer] menu item's index.
368
+ def get_item_by_number(selection)
369
+ @items[selection.to_i - 1]
370
+ end
371
+
372
+ # Returns the menu item referenced by its title/header/name.
373
+ # @param selection [String] menu's title/header/name
374
+ def get_item_by_letter(selection)
375
+ l_index = "`" # character before the letter "a"
376
+ index = @items.map { "#{l_index.succ!}" }.index(selection)
377
+ @items.find { |c| c.first == selection } or @items[index]
378
+ end
379
+
325
380
  #
326
381
  # Allows Menu objects to pass as Arrays, for use with HighLine.list().
327
382
  # This method returns all menu items to be displayed, complete with
@@ -1,9 +1,17 @@
1
1
  # coding: utf-8
2
2
 
3
3
  class HighLine
4
+ # Take the task of paginating some piece of text given a HighLine context
4
5
  class Paginator
6
+
7
+ # @return [HighLine] HighLine context
5
8
  attr_reader :highline
6
9
 
10
+ # Returns a HighLine::Paginator instance where you can
11
+ # call {#page_print} on it.
12
+ # @param highline [HighLine] context
13
+ # @example
14
+ # HighLine::Paginator.new(highline).page_print(statement)
7
15
  def initialize(highline)
8
16
  @highline = highline
9
17
  end
@@ -16,6 +24,8 @@ class HighLine
16
24
  # Note that the final page of _output_ is *not* printed, but returned
17
25
  # instead. This is to support any special handling for the final sequence.
18
26
  #
27
+ # @param text [String] text to be paginated
28
+ # @return [String] last line if paging is aborted
19
29
  def page_print(text)
20
30
  return text unless highline.page_at
21
31
 
@@ -27,6 +27,10 @@ class HighLine
27
27
  # If _template_or_question_ is already a Question object just return it.
28
28
  # If not, build it.
29
29
  #
30
+ # @param template_or_question [String, Question] what to ask
31
+ # @param answer_type [Class] to what class to convert the answer
32
+ # @param details to be passed to Question.new
33
+ # @return [Question]
30
34
  def self.build(template_or_question, answer_type = nil, &details)
31
35
  if template_or_question.is_a? Question
32
36
  template_or_question
@@ -42,26 +46,18 @@ class HighLine
42
46
  # Question.convert(). If given, a block is yielded the new Question
43
47
  # object to allow custom initialization.
44
48
  #
49
+ # @param template [String] any String
50
+ # @param answer_type [Class] the type the answer will be converted to it.
45
51
  def initialize(template, answer_type)
46
52
  # initialize instance data
47
- @template = template.dup
53
+ @template = String(template).dup
48
54
  @answer_type = answer_type
49
55
  @completion = @answer_type
50
56
 
51
- @character = nil
52
- @limit = nil
53
57
  @echo = true
54
- @readline = false
55
58
  @whitespace = :strip
56
59
  @case = nil
57
- @default = nil
58
- @validate = nil
59
- @above = nil
60
- @below = nil
61
60
  @in = nil
62
- @confirm = nil
63
- @gather = false
64
- @verify_match = false
65
61
  @first_answer = nil
66
62
  @directory = Pathname.new(File.expand_path(File.dirname($0)))
67
63
  @glob = "*"
@@ -231,8 +227,10 @@ class HighLine
231
227
  # Returns the provided _answer_string_ or the default answer for this
232
228
  # Question if a default was set and the answer is empty.
233
229
  #
230
+ # @param answer_string [String]
231
+ # @return [String] the answer itself or a default message.
234
232
  def answer_or_default(answer_string)
235
- return @default if answer_string.empty? && @default
233
+ return default if answer_string.empty? && default
236
234
  answer_string
237
235
  end
238
236
 
@@ -241,16 +239,24 @@ class HighLine
241
239
  # responses based on the details of this Question object.
242
240
  # Also used by Menu#update_responses.
243
241
  #
242
+ # @return [Hash] responses Hash winner (new and old merge).
243
+ # @param message_source [Class] Array or String for example.
244
+ # Same as {#answer_type}.
245
+ # @param new_hash_wins [Boolean] merge precedence (new vs. old).
246
+
244
247
  def build_responses(message_source = answer_type, new_hash_wins = false)
245
248
  append_default if [::String, Symbol].include? default.class
246
249
 
247
- old_hash = @responses
250
+ old_hash = responses
248
251
 
249
252
  new_hash = build_responses_new_hash(message_source)
250
253
 
251
254
  @responses = new_hash_wins ? old_hash.merge(new_hash) : new_hash.merge(old_hash)
252
255
  end
253
256
 
257
+ # When updating the responses hash, it generates the new one.
258
+ # @param message_source (see #build_responses)
259
+ # @return [Hash] responses hash
254
260
  def build_responses_new_hash(message_source)
255
261
  { :ambiguous_completion => "Ambiguous choice. Please choose one of " +
256
262
  choice_error_str(message_source) + '.',
@@ -262,7 +268,7 @@ class HighLine
262
268
  "(#{expected_range}).",
263
269
  :mismatch => "Your entries didn't match.",
264
270
  :not_valid => "Your answer isn't valid (must match " +
265
- "#{@validate.inspect})." }
271
+ "#{validate.inspect})." }
266
272
  end
267
273
 
268
274
 
@@ -280,6 +286,9 @@ class HighLine
280
286
  #
281
287
  # An unrecognized choice (like <tt>:none</tt>) is treated as +nil+.
282
288
  #
289
+ # @param answer_string [String]
290
+ # @return [String] upcased, downcased, capitalized
291
+ # or unchanged answer String.
283
292
  def change_case(answer_string)
284
293
  if [:up, :upcase].include?(@case)
285
294
  answer_string.upcase
@@ -325,10 +334,14 @@ class HighLine
325
334
  AnswerConverter.new(self).convert
326
335
  end
327
336
 
337
+ # Run {#in_range?} and raise an error if not succesful
328
338
  def check_range
329
339
  raise NotInRangeQuestionError unless in_range?
330
340
  end
331
341
 
342
+ # Try to auto complete answer_string
343
+ # @param answer_string [String]
344
+ # @return [String]
332
345
  def choices_complete(answer_string)
333
346
  # cheating, using OptionParser's Completion module
334
347
  choices = selection
@@ -342,8 +355,8 @@ class HighLine
342
355
  def expected_range
343
356
  expected = [ ]
344
357
 
345
- expected << "above #{@above}" if @above
346
- expected << "below #{@below}" if @below
358
+ expected << "above #{above}" if above
359
+ expected << "below #{below}" if below
347
360
  expected << "included in #{@in.inspect}" if @in
348
361
 
349
362
  case expected.size
@@ -373,8 +386,8 @@ class HighLine
373
386
  # are not checked.
374
387
  #
375
388
  def in_range?
376
- (!@above or answer > @above) and
377
- (!@below or answer < @below) and
389
+ (!above or answer > above) and
390
+ (!below or answer < below) and
378
391
  (!@in or @in.include?(answer))
379
392
  end
380
393
 
@@ -397,23 +410,29 @@ class HighLine
397
410
  #
398
411
  # This process is skipped for single character input.
399
412
  #
413
+ # @param answer_string [String]
414
+ # @return [String] answer string with whitespaces removed
400
415
  def remove_whitespace(answer_string)
401
- if !@whitespace
416
+ if !whitespace
402
417
  answer_string
403
- elsif [:strip, :chomp].include?(@whitespace)
404
- answer_string.send(@whitespace)
405
- elsif @whitespace == :collapse
418
+ elsif [:strip, :chomp].include?(whitespace)
419
+ answer_string.send(whitespace)
420
+ elsif whitespace == :collapse
406
421
  answer_string.gsub(/\s+/, " ")
407
- elsif [:strip_and_collapse, :chomp_and_collapse].include?(@whitespace)
408
- result = answer_string.send(@whitespace.to_s[/^[a-z]+/])
422
+ elsif [:strip_and_collapse, :chomp_and_collapse].include?(whitespace)
423
+ result = answer_string.send(whitespace.to_s[/^[a-z]+/])
409
424
  result.gsub(/\s+/, " ")
410
- elsif @whitespace == :remove
425
+ elsif whitespace == :remove
411
426
  answer_string.gsub(/\s+/, "")
412
427
  else
413
428
  answer_string
414
429
  end
415
430
  end
416
431
 
432
+ # Convert to String, remove whitespace and change case
433
+ # when necessary
434
+ # @param answer_string [String]
435
+ # @return [String] converted String
417
436
  def format_answer(answer_string)
418
437
  answer_string = String(answer_string)
419
438
  answer_string = remove_whitespace(answer_string)
@@ -426,10 +445,10 @@ class HighLine
426
445
  # Pathname. Any other time, this method will return an empty Array.
427
446
  #
428
447
  def selection
429
- if @completion.is_a?(Array)
430
- @completion
431
- elsif [File, Pathname].include?(@completion)
432
- Dir[File.join(@directory.to_s, @glob)].map do |file|
448
+ if completion.is_a?(Array)
449
+ completion
450
+ elsif [File, Pathname].include?(completion)
451
+ Dir[File.join(directory.to_s, glob)].map do |file|
433
452
  File.basename(file)
434
453
  end
435
454
  else
@@ -439,7 +458,7 @@ class HighLine
439
458
 
440
459
  # Stringifies the template to be asked.
441
460
  def to_s
442
- @template
461
+ template
443
462
  end
444
463
 
445
464
  #
@@ -450,9 +469,9 @@ class HighLine
450
469
  # and case handling.
451
470
  #
452
471
  def valid_answer?
453
- !@validate or
454
- (@validate.is_a?(Regexp) and answer =~ @validate) or
455
- (@validate.is_a?(Proc) and @validate[answer])
472
+ !validate or
473
+ (validate.is_a?(Regexp) and answer =~ validate) or
474
+ (validate.is_a?(Proc) and validate[answer])
456
475
  end
457
476
 
458
477
  #
@@ -464,6 +483,9 @@ class HighLine
464
483
  #
465
484
  # Raises EOFError if input is exhausted.
466
485
  #
486
+ # @param highline [HighLine] context
487
+ # @return [String] a character or line
488
+
467
489
  def get_response(highline)
468
490
  return first_answer if first_answer?
469
491
 
@@ -477,10 +499,21 @@ class HighLine
477
499
  end
478
500
  end
479
501
 
502
+ # Uses {#get_response} but returns a default answer
503
+ # using {#answer_or_default} in case no answers was
504
+ # returned.
505
+ #
506
+ # @param highline [HighLine] context
507
+ # @return [String]
508
+
480
509
  def get_response_or_default(highline)
481
510
  self.answer = answer_or_default(get_response(highline))
482
511
  end
483
512
 
513
+ # Returns the String to be shown when asking for an answer confirmation.
514
+ # @param highline [HighLine] context
515
+ # @return [String] default "Are you sure?" if {#confirm} is +true+
516
+ # @return [String] {#confirm} rendered as a template if it is a String
484
517
  def confirm_question(highline)
485
518
  if confirm == true
486
519
  "Are you sure? "
@@ -493,6 +526,10 @@ class HighLine
493
526
  end
494
527
  end
495
528
 
529
+ # Provides the String to be asked when at an error situation.
530
+ # It may be just the question itself (repeat on error).
531
+ # @return [self] if :ask_on_error on responses Hash is set to :question
532
+ # @return [String] if :ask_on_error on responses Hash is set to something else
496
533
  def ask_on_error_msg
497
534
  if responses[:ask_on_error] == :question
498
535
  self
@@ -506,8 +543,28 @@ class HighLine
506
543
  # the prompt will not be issued. And we have to account for that now.
507
544
  # Also, JRuby-1.7's ConsoleReader.readLine() needs to be passed the prompt
508
545
  # to handle line editing properly.
546
+ # @param highline [HighLine] context
547
+ # @return [void]
509
548
  def show_question(highline)
510
- highline.say(self) unless (@readline && (@echo == true && !@limit))
549
+ highline.say(self) unless (readline && (echo == true && !limit))
550
+ end
551
+
552
+ # Returns an echo string that is adequate for this Question settings.
553
+ # @param response [String]
554
+ # @return [String] the response itself if {#echo} is +true+.
555
+ # @return [String] echo character if {#echo} is truethy. Mainly a String.
556
+ # @return [String] empty string if {#echo} is falsy.
557
+ def get_echo_for_response(response)
558
+ # actually true, not only truethy value
559
+ if echo == true
560
+ response
561
+ # any truethy value, probably a String
562
+ elsif !!echo
563
+ echo
564
+ # any falsy value, false or nil
565
+ else
566
+ ""
567
+ end
511
568
  end
512
569
 
513
570
  private
@@ -518,14 +575,14 @@ class HighLine
518
575
  # not affected.
519
576
  #
520
577
  def append_default
521
- if @template =~ /([\t ]+)\Z/
522
- @template << "|#{@default}|#{$1}"
523
- elsif @template == ""
524
- @template << "|#{@default}| "
525
- elsif @template[-1, 1] == "\n"
526
- @template[-2, 0] = " |#{@default}|"
578
+ if template =~ /([\t ]+)\Z/
579
+ template << "|#{default}|#{$1}"
580
+ elsif template == ""
581
+ template << "|#{default}| "
582
+ elsif template[-1, 1] == "\n"
583
+ template[-2, 0] = " |#{default}|"
527
584
  else
528
- @template << " |#{@default}|"
585
+ template << " |#{default}|"
529
586
  end
530
587
  end
531
588