highline 1.7.10 → 2.0.0.pre.develop.2

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