highline 1.7.10 → 2.0.3

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 (65) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +5 -0
  3. data/.rubocop.yml +84 -0
  4. data/.simplecov +5 -0
  5. data/.travis.yml +35 -9
  6. data/Changelog.md +214 -15
  7. data/Gemfile +16 -5
  8. data/README.md +202 -0
  9. data/Rakefile +8 -20
  10. data/appveyor.yml +37 -0
  11. data/examples/ansi_colors.rb +6 -11
  12. data/examples/asking_for_arrays.rb +6 -2
  13. data/examples/basic_usage.rb +31 -21
  14. data/examples/color_scheme.rb +11 -10
  15. data/examples/get_character.rb +8 -4
  16. data/examples/limit.rb +4 -0
  17. data/examples/menus.rb +16 -10
  18. data/examples/overwrite.rb +9 -5
  19. data/examples/page_and_wrap.rb +5 -4
  20. data/examples/password.rb +4 -0
  21. data/examples/repeat_entry.rb +10 -5
  22. data/examples/trapping_eof.rb +2 -1
  23. data/examples/using_readline.rb +2 -1
  24. data/highline.gemspec +25 -27
  25. data/lib/highline/builtin_styles.rb +129 -0
  26. data/lib/highline/color_scheme.rb +49 -32
  27. data/lib/highline/compatibility.rb +11 -4
  28. data/lib/highline/custom_errors.rb +57 -0
  29. data/lib/highline/import.rb +19 -12
  30. data/lib/highline/io_console_compatible.rb +37 -0
  31. data/lib/highline/list.rb +177 -0
  32. data/lib/highline/list_renderer.rb +261 -0
  33. data/lib/highline/menu/item.rb +32 -0
  34. data/lib/highline/menu.rb +306 -111
  35. data/lib/highline/paginator.rb +52 -0
  36. data/lib/highline/question/answer_converter.rb +103 -0
  37. data/lib/highline/question.rb +281 -131
  38. data/lib/highline/question_asker.rb +150 -0
  39. data/lib/highline/simulate.rb +24 -13
  40. data/lib/highline/statement.rb +88 -0
  41. data/lib/highline/string.rb +36 -0
  42. data/lib/highline/string_extensions.rb +83 -64
  43. data/lib/highline/style.rb +196 -63
  44. data/lib/highline/template_renderer.rb +62 -0
  45. data/lib/highline/terminal/io_console.rb +36 -0
  46. data/lib/highline/terminal/ncurses.rb +38 -0
  47. data/lib/highline/terminal/unix_stty.rb +51 -0
  48. data/lib/highline/terminal.rb +190 -0
  49. data/lib/highline/version.rb +3 -1
  50. data/lib/highline/wrapper.rb +53 -0
  51. data/lib/highline.rb +390 -788
  52. metadata +56 -35
  53. data/INSTALL +0 -59
  54. data/README.rdoc +0 -74
  55. data/lib/highline/system_extensions.rb +0 -254
  56. data/setup.rb +0 -1360
  57. data/test/string_methods.rb +0 -32
  58. data/test/tc_color_scheme.rb +0 -96
  59. data/test/tc_highline.rb +0 -1402
  60. data/test/tc_import.rb +0 -52
  61. data/test/tc_menu.rb +0 -439
  62. data/test/tc_simulator.rb +0 -33
  63. data/test/tc_string_extension.rb +0 -33
  64. data/test/tc_string_highline.rb +0 -38
  65. data/test/tc_style.rb +0 -578
