chartkick 1.3.2 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of chartkick might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ca23cf2a483dec9fdb551db05a0026cebaffb654
4
- data.tar.gz: 787086c3fa0287b611c5895c1fe5c43902347b05
3
+ metadata.gz: 9e4271e7d959b8fcad6ab2a8265231d64f16c3c8
4
+ data.tar.gz: 96e3d4ef110a67385e2973357cf390ec334db12b
5
5
  SHA512:
6
- metadata.gz: 6a4dcfde3aeb6f4068cda287772d4d36d3280eb88bbc9b64172b8d678f34319ffc89ab55e50b7e88ae38bd3041e9f56c26826ca151ad6880c9cbefba6518d96c
7
- data.tar.gz: 2a3ee87316b2aa0c8faef093a042c8a7126d13b8b96adacec8623cb331465e8b26dc10a22c5c2a0cf546c0236d3dbba0e2ea49d7517fc0d28a46e8706df0f370
6
+ metadata.gz: 5811e2cd0fc00aaa8f49686d001f965283296a6e75ebd3ec80251c9b67063ffd79b8e08e57474bec3924518fec3570a73da3663b8ec54c9b14e42605f976b2ab
7
+ data.tar.gz: 17f0605334a89a8a29bf16f3ea5534806d2b6eb6ede00bffc577f1e785764984fe32f391595fb62a283dd897155d29d2ec4f502fe9481b6377263fd9dfc4805a
@@ -1,3 +1,8 @@
1
+ ## 1.4.0
2
+
3
+ - Added scatter chart
4
+ - Added axis titles
5
+
1
6
  ## 1.3.2
2
7
 
3
8
  - Fixed `except` error when not using Rails
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in chartkick.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -8,7 +8,7 @@ Works with Rails, Sinatra and most browsers (including IE 6)
8
8
 
