highcharts-rails 3.0.1 → 3.0.1.5.pre1

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.
data/CHANGELOG.markdown CHANGED
@@ -1,3 +1,7 @@
1
+ # 3.0.1.5 / unreleased
2
+
3
+ * Add a Rack endpoint for exporting charts to image files
4
+
1
5
  # 3.0.1 / 2013-04-09
2
6
 
3
7
  * Updated Highcharts to 3.0.1
data/README.markdown CHANGED
@@ -45,6 +45,54 @@ Or one of the themes
45
45
 
46
46
  Other than that, refer to the [Highcharts documentation](http://highcharts.com/documentation/how-to-use)
47
47
 
48
+ ## Export endpoint
49
+
50
+ This gem contains a Rack endpoint for converting the chart to a downloadable file.
51
+ It is not required by default, so to use it, `require
52
+ 'highcharts/export_endpoint'`
53
+
54
+ The endpoint is basically a port of the [PHP version made available](https://github.com/highslide-software/highcharts.com/blob/master/exporting-server/php/php-batik/index.php).
55
+ It currently needs a lot of cleanup, but it is working fine for me. Your milage
56
+ may vary.
57
+
58
+ It uses [Apache Batik](http://xmlgraphics.apache.org/batik/) for the conversion, which must be
59
+ installed separately, as well as a Java Runtime Environment.
60
+
61
+ It expects to find a JRE in `/usr/bin/java`, and Batik in
62
+ `/usr/share/java/batik-rasterizer.jar`, but both paths are configurable.
63
+
64
+ Example usage in Rails:
65
+
66
+ # config/routes.rb
67
+ require 'highcharts/export_endpoint'
68
+
69
+ MyRailsApp::Application.routes.draw do
70
+ ...
71
+ mount Highcharts::ExportEndpoint.new({
72
+ java_path: "/usr/bin/java",
73
+ batik: "/usr/share/java/batik-rasterizer.jar"
74
+ }), at: "highcharts-export"
75
+ ...
76
+ end
77
+
78
+ # When rendering the chart
79
+ new Highcharts.Chart({
80
+ ...
81
+ exporting: {
82
+ url: "/highcharts-export",
83
+ ...
84
+ }
85
+ })
86
+
87
+ ### Cocaine
88
+
89
+ The exporting endpoint uses [Cocaine](https://github.com/thoughtbot/cocaine) for
90
+ handling the command lines and arguments and so on.
91
+
92
+ I don't know a way to get optional dependencies in the gemspec, so for now
93
+ that gets added whether you want it or not. I'd like to get this fixed,
94
+ but would also like to avoid `begin; require 'cocaine'; rescue LoadError; ...; end` and similar hacks.
95
+
48
96
  ## Licensing
49
97
 
50
98
  Highcharts, which makes up the majority of this gem, has [its own, separate licensing](http://highcharts.com/license).
@@ -17,6 +17,7 @@ Gem::Specification.new do |s|
17
17
  s.require_paths = ["lib"]
18
18
 
19
19
  s.add_dependency "railties", ">= 3.1"
20
+ s.add_dependency "cocaine", "~> 0.4.0"
20
21
  s.add_development_dependency "bundler", "~> 1.0"
21
22
  s.add_development_dependency "rails", ">= 3.1"
22
23
  end
@@ -1,2 +1,2 @@
1
1
  require 'highcharts/version'
2
- require 'highcharts/rails'
2
+ require 'highcharts/rails'
@@ -0,0 +1,122 @@
1
+ # encoding: utf-8
2
+ require 'cocaine'
3
+
4
+ module Highcharts
5
+ class ExportEndpoint
6
+ class InsecureSVGError < ArgumentError; end
7
+ class MissingLibrary < RuntimeError; end
8
+ class FailedToGenerateChart < RuntimeError; end
9
+ class InvalidType < ArgumentError; end
10
+
11
+ attr_reader :output, :options
12
+
13
+ def initialize(options = {})
14
+ @options = default_options.merge(options)
15
+ end
16
+
17
+ def default_options
18
+ {
19
+ java_path: "/usr/bin/java",
20
+ batik: "/usr/share/java/batik-rasterizer.jar",
21
+ }
22
+ end
23
+
24
+ def call(env)
25
+ dup._call(env)
26
+ end
27
+
28
+ def _call(env)
29
+ with_rescues do
30
+ raise MissingLibrary.new("Could not find batik-rasterizer.jar in #{options[:batik].inspect}") unless File.exists?(options[:batik].to_s)
31
+
32
+ request = Rack::Request.new(env)
33
+
34
+ filename = request.params["filename"].to_s
35
+ filename = "chart" if filename.blank? || filename !~ /\A[A-Za-z0-9\-_ ]+\Z/
36
+
37
+ type = request.params["type"].to_s
38
+ width = request.params["width"].to_i
39
+ svg = request.params["svg"].to_s
40
+
41
+ raise InsecureSVGError.new if svg.index("<!ENTITY") || svg.index("<!DOCTYPE")
42
+
43
+ if type == "image/svg+xml"
44
+ # We were sent SVG from the client, so can just render that back
45
+ return [200, {
46
+ 'Content-Disposition' => "attachment; filename=\"#{filename}.svg\"",
47
+ 'Content-Type' => 'image/svg+xml'
48
+ }, [svg]]
49
+ end
50
+
51
+ width = width > 0 ? width.to_s : "600"
52
+ extension = case type
53
+ when "image/png" then "png"
54
+ when "image/jpeg" then "jpg"
55
+ when "application/pdf" then "pdf"
56
+ when "image/svg+xml" then "svg"
57
+ else raise InvalidType.new("#{type} is not a valid type.")
58
+ end
59
+
60
+ input = write_svg_to_file(svg)
61
+ @output = create_output_file(extension)
62
+
63
+ command.run(batik: options[:batik], outfile: output.path, type: type, width: width, infile: input.path)
64
+ input.close
65
+ content_length = output.size
66
+ output.rewind
67
+
68
+ raise FailedToGenerateChart.new("Nothing written to file") if !File.exists?(output.path) || content_length < 10
69
+
70
+ Rack::Response.new(self, 200, {
71
+ 'Content-Disposition' => "attachment; filename=\"#{filename}.#{extension}\"",
72
+ 'Content-Type' => type
73
+ }).finish
74
+ end
75
+ end
76
+
77
+ # Pass the block along to the output file, and
78
+ # make sure to close the file afterwards
79
+ def each(&block)
80
+ output.each(&block)
81
+ ensure
82
+ output.close
83
+ end
84
+
85
+ def command
86
+ Cocaine::CommandLine.new(options[:java_path], "-jar :batik -m :type -d :outfile -w :width :infile")
87
+ end
88
+
89
+ def write_svg_to_file(contents)
90
+ file = ::Tempfile.new(["highcharts-input", ".svg"], Dir.tmpdir, encoding: 'utf-8')
91
+ file.puts contents
92
+ file.flush
93
+ file
94
+ end
95
+
96
+ def create_output_file(extension)
97
+ file = ::Tempfile.new(["highcharts-chart", ".#{extension}"])
98
+ file.binmode
99
+ file
100
+ end
101
+
102
+ def with_rescues
103
+ yield
104
+ rescue Highcharts::ExportEndpoint::InsecureSVGError => e
105
+ [400, {'Content-Type' => 'text/plain'}, ["The posted SVG could contain code for a malicious attack"]]
106
+ rescue Highcharts::ExportEndpoint::InvalidType => e
107
+ [400, {'Content-Type' => 'text/plain'}, [e]]
108
+ rescue Cocaine::CommandNotFoundError => e
109
+ [503, {'Content-Type' => 'text/plain'}, ["Unable to find required binary. #{e}"]]
110
+ rescue Highcharts::ExportEndpoint::MissingLibrary => e
111
+ [503, {'Content-Type' => 'text/plain'}, ["Unable to find required library. #{e}"]]
112
+ rescue Highcharts::ExportEndpoint::FailedToGenerateChart => e
113
+ [500, {'Content-Type' => 'text/plain'}, ["Failed to generate chart. More details may be available in the server logs."]]
114
+ rescue => e
115
+ [500, {'Content-Type' => 'text/plain'}, ["Something went wrong. More details may be available in the server logs."]]
116
+ end
117
+
118
+ def error(code, message)
119
+ [code, {}, [message].flatten]
120
+ end
121
+ end
122
+ end
@@ -1,3 +1,3 @@
1
1
  module Highcharts
2
- VERSION = "3.0.1"
2
+ VERSION = "3.0.1.5.pre1"
3
3
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: highcharts-rails
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 3.0.1
4
+ prerelease: 8
5
+ version: 3.0.1.5.pre1
6
6
  platform: ruby
7
7
  authors:
8
8
  - Per Christian B. Viken
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-09 00:00:00.000000000 Z
12
+ date: 2013-04-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: railties
@@ -27,6 +27,22 @@ dependencies:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: '3.1'
30
+ - !ruby/object:Gem::Dependency
31
+ name: cocaine
32
+ type: :runtime
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ~>
37
+ - !ruby/object:Gem::Version
38
+ version: 0.4.0
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.4.0
30
46
  - !ruby/object:Gem::Dependency
31
47
  name: bundler
32
48
  type: :development
@@ -91,6 +107,7 @@ files:
91
107
  - app/assets/javascripts/highcharts/themes/skies.js
92
108
  - highcharts-rails.gemspec
93
109
  - lib/highcharts-rails.rb
110
+ - lib/highcharts/export_endpoint.rb
94
111
  - lib/highcharts/rails.rb
95
112
  - lib/highcharts/version.rb
96
113
  homepage: http://northblue.org/
@@ -108,9 +125,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
108
125
  required_rubygems_version: !ruby/object:Gem::Requirement
109
126
  none: false
110
127
  requirements:
111
- - - ! '>='
128
+ - - ! '>'
112
129
  - !ruby/object:Gem::Version
113
- version: '0'
130
+ version: 1.3.1
114
131
  requirements: []
115
132
  rubyforge_project:
116
133
  rubygems_version: 1.8.24
@@ -118,3 +135,4 @@ signing_key:
118
135
  specification_version: 3
119
136
  summary: Gem for easily adding Highcharts to the Rails Asset Pipeline
120
137
  test_files: []
138
+ has_rdoc:
data.tar.gz.asc DELETED
@@ -1,18 +0,0 @@
1
- -----BEGIN PGP SIGNATURE-----
2
- Version: GnuPG/MacGPG2 v2.0.19 (Darwin)
3
- Comment: GPGTools - http://gpgtools.org
4
-
5
- iQIcBAABAgAGBQJRZCX4AAoJEH1ncb0Txu7XWUgQAKSwMYAj6ECNovM5FQnW9Xrs
6
- 84xcCzwhur/YD5/jOVxl85tTVmboBRdVPp53CmsuquGYUMRrrXRobt6C6/hMp4DP
7
- imZ72AL1ZjYjFPCjHXuJzVXsiwGKLE7SdhGo01l3+69rhGwurpA3fZ9d1DaO+YZ6
8
- BlwHypnXlQGOPapB7Eh2V/U5UbZi3V1kLTlUaJyUu914p99Jo6MutHobM7aShMOx
9
- hdqn+ZqN0ha2ourKFk+wJKWTMKe4jvlp9SDrOLe9JY+AJ9zfCns+QFwFJQaC6+VE
10
- DlxQVt33q2nl+W7DdQr23kBM9/Vr9tQzNPE4E17NvGGZBwTZGxPSwPVzoO1ajRVH
11
- MwzbcKrqo1AkRDY5th1Hf2WAyruQ62DlbHF/5luJo1ezsuakus74/JWIo36BXKdp
12
- R20yR4hnQflxGj9BwOUZ5oroG4+PvIBHEfy9no7R600u5Q/RzCj+C3MCekYTMyKV
13
- KurzDyFfanNU1RNVkV2rJAAB2373cCZRiORqi7HNfdjkzzJiuFCTIWNkEo2eLPx4
14
- KlCZL5czNj1fgiEZclMGqv7Le5RXR3SuQXzxTw/cpEbvy5Zwm1zUGWankUQb+TEg
15
- 7hP/wQD5E8zJ/ZYbYjXCnS2K8e59+EOc5DcGpaPT9ELSmaCDlG3AvNnKuFS3g+nj
16
- pm1NAKtYl8Y62Yf35hba
17
- =qJvF
18
- -----END PGP SIGNATURE-----
metadata.gz.asc DELETED
@@ -1,18 +0,0 @@
1
- -----BEGIN PGP SIGNATURE-----
2
- Version: GnuPG/MacGPG2 v2.0.19 (Darwin)
3
- Comment: GPGTools - http://gpgtools.org
4
-
5
- iQIbBAABAgAGBQJRZCX4AAoJEH1ncb0Txu7X/t4P9R5ENkevewpnG4BmYF2THRJd
6
- bws9bcN4bPeSYpceQBphAnFEVQfL+QAXzTweHHw5nk/mGg1iPl18ySyWWeWdSq+G
7
- bbjIAqcG9IdeX/ItzzWXn47WvXhjsu7s6LZ3vP57Y7q0+AcHW45Tl90p1XYSFI3D
8
- 1Cx1lh6ljZ7JtsNe8j3lnxHq/gL29RA+f4u+k8u+jdas69I+dpcc5EiuI+9C2AnB
9
- usTAafb8Q7lVwsiv9Mkgufai5c3J9LkSZOjLF8GhoXSVE/xybAFpFs4SzyoGKm6K
10
- C/xXtZV+78/sv5WUm1UK//T/9LXO0jebYiE5/WZgJZDnt/zDVJDYU5Nmsr9IF1ty
11
- QYRHUO3iMZODpDyeRxKDEL1DMYoKrZJbvZqbJx+0FmYSfvxqcyX0cKoIFYOLhZUo
12
- rxWKoEw3otoWiJhKJPC6A+CkYS3NxOjHjixhRs6T59Dxo5Hb5dYO9cF823feCIGU
13
- ge/FlB4wBTJeFrSSMTjl/ABU5Vrdfx2/Ng5e/nyAwFaki8p4n8CRNnfYvsJ8SvQC
14
- 8jAspjcyeiXzQx27ujgny45NTYNMkfrl01OLHsRjVaXnTw97z8+96QQaCV/5M0tA
15
- ZZtWxDI2BTWXIkzBp0BzyAq1utDiS++CE/p3nEhlDGRMwj49iAg3JD3mfS0/OfmB
16
- cHbhD6RW3WPUEGUKW4M=
17
- =LkjG
18
- -----END PGP SIGNATURE-----