@@ -0,0 +1,150 @@
1
+ # encoding: utf-8
2
+
3
+ class HighLine
4
+ # Deals with the task of "asking" a question
5
+ class QuestionAsker
6
+ # @return [Question] question to be asked
7
+ attr_reader :question
8
+
9
+ include CustomErrors
10
+
11
+ # To do its work QuestionAsker needs a Question
12
+ # to be asked and a HighLine context where to
13
+ # direct output.
14
+ #
15
+ # @param question [Question] question to be asked
16
+ # @param highline [HighLine] context
17
+ def initialize(question, highline)
18
+ @question = question
19
+ @highline = highline
20
+ end
21
+
22
+ #
23
+ # Gets just one answer, as opposed to #gather_answers
24
+ #
25
+ # @return [String] answer
26
+ def ask_once
27
+ question.show_question(@highline)
28
+
29
+ begin
30
+ question.get_response_or_default(@highline)
31
+ raise NotValidQuestionError unless question.valid_answer?
32
+
33
+ question.convert
34
+
35
+ if question.confirm
36
+ confirmation = @highline.send(:confirm, question)
37
+ raise NoConfirmationQuestionError unless confirmation
38
+ end
39
+ rescue ExplainableError => e
40
+ explain_error(e.explanation_key)
41
+ retry
42
+ rescue ArgumentError => error
43
+ case error.message
44
+ when /ambiguous/
45
+ # the assumption here is that OptionParser::Completion#complete
46
+ # (used for ambiguity resolution) throws exceptions containing
47
+ # the word 'ambiguous' whenever resolution fails
48
+ explain_error(:ambiguous_completion)
49
+ retry
50
+ when /invalid value for/
51
+ explain_error(:invalid_type)
52
+ retry
53
+ else
54
+ raise
55
+ end
56
+ end
57
+
58
+ question.answer
59
+ end
60
+
61
+ ## Multiple questions
62
+
63
+ #
64
+ # Collects an Array/Hash full of answers as described in
65
+ # HighLine::Question.gather().
66
+ #
67
+ # @return [Array, Hash] answers
68
+ def gather_answers
69
+ verify_match = question.verify_match
70
+ answers = []
71
+
72
+ # when verify_match is set this loop will repeat until unique_answers == 1
73
+ loop do
74
+ answers = gather_answers_based_on_type
75
+
76
+ break unless verify_match &&
77
+ (@highline.send(:unique_answers, answers).size > 1)
78
+
79
+ explain_error(:mismatch)
80
+ end
81
+
82
+ verify_match ? @highline.send(:last_answer, answers) : answers
83
+ end
84
+
85
+ # Gather multiple integer values based on {Question#gather} count
86
+ # @return [Array] answers
87
+ def gather_integer
88
+ gather_with_array do |answers|
89
+ (question.gather - 1).times { answers << ask_once }
90
+ end
91
+ end
92
+
93
+ # Gather multiple values until any of them matches the
94
+ # {Question#gather} Regexp.
95
+ # @return [Array] answers
96
+ def gather_regexp
97
+ gather_with_array do |answers|
98
+ answers << ask_once until answer_matches_regex(answers.last)
99
+ answers.pop
100
+ end
101
+ end
102
+
103
+ # Gather multiple values and store them on a Hash
104
+ # with keys provided by the Hash on {Question#gather}
105
+ # @return [Hash]
106
+ def gather_hash
107
+ sorted_keys = question.gather.keys.sort_by(&:to_s)
108
+ sorted_keys.each_with_object({}) do |key, answers|
109
+ @highline.key = key
110
+ answers[key] = ask_once
111
+ end
112
+ end
113
+
114
+ private
115
+
116
+ ## Delegate to Highline
117
+ def explain_error(explanation_key) # eg: :not_valid, :not_in_range
118
+ @highline.say(question.final_response(explanation_key))
119
+ @highline.say(question.ask_on_error_msg)
120
+ end
121
+
122
+ def gather_with_array
123
+ [].tap do |answers|
124
+ answers << ask_once
125
+ question.template = ""
126
+
127
+ yield answers
128
+ end
129
+ end
130
+
131
+ def answer_matches_regex(answer)
132
+ if question.gather.is_a?(::String) || question.gather.is_a?(Symbol)
133
+ answer.to_s == question.gather.to_s
134
+ elsif question.gather.is_a?(Regexp)
135
+ answer.to_s =~ question.gather
136
+ end
137
+ end
138
+
139
+ def gather_answers_based_on_type
140
+ case question.gather
141
+ when Integer
142
+ gather_integer
143
+ when ::String, Symbol, Regexp
144
+ gather_regexp
145
+ when Hash
146
+ gather_hash
147
+ end
148
+ end
149
+ end
150
+ end
@@ -1,3 +1,6 @@
1
+ # coding: utf-8
2
+
3
+ #--
1
4
  # simulate.rb
2
5
  #
3
6
  # Created by Andy Rossmeissl on 2012-04-29.
@@ -6,12 +9,13 @@
6
9
  # This is Free Software. See LICENSE and COPYING for details.
7
10
  #
8
11
  # adapted from https://gist.github.com/194554
9
- class HighLine
10
12
 
13
+ class HighLine
11
14
  # Simulates Highline input for use in tests.
12
15
  class Simulate
13
-
14
16
  # Creates a simulator with an array of Strings as a script
