highline 1.7.10 → 2.0.0.pre.develop.2

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.simplecov +5 -0
  4. data/.travis.yml +11 -6
  5. data/Changelog.md +112 -20
  6. data/Gemfile +8 -7
  7. data/README.rdoc +3 -0
  8. data/Rakefile +7 -2
  9. data/appveyor.yml +19 -0
  10. data/examples/asking_for_arrays.rb +3 -0
  11. data/examples/basic_usage.rb +3 -0
  12. data/examples/get_character.rb +3 -0
  13. data/examples/limit.rb +3 -0
  14. data/examples/menus.rb +3 -0
  15. data/examples/overwrite.rb +3 -0
  16. data/examples/password.rb +3 -0
  17. data/examples/repeat_entry.rb +4 -1
  18. data/lib/highline.rb +182 -704
  19. data/lib/highline/builtin_styles.rb +109 -0
  20. data/lib/highline/color_scheme.rb +4 -1
  21. data/lib/highline/compatibility.rb +2 -0
  22. data/lib/highline/custom_errors.rb +19 -0
  23. data/lib/highline/import.rb +4 -2
  24. data/lib/highline/list.rb +93 -0
  25. data/lib/highline/list_renderer.rb +232 -0
  26. data/lib/highline/menu.rb +20 -20
  27. data/lib/highline/paginator.rb +43 -0
  28. data/lib/highline/question.rb +157 -97
  29. data/lib/highline/question/answer_converter.rb +84 -0
  30. data/lib/highline/question_asker.rb +147 -0
  31. data/lib/highline/simulate.rb +5 -1
  32. data/lib/highline/statement.rb +58 -0
  33. data/lib/highline/string.rb +34 -0
  34. data/lib/highline/string_extensions.rb +3 -28
  35. data/lib/highline/style.rb +18 -8
  36. data/lib/highline/template_renderer.rb +38 -0
  37. data/lib/highline/terminal.rb +78 -0
  38. data/lib/highline/terminal/io_console.rb +98 -0
  39. data/lib/highline/terminal/ncurses.rb +38 -0
  40. data/lib/highline/terminal/unix_stty.rb +94 -0
  41. data/lib/highline/version.rb +3 -1
  42. data/lib/highline/wrapper.rb +43 -0
  43. data/test/acceptance/acceptance.rb +62 -0
  44. data/test/acceptance/acceptance_test.rb +69 -0
  45. data/test/acceptance/at_color_output_using_erb_templates.rb +17 -0
  46. data/test/acceptance/at_echo_false.rb +23 -0
  47. data/test/acceptance/at_readline.rb +37 -0
  48. data/test/io_console_compatible.rb +37 -0
  49. data/test/string_methods.rb +3 -0
  50. data/test/test_answer_converter.rb +26 -0
  51. data/test/{tc_color_scheme.rb → test_color_scheme.rb} +7 -9
  52. data/test/test_helper.rb +26 -0
  53. data/test/{tc_highline.rb → test_highline.rb} +193 -136
  54. data/test/{tc_import.rb → test_import.rb} +5 -2
  55. data/test/test_list.rb +60 -0
  56. data/test/{tc_menu.rb → test_menu.rb} +6 -3
  57. data/test/test_paginator.rb +73 -0
  58. data/test/test_question_asker.rb +20 -0
  59. data/test/test_simulator.rb +24 -0
  60. data/test/test_string_extension.rb +72 -0
  61. data/test/{tc_string_highline.rb → test_string_highline.rb} +7 -3
  62. data/test/{tc_style.rb → test_style.rb} +70 -35
  63. data/test/test_wrapper.rb +188 -0
  64. metadata +57 -22
  65. data/lib/highline/system_extensions.rb +0 -254
  66. data/test/tc_simulator.rb +0 -33
  67. data/test/tc_string_extension.rb +0 -33
