collimator 0.0.2 → 0.0.3

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