17
+ # @param strings [Array<String>] preloaded string to be used
18
+ # as input buffer when simulating.
15
19
  def initialize(strings)
16
20
  @strings = strings
17
21
  end
@@ -21,14 +25,15 @@ class HighLine
21
25
  @strings.shift
22
26
  end
23
27
 
24
- # Simulate StringIO#getbyte by shifting a single character off of the next line of the script
28
+ # Simulate StringIO#getbyte by shifting a single character off of
29
+ # the next line of the script
25
30
  def getbyte
26
- line = gets.dup
27
- if line.length > 0
28
- char = line.slice! 0
29
- @strings.unshift line
30
- char
31
- end
31
+ line = gets
32
+ return if line.empty?
33
+
34
+ char = line.slice! 0
35
+ @strings.unshift line
36
+ char
32
37
  end
33
38
 
34
39
  # The simulator handles its own EOF
@@ -36,13 +41,19 @@ class HighLine
36
41
  false
37
42
  end
38
43
 
39
- # A wrapper method that temporarily replaces the Highline instance in $terminal with an instance of this object for the duration of the block
44
+ # A wrapper method that temporarily replaces the Highline
45
+ # instance in HighLine.default_instance with an instance of this object
46
+ # for the duration of the block
47
+ #
48
+ # @param strings [String] preloaded string buffer that
49
+ # will feed the input operations when simulating.
50
+
40
51
  def self.with(*strings)
41
- @input = $terminal.instance_variable_get :@input
42
- $terminal.instance_variable_set :@input, new(strings)
52
+ @input = HighLine.default_instance.instance_variable_get :@input
53
+ HighLine.default_instance.instance_variable_set :@input, new(strings)
43
54
  yield
44
55
  ensure
45
- $terminal.instance_variable_set :@input, @input
56
+ HighLine.default_instance.instance_variable_set :@input, @input
46
57
  end
47
58
  end
48
59
  end
