gchart 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +3 -0
- data/Manifest.txt +7 -0
- data/README.txt +72 -0
- data/Rakefile +13 -0
- data/lib/gchart.rb +143 -0
- data/lib/version.rb +3 -0
- data/test/test_gchart.rb +114 -0
- metadata +70 -0
data/CHANGELOG.txt
ADDED
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
= GChart
|
2
|
+
|
3
|
+
== DESCRIPTION
|
4
|
+
|
5
|
+
GChart exposes the Google Chart API (http://code.google.com/apis/chart) via
|
6
|
+
a friendly Ruby interface. It can generate the URL for a given chart
|
7
|
+
(for webpage use), or download the generated PNG (for offline use).
|
8
|
+
|
9
|
+
== PROBLEMS/TODO
|
10
|
+
|
11
|
+
* Add support for legends, fills (area or background), grid lines, shape markers, range markers
|
12
|
+
* Support shorthand colors and color names
|
13
|
+
* Make venn data specification friendlier
|
14
|
+
|
15
|
+
There are lots of missing features. Until they're implemented, you can directly specify
|
16
|
+
query parameters using the :extras key, e.g.,
|
17
|
+
|
18
|
+
# provides a legend for each data set
|
19
|
+
g = GChart.line(:data => [[1, 2], [3, 4]], :extras => { "chdl" => ["First", "Second"] })
|
20
|
+
|
21
|
+
== SYNOPSIS
|
22
|
+
|
23
|
+
# line chart
|
24
|
+
g = GChart.line(:data => [0, 10, 100])
|
25
|
+
|
26
|
+
# bar chart
|
27
|
+
g = GChart.bar(:data => [100, 1000, 10000])
|
28
|
+
|
29
|
+
# pie chart
|
30
|
+
g = GChart.pie(:data => [33, 33, 34])
|
31
|
+
|
32
|
+
# venn diagram (asize, bsize, csize, ab%, bc%, ca%, abc%)
|
33
|
+
g = GChart.venn(:data => [100, 80, 60, 30, 30, 30, 10])
|
34
|
+
|
35
|
+
# scatter plot (x coords, y coords [, sizes])
|
36
|
+
g = GChart.scatter(:data => [[1, 2, 3, 4, 5], [5, 4, 3, 2, 1], [1, 2, 3, 4, 5]])
|
37
|
+
|
38
|
+
# chart title
|
39
|
+
g = GChart.line(:title => "Awesomeness over Time", :data => [0, 10, 100])
|
40
|
+
|
41
|
+
# data set colors
|
42
|
+
g = GChart.line(:data => [[0, 10, 100], [100, 10, 0]], :colors => ["ff0000", "0000ff"])
|
43
|
+
|
44
|
+
g.to_url # generate the chart's URL, or
|
45
|
+
g.fetch # get the bytes, or
|
46
|
+
g.write("foo.png") # write to a file (defaults to "chart.png")
|
47
|
+
g.write(stream) # write to anything that quacks like IO
|
48
|
+
|
49
|
+
== LICENSE
|
50
|
+
|
51
|
+
(The MIT License)
|
52
|
+
|
53
|
+
Copyright 2007 John Barnette (jbarnette@rubyforge.org)
|
54
|
+
|
55
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
56
|
+
a copy of this software and associated documentation files (the
|
57
|
+
'Software'), to deal in the Software without restriction, including
|
58
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
59
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
60
|
+
permit persons to whom the Software is furnished to do so, subject to
|
61
|
+
the following conditions:
|
62
|
+
|
63
|
+
The above copyright notice and this permission notice shall be
|
64
|
+
included in all copies or substantial portions of the Software.
|
65
|
+
|
66
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
67
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
68
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
69
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
70
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
71
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
72
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "hoe"
|
3
|
+
require "./lib/version.rb"
|
4
|
+
|
5
|
+
hoe = Hoe.new("gchart", GChart::VERSION) do |p|
|
6
|
+
p.rubyforge_name = "gchart"
|
7
|
+
p.author = "John Barnette"
|
8
|
+
p.email = "jbarnette@rubyforge.org"
|
9
|
+
p.summary = "GChart uses the Google Chart API to create pretty pictures."
|
10
|
+
p.description = p.paragraphs_of("README.txt", 2..5).join("\n\n")
|
11
|
+
p.url = "http://gchart.rubyforge.org"
|
12
|
+
p.changes = p.paragraphs_of("CHANGELOG.txt", 0..1).join("\n\n")
|
13
|
+
end
|
data/lib/gchart.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/version"
|
2
|
+
|
3
|
+
require "open-uri"
|
4
|
+
require "uri"
|
5
|
+
|
6
|
+
class GChart
|
7
|
+
URL = "http://chart.apis.google.com/chart"
|
8
|
+
TYPES = %w(line linexy bar pie venn scatter).collect { |t| t.to_sym }
|
9
|
+
CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.".split("")
|
10
|
+
PAIRS = CHARS.collect { |first| CHARS.collect { |second| first + second } }.flatten
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def encode_extended(number) #:nodoc:
|
14
|
+
return "__" if number.nil?
|
15
|
+
PAIRS[number.to_i]
|
16
|
+
end
|
17
|
+
|
18
|
+
TYPES.each do |type|
|
19
|
+
class_eval <<-END
|
20
|
+
def #{type}(options={}, &block)
|
21
|
+
new(options.merge(:type => #{type.inspect}, &block))
|
22
|
+
end
|
23
|
+
END
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Array of chart data
|
28
|
+
attr_accessor :data
|
29
|
+
|
30
|
+
# Hash of additional query params
|
31
|
+
attr_accessor :extras
|
32
|
+
|
33
|
+
# Width (in pixels)
|
34
|
+
attr_accessor :width
|
35
|
+
|
36
|
+
# Height (in pixels)
|
37
|
+
attr_accessor :height
|
38
|
+
|
39
|
+
# Orientation. Applies to bar charts
|
40
|
+
attr_accessor :horizontal
|
41
|
+
|
42
|
+
# Grouping. Applies to bar charts
|
43
|
+
attr_accessor :grouped
|
44
|
+
|
45
|
+
# Overall chart title
|
46
|
+
attr_accessor :title
|
47
|
+
|
48
|
+
# Array of RRGGBB colors, one per data set
|
49
|
+
attr_accessor :colors
|
50
|
+
|
51
|
+
# The chart type
|
52
|
+
attr_reader :type
|
53
|
+
|
54
|
+
alias_method :horizontal?, :horizontal
|
55
|
+
alias_method :grouped?, :grouped
|
56
|
+
|
57
|
+
def initialize(options={}, &block)
|
58
|
+
@type = :line
|
59
|
+
@data = []
|
60
|
+
@extras = {}
|
61
|
+
@width = 300
|
62
|
+
@height = 200
|
63
|
+
@horizontal = false
|
64
|
+
@grouped = false
|
65
|
+
|
66
|
+
options.each { |k, v| send("#{k}=", v) }
|
67
|
+
yield(self) if block_given?
|
68
|
+
end
|
69
|
+
|
70
|
+
# Sets the chart type. Raises +ArgumentError+ if +type+ isn't in +TYPES+.
|
71
|
+
def type=(type)
|
72
|
+
unless TYPES.include?(type)
|
73
|
+
raise ArgumentError, %Q(Invalid type #{type.inspect}. Valid types: #{TYPES.inspect}.)
|
74
|
+
end
|
75
|
+
|
76
|
+
@type = type
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns the chart's size as "WIDTHxHEIGHT".
|
80
|
+
def size
|
81
|
+
"#{width}x#{height}"
|
82
|
+
end
|
83
|
+
|
84
|
+
# Allows the chart's size to be set as "WIDTHxHEIGHT".
|
85
|
+
def size=(size)
|
86
|
+
self.width, self.height = size.split("x").collect { |n| Integer(n) }
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns the chart's URL.
|
90
|
+
def to_url
|
91
|
+
query = google_query_params.collect { |k, v| "#{k}=#{URI.escape(v)}" }.join("&")
|
92
|
+
"#{URL}?#{query}"
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns the chart's generated PNG as a blob.
|
96
|
+
def fetch
|
97
|
+
open(to_url) { |data| data.read }
|
98
|
+
end
|
99
|
+
|
100
|
+
# Writes the chart's generated PNG. If +io_or_file+ quacks like an IO,
|
101
|
+
# +write+ will be called. Otherwise, write to disk. +io_or_file+ defaults
|
102
|
+
# to "chart.png".
|
103
|
+
def write(io_or_file="chart.png")
|
104
|
+
return io_or_file.write(fetch) if io_or_file.respond_to?(:write)
|
105
|
+
open(io_or_file, "w+") { |f| f.write(fetch) }
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def google_query_params
|
111
|
+
params = { "cht" => google_chart_type, "chd" => google_data, "chs" => size }
|
112
|
+
params["chtt"] = title.tr("\n", "|").gsub(/\s+/, "+") if title
|
113
|
+
params["chco"] = colors.join(",") if colors
|
114
|
+
params.merge(extras)
|
115
|
+
end
|
116
|
+
|
117
|
+
def google_chart_type
|
118
|
+
case type
|
119
|
+
when :line
|
120
|
+
"lc"
|
121
|
+
when :linexy
|
122
|
+
"lxy"
|
123
|
+
when :bar
|
124
|
+
"b" + (horizontal? ? "h" : "v") + (grouped? ? "g" : "s")
|
125
|
+
when :pie
|
126
|
+
"p"
|
127
|
+
when :venn
|
128
|
+
"v"
|
129
|
+
when :scatter
|
130
|
+
"s"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def google_data
|
135
|
+
# we'll just always use the extended encoding for now
|
136
|
+
sets = (Array === data.first ? data : [data]).collect do |set|
|
137
|
+
max = set.max
|
138
|
+
set.collect { |n| GChart.encode_extended(n * (PAIRS.size - 1) / max) }.join
|
139
|
+
end
|
140
|
+
|
141
|
+
"e:#{sets.join(",")}"
|
142
|
+
end
|
143
|
+
end
|
data/lib/version.rb
ADDED
data/test/test_gchart.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "gchart"
|
3
|
+
|
4
|
+
class GChart
|
5
|
+
public :google_query_params, :google_chart_type, :google_data
|
6
|
+
end
|
7
|
+
|
8
|
+
class TestGChart < Test::Unit::TestCase
|
9
|
+
def test_supplies_a_version
|
10
|
+
assert_not_nil(GChart::VERSION)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_allows_hash_initialization
|
14
|
+
assert_equal(:line, GChart.new(:type => :line).type)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_complains_about_unknown_options
|
18
|
+
assert_raise(NoMethodError) { GChart.new(:monkey => :chimchim) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_allows_block_initialization
|
22
|
+
c = GChart.new do |chart|
|
23
|
+
chart.type = :line
|
24
|
+
end
|
25
|
+
|
26
|
+
assert_equal(:line, c.type)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_allows_all_valid_chart_types
|
30
|
+
GChart::TYPES.each do |type|
|
31
|
+
assert_nothing_raised(ArgumentError) { GChart.new(:type => type) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_complains_about_invalid_chart_types
|
36
|
+
assert_raise(ArgumentError) { GChart.new(:type => :monkey) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_allows_size_to_be_specified_combined
|
40
|
+
c = GChart.new(:size => "13x14")
|
41
|
+
assert_equal(13, c.width)
|
42
|
+
assert_equal(14, c.height)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_defaults
|
46
|
+
c = GChart.new
|
47
|
+
assert_equal(:line, c.type)
|
48
|
+
assert_equal([], c.data)
|
49
|
+
assert_equal(300, c.width)
|
50
|
+
assert_equal(200, c.height)
|
51
|
+
assert(!c.horizontal?)
|
52
|
+
assert(!c.grouped?)
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_google_chart_types
|
56
|
+
assert_equal("lc", GChart.new(:type => :line).google_chart_type)
|
57
|
+
assert_equal("lxy", GChart.new(:type => :linexy).google_chart_type)
|
58
|
+
|
59
|
+
assert_equal("bhg", GChart.new(:type => :bar, :horizontal => true, :grouped => true).google_chart_type)
|
60
|
+
assert_equal("bhs", GChart.new(:type => :bar, :horizontal => true, :grouped => false).google_chart_type)
|
61
|
+
assert_equal("bvs", GChart.new(:type => :bar, :horizontal => false, :grouped => false).google_chart_type)
|
62
|
+
|
63
|
+
assert_equal("p", GChart.new(:type => :pie).google_chart_type)
|
64
|
+
assert_equal("v", GChart.new(:type => :venn).google_chart_type)
|
65
|
+
assert_equal("s", GChart.new(:type => :scatter).google_chart_type)
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_google_extended_notation
|
69
|
+
expected = {
|
70
|
+
0 => "AA", 25 => "AZ", 26 => "Aa", 51 => "Az", 52 => "A0", 61 => "A9", 62 => "A-", 63 => "A.",
|
71
|
+
64 => "BA", 89 => "BZ", 90 => "Ba", 115 => "Bz", 116 => "B0", 125 => "B9", 126 => "B-", 127 => "B.",
|
72
|
+
4032 => ".A", 4057 => ".Z", 4058 => ".a", 4083 => ".z", 4084 => ".0", 4093 => ".9", 4094 => ".-", 4095 => ".."
|
73
|
+
}
|
74
|
+
|
75
|
+
expected.each do |original, encoded|
|
76
|
+
assert_equal(encoded, GChart.encode_extended(original))
|
77
|
+
end
|
78
|
+
|
79
|
+
assert_equal("__", GChart.encode_extended(nil))
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_allows_extra_query_params
|
83
|
+
c = GChart.new(:extras => { :foo => :bar })
|
84
|
+
assert(c.google_query_params.include?(:foo))
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_supports_title
|
88
|
+
chart = GChart.new
|
89
|
+
|
90
|
+
assert_nil(chart.google_query_params["chtt"])
|
91
|
+
|
92
|
+
chart.title = "foo"
|
93
|
+
assert_equal("foo", chart.google_query_params["chtt"])
|
94
|
+
|
95
|
+
chart.title = "a space"
|
96
|
+
assert_equal("a+space", chart.google_query_params["chtt"])
|
97
|
+
|
98
|
+
chart.title = "a\nnewline"
|
99
|
+
assert_equal("a|newline", chart.google_query_params["chtt"])
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_supports_colors
|
103
|
+
chart = GChart.new(:colors => ["cccccc", "eeeeee"])
|
104
|
+
assert_equal(chart.google_query_params["chco"], "cccccc,eeeeee")
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_generates_correct_query_params_and_url_for_simple_example
|
108
|
+
expected = { "cht" => "lc", "chs" => "300x200", "chd" => "e:AAAo..", "chtt" => "test" }
|
109
|
+
|
110
|
+
chart = GChart.new(:title => "test", :data => [1, 100, 10000])
|
111
|
+
assert_equal(expected, chart.google_query_params)
|
112
|
+
assert_equal("http://chart.apis.google.com/chart?chs=300x200&cht=lc&chtt=test&chd=e:AAAo..", chart.to_url)
|
113
|
+
end
|
114
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gchart
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ""
|
6
|
+
authors:
|
7
|
+
- John Barnette
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2007-12-10 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.3.0
|
23
|
+
version:
|
24
|
+
description: "== PROBLEMS/TODO * Add support for legends, fills (area or background), grid lines, shape markers, range markers * Support shorthand colors and color names * Make venn data specification friendlier There are lots of missing features. Until they're implemented, you can directly specify query parameters using the :extras key, e.g., # provides a legend for each data set g = GChart.line(:data => [[1, 2], [3, 4]], :extras => { \"chdl\" => [\"First\", \"Second\"] })"
|
25
|
+
email: jbarnette@rubyforge.org
|
26
|
+
executables: []
|
27
|
+
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files:
|
31
|
+
- CHANGELOG.txt
|
32
|
+
- Manifest.txt
|
33
|
+
- README.txt
|
34
|
+
files:
|
35
|
+
- CHANGELOG.txt
|
36
|
+
- Manifest.txt
|
37
|
+
- README.txt
|
38
|
+
- Rakefile
|
39
|
+
- lib/gchart.rb
|
40
|
+
- lib/version.rb
|
41
|
+
- test/test_gchart.rb
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://gchart.rubyforge.org
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options:
|
46
|
+
- --main
|
47
|
+
- README.txt
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
version:
|
62
|
+
requirements: []
|
63
|
+
|
64
|
+
rubyforge_project: gchart
|
65
|
+
rubygems_version: 0.9.5
|
66
|
+
signing_key:
|
67
|
+
specification_version: 2
|
68
|
+
summary: GChart uses the Google Chart API to create pretty pictures.
|
69
|
+
test_files:
|
70
|
+
- test/test_gchart.rb
|