color-console 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013, Adam Gardiner
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
17
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,76 @@
1
+ # ColorConsole
2
+
3
+ ColorConsole is a small cross-platform library for outputting text to the console.
4
+
5
+
6
+ ## Usage
7
+
8
+ ColorConsole is supplied as a gem, and has no dependencies. To use it, simply:
9
+ ```
10
+ gem install color-console
11
+ ```
12
+
13
+ ColorConsole provides methods for outputting lines of text in different colors, using the `Console.write` and `Console.puts` functions.
14
+
15
+ ```ruby
16
+ require 'color-console'
17
+
18
+ Console.puts "Some text" # Outputs text using the current console colours
19
+ Console.puts "Some other text", :red # Outputs red text with the current background
20
+ Console.puts "Yet more text", nil, :blue # Outputs text using the current foreground and a blue background
21
+
22
+ # The following lines output BlueRedGreen on a single line, each word in the appropriate color
23
+ Console.write "Blue ", :blue
24
+ Console.write "Red ", :red
25
+ Console.write "Green", :green
26
+ ```
27
+
28
+ ## Features
29
+
30
+ In addition to `Console.puts` and `Console.write` for outputting text in color, ColorConsole also supports:
31
+ * __Setting the console title__: The title bar of the console window can be set using `Console.title = 'My title'`.
32
+ * __Status messages__: Status messages (i.e. a line of text at the current scroll position) can be output and
33
+ updated at any time. The status message will remain at the current scroll point even as new text is output
34
+ using `Console.puts`.
35
+ * __Progress bars__: A progress bar can be rendered like a status message, but with a pseudo-graphical representation
36
+ of the current completion percentage:
37
+
38
+ ```ruby
39
+ (0..100).do |i|
40
+ Console.show_progress('Processing data', i)
41
+ end
42
+ Console.clear_progress
43
+ ```
44
+ Output:
45
+ ```
46
+ [============== 35% ] Processing data
47
+ ```
48
+ * __Tables__: Data can be output in a tabular representation:
49
+
50
+ ```ruby
51
+ HEADER_ROW = ['Column 1', 'Column 2', 'Column 3', 'Column 4']
52
+ MIXED_ROW = [17,
53
+ 'A somewhat longer column',
54
+ 'A very very very long column that should wrap multple lines',
55
+ 'Another medium length column']
56
+ SECOND_ROW = [24,
57
+ 'Lorem ipsum',
58
+ 'Some more text',
59
+ 'Lorem ipsum dolor sit amet']
60
+
61
+ Console.display_table([HEADER_ROW, MIXED_ROW, SECOND_ROW], width: 100,
62
+ col_sep: '|', row_sep: '-')
63
+ ```
64
+ Output:
65
+ ```
66
+ +----------+--------------------------+-----------------------------+-----------------------------+
67
+ | Column 1 | Column 2 | Column 3 | Column 4 |
68
+ +----------+--------------------------+-----------------------------+-----------------------------+
69
+ | 17 | A somewhat longer column | A very very very long | Another medium length |
70
+ | | | column that should wrap | column |
71
+ | | | multple lines | |
72
+ +----------+--------------------------+-----------------------------+-----------------------------+
73
+ | 24 | Lorem ipsum | Some more text | Lorem ipsum dolor sit amet |
74
+ +----------+--------------------------+-----------------------------+-----------------------------+
75
+ ```
76
+
@@ -0,0 +1,2 @@
1
+ require 'console/console'
2
+
@@ -0,0 +1,2 @@
1
+ require 'console/console'
2
+
@@ -0,0 +1,135 @@
1
+ # Require the platform specific functionality
2
+ if Gem.win_platform?
3
+ require_relative 'platform/windows'
4
+ else
5
+ require_relative 'platform/ansi'
6
+ end
7
+
8
+
9
+ # Implements cross-platform functionality for working with a console window to
10
+ # provide color and progress-bar functionality.
11
+ module Console
12
+
13
+ attr_reader :status, :status_enabled
14
+
15
+
16
+ # Mutex used to ensure we don't intermingle output from multiple threads
17
+ @lock = Mutex.new
18
+
19
+
20
+ # Returns the width of the console window
21
+ #
22
+ # @return the width in characters, or nil if no console is available.
23
+ def width
24
+ sz = _window_size
25
+ sz && sz.first
26
+ end
27
+ module_function :width
28
+
29
+
30
+ # Returns the height of the console window
31
+ #
32
+ # @return the height in characters, or nil if no console is available.
33
+ def height
34
+ sz = _window_size
35
+ sz && sz.last
36
+ end
37
+ module_function :height
38
+
39
+
40
+ # Writes a partital line of text to the console, with optional foreground
41
+ # and background colors. No line-feed is output.
42
+ #
43
+ # @see #puts
44
+ #
45
+ # @param text [String] The text to be written to the console.
46
+ # @param fg [Symbol] An optional foreground colour name or value.
47
+ # @param bg [Symbol] An optional background color name or value.
48
+ def write(text, fg = nil, bg = nil)
49
+ @lock.synchronize do
50
+ _write(text, fg, bg)
51
+ end
52
+ end
53
+ module_function :write
54
+
55
+
56
+ # Send a line of text to the screen, terminating with a new-line.
57
+ #
58
+ # @see #write
59
+ #
60
+ # @param text [String] The optional text to be written to the console.
61
+ # @param fg [Symbol] An optional foreground colour name or value.
62
+ # @param bg [Symbol] An optional background color name or value.
63
+ def puts(text = nil, fg = nil, bg = nil)
64
+ @lock.synchronize do
65
+ _puts(text, fg, bg)
66
+ end
67
+ end
68
+ module_function :puts
69
+
70
+
71
+
72
+ # Utility method for wrapping lines of +text+ at +width+ characters.
73
+ #
74
+ # @param text [Object] a string of text that is to be wrapped to a
75
+ # maximum width. If +text+ is not a String, #to_s is called to convert it.
76
+ # @param width [Integer] the maximum length of each line of text.
77
+ # @return [Array] an Array of lines of text, each no longer than +width+
78
+ # characters.
79
+ def wrap_text(text, width)
80
+ text = text.to_s
81
+ if width > 0 && (text.length > width || text.index("\n"))
82
+ lines = []
83
+ start, nl_pos, ws_pos, wb_pos, end_pos = 0, 0, 0, 0, text.rindex(/[^\s]/)
84
+ while start < end_pos
85
+ last_start = start
86
+ nl_pos = text.index("\n", start)
87
+ ws_pos = text.rindex(/ +/, start + width)
88
+ wb_pos = text.rindex(/[\-,.;#)}\]\/\\]/, start + width - 1)
89
+ ### Debug code ###
90
+ #STDERR.puts self
91
+ #ind = ' ' * end_pos
92
+ #ind[start] = '('
93
+ #ind[start+width < end_pos ? start+width : end_pos] = ']'
94
+ #ind[nl_pos] = 'n' if nl_pos
95
+ #ind[wb_pos] = 'b' if wb_pos
96
+ #ind[ws_pos] = 's' if ws_pos
97
+ #STDERR.puts ind
98
+ ### End debug code ###
99
+ if nl_pos && nl_pos <= start + width
100
+ lines << text[start...nl_pos].strip
101
+ start = nl_pos + 1
102
+ elsif end_pos < start + width
103
+ lines << text[start..end_pos]
104
+ start = end_pos
105
+ elsif ws_pos && ws_pos > start && ((wb_pos.nil? || ws_pos > wb_pos) ||
106
+ (wb_pos && wb_pos > 5 && wb_pos - 5 < ws_pos))
107
+ lines << text[start...ws_pos]
108
+ start = text.index(/[^\s]/, ws_pos + 1)
109
+ elsif wb_pos && wb_pos > start
110
+ lines << text[start..wb_pos]
111
+ start = wb_pos + 1
112
+ else
113
+ lines << text[start...(start+width)]
114
+ start += width
115
+ end
116
+ if start <= last_start
117
+ # Detect an infinite loop, and just return the original text
118
+ STDERR.puts "Inifinite loop detected at #{__FILE__}:#{__LINE__}"
119
+ STDERR.puts " width: #{width}, start: #{start}, nl_pos: #{nl_pos}, " +
120
+ "ws_pos: #{ws_pos}, wb_pos: #{wb_pos}"
121
+ return [text]
122
+ end
123
+ end
124
+ lines
125
+ else
126
+ [text]
127
+ end
128
+ end
129
+ module_function :wrap_text
130
+
131
+ end
132
+
133
+ require_relative 'progress'
134
+ require_relative 'table'
135
+
@@ -0,0 +1,134 @@
1
+ module Console
2
+
3
+ FOREGROUND_COLORS = {
4
+ black: '30',
5
+ blue: '34',
6
+ dark_blue: '2;34',
7
+ light_blue: '1;34',
8
+ cyan: '36',
9
+ green: '32',
10
+ dark_green: '2;32',
11
+ light_green: '1;32',
12
+ red: '31',
13
+ dark_red: '2;31',
14
+ light_red: '1;31',
15
+ magenta: '35',
16
+ dark_magenta: '2;35',
17
+ light_magenta: '1;35',
18
+ yellow: '33',
19
+ gray: '37',
20
+ dark_gray: '2;37',
21
+ light_gray: '37',
22
+ white: '1;37'
23
+ }
24
+
25
+ BACKGROUND_COLORS = {
26
+ black: '40',
27
+ blue: '44',
28
+ dark_blue: '2;44',
29
+ light_blue: '1;44',
30
+ cyan: '46',
31
+ green: '42',
32
+ dark_green: '2;42',
33
+ light_green: '1;42',
34
+ red: '41',
35
+ dark_red: '2;41',
36
+ light_red: '1;41',
37
+ magenta: '45',
38
+ dark_magenta: '2;45',
39
+ light_magenta: '1;45',
40
+ yellow: '43',
41
+ gray: '47',
42
+ dark_gray: '2;47',
43
+ light_gray: '47',
44
+ white: '1;47'
45
+ }
46
+
47
+
48
+ # Sets the title bar text of the console window.
49
+ def title=(text)
50
+ STDOUT.write "\e]0;#{text}\007"
51
+ end
52
+ module_function :title=
53
+
54
+
55
+ private
56
+
57
+
58
+ # Get the current console window size.
59
+ #
60
+ # @return [Array, nil] Returns a two-dimensional array of [cols, rows], or
61
+ # nil if the console has been redirected.
62
+ def _window_size
63
+ unless @window_size
64
+ rows = `tput lines`
65
+ cols = `tput cols`
66
+ @window_size = [cols.chomp.to_i, rows.chomp.to_i]
67
+ end
68
+ @window_size
69
+ end
70
+ module_function :_window_size
71
+
72
+
73
+ # Write a line of text to the console, with optional foreground and
74
+ # background colors.
75
+ #
76
+ # @param text [String] The text to be written to the console.
77
+ # @param fg [Symbol, String] An optional foreground colour name or ANSI code.
78
+ # @param bg [Symbol, String] An optional background color name or ANSI code.
79
+ def _write(text, fg = nil, bg = nil)
80
+ if fg || bg
81
+ reset = true
82
+ if fg
83
+ fg_code = FOREGROUND_COLORS[fg] || fg
84
+ STDOUT.write "\e[#{fg_code}m"
85
+ end
86
+
87
+ if bg
88
+ bg_code = BACKGROUND_COLORS[bg] || bg
89
+ STDOUT.write "\e[#{bg_code}m"
90
+ end
91
+ end
92
+
93
+ STDOUT.write text
94
+
95
+ if reset
96
+ STDOUT.write "\e[0m"
97
+ end
98
+ end
99
+ module_function :_write
100
+
101
+
102
+ # Send a line of text to the screen, terminating with a new-line.
103
+ #
104
+ # @param text [String] The optional text to be written to the console.
105
+ # @param fg [Symbol, String] An optional foreground colour name or ANSI code.
106
+ # @param bg [Symbol, String] An optional background color name or ANSI code.
107
+ def _puts(text = nil, fg = nil, bg = nil)
108
+ if @status
109
+ _clear_line (@status.length / self.width) + 1
110
+ end
111
+ _write("#{text}", fg, bg)
112
+ STDOUT.write "\n"
113
+ if @status
114
+ _write @status, @status_fg, @status_bg
115
+ end
116
+ end
117
+ module_function :_puts
118
+
119
+
120
+ # Clears the current +lines+ line(s)
121
+ #
122
+ # @param lines [Fixnum] Number of lines to clear
123
+ def _clear_line(lines = 1)
124
+ raise ArgumentError, "Number of lines to clear (#{lines}) must be > 0" if lines < 1
125
+ while lines > 0
126
+ STDOUT.write "\r\e[2K"
127
+ lines -= 1
128
+ STDOUT.write "\e[A" if lines > 0
129
+ end
130
+ end
131
+ module_function :_clear_line
132
+
133
+ end
134
+
@@ -0,0 +1,253 @@
1
+ require 'ffi'
2
+
3
+
4
+ module Console
5
+
6
+ # Implements Windows-specific platform functionality
7
+ module Windows
8
+
9
+ extend FFI::Library
10
+
11
+ ffi_lib 'kernel32.dll'
12
+ ffi_convention :stdcall
13
+
14
+
15
+ # FFI structure used to get/set information about the current console
16
+ # window buffer
17
+ class BufferInfo < FFI::Struct
18
+ layout :width, :short,
19
+ :height, :short,
20
+ :cursor_x, :short,
21
+ :cursor_y, :short,
22
+ :text_attributes, :ushort,
23
+ :window_left, :short,
24
+ :window_top, :short,
25
+ :window_right, :short,
26
+ :window_bottom, :short,
27
+ :max_width, :short,
28
+ :max_height, :short
29
+ end
30
+
31
+
32
+ # FFI structure used to get/set buffer co-ordinates
33
+ class Coord < FFI::Struct
34
+ layout :x, :short,
35
+ :y, :short
36
+
37
+ def initialize(x, y)
38
+ self[:x] = x
39
+ self[:y] = y
40
+ end
41
+ end
42
+
43
+ # Define Windows console functions we need
44
+ attach_function :get_std_handle, :GetStdHandle, [:uint], :pointer
45
+ attach_function :get_console_screen_buffer_info, :GetConsoleScreenBufferInfo, [:pointer, :pointer], :bool
46
+ attach_function :set_console_cursor_position, :SetConsoleCursorPosition, [:pointer, Coord.by_value], :bool
47
+ attach_function :set_console_text_attribute, :SetConsoleTextAttribute, [:pointer, :ushort], :bool
48
+ attach_function :set_console_title, :SetConsoleTitleA, [:pointer], :bool
49
+
50
+
51
+ # Constants representing STDIN, STDOUT, and STDERR
52
+ STD_OUTPUT_HANDLE = 0xFFFFFFF5
53
+ STD_INPUT_HANDLE = 0xFFFFFFF6
54
+ STD_ERROR_HANDLE = 0xFFFFFFF7
55
+
56
+
57
+ # Retrieve a handle to STDOUT
58
+ def stdout
59
+ @stdout ||= self.get_std_handle(STD_OUTPUT_HANDLE)
60
+ end
61
+ module_function :stdout
62
+ private :stdout
63
+
64
+
65
+ # Retrieve a BufferInfo object
66
+ def buffer_info
67
+ @buffer_info ||= BufferInfo.new
68
+ end
69
+ module_function :buffer_info
70
+ private :buffer_info
71
+
72
+
73
+ # Populate a BufferInfo structure with details about the current
74
+ # buffer state.
75
+ #
76
+ # @return [BufferInfo] A BufferInfo structure containing fields for
77
+ # various bits of console state.
78
+ def get_buffer_info
79
+ if stdout
80
+ self.get_console_screen_buffer_info(stdout, buffer_info)
81
+ @buffer_info
82
+ end
83
+ end
84
+ module_function :get_buffer_info
85
+
86
+
87
+ # Sets the console foreground and background colors.
88
+ def set_color(color)
89
+ if stdout && color
90
+ self.set_console_text_attribute(stdout, color)
91
+ end
92
+ end
93
+ module_function :set_color
94
+
95
+
96
+ # Sets the cursor position to the specified +x+ and +y+ locations in the
97
+ # console output buffer. If +y+ is nil, the cursor is positioned at +x+ on
98
+ # the current line.
99
+ def set_cursor_position(x, y)
100
+ if stdout && x && y
101
+ coord = Coord.new(x, y)
102
+ self.set_console_cursor_position(stdout, coord)
103
+ end
104
+ end
105
+ module_function :set_cursor_position
106
+
107
+ end
108
+
109
+
110
+ # Constants for colour components
111
+ BLUE = 0x1
112
+ GREEN = 0x2
113
+ RED = 0x4
114
+ INTENSITY = 0x8
115
+
116
+ # Constants for foreground and background colors
117
+ FOREGROUND_COLORS = {
118
+ black: 0,
119
+ blue: BLUE | INTENSITY,
120
+ dark_blue: BLUE,
121
+ light_blue: BLUE | INTENSITY,
122
+ cyan: BLUE | GREEN | INTENSITY,
123
+ green: GREEN,
124
+ dark_green: GREEN,
125
+ light_green: GREEN | INTENSITY,
126
+ red: RED | INTENSITY,
127
+ dark_red: RED,
128
+ light_red: RED | INTENSITY,
129
+ magenta: RED | BLUE,
130
+ dark_magenta: RED | BLUE,
131
+ light_magenta: RED | BLUE | INTENSITY,
132
+ yellow: GREEN | RED | INTENSITY,
133
+ gray: BLUE | GREEN | RED,
134
+ dark_gray: INTENSITY,
135
+ light_gray: BLUE | GREEN | RED,
136
+ white: BLUE | GREEN | RED | INTENSITY
137
+ }
138
+ BACKGROUND_COLORS = {}
139
+ FOREGROUND_COLORS.each{ |k, c| BACKGROUND_COLORS[k] = c << 4 }
140
+
141
+
142
+ # Sets the title bar text of the console window.
143
+ def title=(text)
144
+ Windows.set_console_title(text)
145
+ end
146
+ module_function :title=
147
+
148
+
149
+ private
150
+
151
+
152
+ # Save the reset text and background colors
153
+ buffer = Windows.get_buffer_info
154
+ @reset_colors = buffer && buffer[:text_attributes]
155
+
156
+
157
+ # Get the current console window size.
158
+ #
159
+ # @return [Array, nil] Returns a two-dimensional array of [cols, rows], or
160
+ # nil if the console has been redirected.
161
+ def _window_size
162
+ unless @window_size
163
+ buffer = Windows.get_buffer_info
164
+ if buffer
165
+ if buffer[:window_right] > 0 && buffer[:window_bottom] > 0
166
+ @window_size = [buffer[:window_right] - buffer[:window_left] + 1,
167
+ buffer[:window_bottom] - buffer[:window_top] + 1]
168
+ else
169
+ @window_size = -1
170
+ end
171
+ else
172
+ @window_size = -1
173
+ end
174
+ end
175
+ @window_size == -1 ? nil : @window_size
176
+ end
177
+ module_function :_window_size
178
+
179
+
180
+ # Write a line of text to the console, with optional foreground and
181
+ # background colors.
182
+ #
183
+ # @param text [String] The text to be written to the console.
184
+ # @param fg [Symbol, Integer] An optional foreground colour name or value.
185
+ # @param bg [Symbol, Integer] An optional background color name or value.
186
+ def _write(text, fg = nil, bg = nil)
187
+ if fg || bg
188
+ reset = @reset_colors
189
+ if fg
190
+ fg_code = FOREGROUND_COLORS[fg] || fg
191
+ unless fg_code >= 0 && fg_code <= 0x0F
192
+ raise ArgumentError, "Text color must be a recognised symbol or int"
193
+ end
194
+ else
195
+ fg_code = reset & 0x0F
196
+ end
197
+
198
+ if bg
199
+ bg_code = BACKGROUND_COLORS[bg] || bg
200
+ unless bg_code >= 0 && bg_code <= 0xF0
201
+ raise ArgumentError, "Background color must be a recognised symbol or int"
202
+ end
203
+ else
204
+ bg_code = reset & 0xF0
205
+ end
206
+ Windows.set_color(fg_code | bg_code)
207
+ end
208
+
209
+ STDOUT.write text
210
+
211
+ if reset
212
+ Windows.set_color(reset)
213
+ end
214
+ end
215
+ module_function :_write
216
+
217
+
218
+ # Send a line of text to the screen, terminating with a new-line.
219
+ #
220
+ # @param text [String] The optional text to be written to the console.
221
+ # @param fg [Symbol, Integer] An optional foreground colour name or value.
222
+ # @param bg [Symbol, Integer] An optional background color name or value.
223
+ def _puts(text = nil, fg = nil, bg = nil)
224
+ if @status
225
+ _clear_line (@status.length / self.width) + 1
226
+ end
227
+ _write("#{text}\r\n", fg, bg)
228
+ if @status
229
+ _write(@status, @status_fg, @status_bg)
230
+ end
231
+ end
232
+ module_function :_puts
233
+
234
+
235
+ # Clears the current line
236
+ def _clear_line(lines = 1)
237
+ raise ArgumentError, "Number of lines to clear (#{lines}) must be > 0" if lines < 1
238
+ buffer = Windows.get_buffer_info
239
+ if buffer
240
+ y = buffer[:cursor_y]
241
+ while lines > 0
242
+ Windows.set_cursor_position(0, y)
243
+ STDOUT.write ' ' * (buffer[:window_right] - buffer[:window_left])
244
+ Windows.set_cursor_position(0, y)
245
+ lines -= 1
246
+ y -= 1
247
+ end
248
+ end
249
+ end
250
+ module_function :_clear_line
251
+
252
+ end
253
+
@@ -0,0 +1,70 @@
1
+ module Console
2
+
3
+ # Sets text to be displayed temporarily on the current line. Status text can
4
+ # be updated or cleared.
5
+ #
6
+ # @param status [String] The text to be displayed as the current status.
7
+ # Pass +nil+ to clear the status display.
8
+ # @params opts [Hash] Options to control the colour of the status display.
9
+ # @option opts [Symbol] :text_color The text color to use when displaying
10
+ # the status message.
11
+ # @option opts [Symbol] :background_color The background color to use when
12
+ # rendering the status message
13
+ def status(msg, opts = {})
14
+ if self.width
15
+ @lock.synchronize do
16
+ if @status
17
+ # Clear existing status
18
+ _clear_line (@status.length / self.width) + 1
19
+ end
20
+ @completed = nil
21
+ @status = msg
22
+ if @status
23
+ @status_fg = opts.fetch(:text_color, opts.fetch(:color, :cyan))
24
+ @status_bg = opts[:background_color]
25
+ _write @status, @status_fg, @status_bg
26
+ end
27
+ end
28
+ end
29
+ end
30
+ module_function :status
31
+
32
+
33
+ # Displays a progress bar as the current status line. The status line is a
34
+ # partial line of text printed at the current scroll location, and which
35
+ # can be updated or cleared.
36
+ #
37
+ # @param label [String] The label to be displayed after the progress bar.
38
+ # @param complete [Fixnum] Number of completed steps.
39
+ # @param opts [Fixnum, Hash] If a Fixnum is passed, this is the total number
40
+ # of steps. If a Hash is passed, it is an options hash with the following
41
+ # possible options.
42
+ # @option opts [Fixnum] :total The total number of steps; default is 100.
43
+ # @see #status for other supported options
44
+ def show_progress(label, complete, opts = {})
45
+ if self.width
46
+ opts = {total: opts} if opts.is_a?(Fixnum)
47
+ total = opts.fetch(:total, 100)
48
+ complete = total if complete > total
49
+ bar_length = opts.fetch(:bar_length, 40)
50
+ completion = complete * bar_length / total
51
+ pct = "#{complete * 100 / total}%"
52
+ bar = "#{'=' * completion}#{' ' * (bar_length - completion)}"
53
+ bar[(bar_length - pct.length) / 2, pct.length] = pct
54
+ if @completed.nil? || pct != @completed
55
+ self.status("[#{bar}] #{label}", opts)
56
+ @completed = pct
57
+ end
58
+ end
59
+ end
60
+ module_function :show_progress
61
+
62
+
63
+ # Clears any currently displayed progress bar or status message.
64
+ def clear_progress
65
+ self.status nil
66
+ end
67
+ alias_method :clear_status, :clear_progress
68
+ module_function :clear_progress, :clear_status
69
+
70
+ end
@@ -0,0 +1,155 @@
1
+ module Console
2
+
3
+ # Displays an array of arrays as a table of data. The content of each row
4
+ # is aligned and wrapped if necessary to fit the column widths.
5
+ #
6
+ # @param rows [Array] An array of arrays containing the data to be output.
7
+ # The number of elements in the first array determines the number of
8
+ # columns that will be output, unless the :column_widths options is passed;
9
+ # in that case, the number of column width specifications determines the
10
+ # number of columns output.
11
+ # @param opts [Hash] An options hash containing settings that determine how
12
+ # the table is rendered.
13
+ # @options opts [Array<Fixnum>] :col_widths The width to use for each column.
14
+ # @option opts [Fixnum] :width The width of the table. If omitted, the width
15
+ # is determined instead by the :col_widths option.
16
+ # @options opts [Char] :col_sep The character to use between each column.
17
+ # If omitted, no column line is drawn. Note though that each column is
18
+ # rendered with 1 space of padding on the left and right.
19
+ # @options opts [Char] :row_sep The character to use to render a row
20
+ # separator. If omitted, no row separators are rendered.
21
+ # @options opts [Fixnum] :indent The number of characters to indent the
22
+ # table from the left margin of the console. If omitted, no indent is
23
+ # used.
24
+ # @options opts [Symbol] :color The color in which to render the text of the
25
+ # table.
26
+ # @options opts [Symbol] :text_color The color in which to render the text
27
+ # of the table.
28
+ # @options opts [Symbol] :background_color The color in which to render the
29
+ # background of the table.
30
+ def display_table(rows, opts = {})
31
+ return unless rows && rows.size > 0 && rows.first.size > 0
32
+ col_widths = opts[:col_widths]
33
+ unless col_widths
34
+ col_count = rows.first.size
35
+ avail_width = (opts[:width] || ((width || 10000) - opts.fetch(:indent, 0))) -
36
+ (" #{opts[:col_sep]} ".length * col_count)
37
+ col_widths = _calculate_widths(rows, col_count, avail_width - 1)
38
+ end
39
+
40
+ @lock.synchronize do
41
+ _output_row_sep(col_widths, opts) if opts[:row_sep]
42
+ rows.each do |row|
43
+ _display_row(row, col_widths, opts)
44
+ end
45
+ end
46
+ end
47
+ module_function :display_table
48
+
49
+
50
+ # Displays a single +row+ of data within columns of +widths+ width. If the
51
+ # contents of a cell exceeds the available width, it is wrapped, and the row
52
+ # is displayed over multiple lines.
53
+ #
54
+ # @param row [Array] An array of data to display as a single row.
55
+ # @param widths [Array<Fixnum>] An array of column widths to use for each
56
+ # column.
57
+ # @param opts [Hash] An options hash containing settings that determine how
58
+ # the table is rendered.
59
+ # @see #display_table for details of the supported options.
60
+ def display_row(row, widths, opts = {})
61
+ return unless row && row.size > 0
62
+ @lock.synchronize do
63
+ _display_row(row, widths, opts)
64
+ end
65
+ end
66
+ module_function :display_row
67
+
68
+
69
+ private
70
+
71
+
72
+ # Calculates the widths to use to display a table of data.
73
+ def _calculate_widths(rows, col_count, avail_width)
74
+ max_widths = Array.new(col_count)
75
+ rows.each do |row|
76
+ row.each_with_index do |col, i|
77
+ max_widths[i] = col.to_s.length if !max_widths[i] || col.to_s.length > max_widths[i]
78
+ end
79
+ end
80
+ max_width = max_widths.reduce(0, &:+)
81
+ while avail_width < max_width
82
+ # Reduce longest width(s) to next longest
83
+ longest = max_widths.max
84
+ num_longest = max_widths.count{ |w| w == longest }
85
+ next_longest = max_widths.select{ |w| w < longest }.max
86
+ if !next_longest || (num_longest * (longest - next_longest) > max_width - avail_width)
87
+ reduction = (max_width - avail_width) / num_longest
88
+ reduction = 1 if reduction <= 0
89
+ if !next_longest
90
+ max_widths.map!{ |w| w - reduction }
91
+ else
92
+ max_widths.map!{ |w| w > next_longest ? w - reduction : w }
93
+ end
94
+ else
95
+ max_widths.map!{ |w| w > next_longest ? next_longest : w }
96
+ end
97
+ max_width = max_widths.reduce(0, &:+)
98
+ end
99
+ max_widths
100
+ end
101
+ module_function :_calculate_widths
102
+
103
+
104
+ # Displays a single +row+ of data within columns of +widths+ width. If the
105
+ # contents of a cell exceeds the available width, it is wrapped, and the row
106
+ # is displayed over multiple lines.
107
+ def _display_row(row, widths, opts = {})
108
+ fg = opts.fetch(:text_color, opts.fetch(:color, :cyan))
109
+ bg = opts[:background_color]
110
+ indent = opts.fetch(:indent, 0)
111
+ col_sep = opts[:col_sep]
112
+ row_sep = opts[:row_sep]
113
+
114
+ line_count = 0
115
+ lines = row.each_with_index.map do |col, i|
116
+ cell_lines = wrap_text(col, widths[i])
117
+ line_count = cell_lines.size if cell_lines.size > line_count
118
+ cell_lines
119
+ end
120
+ (0...line_count).each do |i|
121
+ _write(' ' * indent)
122
+ _write("#{col_sep} ", fg, bg) if col_sep
123
+ line = (0...widths.size).map do |col|
124
+ "%#{row[col].is_a?(Numeric) ? '' : '-'}#{widths[col]}s" %
125
+ (lines[col] && lines[col][i])
126
+ end.join(" #{col_sep} ")
127
+ _write(line, fg, bg)
128
+ _write(" #{col_sep}", fg, bg) if col_sep
129
+ _puts
130
+ end
131
+ _output_row_sep(widths, opts) if row_sep
132
+ end
133
+ module_function :_display_row
134
+
135
+
136
+ # Outputs a row separator line
137
+ def _output_row_sep(widths, opts)
138
+ fg = opts.fetch(:text_color, opts.fetch(:color, :cyan))
139
+ bg = opts[:background_color]
140
+ indent = opts.fetch(:indent, 0)
141
+ col_sep = opts[:col_sep]
142
+ row_sep = opts[:row_sep]
143
+ corner = opts.fetch(:corner, col_sep ? '+' * col_sep.length : '')
144
+
145
+ sep_row = widths.map{ |width| row_sep * (width + 2) }
146
+ _write(' ' * indent)
147
+ _write(corner, fg, bg)
148
+ _write(sep_row.join(corner), fg, bg)
149
+ _write(corner, fg, bg)
150
+ _puts
151
+ end
152
+ module_function :_output_row_sep
153
+
154
+ end
155
+
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: color-console
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Adam Gardiner
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-22 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ! " ColorConsole supports cross-platform (ANSI and Windows) colored
15
+ text output to the console.\n It also provides useful methods for building
16
+ command-line interfaces that provide status\n messages and progress bars.\n"
17
+ email: adam.b.gardiner@gmail.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - README.md
23
+ - LICENSE
24
+ - lib/color-console.rb
25
+ - lib/color_console.rb
26
+ - lib/console/console.rb
27
+ - lib/console/platform/ansi.rb
28
+ - lib/console/platform/windows.rb
29
+ - lib/console/progress.rb
30
+ - lib/console/table.rb
31
+ homepage: https://github.com/agardiner/color-console
32
+ licenses: []
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project:
51
+ rubygems_version: 1.8.21
52
+ signing_key:
53
+ specification_version: 3
54
+ summary: ColorConsole is a cross-platform library for outputting colored text to the
55
+ console
56
+ test_files: []