@@ -0,0 +1,109 @@
1
+ #coding: utf-8
2
+
3
+ class HighLine
4
+ module BuiltinStyles
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ STYLE_LIST = {
10
+ erase_line: "\e[K",
11
+ erase_char: "\e[P",
12
+ clear: "\e[0m",
13
+ reset: "\e[0m",
14
+ bold: "\e[1m",
15
+ dark: "\e[2m",
16
+ underline: "\e[4m",
17
+ underscore: "\e[4m",
18
+ blink: "\e[5m",
19
+ reverse: "\e[7m",
20
+ concealed: "\e[8m"
21
+ }
22
+
23
+ STYLE_LIST.each do |style_name, code|
24
+ style = String(style_name).upcase
25
+
26
+ const_set style, code
27
+ const_set style + "_STYLE", Style.new(name: style_name, code: code, builtin: true)
28
+ end
29
+
30
+ STYLES = %w{CLEAR RESET BOLD DARK UNDERLINE UNDERSCORE BLINK REVERSE CONCEALED}
31
+
32
+ COLOR_LIST = {
33
+ black: { code: "\e[30m", rgb: [0, 0, 0] },
34
+ red: { code: "\e[31m", rgb: [128, 0, 0] },
35
+ green: { code: "\e[32m", rgb: [0, 128, 0] },
36
+ blue: { code: "\e[34m", rgb: [0, 0, 128] },
37
+ yellow: { code: "\e[33m", rgb: [128, 128, 0] },
38
+ magenta: { code: "\e[35m", rgb: [128, 0, 128] },
39
+ cyan: { code: "\e[36m", rgb: [0, 128, 128] },
40
+ white: { code: "\e[37m", rgb: [192, 192, 192] },
41
+ gray: { code: "\e[37m", rgb: [192, 192, 192] },
42
+ grey: { code: "\e[37m", rgb: [192, 192, 192] },
43
+ none: { code: "\e[38m", rgb: [0, 0, 0] }
44
+ }
45
+
46
+ COLOR_LIST.each do |color_name, attributes|
47
+ color = String(color_name).upcase
48
+
49
+ style = Style.new(
50
+ name: color_name,
51
+ code: attributes[:code],
52
+ rgb: attributes[:rgb],
53
+ builtin: true
54
+ )
55
+
56
+ const_set color + "_STYLE", style
57
+ end
58
+
59
+ BASIC_COLORS = %w{BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE GRAY GREY NONE}
60
+
61
+ colors = BASIC_COLORS.dup
62
+ BASIC_COLORS.each do |color|
63
+ bright_color = "BRIGHT_#{color}"
64
+ colors << bright_color
65
+ const_set bright_color + '_STYLE', const_get(color + '_STYLE').bright
66
+
67
+ light_color = "LIGHT_#{color}"
68
+ colors << light_color
69
+ const_set light_color + '_STYLE', const_get(color + '_STYLE').light
70
+ end
71
+ COLORS = colors
72
+
73
+ colors.each do |color|
74
+ const_set color, const_get("#{color}_STYLE").code
75
+ const_set "ON_#{color}_STYLE", const_get("#{color}_STYLE").on
76
+ const_set "ON_#{color}", const_get("ON_#{color}_STYLE").code
77
+ end
78
+
79
+ ON_NONE_STYLE.rgb = [255,255,255] # Override; white background
80
+
81
+ module ClassMethods
82
+ RGB_COLOR = /^(ON_)?(RGB_)([A-F0-9]{6})(_STYLE)?$/
83
+
84
+ def const_missing(name)
85
+ if name.to_s =~ RGB_COLOR
86
+ on = $1
87
+ suffix = $4
88
+
89
+ if suffix
90
+ code_name = $1.to_s + $2 + $3
91
+ else
92
+ code_name = name.to_s
93
+ end
94
+
95
+ style_name = code_name + '_STYLE'
96
+ style = Style.rgb($3)
97
+ style = style.on if on
98
+
99
+ const_set(style_name, style)
100
+ const_set(code_name, style.code)
101
+
102
+ suffix ? style : style.code
103
+ else
104
+ raise NameError, "Bad color or uninitialized constant #{name}"
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -1,3 +1,6 @@
1
+ # coding: utf-8
2
+
3
+ #--
1
4
  # color_scheme.rb
