rspec_log_formatter 0.1.0 → 0.2.0

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 (84) hide show
  1. checksums.yaml +5 -13
  2. data/.gitignore +1 -1
  3. data/.ruby-version +1 -1
  4. data/README.md +53 -7
  5. data/bin/rspec_log_formatter +30 -0
  6. data/dummy/.gitignore +16 -0
  7. data/dummy/.rspec +1 -0
  8. data/dummy/Gemfile +47 -0
  9. data/dummy/README.rdoc +28 -0
  10. data/dummy/Rakefile +6 -0
  11. data/dummy/app/assets/images/.keep +0 -0
  12. data/dummy/app/assets/javascripts/application.js +16 -0
  13. data/dummy/app/assets/stylesheets/application.css +13 -0
  14. data/dummy/app/controllers/application_controller.rb +5 -0
  15. data/dummy/app/controllers/concerns/.keep +0 -0
  16. data/dummy/app/helpers/application_helper.rb +2 -0
  17. data/dummy/app/mailers/.keep +0 -0
  18. data/dummy/app/models/.keep +0 -0
  19. data/dummy/app/models/concerns/.keep +0 -0
  20. data/dummy/app/views/layouts/application.html.erb +14 -0
  21. data/dummy/bin/bundle +3 -0
  22. data/dummy/bin/rails +4 -0
  23. data/dummy/bin/rake +4 -0
  24. data/dummy/config/application.rb +23 -0
  25. data/dummy/config/boot.rb +4 -0
  26. data/dummy/config/database.yml +25 -0
  27. data/dummy/config/environment.rb +5 -0
  28. data/dummy/config/environments/development.rb +29 -0
  29. data/dummy/config/environments/production.rb +80 -0
  30. data/dummy/config/environments/test.rb +36 -0
  31. data/dummy/config/initializers/backtrace_silencers.rb +7 -0
  32. data/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  33. data/dummy/config/initializers/inflections.rb +16 -0
  34. data/dummy/config/initializers/mime_types.rb +5 -0
  35. data/dummy/config/initializers/secret_token.rb +12 -0
  36. data/dummy/config/initializers/session_store.rb +3 -0
  37. data/dummy/config/initializers/wrap_parameters.rb +14 -0
  38. data/dummy/config/locales/en.yml +23 -0
  39. data/dummy/config/routes.rb +56 -0
  40. data/dummy/config.ru +4 -0
  41. data/dummy/db/schema.rb +16 -0
  42. data/dummy/db/seeds.rb +7 -0
  43. data/dummy/lib/assets/.keep +0 -0
  44. data/dummy/lib/tasks/.keep +0 -0
  45. data/dummy/log/.keep +0 -0
  46. data/dummy/public/404.html +58 -0
  47. data/dummy/public/422.html +58 -0
  48. data/dummy/public/500.html +57 -0
  49. data/dummy/public/favicon.ico +0 -0
  50. data/dummy/public/robots.txt +5 -0
  51. data/dummy/spec/dummy_spec.rb +7 -0
  52. data/dummy/spec/spec_helper.rb +46 -0
  53. data/dummy/test/controllers/.keep +0 -0
  54. data/dummy/test/fixtures/.keep +0 -0
  55. data/dummy/test/helpers/.keep +0 -0
  56. data/dummy/test/integration/.keep +0 -0
  57. data/dummy/test/mailers/.keep +0 -0
  58. data/dummy/test/models/.keep +0 -0
  59. data/dummy/test/test_helper.rb +15 -0
  60. data/dummy/vendor/assets/javascripts/.keep +0 -0
  61. data/dummy/vendor/assets/stylesheets/.keep +0 -0
  62. data/lib/rspec_log_formatter/analysis/analyzer.rb +15 -50
  63. data/lib/rspec_log_formatter/analysis/pretty_printer.rb +10 -6
  64. data/lib/rspec_log_formatter/analysis/result.rb +8 -4
  65. data/lib/rspec_log_formatter/analysis/score.rb +42 -6
  66. data/lib/rspec_log_formatter/analyzer_formatter.rb +18 -6
  67. data/lib/rspec_log_formatter/formatter.rb +19 -12
  68. data/lib/rspec_log_formatter/history_manager.rb +46 -0
  69. data/lib/rspec_log_formatter/javascripts/chartkick.js +678 -0
  70. data/lib/rspec_log_formatter/performance_analyzer.rb +40 -0
  71. data/lib/rspec_log_formatter/templates/charts.html.erb +11 -0
  72. data/lib/rspec_log_formatter/version.rb +1 -1
  73. data/lib/rspec_log_formatter.rb +2 -1
  74. data/rspec_log_formatter.gemspec +1 -0
  75. data/spec/fixtures/test_slowing_down_over_time.history +7 -0
  76. data/spec/fixtures/varying_flakiness.history +9 -9
  77. data/spec/lib/rspec_log_analyzer/analysis/analyzer_spec.rb +25 -21
  78. data/spec/lib/rspec_log_analyzer/analysis/pretty_printer_spec.rb +10 -8
  79. data/spec/lib/rspec_log_analyzer/analyzer_formatter_spec.rb +3 -3
  80. data/spec/lib/rspec_log_analyzer/formatter_spec.rb +16 -18
  81. data/spec/lib/rspec_log_analyzer/history_manager_spec.rb +16 -0
  82. data/spec/lib/rspec_log_analyzer/performance_analyzer_spec.rb +16 -0
  83. data/specs.sh +4 -0
  84. metadata +95 -12