9
9
  :two_hearts: A perfect companion to [groupdate](https://github.com/ankane/groupdate), [hightop](https://github.com/ankane/hightop), and [active_median](https://github.com/ankane/active_median)
10
10
 
11
- :speech_balloon: Get [handcrafted updates](http://chartkick.us7.list-manage.com/subscribe?u=952c861f99eb43084e0a49f98&id=6ea6541e8e&group[0][1]=true) for new features
11
+ :speech_balloon: Get [handcrafted updates](http://chartkick.us7.list-manage.com/subscribe?u=952c861f99eb43084e0a49f98&id=6ea6541e8e&group[0][2]=true) for new features
12
12
 
13
13
  ## Charts
14
14
 
@@ -42,6 +42,12 @@ Area chart
42
42
  <%= area_chart Visit.group_by_minute(:created_at).maximum(:load_time) %>
43
43
  ```
44
44
 
45
+ Scatter chart
46
+
47
+ ```erb
48
+ <%= scatter_chart City.pluck(:size, :population) %>
49
+ ```
50
+
45
51
  Geo chart
46
52
 
47
53
  ```erb
@@ -132,6 +138,12 @@ Discrete axis
132
138
  <%= line_chart data, discrete: true %>
133
139
  ```
134
140
 
141
+ Axis titles
142
+
143
+ ```erb
144
+ <%= line_chart data, xtitle: "Time", ytitle: "Population" %>
145
+ ```
146
+
135
147
  You can pass options directly to the charting library with:
136
148
 
137
149
  ```erb
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
3
 
4
- task :default => :test
4
+ task default: :test
5
5
  Rake::TestTask.new do |t|
6
6
  t.libs << "test"
7
7
  t.pattern = "test/**/*_test.rb"
@@ -2,7 +2,7 @@
2
2
  * Chartkick.js
3
3
  * Create beautiful Javascript charts with minimal code
4
4
  * https://github.com/ankane/chartkick.js
5
- * v1.2.2
5
+ * v1.4.0
6
6
  * MIT License
7
7
  */
8
8
 
@@ -101,7 +101,7 @@
101
101
  return false;
102
102
  }
103
103
 
104
- function jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked) {
104
+ function jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle) {
105
105
  return function (series, opts, chartOptions) {
106
106
  var options = merge({}, defaultOptions);
107
107
  options = merge(options, chartOptions || {});
@@ -113,14 +113,14 @@
113
113
  }
114
114
 
115
115
  // min
116
- if ("min" in opts) {
116
+ if (opts.min) {
117
117
  setMin(options, opts.min);
118
118
  } else if (!negativeValues(series)) {
119
119
  setMin(options, 0);
120
120
  }
121
121
 
122
122
  // max
123
- if ("max" in opts) {
123
+ if (opts.max) {
124
124
  setMax(options, opts.max);
125
125
  }
126
126
 
@@ -132,6 +132,14 @@
132
132
  options.colors = opts.colors;
133
133
  }
134
134
 
135
+ if (opts.xtitle) {
136
+ setXtitle(options, opts.xtitle);
137
+ }
138
+
139
+ if (opts.ytitle) {
140
+ setYtitle(options, opts.ytitle);
141
+ }
142
+
135
143
  // merge library last
136
144
  options = merge(options, opts.library || {});
137
145
 
@@ -231,9 +239,14 @@
231
239
  var HighchartsAdapter = new function () {
232
240
  var Highcharts = window.Highcharts;
233
241
 
242
+ this.name = "highcharts";
243
+
234
244
  var defaultOptions = {
235
245
  chart: {},
236
246
  xAxis: {
247
+ title: {
248
+ text: null
249
+ },
237
250
  labels: {
238
251
  style: {
239
252
  fontSize: "12px"
@@ -288,7 +301,15 @@
288
301
  options.plotOptions.series.stacking = "normal";
289
302
  };
290
303
 
291
- var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked);
304
+ var setXtitle = function (options, title) {
305
+ options.xAxis.title.text = title;
306
+ };
307
+
308
+ var setYtitle = function (options, title) {
309
+ options.yAxis.title.text = title;
310
+ };
311
+
312
+ var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle);
292
313
 
293
314
  this.renderLineChart = function (chart, chartType) {
294
315
  chartType = chartType || "spline";
@@ -326,6 +347,15 @@
326
347
  new Highcharts.Chart(options);
327
348
  };
328
349
 
350
+ this.renderScatterChart = function (chart) {
351
+ var chartOptions = {};
352
+ var options = jsOptions(chart.data, chart.options, chartOptions);
353
+ options.chart.type = 'scatter';
354
+ options.chart.renderTo = chart.element.id;
355
+ options.series = chart.data;
356
+ new Highcharts.Chart(options);
357
+ };
358
+
329
359
  this.renderPieChart = function (chart) {
330
360
  var chartOptions = {};
331
361
  if (chart.options.colors) {
@@ -401,6 +431,8 @@
401
431
  var GoogleChartsAdapter = new function () {
402
432
  var google = window.google;
403
433
 
434
+ this.name = "google";
435
+
404
436
  var loaded = {};
405
437
  var callbacks = [];
406
438
 
@@ -408,7 +440,7 @@
408
440
  var cb, call;
409
441
  for (var i = 0; i < callbacks.length; i++) {
410
442
  cb = callbacks[i];
411
- call = google.visualization && ((cb.pack == "corechart" && google.visualization.LineChart) || (cb.pack == "timeline" && google.visualization.Timeline))
443
+ call = google.visualization && ((cb.pack === "corechart" && google.visualization.LineChart) || (cb.pack === "timeline" && google.visualization.Timeline))
412
444
  if (call) {
413
445
  cb.callback();
414
446
  callbacks.splice(i, 1);
@@ -461,6 +493,7 @@
461
493
  color: "#666",
462
494
  fontSize: 12
463
495
  },
496
+ titleTextStyle: {},
464
497
  gridlines: {
465
498
  color: "transparent"
466
499
  },
@@ -472,6 +505,7 @@
472
505
  color: "#666",
473
506
  fontSize: 12
474
507
  },
508
+ titleTextStyle: {},
475
509
  baselineColor: "#ccc",
476
510
  viewWindow: {}
477
511
  },
@@ -507,7 +541,17 @@
507
541
  options.isStacked = true;
508
542
  };
509
543
 
510
- var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked);
544
+ var setXtitle = function (options, title) {
545
+ options.hAxis.title = title;
546
+ options.hAxis.titleTextStyle.italic = false;
547
+ }
548
+
549
+ var setYtitle = function (options, title) {
550
+ options.vAxis.title = title;
551
+ options.vAxis.titleTextStyle.italic = false;
552
+ };
553
+
554
+ var jsOptions = jsOptionsFunc(defaultOptions, hideLegend, setMin, setMax, setStacked, setXtitle, setYtitle);
511
555
 
512
556
  // cant use object as key
513
557
  var createDataTable = function (series, columnType) {
@@ -530,9 +574,17 @@
530
574
  }
531
575
 
532
576
  var rows2 = [];
577
+ var value;
533
578
  for (i in rows) {
534
579
  if (rows.hasOwnProperty(i)) {
535
- rows2.push([(columnType === "datetime") ? new Date(toFloat(i)) : i].concat(rows[i]));
580
+ if (columnType === "datetime") {
581
+ value = new Date(toFloat(i));
582
+ } else if (columnType === "number") {
583
+ value = toFloat(i);
584
+ } else {
585
+ value = i;
586
+ }
587
+ rows2.push([value].concat(rows[i]));
536
588
  }
537
589
  }
538
590
  if (columnType === "datetime") {
@@ -655,6 +707,19 @@
655
707
  });
656
708
  };
657
709
 
710
+ this.renderScatterChart = function (chart) {
711
+ waitForLoaded(function () {
712
+ var chartOptions = {};
713
+ var options = jsOptions(chart.data, chart.options, chartOptions);
714
+ var data = createDataTable(chart.data, "number");
715
+
716
+ chart.chart = new google.visualization.ScatterChart(chart.element);
717
+ resize(function () {
718
+ chart.chart.draw(data, options);
719
+ });
720
+ });
721
+ };
722
+
658
723
  this.renderTimeline = function (chart) {
659
724
  waitForLoaded("timeline", function () {
660
725
  var chartOptions = {
@@ -662,7 +727,7 @@
662
727
  };
663
728
 
664
729
  if (chart.options.colors) {
665
- chartOptions.colorAxis.colors = chart.options.colors;
730
+ chartOptions.colors = chart.options.colors;
666
731
  }
667
732
  var options = merge(merge(defaultOptions, chartOptions), chart.options.library || {});
668
733
 
@@ -684,16 +749,16 @@
684
749
  adapters.push(GoogleChartsAdapter);
685
750
  }
686
751
 
687
- // TODO add adapter option
688
752
  // TODO remove chartType if cross-browser way
689
753
  // to get the name of the chart class
690
754
  function renderChart(chartType, chart) {
691
- var i, adapter, fnName;
755
+ var i, adapter, fnName, adapterName;
692
756
  fnName = "render" + chartType;
757
+ adapterName = chart.options.adapter;
693
758
 
694
759
  for (i = 0; i < adapters.length; i++) {
695
760
  adapter = adapters[i];
696
- if (isFunction(adapter[fnName])) {
761
+ if ((!adapterName || adapterName === adapter.name) && isFunction(adapter[fnName])) {
697
762
  return adapter[fnName](chart);
698
763
  }
699
764
  }
@@ -702,8 +767,31 @@
702
767
 
703
768
  // process data
704
769
 
705
- function processSeries(series, opts, time) {
706
- var i, j, data, r, key;
770
+ var toFormattedKey = function (key, keyType) {
771
+ if (keyType === "number") {
772
+ key = toFloat(key);
773
+ } else if (keyType === "datetime") {
774
+ key = toDate(key);
775
+ } else {
776
+ key = toStr(key);
777
+ }
778
+ return key;
779
+ };
780
+
781
+ var formatSeriesData = function (data, keyType) {
782
+ var r = [], key, j;
783
+ for (j = 0; j < data.length; j++) {
784
+ key = toFormattedKey(data[j][0], keyType);
785
+ r.push([key, toFloat(data[j][1])]);
786
+ }
787
+ if (keyType === "datetime") {
788
+ r.sort(sortByTime);
789
+ }
790
+ return r;
791
+ };
792
+
793
+ function processSeries(series, opts, keyType) {
794
+ var i;
707
795
 
708
796
  // see if one series or multiple
709
797
  if (!isArray(series) || typeof series[0] !== "object" || isArray(series[0])) {
@@ -713,22 +801,12 @@
713
801
  opts.hideLegend = false;
714
802
  }
715
803
  if (opts.discrete) {
716
- time = false;
804
+ keyType = "string";
717
805
  }
718
806
 
719
807
  // right format
720
808
  for (i = 0; i < series.length; i++) {
721
- data = toArr(series[i].data);
722
- r = [];
723
- for (j = 0; j < data.length; j++) {
724
- key = data[j][0];
725
- key = time ? toDate(key) : toStr(key);
726
- r.push([key, toFloat(data[j][1])]);
727
- }
728
- if (time) {
729
- r.sort(sortByTime);
730
- }
731
- series[i].data = r;
809
+ series[i].data = formatSeriesData(toArr(series[i].data), keyType);
732
810
  }
733
811
 
734
812
  return series;
@@ -753,12 +831,12 @@
753
831
  }
754
832
 
755
833
  function processLineData(chart) {
756
- chart.data = processSeries(chart.data, chart.options, true);
834
+ chart.data = processSeries(chart.data, chart.options, "datetime");
757
835
  renderChart("LineChart", chart);
758
836
  }
759
837
 
760
838
  function processColumnData(chart) {
761
- chart.data = processSeries(chart.data, chart.options, false);
839
+ chart.data = processSeries(chart.data, chart.options, "string");
762
840
  renderChart("ColumnChart", chart);
763
841
  }
764
842
 
@@ -768,12 +846,12 @@
768
846
  }
769
847
 
770
848
  function processBarData(chart) {
771
- chart.data = processSeries(chart.data, chart.options, false);
849
+ chart.data = processSeries(chart.data, chart.options, "string");
772
850
  renderChart("BarChart", chart);
773
851
  }
774
852
 
775
853
  function processAreaData(chart) {
776
- chart.data = processSeries(chart.data, chart.options, true);
854
+ chart.data = processSeries(chart.data, chart.options, "datetime");
777
855
  renderChart("AreaChart", chart);
778
856
  }
779
857
 
@@ -782,6 +860,11 @@
782
860
  renderChart("GeoChart", chart);
783
861
  }
784
862
 
863
+ function processScatterData(chart) {
864
+ chart.data = processSeries(chart.data, chart.options, "number");
865
+ renderChart("ScatterChart", chart);
866
+ }
867
+
785
868
  function processTimelineData(chart) {
786
869
  chart.data = processTime(chart.data);
787
870
  renderChart("Timeline", chart);
@@ -819,6 +902,9 @@
819
902
  GeoChart: function (element, dataSource, opts) {
820
903
  setElement(this, element, dataSource, opts, processGeoData);
821
904
  },
905
+ ScatterChart: function (element, dataSource, opts) {
906
+ setElement(this, element, dataSource, opts, processScatterData);
907
+ },
822
908
  Timeline: function (element, dataSource, opts) {
823
909
  setElement(this, element, dataSource, opts, processTimelineData);
824
910
  },
@@ -1,19 +1,19 @@
1
1
  # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
2
+ lib = File.expand_path("../lib", __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'chartkick/version'
4
+ require "chartkick/version"
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "chartkick"
8
8
  spec.version = Chartkick::VERSION
9
9
  spec.authors = ["Andrew Kane"]
10
10
  spec.email = ["acekane1@gmail.com"]
11
- spec.description = %q{Create beautiful Javascript charts with one line of Ruby}
12
- spec.summary = %q{Create beautiful Javascript charts with one line of Ruby}
13
- spec.homepage = ""
11
+ spec.description = "Create beautiful Javascript charts with one line of Ruby"
12
+ spec.summary = "Create beautiful Javascript charts with one line of Ruby"
13
+ spec.homepage = "http://chartkick.com"
14
14
  spec.license = "MIT"
15
15
 
16
- spec.files = `git ls-files`.split($/)
16
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
@@ -15,9 +15,9 @@ end
15
15
  # use Enumerable so it can be called on arrays
16
16
  module Enumerable
17
17
  def chart_json
18
- if is_a?(Hash) and (key = keys.first) and key.is_a?(Array) and key.size == 2
19
- group_by{|k, v| k[0] }.map do |name, data|
20
- {name: name, data: data.map{|k, v| [k[1], v] }}
18
+ if is_a?(Hash) && (key = keys.first) && key.is_a?(Array) && key.size == 2
19
+ group_by { |k, _v| k[0] }.map do |name, data|
20
+ {name: name, data: data.map { |k, v| [k[1], v] }}
21
21
  end
22
22
  else
23
23
  self
@@ -1,16 +1,14 @@
1
1
  module Chartkick
2
2
  class Engine < ::Rails::Engine
3
-
4
- initializer "precompile", :group => :all do |app|
3
+ initializer "precompile", group: :all do |app|
5
4
  # use a proc instead of a string
6
- app.config.assets.precompile << Proc.new{|path| path == "chartkick.js" }
5
+ app.config.assets.precompile << proc { |path| path == "chartkick.js" }
7
6
  end
8
7
 
9
- initializer "helper" do |app|
8
+ initializer "helper" do
10
9
  ActiveSupport.on_load(:action_view) do
11
10
  include Helper
12
11
  end
13
12
  end
14
-
15
13
  end
16
14
  end
@@ -3,7 +3,6 @@ require "erb"
3
3
 
4
4
  module Chartkick
5
5
  module Helper
6
-
7
6
  def line_chart(data_source, options = {})
8
7
  chartkick_chart "LineChart", data_source, options
9
8
  end
@@ -24,6 +23,10 @@ module Chartkick
24
23
  chartkick_chart "AreaChart", data_source, options
25
24
  end
26
25
 
26
+ def scatter_chart(data_source, options = {})
27
+ chartkick_chart "ScatterChart", data_source, options
28
+ end
29
+
27
30
  def geo_chart(data_source, options = {})
28
31
  chartkick_chart "GeoChart", data_source, options
29
32
  end
@@ -34,15 +37,15 @@ module Chartkick
34
37
 
35
38
  private
36
39
 
37
- def chartkick_chart(klass, data_source, options, &block)
40
+ def chartkick_chart(klass, data_source, options)
38
41
  @chartkick_chart_id ||= 0
39
42
  options = chartkick_deep_merge(Chartkick.options, options)
40
43
  element_id = options.delete(:id) || "chart-#{@chartkick_chart_id += 1}"
41
44
  height = options.delete(:height) || "300px"
42
45
  # content_for: nil must override default
43
- content_for = options.has_key?(:content_for) ? options.delete(:content_for) : Chartkick.content_for
46
+ content_for = options.key?(:content_for) ? options.delete(:content_for) : Chartkick.content_for
44
47
 
45
- html = (options.delete(:html) || %[<div id="%{id}" style="height: %{height}; text-align: center; color: #999; line-height: %{height}; font-size: 14px; font-family: 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, Helvetica, sans-serif;">Loading...</div>]) % {id: ERB::Util.html_escape(element_id), height: ERB::Util.html_escape(height)}
48
+ html = (options.delete(:html) || %(<div id="%{id}" style="height: %{height}; text-align: center; color: #999; line-height: %{height}; font-size: 14px; font-family: 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, Helvetica, sans-serif;">Loading...</div>)) % {id: ERB::Util.html_escape(element_id), height: ERB::Util.html_escape(height)}
46
49
 
47
50
  js = <<JS
48
51
  <script type="text/javascript">
@@ -61,12 +64,11 @@ JS
61
64
  # https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
62
65
  def chartkick_deep_merge(hash_a, hash_b)
63
66
  hash_a = hash_a.dup
64
- hash_b.each_pair do |k,v|
67
+ hash_b.each_pair do |k, v|
65
68
  tv = hash_a[k]
66
69
  hash_a[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_merge(v) : v
67
70
  end
68
71
  hash_a
69
72
  end
70
-
71
73
  end
72
74
  end
@@ -1,3 +1,5 @@
1
+ require "sinatra/base"
2
+
1
3
  class Sinatra::Base
2
4
  helpers Chartkick::Helper
3
5
  end
@@ -1,3 +1,3 @@
1
1
  module Chartkick
2
- VERSION = "1.3.2"
2
+ VERSION = "1.4.0"
3
3
  end
@@ -21,4 +21,9 @@ class TestChartkick < Minitest::Test
21
21
  assert column_chart(@data)
22
22
  end
23
23
 
24
+ def test_options_not_mutated
25
+ options = {id: "boom"}
26
+ line_chart @data, options
27
+ assert_equal "boom", options[:id]
28
+ end
24
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chartkick
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.2
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-04 00:00:00.000000000 Z
11
+ date: 2015-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -75,7 +75,7 @@ files:
75
75
  - lib/chartkick/version.rb
76
76
  - test/chartkick_test.rb
77
77
  - test/test_helper.rb
78
- homepage: ''
78
+ homepage: http://chartkick.com
79
79
  licenses:
80
80
  - MIT
81
81
  metadata: {}
@@ -95,10 +95,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
95
  version: '0'
96
96
  requirements: []
97
97
  rubyforge_project:
98
- rubygems_version: 2.2.2
98
+ rubygems_version: 2.4.5
99
99
  signing_key:
100
100
  specification_version: 4
101
101
  summary: Create beautiful Javascript charts with one line of Ruby
102
102
  test_files:
103
103
  - test/chartkick_test.rb
104
104
  - test/test_helper.rb
105
+ has_rdoc: