highline 1.7.10 → 2.0.3

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