@@ -0,0 +1,46 @@
1
+ require 'csv'
2
+ require 'set'
3
+
4
+ module RspecLogFormatter
5
+ class HistoryManager
6
+ def initialize(filepath)
7
+ @filepath = filepath
8
+ end
9
+
10
+ def builds
11
+ results.map{|r| r.build_number.to_i}.reduce(SortedSet.new, &:<<).to_a
12
+ end
13
+
14
+ def truncate(number_of_builds)
15
+ kept_builds = builds.to_a.last(number_of_builds)
16
+ sio = StringIO.new
17
+
18
+ lines.each do |line|
19
+ sio.puts line if kept_builds.include? (parse(line).build_number.to_i)
20
+ end
21
+
22
+ sio.rewind
23
+
24
+ File.open(@filepath, 'w') do |f|
25
+ f.write sio.read
26
+ end
27
+ end
28
+
29
+ def results
30
+ lines.map do |line|
31
+ parse(line)
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def parse(line)
38
+ RspecLogFormatter::Analysis::Result.new(*CSV.parse_line(line, col_sep: "\t").first(8))
39
+ end
40
+
41
+
42
+ def lines
43
+ File.open(@filepath, 'r').lazy
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,678 @@
1
+ /*
2
+ * Chartkick.js
3
+ * Create beautiful Javascript charts with minimal code
4
+ * https://github.com/ankane/chartkick.js
5
+ * v1.1.1
6
+ * MIT License
7
+ */
8
+
9
+ /*jslint browser: true, indent: 2, plusplus: true, vars: true */
10
+ /*global google, Highcharts, $*/
11
+
12
+ (function () {
13
+ 'use strict';
14
+
15
+ var Chartkick, ISO8601_PATTERN, DECIMAL_SEPARATOR, defaultOptions, hideLegend,
16
+ setMin, setMax, setStacked, jsOptions, loaded, waitForLoaded, setBarMin, setBarMax, createDataTable, resize;
17
+
18
+ // only functions that need defined specific to charting library
19
+ var renderLineChart, renderPieChart, renderColumnChart, renderBarChart, renderAreaChart;
20
+
21
+ // helpers
22
+
23
+ function isArray(variable) {
24
+ return Object.prototype.toString.call(variable) === "[object Array]";
25
+ }
26
+
27
+ function isFunction(variable) {
28
+ return variable instanceof Function;
29
+ }
30
+
31
+ function isPlainObject(variable) {
32
+ return !isFunction(variable) && variable instanceof Object;
33
+ }
34
+
35
+ // https://github.com/madrobby/zepto/blob/master/src/zepto.js
36
+ function extend(target, source) {
37
+ var key;
38
+ for (key in source) {
39
+ if (isPlainObject(source[key]) || isArray(source[key])) {
40
+ if (isPlainObject(source[key]) && !isPlainObject(target[key])) {
41
+ target[key] = {};
42
+ }
43
+ if (isArray(source[key]) && !isArray(target[key])) {
44
+ target[key] = [];
45
+ }
46
+ extend(target[key], source[key]);
47
+ } else if (source[key] !== undefined) {
48
+ target[key] = source[key];
49
+ }
50
+ }
51
+ }
52
+
53
+ function merge(obj1, obj2) {
54
+ var target = {};
55
+ extend(target, obj1);
56
+ extend(target, obj2);
57
+ return target;
58
+ }
59
+
60
+ // https://github.com/Do/iso8601.js
61
+ ISO8601_PATTERN = /(\d\d\d\d)(\-)?(\d\d)(\-)?(\d\d)(T)?(\d\d)(:)?(\d\d)?(:)?(\d\d)?([\.,]\d+)?($|Z|([\+\-])(\d\d)(:)?(\d\d)?)/i;
62
+ DECIMAL_SEPARATOR = String(1.5).charAt(1);
63
+
64
+ function parseISO8601(input) {
65
+ var day, hour, matches, milliseconds, minutes, month, offset, result, seconds, type, year;
66
+ type = Object.prototype.toString.call(input);
67
+ if (type === '[object Date]') {
68
+ return input;
69
+ }
70
+ if (type !== '[object String]') {
71
+ return;
72
+ }
73
+ if (matches = input.match(ISO8601_PATTERN)) {
74
+ year = parseInt(matches[1], 10);
75
+ month = parseInt(matches[3], 10) - 1;
76
+ day = parseInt(matches[5], 10);
77
+ hour = parseInt(matches[7], 10);
78
+ minutes = matches[9] ? parseInt(matches[9], 10) : 0;
79
+ seconds = matches[11] ? parseInt(matches[11], 10) : 0;
80
+ milliseconds = matches[12] ? parseFloat(DECIMAL_SEPARATOR + matches[12].slice(1)) * 1000 : 0;
81
+ result = Date.UTC(year, month, day, hour, minutes, seconds, milliseconds);
82
+ if (matches[13] && matches[14]) {
83
+ offset = matches[15] * 60;
84
+ if (matches[17]) {
85
+ offset += parseInt(matches[17], 10);
86
+ }
87
+ offset *= matches[14] === '-' ? -1 : 1;
88
+ result -= offset * 60 * 1000;
89
+ }
90
+ return new Date(result);
91
+ }
92
+ }
93
+ // end iso8601.js
94
+
95
+ function negativeValues(series) {
96
+ var i, j, data;
97
+ for (i = 0; i < series.length; i++) {
98
+ data = series[i].data;
99
+ for (j = 0; j < data.length; j++) {
100
+ if (data[j][1] < 0) {
101
+ return true;
102
+ }
103
+ }
104
+ }
105
+ return false;
106
+ }
107
+
108
+ function jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked) {
109
+ return function (series, opts, chartOptions) {
110
+ var options = merge({}, defaultOptions);
111
+ options = merge(options, chartOptions || {});
112
+
113
+ // hide legend
114
+ // this is *not* an external option!
115
+ if (opts.hideLegend) {
116
+ hideLegend(options);
117
+ }
118
+
119
+ // min
120
+ if ("min" in opts) {
121
+ setMin(options, opts.min);
122
+ } else if (!negativeValues(series)) {
123
+ setMin(options, 0);
124
+ }
125
+
126
+ // max
127
+ if ("max" in opts) {
128
+ setMax(options, opts.max);
129
+ }
130
+
131
+ if (opts.stacked) {
132
+ setStacked(options);
133
+ }
134
+
135
+ // merge library last
136
+ options = merge(options, opts.library || {});
137
+
138
+ return options;
139
+ };
140
+ }
141
+
142
+ function setText(element, text) {
143
+ if (document.body.innerText) {
144
+ element.innerText = text;
145
+ } else {
146
+ element.textContent = text;
147
+ }
148
+ }
149
+
150
+ function chartError(element, message) {
151
+ setText(element, "Error Loading Chart: " + message);
152
+ element.style.color = "#ff0000";
153
+ }
154
+
155
+ function getJSON(element, url, success) {
156
+ jQuery.ajax({
157
+ dataType: "json",
158
+ url: url,
159
+ success: success,
160
+ error: function (jqXHR, textStatus, errorThrown) {
161
+ var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message;
162
+ chartError(element, message);
163
+ }
164
+ });
165
+ }
166
+
167
+ function errorCatcher(element, data, opts, callback) {
168
+ try {
169
+ callback(element, data, opts);
170
+ } catch (err) {
171
+ chartError(element, err.message);
172
+ throw err;
173
+ }
174
+ }
175
+
176
+ function fetchDataSource(element, dataSource, opts, callback) {
177
+ if (typeof dataSource === "string") {
178
+ getJSON(element, dataSource, function (data, textStatus, jqXHR) {
179
+ errorCatcher(element, data, opts, callback);
180
+ });
181
+ } else {
182
+ errorCatcher(element, dataSource, opts, callback);
183
+ }
184
+ }
185
+
186
+ // type conversions
187
+
188
+ function toStr(n) {
189
+ return "" + n;
190
+ }
191
+
192
+ function toFloat(n) {
193
+ return parseFloat(n);
194
+ }
195
+
196
+ function toDate(n) {
197
+ if (typeof n !== "object") {
198
+ if (typeof n === "number") {
199
+ n = new Date(n * 1000); // ms
200
+ } else { // str
201
+ // try our best to get the str into iso8601
202
+ // TODO be smarter about this
203
+ var str = n.replace(/ /, "T").replace(" ", "").replace("UTC", "Z");
204
+ n = parseISO8601(str) || new Date(n);
205
+ }
206
+ }
207
+ return n;
208
+ }
209
+
210
+ function toArr(n) {
211
+ if (!isArray(n)) {
212
+ var arr = [], i;
213
+ for (i in n) {
214
+ if (n.hasOwnProperty(i)) {
215
+ arr.push([i, n[i]]);
216
+ }
217
+ }
218
+ n = arr;
219
+ }
220
+ return n;
221
+ }
222
+
223
+ function sortByTime(a, b) {
224
+ return a[0].getTime() - b[0].getTime();
225
+ }
226
+
227
+ if ("Highcharts" in window) {
228
+
229
+ defaultOptions = {
230
+ chart: {},
231
+ xAxis: {
232
+ labels: {
233
+ style: {
234
+ fontSize: "12px"
235
+ }
236
+ }
237
+ },
238
+ yAxis: {
239
+ title: {
240
+ text: null
241
+ },
242
+ labels: {
243
+ style: {
244
+ fontSize: "12px"
245
+ }
246
+ }
247
+ },
248
+ title: {
249
+ text: null
250
+ },
251
+ credits: {
252
+ enabled: false
253
+ },
254
+ legend: {
255
+ borderWidth: 0
256
+ },
257
+ tooltip: {
258
+ style: {
259
+ fontSize: "12px"
260
+ }
261
+ },
262
+ plotOptions: {
263
+ areaspline: {},
264
+ series: {
265
+ marker: {}
266
+ }
267
+ }
268
+ };
269
+
270
+ hideLegend = function (options) {
271
+ options.legend.enabled = false;
272
+ };
273
+
274
+ setMin = function (options, min) {
275
+ options.yAxis.min = min;
276
+ };
277
+
278
+ setMax = function (options, max) {
279
+ options.yAxis.max = max;
280
+ };
281
+
282
+ setStacked = function (options) {
283
+ options.plotOptions.series.stacking = "normal";
284
+ };
285
+
286
+ jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked);
287
+
288
+ renderLineChart = function (element, series, opts, chartType) {
289
+ chartType = chartType || "spline";
290
+ var chartOptions = {};
291
+ if (chartType === "areaspline") {
292
+ chartOptions = {
293
+ plotOptions: {
294
+ areaspline: {
295
+ stacking: "normal"
296
+ },
297
+ series: {
298
+ marker: {
299
+ enabled: false
300
+ }
301
+ }
302
+ }
303
+ };
304
+ }
305
+ var options = jsOptions(series, opts, chartOptions), data, i, j;
306
+ options.xAxis.type = "datetime";
307
+ options.chart.type = chartType;
308
+ options.chart.renderTo = element.id;
309
+
310
+ for (i = 0; i < series.length; i++) {
311
+ data = series[i].data;
312
+ for (j = 0; j < data.length; j++) {
313
+ data[j][0] = data[j][0].getTime();
314
+ }
315
+ series[i].marker = {symbol: "circle"};
316
+ }
317
+ options.series = series;
318
+ new Highcharts.Chart(options);
319
+ };
320
+
321
+ renderPieChart = function (element, series, opts) {
322
+ var options = merge(defaultOptions, opts.library || {});
323
+ options.chart.renderTo = element.id;
324
+ options.series = [{
325
+ type: "pie",
326
+ name: "Value",
327
+ data: series
328
+ }];
329
+ new Highcharts.Chart(options);
330
+ };
331
+
332
+ renderColumnChart = function (element, series, opts, chartType) {
333
+ chartType = chartType || "column";
334
+ var options = jsOptions(series, opts), i, j, s, d, rows = [];
335
+ options.chart.type = chartType;
336
+ options.chart.renderTo = element.id;
337
+
338
+ for (i = 0; i < series.length; i++) {
339
+ s = series[i];
340
+
341
+ for (j = 0; j < s.data.length; j++) {
342
+ d = s.data[j];
343
+ if (!rows[d[0]]) {
344
+ rows[d[0]] = new Array(series.length);
345
+ }
346
+ rows[d[0]][i] = d[1];
347
+ }
348
+ }
349
+
350
+ var categories = [];
351
+ for (i in rows) {
352
+ if (rows.hasOwnProperty(i)) {
353
+ categories.push(i);
354
+ }
355
+ }
356
+ options.xAxis.categories = categories;
357
+
358
+ var newSeries = [];
359
+ for (i = 0; i < series.length; i++) {
360
+ d = [];
361
+ for (j = 0; j < categories.length; j++) {
362
+ d.push(rows[categories[j]][i] || 0);
363
+ }
364
+
365
+ newSeries.push({
366
+ name: series[i].name,
367
+ data: d
368
+ });
369
+ }
370
+ options.series = newSeries;
371
+
372
+ new Highcharts.Chart(options);
373
+ };
374
+
375
+ renderBarChart = function (element, series, opts) {
376
+ renderColumnChart(element, series, opts, "bar");
377
+ };
378
+
379
+ renderAreaChart = function (element, series, opts) {
380
+ renderLineChart(element, series, opts, "areaspline");
381
+ };
382
+ } else if ("google" in window) { // Google charts
383
+ // load from google
384
+ loaded = false;
385
+ google.setOnLoadCallback(function () {
386
+ loaded = true;
387
+ });
388
+ var loadOptions = {"packages": ["corechart"]};
389
+ var config = window.Chartkick || {};
390
+ if (config.language) {
391
+ loadOptions.language = config.language;
392
+ }
393
+ google.load("visualization", "1.0", loadOptions);
394
+
395
+ waitForLoaded = function (callback) {
396
+ google.setOnLoadCallback(callback); // always do this to prevent race conditions (watch out for other issues due to this)
397
+ if (loaded) {
398
+ callback();
399
+ }
400
+ };
401
+
402
+ // Set chart options
403
+ defaultOptions = {
404
+ chartArea: {},
405
+ fontName: "'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, Helvetica, sans-serif",
406
+ pointSize: 6,
407
+ legend: {
408
+ textStyle: {
409
+ fontSize: 12,
410
+ color: "#444"
411
+ },
412
+ alignment: "center",
413
+ position: "right"
414
+ },
415
+ curveType: "function",
416
+ hAxis: {
417
+ textStyle: {
418
+ color: "#666",
419
+ fontSize: 12
420
+ },
421
+ gridlines: {
422
+ color: "transparent"
423
+ },
424
+ baselineColor: "#ccc",
425
+ viewWindow: {}
426
+ },
427
+ vAxis: {
428
+ textStyle: {
429
+ color: "#666",
430
+ fontSize: 12
431
+ },
432
+ baselineColor: "#ccc",
433
+ viewWindow: {}
434
+ },
435
+ tooltip: {
436
+ textStyle: {
437
+ color: "#666",
438
+ fontSize: 12
439
+ }
440
+ }
441
+ };
442
+
443
+ hideLegend = function (options) {
444
+ options.legend.position = "none";
445
+ };
446
+
447
+ setMin = function (options, min) {
448
+ options.vAxis.viewWindow.min = min;
449
+ };
450
+
451
+ setMax = function (options, max) {
452
+ options.vAxis.viewWindow.max = max;
453
+ };
454
+
455
+ setBarMin = function (options, min) {
456
+ options.hAxis.viewWindow.min = min;
457
+ };
458
+
459
+ setBarMax = function (options, max) {
460
+ options.hAxis.viewWindow.max = max;
461
+ };
462
+
463
+ setStacked = function (options) {
464
+ options.isStacked = true;
465
+ };
466
+
467
+ jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked);
468
+
469
+ // cant use object as key
470
+ createDataTable = function (series, columnType) {
471
+ var data = new google.visualization.DataTable();
472
+ data.addColumn(columnType, "");
473
+
474
+ var i, j, s, d, key, rows = [];
475
+ for (i = 0; i < series.length; i++) {
476
+ s = series[i];
477
+ data.addColumn("number", s.name);
478
+
479
+ for (j = 0; j < s.data.length; j++) {
480
+ d = s.data[j];
481
+ key = (columnType === "datetime") ? d[0].getTime() : d[0];
482
+ if (!rows[key]) {
483
+ rows[key] = new Array(series.length);
484
+ }
485
+ rows[key][i] = toFloat(d[1]);
486
+ }
487
+ }
488
+
489
+ var rows2 = [];
490
+ for (i in rows) {
491
+ if (rows.hasOwnProperty(i)) {
492
+ rows2.push([(columnType === "datetime") ? new Date(toFloat(i)) : i].concat(rows[i]));
493
+ }
494
+ }
495
+ if (columnType === "datetime") {
496
+ rows2.sort(sortByTime);
497
+ }
498
+ data.addRows(rows2);
499
+
500
+ return data;
501
+ };
502
+
503
+ resize = function (callback) {
504
+ if (window.attachEvent) {
505
+ window.attachEvent("onresize", callback);
506
+ } else if (window.addEventListener) {
507
+ window.addEventListener("resize", callback, true);
508
+ }
509
+ callback();
510
+ };
511
+
512
+ renderLineChart = function (element, series, opts) {
513
+ waitForLoaded(function () {
514
+ var options = jsOptions(series, opts);
515
+ var data = createDataTable(series, "datetime");
516
+ var chart = new google.visualization.LineChart(element);
517
+ resize(function () {
518
+ chart.draw(data, options);
519
+ });
520
+ });
521
+ };
522
+
523
+ renderPieChart = function (element, series, opts) {
524
+ waitForLoaded(function () {
525
+ var chartOptions = {
526
+ chartArea: {
527
+ top: "10%",
528
+ height: "80%"
529
+ }
530
+ };
531
+ var options = merge(merge(defaultOptions, chartOptions), opts.library || {});
532
+
533
+ var data = new google.visualization.DataTable();
534
+ data.addColumn("string", "");
535
+ data.addColumn("number", "Value");
536
+ data.addRows(series);
537
+
538
+ var chart = new google.visualization.PieChart(element);
539
+ resize(function () {
540
+ chart.draw(data, options);
541
+ });
542
+ });
543
+ };
544
+
545
+ renderColumnChart = function (element, series, opts) {
546
+ waitForLoaded(function () {
547
+ var options = jsOptions(series, opts);
548
+ var data = createDataTable(series, "string");
549
+ var chart = new google.visualization.ColumnChart(element);
550
+ resize(function () {
551
+ chart.draw(data, options);
552
+ });
553
+ });
554
+ };
555
+
556
+ renderBarChart = function (element, series, opts) {
557
+ waitForLoaded(function () {
558
+ var chartOptions = {
559
+ hAxis: {
560
+ gridlines: {
561
+ color: "#ccc"
562
+ }
563
+ }
564
+ };
565
+ var options = jsOptionsFunc(defaultOptions, hideLegend, setBarMin, setBarMax, setStacked)(series, opts, chartOptions);
566
+ var data = createDataTable(series, "string");
567
+ var chart = new google.visualization.BarChart(element);
568
+ resize(function () {
569
+ chart.draw(data, options);
570
+ });
571
+ });
572
+ };
573
+
574
+ renderAreaChart = function (element, series, opts) {
575
+ waitForLoaded(function () {
576
+ var chartOptions = {
577
+ isStacked: true,
578
+ pointSize: 0,
579
+ areaOpacity: 0.5
580
+ };
581
+ var options = jsOptions(series, opts, chartOptions);
582
+ var data = createDataTable(series, "datetime");
583
+ var chart = new google.visualization.AreaChart(element);
584
+ resize(function () {
585
+ chart.draw(data, options);
586
+ });
587
+ });
588
+ };
589
+ } else { // no chart library installed
590
+ renderLineChart = renderPieChart = renderColumnChart = renderBarChart = renderAreaChart = function () {
591
+ throw new Error("Please install Google Charts or Highcharts");
592
+ };
593
+ }
594
+
595
+ // process data
596
+
597
+ function processSeries(series, opts, time) {
598
+ var i, j, data, r, key;
599
+
600
+ // see if one series or multiple
601
+ if (!isArray(series) || typeof series[0] !== "object" || isArray(series[0])) {
602
+ series = [{name: "Value", data: series}];
603
+ opts.hideLegend = true;
604
+ } else {
605
+ opts.hideLegend = false;
606
+ }
607
+
608
+ // right format
609
+ for (i = 0; i < series.length; i++) {
610
+ data = toArr(series[i].data);
611
+ r = [];
612
+ for (j = 0; j < data.length; j++) {
613
+ key = data[j][0];
614
+ key = time ? toDate(key) : toStr(key);
615
+ r.push([key, toFloat(data[j][1])]);
616
+ }
617
+ if (time) {
618
+ r.sort(sortByTime);
619
+ }
620
+ series[i].data = r;
621
+ }
622
+
623
+ return series;
624
+ }
625
+
626
+ function processLineData(element, data, opts) {
627
+ renderLineChart(element, processSeries(data, opts, true), opts);
628
+ }
629
+
630
+ function processColumnData(element, data, opts) {
631
+ renderColumnChart(element, processSeries(data, opts, false), opts);
632
+ }
633
+
634
+ function processPieData(element, data, opts) {
635
+ var perfectData = toArr(data), i;
636
+ for (i = 0; i < perfectData.length; i++) {
637
+ perfectData[i] = [toStr(perfectData[i][0]), toFloat(perfectData[i][1])];
638
+ }
639
+ renderPieChart(element, perfectData, opts);
640
+ }
641
+
642
+ function processBarData(element, data, opts) {
643
+ renderBarChart(element, processSeries(data, opts, false), opts);
644
+ }
645
+
646
+ function processAreaData(element, data, opts) {
647
+ renderAreaChart(element, processSeries(data, opts, true), opts);
648
+ }
649
+
650
+ function setElement(element, data, opts, callback) {
651
+ if (typeof element === "string") {
652
+ element = document.getElementById(element);
653
+ }
654
+ fetchDataSource(element, data, opts || {}, callback);
655
+ }
656
+
657
+ // define classes
658
+
659
+ Chartkick = {
660
+ LineChart: function (element, dataSource, opts) {
661
+ setElement(element, dataSource, opts, processLineData);
662
+ },
663
+ PieChart: function (element, dataSource, opts) {
664
+ setElement(element, dataSource, opts, processPieData);
665
+ },
666
+ ColumnChart: function (element, dataSource, opts) {
667
+ setElement(element, dataSource, opts, processColumnData);
668
+ },
669
+ BarChart: function (element, dataSource, opts) {
670
+ setElement(element, dataSource, opts, processBarData);
671
+ },
672
+ AreaChart: function (element, dataSource, opts) {
673
+ setElement(element, dataSource, opts, processAreaData);
674
+ }
675
+ };
676
+
677
+ window.Chartkick = Chartkick;
678
+ }());