chartkick 3.3.1 → 4.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3d2deed8ea6acea7a2d4e68d7a0adfb2a396ff685ddd4763a6e7dc7cc9f981b5
4
- data.tar.gz: 8c1502d6b8a5b2feda3727c23d0a39e14c1c667b4bc3856f3f106e662490517d
3
+ metadata.gz: d9ca3fe125f71ef5a1a21a191a03244b9235396f4c72f4d8e742f7d1a3feaa98
4
+ data.tar.gz: d7da49117fa84b8525bf76acc421b22e71a0a38a2f2e798c182f96f0d6780a60
5
5
  SHA512:
6
- metadata.gz: 6b662ba0dea0a3c8e116fa6428bc52bd4288a301d20395f9671a3e478199c30278db020043c534f2edd62f0971fb5a7f4fa3608136d23b40103930afa59032d2
7
- data.tar.gz: 78d2f5473da5ce6b60a68eea76bb848339f4a77981595d9bc693e6e2fd83f9010f2ce6628570c7d034140e2c1416eda246d85fcae533a7d177d3ab4ef01e8281
6
+ metadata.gz: b00a3accac3e738dc1d0fa7480d162002f5302a8e725d28bfcd3c455514fa79080559b01cfc8a897c44a8d551fdfad3b80fb39a98a9b69671ec8dfa9c2d082e4
7
+ data.tar.gz: 5a1ccaf63c5bbd240e6ef7a14e240992387cc16c7ce43e6c1b3eab8cb1b632046adbea632db17a96194a21e6e7ce2427d1997ca5734541d51b3c6d2e506a3a8f
data/CHANGELOG.md CHANGED
@@ -1,7 +1,36 @@
1
+ ## 4.0.0 (2021-04-04)
2
+
3
+ - Added support for Chart.js 3
4
+ - Added `loading` option
5
+ - Made charts deferrable by default
6
+ - Set `nonce` automatically when present
7
+ - Prefer `empty` over `messages: {empty: ...}`
8
+ - Removed support for Ruby < 2.6 and Rails < 5.2
9
+
10
+ Breaking changes
11
+
12
+ - Removed support for Chart.js 2
13
+
14
+ ## 3.4.2 (2020-10-20)
15
+
16
+ - Updated Chart.js to 2.9.4
17
+
18
+ ## 3.4.1 (2020-10-06)
19
+
20
+ - Relaxed validation for `width` and `height` options
21
+
22
+ ## 3.4.0 (2020-08-04)
23
+
24
+ - Fixed CSS injection with `width` and `height` options - [more info](https://github.com/ankane/chartkick/issues/546)
25
+
26
+ ## 3.3.2 (2020-07-23)
27
+
28
+ - Updated Chartkick.js to 3.2.1
29
+
1
30
  ## 3.3.1 (2019-12-26)
2
31
 
3
32
  - Updated Chart.js to 2.9.3
4
- - Fixed deprecating warnings in Ruby 2.7
33
+ - Fixed deprecation warnings in Ruby 2.7
5
34
 
6
35
  ## 3.3.0 (2019-11-09)
7
36
 
@@ -19,7 +48,7 @@
19
48
 
20
49
  ## 3.2.0 (2019-06-04)
21
50
 
22
- - Fixed XSS vulnerability - see [#488](https://github.com/ankane/chartkick/issues/488)
51
+ - Fixed XSS vulnerability - [more info](https://github.com/ankane/chartkick/issues/488)
23
52
 
24
53
  ## 3.1.0 (2019-05-26)
25
54
 
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013-2019 Andrew Kane
1
+ Copyright (c) 2013-2021 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -4,13 +4,17 @@ Create beautiful JavaScript charts with one line of Ruby. No more fighting with
4
4
 
5
5
  [See it in action](https://chartkick.com)
6
6
 
7
- :fire: For admin charts and dashboards, check out [Blazer](https://github.com/ankane/blazer/)
7
+ **Chartkick 4.0 was recently released** - see [how to upgrade](#upgrading)
8
+
9
+ :fire: For admin charts and dashboards, check out [Blazer](https://github.com/ankane/blazer/), and for advanced visualizations, check out [Vega](https://github.com/ankane/vega)
8
10
 
9
11
  :two_hearts: A perfect companion to [Groupdate](https://github.com/ankane/groupdate), [Hightop](https://github.com/ankane/hightop), and [ActiveMedian](https://github.com/ankane/active_median)
10
12
 
13
+ [![Build Status](https://github.com/ankane/chartkick/workflows/build/badge.svg?branch=master)](https://github.com/ankane/chartkick/actions)
14
+
11
15
  ## Quick Start
12
16
 
13
- Add this line to your application's Gemfile:
17
+ Add this line to your applications Gemfile:
14
18
 
15
19
  ```ruby
16
20
  gem "chartkick"
@@ -19,14 +23,13 @@ gem "chartkick"
19
23
  For Rails 6 / Webpacker, run:
20
24
 
21
25
  ```sh
22
- yarn add chartkick chart.js
26
+ yarn add chartkick
23
27
  ```
24
28
 
25
29
  And in `app/javascript/packs/application.js`, add:
26
30
 
27
31
  ```js
28
- require("chartkick")
29
- require("chart.js")
32
+ require("chartkick/chart.js")
30
33
  ```
31
34
 
32
35
  For Rails 5 / Sprockets, in `app/assets/javascripts/application.js`, add:
@@ -36,7 +39,7 @@ For Rails 5 / Sprockets, in `app/assets/javascripts/application.js`, add:
36
39
  //= require Chart.bundle
37
40
  ```
38
41
 
39
- This sets up Chartkick with Chart.js. For other charting libraries, see [detailed instructions](#installation).
42
+ This sets up Chartkick with [Chart.js](https://www.chartjs.org/). For other charting libraries, see [detailed instructions](#installation).
40
43
 
41
44
  ## Charts
42
45
 
@@ -206,12 +209,6 @@ Specify legend position
206
209
  <%= line_chart data, legend: "bottom" %>
207
210
  ```
208
211
 
209
- Defer chart creation until after the page loads
210
-
211
- ```erb
212
- <%= line_chart data, defer: true %>
213
- ```
214
-
215
212
  Donut chart
216
213
 
217
214
  ```erb
@@ -260,16 +257,22 @@ Show insignificant zeros, useful for currency - *Chart.js, Highcharts*
260
257
  <%= line_chart data, round: 2, zeros: true %>
261
258
  ```
262
259
 
263
- Friendly byte sizes - *Chart.js 2.8+*
260
+ Friendly byte sizes
264
261
 
265
262
  ```erb
266
263
  <%= line_chart data, bytes: true %>
267
264
  ```
268
265
 
266
+ Show a message when data is loading
267
+
268
+ ```erb
269
+ <%= line_chart data, loading: "Loading..." %>
270
+ ```
271
+
269
272
  Show a message when data is empty
270
273
 
271
274
  ```erb
272
- <%= line_chart data, messages: {empty: "No data"} %>
275
+ <%= line_chart data, empty: "No data" %>
273
276
  ```
274
277
 
275
278
  Refresh data from a remote source every `n` seconds
@@ -308,7 +311,7 @@ Chartkick.options = {
308
311
  Customize the html
309
312
 
310
313
  ```ruby
311
- Chartkick.options[:html] = '<div id="%{id}" style="height: %{height};">Loading...</div>'
314
+ Chartkick.options[:html] = '<div id="%{id}" style="height: %{height};">%{loading}</div>'
312
315
  ```
313
316
 
314
317
  You capture the JavaScript in a content block with:
@@ -323,7 +326,7 @@ Then, in your layout, use:
323
326
  <%= yield :charts_js %>
324
327
  ```
325
328
 
326
- > For Padrino, use `yield_content` instead of `yield`
329
+ For Padrino, use `yield_content` instead of `yield`.
327
330
 
328
331
  This is great for including all of your JavaScript at the bottom of the page.
329
332
 
@@ -370,9 +373,7 @@ If you want to use the charting library directly, get the code with:
370
373
  <%= line_chart data, code: true %>
371
374
  ```
372
375
 
373
- The code will be logged to the JavaScript console.
374
-
375
- > JavaScript functions cannot be logged, so it may not be identical.
376
+ The code will be logged to the JavaScript console. JavaScript functions cannot be logged, so it may not be identical.
376
377
 
377
378
  ### Download Charts
378
379
 
@@ -384,7 +385,7 @@ Give users the ability to download charts. It all happens in the browser - no se
384
385
  <%= line_chart data, download: true %>
385
386
  ```
386
387
 
387
- > Safari will open the image in a new window instead of downloading.
388
+ Safari will open the image in a new window instead of downloading.
388
389
 
389
390
  Set the filename
390
391
 
@@ -418,21 +419,20 @@ Next, choose your charting library.
418
419
  - [Google Charts](#google-charts)
419
420
  - [Highcharts](#highcharts)
420
421
 
421
- > In the instructions below, `application.js` must be included **before** the charts in your views, unless using the `:content_for` option.
422
+ In the instructions below, `application.js` must be included **before** the charts in your views, unless using the `:content_for` option.
422
423
 
423
424
  ### Chart.js
424
425
 
425
426
  For Rails 6 / Webpacker, run:
426
427
 
427
428
  ```sh
428
- yarn add chartkick chart.js
429
+ yarn add chartkick
429
430
  ```
430
431
 
431
432
  And in `app/javascript/packs/application.js`, add:
432
433
 
433
434
  ```js
434
- require("chartkick")
435
- require("chart.js")
435
+ require("chartkick/chart.js")
436
436
  ```
437
437
 
438
438
  For Rails 5 / Sprockets, in `app/assets/javascripts/application.js`, add:
@@ -487,7 +487,7 @@ yarn add chartkick highcharts
487
487
  And in `app/javascript/packs/application.js`, add:
488
488
 
489
489
  ```js
490
- require("chartkick").use(require("highcharts"))
490
+ require("chartkick/highcharts")
491
491
  ```
492
492
 
493
493
  For Rails 5 / Sprockets, download [highcharts.js](https://code.highcharts.com/highcharts.js) into `vendor/assets/javascripts` (or use `yarn add highcharts` in Rails 5.1+), and in `app/assets/javascripts/application.js`, add:
@@ -505,6 +505,27 @@ Download [chartkick.js](https://raw.githubusercontent.com/ankane/chartkick/maste
505
505
  <script src="chartkick.js"></script>
506
506
  ```
507
507
 
508
+ Then include the charting library.
509
+
510
+ Chart.js - download [Chart.js](https://unpkg.com/chart.js@3/dist/chart.js) and the [date-fns adapter bundle](https://unpkg.com/chartjs-adapter-date-fns@2.0.0/dist/chartjs-adapter-date-fns.bundle.js)
511
+
512
+ ```html
513
+ <script src="chart.js"></script>
514
+ <script src="chartjs-adapter-date-fns.bundle.js"></script>
515
+ ```
516
+
517
+ Google Charts
518
+
519
+ ```html
520
+ <script src="https://www.gstatic.com/charts/loader.js"></script>
521
+ ```
522
+
523
+ Highcharts - download [highcharts.js](https://code.highcharts.com/highcharts.js)
524
+
525
+ ```html
526
+ <script src="highcharts.js"></script>
527
+ ```
528
+
508
529
  ### Multiple Libraries
509
530
 
510
531
  If more than one charting library is loaded, choose between them with:
@@ -562,6 +583,12 @@ Redraw the chart with:
562
583
  chart.redraw()
563
584
  ```
564
585
 
586
+ Destroy the chart with:
587
+
588
+ ```javascript
589
+ chart.destroy()
590
+ ```
591
+
565
592
  Loop over charts with:
566
593
 
567
594
  ```javascript
@@ -587,23 +614,28 @@ Check out [chartkick.js](https://github.com/ankane/chartkick.js)
587
614
 
588
615
  ## Upgrading
589
616
 
590
- ### 3.0
617
+ ### 4.0
591
618
 
592
- Breaking changes
619
+ If you use Sprockets, update the gem and you’re good to go!
593
620
 
594
- - Removed support for Rails < 4.2
595
- - Removed chartkick.js from asset precompile (no longer needed)
596
- - Removed `xtype` option - numeric axes are automatically detected
597
- - Removed `window.Chartkick = {...}` way to set config - use `Chartkick.configure` instead
598
- - Removed support for the Google Charts jsapi loader - use loader.js instead
621
+ If you use Webpacker, run:
599
622
 
600
- ### 2.0
623
+ ```sh
624
+ yarn upgrade chartkick --latest
625
+ ```
601
626
 
602
- Breaking changes
627
+ If you use Chart.js with Webpacker, in `app/javascript/packs/application.js`, change:
603
628
 
604
- - Chart.js is now the default adapter if multiple are loaded - yay open source!
605
- - Axis types are automatically detected - no need for `discrete: true`
606
- - Better date support - dates are no longer treated as UTC
629
+ ```js
630
+ require("chartkick")
631
+ require("chart.js")
632
+ ```
633
+
634
+ to:
635
+
636
+ ```js
637
+ require("chartkick/chart.js")
638
+ ```
607
639
 
608
640
  ## Credits
609
641
 
@@ -613,8 +645,6 @@ Chartkick uses [iso8601.js](https://github.com/Do/iso8601.js) to parse dates and
613
645
 
614
646
  View the [changelog](https://github.com/ankane/chartkick/blob/master/CHANGELOG.md)
615
647
 
616
- Chartkick follows [Semantic Versioning](https://semver.org/)
617
-
618
648
  ## Contributing
619
649
 
620
650
  Everyone is encouraged to help improve this project. Here are a few ways you can help:
@@ -623,3 +653,12 @@ Everyone is encouraged to help improve this project. Here are a few ways you can
623
653
  - Fix bugs and [submit pull requests](https://github.com/ankane/chartkick/pulls)
624
654
  - Write, clarify, or fix documentation
625
655
  - Suggest or add new features
656
+
657
+ To get started with development:
658
+
659
+ ```sh
660
+ git clone https://github.com/ankane/chartkick.git
661
+ cd chartkick
662
+ bundle install
663
+ bundle exec rake test
664
+ ```
data/lib/chartkick.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # modules
2
+ require "chartkick/enumerable"
1
3
  require "chartkick/helper"
2
4
  require "chartkick/version"
3
5
 
@@ -18,29 +20,3 @@ module Chartkick
18
20
  end
19
21
  self.options = {}
20
22
  end
21
-
22
- # for multiple series
23
- # use Enumerable so it can be called on arrays
24
- module Enumerable
25
- def chart_json
26
- if is_a?(Hash)
27
- if (key = keys.first) && key.is_a?(Array) && key.size == 2
28
- group_by { |k, _v| k[0] }.map do |name, data|
29
- {name: name, data: data.map { |k, v| [k[1], v] }}
30
- end
31
- else
32
- to_a
33
- end
34
- elsif is_a?(Array)
35
- map do |v|
36
- if v.is_a?(Hash) && v[:data].is_a?(Hash)
37
- v = v.dup
38
- v[:data] = v[:data].to_a
39
- end
40
- v
41
- end
42
- else
43
- self
44
- end.to_json
45
- end
46
- end
@@ -0,0 +1,25 @@
1
+ # for both multiple series and
2
+ # making sure hash order is preserved in JavaScript
3
+ module Enumerable
4
+ def chart_json
5
+ if is_a?(Hash)
6
+ if (key = keys.first) && key.is_a?(Array) && key.size == 2
7
+ group_by { |k, _v| k[0] }.map do |name, data|
8
+ {name: name, data: data.map { |k, v| [k[1], v] }}
9
+ end
10
+ else
11
+ to_a
12
+ end
13
+ elsif is_a?(Array)
14
+ map do |v|
15
+ if v.is_a?(Hash) && v[:data].is_a?(Hash)
16
+ v = v.dup
17
+ v[:data] = v[:data].to_a
18
+ end
19
+ v
20
+ end
21
+ else
22
+ self
23
+ end.to_json
24
+ end
25
+ end
@@ -37,17 +37,16 @@ module Chartkick
37
37
 
38
38
  private
39
39
 
40
- def chartkick_chart(klass, data_source, **options)
40
+ def chartkick_chart(klass, data_source, nonce: true, **options)
41
41
  @chartkick_chart_id ||= 0
42
42
  options = chartkick_deep_merge(Chartkick.options, options)
43
43
  element_id = options.delete(:id) || "chart-#{@chartkick_chart_id += 1}"
44
- height = options.delete(:height) || "300px"
45
- width = options.delete(:width) || "100%"
44
+ height = (options.delete(:height) || "300px").to_s
45
+ width = (options.delete(:width) || "100%").to_s
46
46
  defer = !!options.delete(:defer)
47
47
  # content_for: nil must override default
48
48
  content_for = options.key?(:content_for) ? options.delete(:content_for) : Chartkick.content_for
49
49
 
50
- nonce = options.delete(:nonce)
51
50
  if nonce == true
52
51
  # Secure Headers also defines content_security_policy_nonce but it takes an argument
53
52
  # Rails 5.2 overrides this method, but earlier versions do not
@@ -57,6 +56,8 @@ module Chartkick
57
56
  elsif respond_to?(:content_security_policy_script_nonce)
58
57
  # Secure Headers
59
58
  nonce = content_security_policy_script_nonce
59
+ else
60
+ nonce = nil
60
61
  end
61
62
  end
62
63
  nonce_html = nonce ? " nonce=\"#{ERB::Util.html_escape(nonce)}\"" : nil
@@ -65,12 +66,26 @@ module Chartkick
65
66
  html_vars = {
66
67
  id: element_id,
67
68
  height: height,
68
- width: width
69
+ width: width,
70
+ # don't delete loading option since it needs to be passed to JS
71
+ loading: options[:loading] || "Loading..."
69
72
  }
73
+
74
+ [:height, :width].each do |k|
75
+ # limit to alphanumeric and % for simplicity
76
+ # this prevents things like calc() but safety is the priority
77
+ # dot does not need escaped in square brackets
78
+ raise ArgumentError, "Invalid #{k}" unless html_vars[k] =~ /\A[a-zA-Z0-9%.]*\z/
79
+ end
80
+
70
81
  html_vars.each_key do |k|
82
+ # escape all variables
83
+ # we already limit height and width above, but escape for safety as fail-safe
84
+ # to prevent XSS injection in worse-case scenario
71
85
  html_vars[k] = ERB::Util.html_escape(html_vars[k])
72
86
  end
73
- html = (options.delete(:html) || %(<div id="%{id}" style="height: %{height}; width: %{width}; 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>)) % html_vars
87
+
88
+ html = (options.delete(:html) || %(<div id="%{id}" style="height: %{height}; width: %{width}; 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>)) % html_vars
74
89
 
75
90
  # js vars
76
91
  js_vars = {
@@ -84,28 +99,28 @@ module Chartkick
84
99
  end
85
100
  createjs = "new Chartkick[%{type}](%{id}, %{data}, %{options});" % js_vars
86
101
 
87
- if defer
88
- js = <<JS
89
- <script type="text/javascript"#{nonce_html}>
90
- (function() {
91
- var createChart = function() { #{createjs} };
92
- if (window.addEventListener) {
93
- window.addEventListener("load", createChart, true);
94
- } else if (window.attachEvent) {
95
- window.attachEvent("onload", createChart);
96
- } else {
97
- createChart();
98
- }
99
- })();
100
- </script>
101
- JS
102
- else
103
- js = <<JS
104
- <script type="text/javascript"#{nonce_html}>
105
- #{createjs}
106
- </script>
107
- JS
108
- end
102
+ warn "[chartkick] The defer option is no longer needed and can be removed" if defer
103
+
104
+ # Turbolinks preview restores the DOM except for painted <canvas>
105
+ # since it uses cloneNode(true) - https://developer.mozilla.org/en-US/docs/Web/API/Node/
106
+ #
107
+ # don't rerun JS on preview to prevent
108
+ # 1. animation
109
+ # 2. loading data from URL
110
+ js = <<~JS
111
+ <script#{nonce_html}>
112
+ (function() {
113
+ if (document.documentElement.hasAttribute("data-turbolinks-preview")) return;
114
+
115
+ var createChart = function() { #{createjs} };
116
+ if ("Chartkick" in window) {
117
+ createChart();
118
+ } else {
119
+ window.addEventListener("chartkick:load", createChart, true);
120
+ }
121
+ })();
122
+ </script>
123
+ JS
109
124
 
110
125
  if content_for
111
126
  content_for(content_for) { js.respond_to?(:html_safe) ? js.html_safe : js }