@@ -0,0 +1,88 @@
1
+ # coding: utf-8
2
+
3
+ require "highline/wrapper"
4
+ require "highline/paginator"
5
+ require "highline/template_renderer"
6
+
7
+ class HighLine
8
+ # This class handles proper formatting based
9
+ # on a HighLine context, applying wrapping,
10
+ # pagination, indentation and color rendering
11
+ # when necessary. It's used by {HighLine#render_statement}
12
+ # @see HighLine#render_statement
13
+ class Statement
14
+ # The source object to be stringfied and formatted.
15
+ attr_reader :source
16
+
17
+ # The HighLine context
18
+ # @return [HighLine]
19
+ attr_reader :highline
20
+
21
+ # The stringfied source object
22
+ # @return [String]
23
+ attr_reader :template_string
24
+
25
+ # It needs the input String and the HighLine context
26
+ # @param source [#to_s]
27
+ # @param highline [HighLine] context
28
+ def initialize(source, highline)
29
+ @highline = highline
30
+ @source = source
31
+ @template_string = stringfy(source)
32
+ end
33
+
34
+ # Returns the formated statement.
35
+ # Applies wrapping, pagination, indentation and color rendering
36
+ # based on HighLine instance settings.
37
+ # @return [String] formated statement
38
+ def statement
39
+ @statement ||= format_statement
40
+ end
41
+
42
+ # (see #statement)
43
+ # Delegates to {#statement}
44
+ def to_s
45
+ statement
46
+ end
47
+
48
+ def self.const_missing(constant)
49
+ HighLine.const_get(constant)
50
+ end
51
+
52
+ private
53
+
54
+ def stringfy(template_string)
55
+ String(template_string || "").dup
56
+ end
57
+
58
+ def format_statement
59
+ return template_string if template_string.empty?
60
+
61
+ statement = render_template
62
+
63
+ statement = HighLine::Wrapper.wrap(statement, highline.wrap_at)
64
+ statement = HighLine::Paginator.new(highline).page_print(statement)
65
+
66
+ statement = statement.gsub(/\n(?!$)/, "\n#{highline.indentation}") if
67
+ highline.multi_indent
68
+
69
+ statement
70
+ end
71
+
72
+ def render_template
73
+ # Assigning to a local var so it may be
74
+ # used inside instance eval block
75
+
76
+ template_renderer = TemplateRenderer.new(template, source, highline)
77
+ template_renderer.render
78
+ end
79
+
80
+ def template
81
+ @template ||= if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
82
+ ERB.new(template_string, trim_mode: "%")
83
+ else
84
+ ERB.new(template_string, nil, "%")
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+
3
+ require "highline/string_extensions"
4
+
5
+ class HighLine
6
+ #
7
+ # HighLine::String is a subclass of String with convenience methods added
8
+ # for colorization.
9
+ #
10
+ # Available convenience methods include:
11
+ # * 'color' method e.g. highline_string.color(:bright_blue,
12
+ # :underline)
13
+ # * colors e.g. highline_string.magenta
14
+ # * RGB colors e.g. highline_string.rgb_ff6000
15
+ # or highline_string.rgb(255,96,0)
16
+ # * background colors e.g. highline_string.on_magenta
17
+ # * RGB background colors e.g. highline_string.on_rgb_ff6000
18
+ # or highline_string.on_rgb(255,96,0)
19
+ # * styles e.g. highline_string.underline
20
+ #
21
+ # Additionally, convenience methods can be chained, for instance the
22
+ # following are equivalent:
23
+ # highline_string.bright_blue.blink.underline
24
+ # highline_string.color(:bright_blue, :blink, :underline)
25
+ # HighLine.color(highline_string, :bright_blue, :blink, :underline)
26
+ #
27
+ # For those less squeamish about possible conflicts, the same convenience
28
+ # methods can be added to the built-in String class, as follows:
29
+ #
30
+ # require 'highline'
31
+ # Highline.colorize_strings
32
+ #
33
+ class String < ::String
34
+ include StringExtensions
35
+ end
36
+ end
@@ -1,61 +1,32 @@
1
- # Extensions for class String
2
- #
3
- # HighLine::String is a subclass of String with convenience methods added for colorization.
4
- #
5
- # Available convenience methods include:
6
- # * 'color' method e.g. highline_string.color(:bright_blue, :underline)
7
- # * colors e.g. highline_string.magenta
8
- # * RGB colors e.g. highline_string.rgb_ff6000
9
- # or highline_string.rgb(255,96,0)
10
- # * background colors e.g. highline_string.on_magenta
11
- # * RGB background colors e.g. highline_string.on_rgb_ff6000
12
- # or highline_string.on_rgb(255,96,0)
13
- # * styles e.g. highline_string.underline
14
- #
15
- # Additionally, convenience methods can be chained, for instance the following are equivalent:
16
- # highline_string.bright_blue.blink.underline
17
- # highline_string.color(:bright_blue, :blink, :underline)
18
- # HighLine.color(highline_string, :bright_blue, :blink, :underline)
19
- #
20
- # For those less squeamish about possible conflicts, the same convenience methods can be
21
- # added to the built-in String class, as follows:
22
- #
23
- # require 'highline'
24
- # Highline.colorize_strings
25
-
26
- class HighLine
27
- def self.String(s)
1
+ # coding: utf-8
2
+
3
+ class HighLine #:nodoc:
4
+ # Returns a HighLine::String from any given String.
5
+ # @param s [String]
6
+ # @return [HighLine::String] from the given string.
7
+ def self.String(s) # rubocop:disable Naming/MethodName
28
8
  HighLine::String.new(s)
29
9
  end
30
10
 
11
+ # HighLine extensions for String class.
12
+ # Included by HighLine::String.
31
13
  module StringExtensions