2
5
  #
3
6
  # Created by Jeremy Hinegardner on 2007-01-24
@@ -48,7 +51,7 @@ class HighLine
48
51
  #
49
52
  def initialize( h = nil )
50
53
  @scheme = Hash.new
51
- load_from_hash(h) unless h.nil?
54
+ load_from_hash(h) if h
52
55
  yield self if block_given?
53
56
  end
54
57
 
@@ -1,3 +1,5 @@
1
+ # coding: utf-8
2
+
1
3
  unless STDIN.respond_to? :getbyte
2
4
  class IO
3
5
  alias_method :getbyte, :getc
@@ -0,0 +1,19 @@
1
+ class HighLine
2
+ module CustomErrors
3
+ # Internal HighLine errors.
4
+ class QuestionError < StandardError
5
+ end
6
+
7
+ class NotValidQuestionError < QuestionError
8
+ end
9
+
10
+ class NotInRangeQuestionError < QuestionError
11
+ end
12
+
13
+ class NoConfirmationQuestionError < QuestionError
14
+ end
15
+
16
+ class NoAutoCompleteMatch < StandardError
17
+ end
18
+ end
19
+ end
@@ -1,3 +1,5 @@
1
+ # coding: utf-8
2
+
1
3
  # import.rb
2
4
  #
3
5
  # Created by James Edward Gray II on 2005-04-26.
@@ -33,9 +35,9 @@ class Object
33
35
  #
34
36
  def or_ask( *args, &details )
35
37
  ask(*args) do |question|
36
- question.first_answer = String(self) unless nil?
38
+ question.first_answer = String(self)
37
39
 
38
- details.call(question) unless details.nil?
40
+ details.call(question) if details
39
41
  end
40
42
  end
41
43
  end
@@ -0,0 +1,93 @@
1
+ # coding: utf-8
2
+
3
+ class HighLine
4
+ class List
5
+ attr_reader :items, :cols
6
+ attr_reader :transpose_mode, :col_down_mode
7
+
8
+ def initialize(items, options = {})
9
+ @items = items
10
+ @transpose_mode = options.fetch(:transpose) { false }
11
+ @col_down_mode = options.fetch(:col_down) { false }
12
+ @cols = options.fetch(:cols) { 1 }
13
+ build
14
+ end
15
+
16
+ def transpose
17
+ first_row = @list[0]
18
+ other_rows = @list[1..-1]
19
+ @list = first_row.zip(*other_rows)
20
+ self
21
+ end
22
+
23
+ def col_down
24
+ slice_by_rows
25
+ transpose
26
+ self
27
+ end
28
+
29
+ def slice_by_rows
30
+ @list = items_sliced_by_rows
31
+ self
32
+ end
33
+
34
+ def slice_by_cols
35
+ @list = items_sliced_by_cols
36
+ self
37
+ end
38
+
39
+ def cols=(cols)
40
+ @cols = cols
41
+ build
42
+ end
43
+
44
+ def to_a
45
+ list
46
+ end
47
+
48
+ def list
49
+ @list.dup
50
+ end
51
+
52
+ def to_s
53
+ list.map { |row| stringfy(row) }.join
54
+ end
55
+
56
+ def row_join_string
57
+ @row_join_string ||= " "
58
+ end
59
+
60
+ def row_join_string=(string)
61
+ @row_join_string = string
62
+ end
63
+
64
+ def row_join_str_size
65
+ row_join_string.size
66
+ end
67
+
68
+ private
69
+
70
+ def build
71
+ slice_by_cols
72
+ transpose if transpose_mode
73
+ col_down if col_down_mode
74
+ self
75
+ end
76
+
77
+ def items_sliced_by_cols
78
+ items.each_slice(cols).to_a
79
+ end
80
+
81
+ def items_sliced_by_rows
82
+ items.each_slice(row_count).to_a
83
+ end
84
+
85
+ def row_count
86
+ (items.count / cols.to_f).ceil
87
+ end
88
+
89
+ def stringfy(row)
90
+ row.compact.join(row_join_string) + "\n"
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,232 @@
1
+ # coding: utf-8
2
+
3
+ require 'highline/template_renderer'
4
+ require 'highline/wrapper'
5
+ require 'highline/list'
6
+
7
+ class HighLine::ListRenderer
8
+ attr_reader :items, :mode, :option, :highline
9
+
10
+ def initialize(items, mode = :rows, option = nil, highline)
11
+ @highline = highline
12
+ @mode = mode
13
+ @option = option
14
+ @items = render_list_items(items)
15
+ end
16
+
17
+ #
18
+ # This method is a utility for quickly and easily laying out lists. It can
19
+ # be accessed within ERb replacements of any text that will be sent to the
20
+ # user.
21
+ #
22
+ # The only required parameter is _items_, which should be the Array of items
23
+ # to list. A specified _mode_ controls how that list is formed and _option_
24
+ # has different effects, depending on the _mode_. Recognized modes are:
25
+ #
26
+ # <tt>:columns_across</tt>:: _items_ will be placed in columns,
27
+ # flowing from left to right. If given,
28
+ # _option_ is the number of columns to be
29
+ # used. When absent, columns will be
30
+ # determined based on _wrap_at_ or a
31
+ # default of 80 characters.
32
+ # <tt>:columns_down</tt>:: Identical to <tt>:columns_across</tt>,
33
+ # save flow goes down.
34
+ # <tt>:uneven_columns_across</tt>:: Like <tt>:columns_across</tt> but each
35
+ # column is sized independently.
36
+ # <tt>:uneven_columns_down</tt>:: Like <tt>:columns_down</tt> but each
37
+ # column is sized independently.
38
+ # <tt>:inline</tt>:: All _items_ are placed on a single line.
39
+ # The last two _items_ are separated by
40
+ # _option_ or a default of " or ". All
41
+ # other _items_ are separated by ", ".
42
+ # <tt>:rows</tt>:: The default mode. Each of the _items_ is
43
+ # placed on its own line. The _option_
44
+ # parameter is ignored in this mode.
45
+ #
46
+ # Each member of the _items_ Array is passed through ERb and thus can contain
47
+ # their own expansions. Color escape expansions do not contribute to the
48
+ # final field width.
49
+ #
50
+ def render
51
+ return "" if items.empty?
52
+
53
+ case mode
54
+ when :inline
55
+ list_inline_mode
56
+ when :columns_across
57
+ list_columns_across_mode
58
+ when :columns_down
59
+ list_columns_down_mode
60
+ when :uneven_columns_across
61
+ list_uneven_columns_mode
62
+ when :uneven_columns_down
63
+ list_uneven_columns_down_mode
64
+ else
65
+ list_default_mode
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def render_list_items(items)
72
+ items.to_ary.map do |item|
73
+ item = String(item)
74
+ template = ERB.new(item, nil, "%")
75
+ template_renderer = HighLine::TemplateRenderer.new(template, self, highline)
76
+ template_renderer.render
77
+ end
78
+ end
79
+
80
+ def list_default_mode
81
+ items.map { |i| "#{i}\n" }.join
82
+ end
83
+
84
+ def list_inline_mode
85
+ end_separator = option || " or "
86
+
87
+ if items.size == 1
88
+ items.first
89
+ else
90
+ items[0..-2].join(", ") + "#{end_separator}#{items.last}"
91
+ end
92
+ end
93
+
94
+ def list_columns_across_mode
95
+ HighLine::List.new(right_padded_items, cols: col_count).to_s
96
+ end
97
+
98
+ def list_columns_down_mode
99
+ HighLine::List.new(right_padded_items, cols: col_count, col_down: true).to_s
100
+ end
101
+
102
+ def list_uneven_columns_mode(list=nil)
103
+ list ||= HighLine::List.new(items)
104
+
105
+ col_max = option || items.size
106
+ col_max.downto(1) do |column_count|
107
+ list.cols = column_count
108
+ widths = get_col_widths(list)
109
+
110
+ if column_count == 1 or # last guess
111
+ inside_line_size_limit?(widths) or # good guess
112
+ option # defined by user
113
+ return pad_uneven_rows(list, widths)
114
+ end
115
+ end
116
+ end
117
+
118
+ def list_uneven_columns_down_mode
119
+ list = HighLine::List.new(items, col_down: true)
120
+ list_uneven_columns_mode(list)
121
+ end
122
+
123
+ def pad_uneven_rows(list, widths)
124
+ right_padded_list = Array(list).map do |row|
125
+ right_pad_row(row.compact, widths)
126
+ end
127
+ stringfy_list(right_padded_list)
128
+ end
129
+
130
+ def stringfy_list(list)
131
+ list.map { |row| row_to_s(row) }.join
132
+ end
133
+
134
+ def row_to_s(row)
135
+ row.compact.join(row_join_string) + "\n"
136
+ end
137
+
138
+ def right_pad_row(row, widths)
139
+ row.zip(widths).map do |field, width|
140
+ right_pad_field(field, width)
141
+ end
142
+ end
143
+
144
+ def right_pad_field(field, width)
145
+ field = String(field) # nil protection
146
+ pad_size = width - actual_length(field)
147
+ field + (pad_char * pad_size)
148
+ end
149
+
150
+ def get_col_widths(lines)
151
+ lines = transpose(lines)
152
+ get_segment_widths(lines)
153
+ end
154
+
155
+ def get_row_widths(lines)
156
+ get_segment_widths(lines)
157
+ end
158
+
159
+ def get_segment_widths(lines)
160
+ lines.map do |col|
161
+ actual_lengths_for(col).max
162
+ end
163
+ end
164
+
165
+ def actual_lengths_for(line)
166
+ line.map do |item|
167
+ actual_length(item)
168
+ end
169
+ end
170
+
171
+ def transpose(lines)
172
+ lines = Array(lines)
173
+ first_line = lines.shift
174
+ first_line.zip(*lines)
175
+ end
176
+
177
+ def inside_line_size_limit?(widths)
178
+ line_size = widths.inject(0) { |sum, n| sum + n + row_join_str_size }
179
+ line_size <= line_size_limit + row_join_str_size
180
+ end
181
+
182
+ def actual_length(text)
183
+ HighLine::Wrapper.actual_length text
184
+ end
185
+
186
+ def items_max_length
187
+ @items_max_length ||= max_length(items)
188
+ end
189
+
190
+ def max_length(items)
191
+ items.map { |item| actual_length(item) }.max
192
+ end
193
+
194
+ def line_size_limit
195
+ @line_size_limit ||= ( highline.wrap_at || 80 )
196
+ end
197
+
198
+ def row_join_string
199
+ @row_join_string ||= " "
200
+ end
201
+
202
+ def row_join_string=(string)
203
+ @row_join_string = string
204
+ end
205
+
206
+ def row_join_str_size
207
+ row_join_string.size
208
+ end
209
+
210
+ def get_col_count
211
+ (line_size_limit + row_join_str_size) /
212
+ (items_max_length + row_join_str_size)
213
+ end
214
+
215
+ def col_count
216
+ option || get_col_count
217
+ end
218
+
219
+ def right_padded_items
220
+ items.map do |item|
221
+ right_pad_field(item, items_max_length)
222
+ end
223
+ end
224
+
225
+ def pad_char
226
+ " "
227
+ end
228
+
229
+ def row_count
230
+ (items.count / col_count.to_f).ceil
231
+ end
232
+ end