collimator 0.0.2 → 0.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.
@@ -0,0 +1,70 @@
1
+ module Collimator
2
+ module BarGraph
3
+
4
+ @bar_character = "\u2588"
5
+ @data = []
6
+ @options = {}
7
+
8
+ def self.clear_all
9
+ @bar_character = "\u2588"
10
+ @data = []
11
+ @options = {}
12
+ end
13
+
14
+ def self.clear_data
15
+ @data = []
16
+ end
17
+
18
+ def self.data=(data)
19
+ if data.is_a?(Array)
20
+ @data = data
21
+ elsif data.is_a?(Hash)
22
+ @data << data
23
+ end
24
+ end
25
+
26
+ def self.data
27
+ @data
28
+ end
29
+
30
+ def self.options
31
+ @options
32
+ end
33
+
34
+ def self.options=(options)
35
+ @options.merge!(options)
36
+ end
37
+
38
+ def self.plot(options = {})
39
+ self.options = options
40
+ header_width = max_header_length
41
+
42
+ @data.each do |data|
43
+ value = data[:value].to_i
44
+ header = data[:name].ljust(header_width)
45
+ bar = @bar_character * value
46
+
47
+ color = data.fetch(:color, 'cyan')
48
+
49
+ begin
50
+ bar = bar.send(color)
51
+ rescue NoMethodError
52
+ bar = bar
53
+ end
54
+
55
+ value = @options[:show_values] ? "#{value.to_s.rjust(3)}" : ''
56
+ full_output = " #{header} #{value} #{bar}"
57
+
58
+ puts full_output
59
+ end
60
+
61
+ end
62
+
63
+ private
64
+
65
+ def self.max_header_length
66
+ @data.map { |data| data[:name].length }.max
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,4 @@
1
+ module Collimator
2
+ class TooManyDataPoints < Exception
3
+ end
4
+ end
@@ -0,0 +1,62 @@
1
+ module Collimator
2
+ module ProgressBar
3
+ @display_width = 100
4
+ @max = 100
5
+ @min = 0
6
+ @method = :percent
7
+ @step_size = 5
8
+ @current = 0
9
+
10
+ def self.set_parameters(params)
11
+ @display_width = params[:display_width] if params.has_key?(:display_width)
12
+ @max = params[:max] if params.has_key?(:max)
13
+ @min = params[:min] if params.has_key?(:min)
14
+ @method = params[:method] if params.has_key?(:method)
15
+ @step_size = params[:step_size] if params.has_key?(:step_size)
16
+ end
17
+
18
+ def self.start(params = {})
19
+ set_parameters params
20
+ @current = @min
21
+ put_current_progress
22
+ end
23
+
24
+ def self.increment
25
+ @current += @step_size
26
+ put_current_progress unless @current > @max
27
+ end
28
+
29
+ def self.complete
30
+ clear
31
+ puts " "*(@display_width + 2)
32
+ end
33
+
34
+ def self.clear
35
+ print "\b"*(@display_width + 2)
36
+ end
37
+
38
+ private
39
+
40
+ def self.put_current_progress
41
+ clear
42
+ STDOUT.flush
43
+ print build_bar_string
44
+ #puts build_bar_string
45
+ end
46
+
47
+ def self.build_bar_string
48
+ units = @max - @min
49
+ unit_width = @display_width/units
50
+ percent_complete = @current / (units * 1.0)
51
+
52
+ s = '-'*(percent_complete * @display_width) + ' '*((100-percent_complete) * @display_width)
53
+ s1 = s[0..(@display_width/2 - 4)]
54
+ s2 = s[(@display_width/2 + 3)..(@display_width - 1)]
55
+
56
+ formatted_percent = "%0.1f" % (percent_complete * 100)
57
+
58
+ s = '|' + s1 + "#{formatted_percent}%".center(6) + s2 + '|'
59
+ #s = "#{units} - #{unit_width} - #{percent_complete} - #{@current}"
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,32 @@
1
+ module Collimator
2
+
3
+ module Spinner
4
+ @spinning = nil
5
+ @icons = ['-', '\\', '|', '/']
6
+
7
+ def self.spin
8
+ @spinning = Thread.new(@icons) do |myIcons|
9
+ i = 0
10
+ while true do
11
+ print myIcons[i]
12
+ STDOUT.flush
13
+ if i == myIcons.length - 1
14
+ i = 0
15
+ else
16
+ i += 1
17
+ end
18
+ sleep 0.1
19
+
20
+ print "\b"
21
+ STDOUT.flush
22
+ end
23
+ end
24
+ end
25
+
26
+ def self.stop
27
+ @spinning.exit
28
+ print "\b"
29
+ STDOUT.flush
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,484 @@
1
+
2
+ module Collimator
3
+
4
+ module Table
5
+
6
+ @horiz = '-'
7
+ @border = '|'
8
+ @corner = '+'
9
+ @columns = []
10
+ @rows = []
11
+ @headers = []
12
+ @footers = []
13
+ @column_names = []
14
+ @use_column_headings = false
15
+ @auto_clear = true
16
+ @separators = []
17
+ @live_update = false
18
+ @table_string = []
19
+ @use_capture_string = false
20
+ @use_capture_html = false
21
+ @last_header_color = '#EEEEEE'
22
+
23
+ def self.live_update=(live_upate)
24
+ @live_update = live_upate
25
+ end
26
+
27
+ def self.live_update
28
+ @live_update
29
+ end
30
+
31
+ def self.set_auto_clear(auto_clear = true)
32
+ @auto_clear = auto_clear
33
+ end
34
+
35
+ def self.set_corner(corner_character)
36
+ @corner = corner_character
37
+ end
38
+
39
+ def self.set_border(border_character)
40
+ @border = border_character
41
+ end
42
+
43
+ def self.set_horizontal(horizontal_character)
44
+ @horiz = horizontal_character
45
+ end
46
+
47
+ def self.header(text, opts = {})
48
+ width, padding, justification, color = parse_options(opts)
49
+
50
+ @headers << { :text => text, :padding => padding, :justification => justification , :color => color}
51
+ end
52
+
53
+ def self.footer(text, opts)
54
+ width, padding, justification = parse_options(opts)
55
+
56
+ @footers << { :text => text, :padding => padding, :justification => justification }
57
+ end
58
+
59
+ def self.column(heading, opts = {})
60
+ width, padding, justification, color = parse_options(opts)
61
+
62
+ @columns << { :width => width, :padding => padding, :justification => justification, :color => color }
63
+ @use_column_headings = true if heading.length > 0
64
+ @column_names << heading
65
+ end
66
+
67
+ def self.row(row)
68
+ colored_data_array = []
69
+ if row.class.to_s == "Hash"
70
+ colored_data_array = row[:data].map { |v| {:data => v, :color => row[:color] } }
71
+ else
72
+ colored_data_array = row.clone
73
+ end
74
+ if @live_update
75
+ put_line_of_data(colored_data_array)
76
+ else
77
+ @rows << colored_data_array
78
+ end
79
+ end
80
+
81
+ def self.separator
82
+ if @live_update
83
+ put_horizontal_line_with_dividers
84
+ else
85
+ @separators << @rows.length
86
+ end
87
+ end
88
+
89
+ def self.tabulate
90
+ put_header
91
+ put_column_heading_text
92
+ prep_data
93
+ put_table
94
+ put_horizontal_line
95
+ put_footer
96
+
97
+ clear_all if @auto_clear
98
+ end
99
+
100
+ def self.tabulate_to_string
101
+ @use_capture_html = false
102
+ @use_capture_string = true
103
+ @auto_clear = false
104
+ tabulate
105
+ @table_string.join("\n")
106
+ end
107
+
108
+ def self.tabulate_to_html
109
+ @use_capture_string = false
110
+ @use_capture_html = true
111
+ @auto_clear = false
112
+
113
+ prep_html_table
114
+ tabulate
115
+ complete_html_table
116
+
117
+ @table_string.join("\n")
118
+ end
119
+
120
+ def self.prep_html_table
121
+ @table_string = []
122
+ @table_string << "<table STYLE=\"font-family: helvetica, verdana, tahoma; border-collapse: collapse;\">"
123
+ end
124
+
125
+ def self.complete_html_table
126
+ @table_string << "</table>"
127
+ end
128
+
129
+ def self.start_live_update
130
+ put_header
131
+ put_column_heading_text
132
+ end
133
+
134
+ def self.complete_live_update
135
+ put_horizontal_line
136
+ put_footer
137
+
138
+ clear_all if @auto_clear
139
+ end
140
+
141
+ def self.csv
142
+ lines = []
143
+
144
+ lines.concat(@headers.map { |h| h[:text] }) if @headers.count > 0
145
+ lines << @column_names.join(',') if @use_column_headings
146
+ @rows.each { |row| lines << row.join(',') }
147
+ lines.concat(@footers.map { |h| h[:text] }) if @headers.count > 0
148
+
149
+ out = lines.join("\n")
150
+
151
+ clear_all if @auto_clear
152
+
153
+ out
154
+ end
155
+
156
+ def self.clear_all
157
+ @columns = []
158
+ @rows = []
159
+ @headers = []
160
+ @footers = []
161
+ @column_names = []
162
+ @use_column_headings = false
163
+ @horiz = '-'
164
+ @border = '|'
165
+ @corner = '+'
166
+ @auto_clear = true
167
+ @separators = []
168
+ @live_update = false
169
+ @table_string = []
170
+ @use_capture_string = false
171
+ @use_capture_html = false
172
+ end
173
+
174
+ def self.clear_data
175
+ @rows = []
176
+ @separators = []
177
+ end
178
+
179
+ private
180
+
181
+ def self.send_line(line)
182
+ if @use_capture_string || @use_capture_html
183
+ @table_string << line
184
+ else
185
+ puts line
186
+ end
187
+ end
188
+
189
+ def self.parse_options(opts)
190
+ width = opts.has_key?(:width) ? opts[:width] : 10
191
+ padding = opts.has_key?(:padding) ? opts[:padding] : 0
192
+ justification = opts.has_key?(:justification) ? opts[:justification] : :center
193
+ col_color = opts.has_key?(:color) ? opts[:color] : nil
194
+
195
+ padding = 0 if justification == :decimal
196
+
197
+ [width, padding, justification, col_color]
198
+ end
199
+
200
+ def self.put_table
201
+ put_horizontal_line if @headers.length == 0 and !@use_column_headings
202
+ row_number = 0
203
+ @table_string << "<tbody>" if @use_capture_html
204
+ @rows.each do |row|
205
+ put_horizontal_line_with_dividers if @separators.include?(row_number)
206
+ put_line_of_data(row)
207
+ row_number += 1
208
+ end
209
+ @table_string << "</tbody>" if @use_capture_html
210
+ end
211
+
212
+ def self.prep_data
213
+ column = 0
214
+ @columns.each do |c|
215
+ if c[:justification] == :decimal
216
+ column_width = c[:width]
217
+ column_center = column_width / 2
218
+ values = []
219
+ @rows.each do |r|
220
+ value = r[column].class.to_s == "Hash" ? r[column][:data] : r[column].to_s
221
+ color = r[column].class.to_s == "Hash" ? r[column][:color] : nil
222
+
223
+ v2 = value
224
+ decimal_place = v2.index('.') ? v2.index('.') : v2.length
225
+ v2 = ' '*(column_center -decimal_place) + v2
226
+ v2 = v2.ljust(column_width)
227
+
228
+ v2 = {:data => v2, :color => color} if color
229
+ values << v2
230
+ end
231
+
232
+ row = 0
233
+ @rows.each do |r|
234
+ r[column] = values[row]
235
+ row += 1
236
+ end
237
+ end
238
+
239
+ if c[:color]
240
+ @rows.each do |r|
241
+ value = ''
242
+ color = nil
243
+ if r[column].class.to_s == "Hash" # don't change the data point color if already set. single data point color overrides column color.
244
+ color = r[column][:color]
245
+ value = r[column][:data]
246
+ else
247
+ color = c[:color]
248
+ value = r[column]
249
+ end
250
+ r[column] = {:data => value, :color => color}
251
+ end
252
+ end
253
+ column += 1
254
+ end
255
+ end
256
+
257
+ def self.prep_data_orig
258
+ column = 0
259
+ @columns.each do |c|
260
+ if c[:justification] == :decimal
261
+ column_width = c[:width]
262
+ column_center = column_width / 2
263
+ values = []
264
+ length = 0
265
+ decimal_place = 0
266
+ @rows.each do |r|
267
+ value = r[column].class.to_s == "Hash" ? r[column][:data] : r[column].to_s
268
+ values << value
269
+ length = value.length if value.length > length
270
+ end
271
+ values = values.map { |v| v.ljust(length) }
272
+ values.each do |value|
273
+ decimal_place = value.index('.') if (value.index('.') and (value.index('.') > decimal_place))
274
+ end
275
+ values = values.map do |v|
276
+ place = v.index('.') ? v.index('.') : v.length
277
+ ' '*(decimal_place - place) + v
278
+ end
279
+
280
+ row = 0
281
+ @rows.each do |r|
282
+ r[column] = values[row]
283
+ row += 1
284
+ end
285
+ end
286
+
287
+ if c[:color]
288
+ @rows.each do |r|
289
+ value = ''
290
+ color = nil
291
+ if r[column].class.to_s == "Hash" # don't change the data point color if already set. single data point color overrides column color.
292
+ color = r[column][:color]
293
+ value = r[column][:data]
294
+ else
295
+ color = c[:color]
296
+ value = r[column]
297
+ end
298
+ r[column] = {:data => value, :color => color}
299
+ end
300
+ end
301
+ column += 1
302
+ end
303
+ end
304
+
305
+ def self.put_column_heading_text
306
+ @use_capture_html ? put_column_heading_text_html : put_column_heading_text_string if @use_column_headings
307
+ end
308
+
309
+ def self.put_column_heading_text_string
310
+ put_line_of_data(@column_names)
311
+ put_horizontal_line_with_dividers
312
+ end
313
+
314
+ def self.style_color(rgb)
315
+ luminance = get_luminance(rgb)
316
+ color = luminance < 50 ? '#EEEEEE' : '#222222'
317
+ color_style = "color: #{color};"
318
+ end
319
+
320
+ def self.style_header_border(rgb)
321
+ luminance = get_luminance(rgb)
322
+ color = luminance < 50 ? '#EEEEEE' : '#222222'
323
+ color_style = "border-bottom: 1px solid #{color};"
324
+ end
325
+
326
+ def self.get_luminance(rgb)
327
+ rgb_temp = rgb.gsub("#", '')
328
+ luminance = 0
329
+ if rgb_temp.length == 6
330
+ r = rgb_temp[0..1].hex
331
+ g = rgb_temp[2..3].hex
332
+ b = rgb_temp[4..5].hex
333
+ luminance = (0.299*r + 0.587*g + 0.114*b)
334
+ end
335
+ luminance
336
+ end
337
+
338
+ def self.put_column_heading_text_html
339
+ c = @last_header_color
340
+ text_color = style_color(c)
341
+ border_color = style_header_border(c)
342
+ out = "<tr STYLE=\"background-color: #{@last_header_color}; #{text_color} #{border_color}\">\n"
343
+ column = 0
344
+ @column_names.each do |cname|
345
+ padding_style = @columns[column][:padding] ? "STYLE=\"padding-left: #{@columns[column][:padding]}em; padding-right: #{@columns[column][:padding]}em;\"" : ""
346
+ out += "<th #{padding_style}>#{cname}</th>\n"
347
+ column += 1
348
+ end
349
+
350
+ out += "</tr>\n"
351
+ out += "</thead>"
352
+
353
+ send_line out
354
+ end
355
+
356
+ def self.put_header_or_footer(header_or_footer = :header)
357
+ data = header_or_footer == :footer ? @footers.clone : @headers.clone
358
+
359
+ unless @use_capture_html
360
+ if header_or_footer == :header and data.length > 0
361
+ put_horizontal_line
362
+ end
363
+ end
364
+
365
+ return if data.length == 0
366
+
367
+ @table_string << "<thead>" if @use_capture_html if header_or_footer == :header
368
+
369
+ data.each do | header |
370
+ send_line make_header_line(header)
371
+ end
372
+
373
+ put_horizontal_line
374
+ end
375
+
376
+ def self.make_header_line(data)
377
+ out = @use_capture_html ? make_header_line_html(data) : make_header_line_string(data)
378
+ out
379
+ end
380
+
381
+ def self.make_header_line_string(header)
382
+ header_width = line_width
383
+ header_line = @border
384
+ header_line += ' '*header[:padding] if header[:justification] == :left
385
+ header_line += header[:text].center(header_width - 2) if header[:justification] == :center
386
+ header_line += header[:text].ljust(header_width - header[:padding] - 2) if header[:justification] == :left
387
+ header_line += header[:text].rjust(header_width - header[:padding] - 2) if header[:justification] == :right
388
+ header_line += ' '*header[:padding] if header[:justification] == :right
389
+
390
+ header_line += @border
391
+ end
392
+
393
+ def self.make_header_line_html(data)
394
+ @last_header_color = data[:color] || @last_header_color
395
+
396
+ text_color = style_color(@last_header_color)
397
+ header_line = "<tr>"
398
+ header_line += "<th STYLE=\"background-color: #{@last_header_color}; #{text_color}\" colspan='#{@column_names.count + 1}'>#{data[:text]}</th>"
399
+ header_line += "</tr>"
400
+ header_line
401
+ end
402
+
403
+ def self.put_footer
404
+ put_header_or_footer(:footer) unless @use_capture_html
405
+ end
406
+
407
+ def self.put_header
408
+ put_header_or_footer(:header)
409
+ end
410
+
411
+ def self.line_width
412
+ @columns.length + 1 + @columns.inject(0) { |sum, c| sum + c[:width] + c[:padding] }
413
+ end
414
+
415
+ def self.format_data(value, width, padding, justification)
416
+ data_value = value.class.to_s == "Hash" ? value[:data] : value
417
+
418
+ s = ''
419
+ s = data_value.to_s
420
+ s = ' '*padding + s.ljust(width) if justification == :left
421
+ s = s.rjust(width) + ' '*padding if justification == :right
422
+ s = s.center(width) if justification == :center || justification == :decimal
423
+ #s = s.send(value[:color].to_s) if value.class.to_s == "Hash"
424
+ s = s.gsub(data_value.to_s, data_value.to_s.send(value[:color])) if value.class.to_s == "Hash"
425
+ s
426
+ end
427
+
428
+ def self.put_row_of_html_data(row_data)
429
+ column = 0
430
+ row_string = "<tr>\n"
431
+
432
+ row_data.each do | val |
433
+ style_info = @columns[column][:padding] ? " STYLE=\"padding-left: #{@columns[column][:padding]}em; padding-right: #{@columns[column][:padding]}em;\"" : ''
434
+ row_string += "<td#{style_info}>#{val}</td>\n"
435
+ column += 1
436
+ end
437
+
438
+ row_string += "</tr>"
439
+
440
+ send_line row_string
441
+ end
442
+
443
+ def self.put_row_of_string_data(row_data)
444
+ column = 0
445
+ row_string = @border
446
+
447
+ row_data.each do | val |
448
+ s = format_data(val, @columns[column][:width], @columns[column][:padding], @columns[column][:justification])
449
+ row_string = row_string + s + @border
450
+ column += 1
451
+ end
452
+
453
+ send_line row_string
454
+ end
455
+
456
+ def self.put_line_of_data(row_data)
457
+ raise TooManyDataPoints if row_data.count > @columns.length
458
+
459
+ row_data << '' while row_data.length < @columns.length
460
+
461
+ if @use_capture_html
462
+ put_row_of_html_data row_data
463
+ else
464
+ put_row_of_string_data row_data
465
+ end
466
+ end
467
+
468
+ def self.put_horizontal_line
469
+ unless @use_capture_html
470
+ width = line_width
471
+ send_line @corner + @horiz * (width - 2) + @corner
472
+ end
473
+ end
474
+
475
+ def self.put_horizontal_line_with_dividers
476
+ unless @use_capture_html
477
+ a = []
478
+ @columns.each { | c | a << @horiz*(c[:width] + c[:padding]) }
479
+ s = @border + a.join(@corner) + @border
480
+ send_line s
481
+ end
482
+ end
483
+ end
484
+ end