32
- def self.included(base)
33
- HighLine::COLORS.each do |color|
34
- color = color.downcase
35
- base.class_eval <<-END
36
- undef :#{color} if method_defined? :#{color}
37
- def #{color}
38
- color(:#{color})
39
- end
40
- END
14
+ STYLE_METHOD_NAME_PATTERN = /^(on_)?rgb_([0-9a-fA-F]{6})$/
41
15
 
42
- base.class_eval <<-END
43
- undef :on_#{color} if method_defined? :on_#{color}
44
- def on_#{color}
45
- on(:#{color})
46
- end
47
- END
48
- HighLine::STYLES.each do |style|
49
- style = style.downcase
50
- base.class_eval <<-END
51
- undef :#{style} if method_defined? :#{style}
52
- def #{style}
53
- color(:#{style})
54
- end
55
- END
56
- end
57
- end
16
+ # Included hook. Actions to take when being included.
17
+ # @param base [Class, Module] base class
18
+ def self.included(base)
19
+ define_builtin_style_methods(base)
20
+ define_style_support_methods(base)
21
+ end
58
22
 
23
+ # At include time, it defines all basic style
24
+ # support method like #color, #on, #uncolor,
25
+ # #rgb, #on_rgb and the #method_missing callback
26
+ # @return [void]
27
+ # @param base [Class, Module] the base class/module
28
+ #
29
+ def self.define_style_support_methods(base)
59
30
  base.class_eval do
60
31
  undef :color if method_defined? :color
61
32
  undef :foreground if method_defined? :foreground
@@ -66,7 +37,7 @@ class HighLine
66
37
 
67
38
  undef :on if method_defined? :on
68
39
  def on(arg)
69
- color(('on_' + arg.to_s).to_sym)
40
+ color(("on_" + arg.to_s).to_sym)
70
41
  end
71
42
 
72
43
  undef :uncolor if method_defined? :uncolor
@@ -76,35 +47,83 @@ class HighLine
76
47
 
77
48
  undef :rgb if method_defined? :rgb
78
49
  def rgb(*colors)
79
- color_code = colors.map{|color| color.is_a?(Numeric) ? '%02x'%color : color.to_s}.join
80
- raise "Bad RGB color #{colors.inspect}" unless color_code =~ /^[a-fA-F0-9]{6}/
50
+ color_code = setup_color_code(*colors)
81
51
  color("rgb_#{color_code}".to_sym)
82
52
  end
83
53
 
84
54
  undef :on_rgb if method_defined? :on_rgb
85
55
  def on_rgb(*colors)
86
- color_code = colors.map{|color| color.is_a?(Numeric) ? '%02x'%color : color.to_s}.join
87
- raise "Bad RGB color #{colors.inspect}" unless color_code =~ /^[a-fA-F0-9]{6}/
56
+ color_code = setup_color_code(*colors)
88
57
  color("on_rgb_#{color_code}".to_sym)
89
58
  end
90
59
 
91
- # TODO Chain existing method_missing
60
+ # @todo Chain existing method_missing?
92
61
  undef :method_missing if method_defined? :method_missing
93
- def method_missing(method, *args, &blk)
94
- if method.to_s =~ /^(on_)?rgb_([0-9a-fA-F]{6})$/
62
+ def method_missing(method, *_args)
63
+ if method.to_s =~ STYLE_METHOD_NAME_PATTERN
95
64
  color(method)
96
65
  else
97
- raise NoMethodError, "undefined method `#{method}' for #<#{self.class}:#{'%#x'%self.object_id}>"
66
+ super
98
67
  end
99
68
  end
69
+
70
+ undef :respond_to_missing if method_defined? :respond_to_missing
71
+ def respond_to_missing?(method_name, include_private = false)
72
+ method_name.to_s =~ STYLE_METHOD_NAME_PATTERN || super
73
+ end
74
+
75
+ private
76
+
77
+ def setup_color_code(*colors)
78
+ color_code = colors.map do |color|
79
+ if color.is_a?(Numeric)
80
+ format("%02x", color)
81
+ else
82
+ color.to_s
83
+ end
84
+ end.join
85
+
86
+ raise "Bad RGB color #{colors.inspect}" unless
87
+ color_code =~ /^[a-fA-F0-9]{6}/
88
+
89
+ color_code
90
+ end
100
91
  end
101
92
  end
102
- end
103
93
 
104
- class HighLine::String < ::String
105
- include StringExtensions
94
+ # At include time, it defines all basic builtin styles.
95
+ # @param base [Class, Module] base Class/Module
96
+ def self.define_builtin_style_methods(base)
97
+ HighLine::COLORS.each do |color|
98
+ color = color.downcase
99
+ base.class_eval <<-METHOD_DEFINITION
100
+ undef :#{color} if method_defined? :#{color}
101
+ def #{color}
102
+ color(:#{color})
103
+ end
104
+ METHOD_DEFINITION
105
+
106
+ base.class_eval <<-METHOD_DEFINITION
107
+ undef :on_#{color} if method_defined? :on_#{color}
108
+ def on_#{color}
109
+ on(:#{color})
110
+ end
111
+ METHOD_DEFINITION
112
+
113
+ HighLine::STYLES.each do |style|
114
+ style = style.downcase
115
+ base.class_eval <<-METHOD_DEFINITION
116
+ undef :#{style} if method_defined? :#{style}
117
+ def #{style}
118
+ color(:#{style})
119
+ end
120
+ METHOD_DEFINITION
121
+ end
122
+ end
123
+ end
106
124
  end
107
125
 
126
+ # Adds color support to the base String class
108
127
  def self.colorize_strings
109
128
  ::String.send(:include, StringExtensions)
110
129
  end