a_la_chart 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -0
- data/README.rdoc +102 -6
- data/configs/fusion/3.0/angular.xml.builder +2 -1
- data/configs/fusion/3.0/bar.xml.builder +3 -2
- data/configs/fusion/3.0/column.xml.builder +3 -2
- data/configs/fusion/3.0/line.xml.builder +2 -1
- data/configs/fusion/3.0/pie.xml.builder +3 -2
- data/configs/fusion/config.yml +1 -0
- data/configs/raphael/config.yml +2 -1
- data/lib/a_la_chart/a_la_chart.rb +66 -32
- data/lib/a_la_chart/a_la_chart_helper.rb +8 -5
- data/lib/a_la_chart.rb +5 -1
- metadata +4 -4
data/History.txt
CHANGED
data/README.rdoc
CHANGED
@@ -4,22 +4,118 @@
|
|
4
4
|
|
5
5
|
== DESCRIPTION:
|
6
6
|
|
7
|
-
|
7
|
+
'a la Chart' (ALC) is a framework for managing various chart implementations - from grabbing the data, to declaring how those values are mapped to the desired type of chart (pie, line, bar, etc), and finally rendering them. Note: ALC is very much in alpha development and not recommended for public use.
|
8
8
|
|
9
9
|
== SUPPORT:
|
10
10
|
|
11
|
-
Note: very much in development
|
11
|
+
Note: very much in alpha development
|
12
12
|
|
13
|
-
*
|
14
|
-
*
|
15
|
-
* gRaphaël (partial)
|
13
|
+
* Google Charts 1.0 (partial)
|
14
|
+
* Fusion Charts 3.x (partial)
|
15
|
+
* gRaphaël 1.2 (partial)
|
16
16
|
|
17
17
|
== INSTALL:
|
18
18
|
|
19
|
-
|
19
|
+
It is recommended to install this as a gem from http://gemcutter.org
|
20
|
+
|
21
|
+
gem sources -a http://gemcutter.org
|
22
|
+
gem install a_la_chart
|
23
|
+
|
24
|
+
Set up the gem in your environment.rb
|
25
|
+
|
26
|
+
config.gem "a_la_chart", :source => "http://gemcutter.org"
|
27
|
+
|
28
|
+
OR - to install this directly as a Rails plugin (not recommended, but available):
|
20
29
|
|
21
30
|
script/plugin install git://github.com/coderoshi/a_la_chart.git
|
22
31
|
|
32
|
+
Once you have installed 'a la Chart', you can start creating charts immediately. Since Google Charts can function without any further install (it just hits a Google URL to generate an image inline), you can technically use 'a la Chart' (ALC) without any further install - but that's also kind of boring. If you need to use another supported charting framework, such as Fusion Charts or gRaphaël, you must install them locally, as per their normal install instructions. This generally means placing the required files (javascript, flash) in your project's /public directory, and linking to them via html header tags in your relevant /app/views/layouts files.
|
33
|
+
|
34
|
+
For example, in our project we use the gRaphaël dot chart, so populate our /app/views/layouts/reports.html.haml with this HAML javascript tag
|
35
|
+
|
36
|
+
= javascript_include_tag "raphael", "jquery", "g.raphael.js", "g.dot.js"
|
37
|
+
|
38
|
+
We also use Fusion charts, and install them in a similar way (along with the necessary flash and javascript files under /public)
|
39
|
+
|
40
|
+
= javascript_include_tag "FusionCharts", "FusionMaps", "FusionChartsDOM"
|
41
|
+
|
42
|
+
== USAGE:
|
43
|
+
|
44
|
+
ALC logic is in two parts: controller and view. To activate a controller add the 'a_la_chart' directive
|
45
|
+
|
46
|
+
class PeopleController < ApplicationController
|
47
|
+
a_la_chart
|
48
|
+
end
|
49
|
+
|
50
|
+
=== Data and Mapping
|
51
|
+
|
52
|
+
To declare the style of data returned, you call 'chart' with the style of chart (you can have more than one, eg. 'chart :pie, :line'). Within the chart directive, you return the list of data objects (Objects or Hashes), and finally a 'meta', which describes how to map each object to the relevant chart data. For example, we have a Person model, tied to a city name. We want to build a pie chart which counts the people in each city.
|
53
|
+
|
54
|
+
class PeopleController < ApplicationController
|
55
|
+
a_la_chart
|
56
|
+
|
57
|
+
chart :pie do
|
58
|
+
data do
|
59
|
+
Person.all(:select => 'city, COUNT(people.*) as total', :group => 'city')
|
60
|
+
end
|
61
|
+
meta :label => :city, :value => :total
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
http://chart.apis.google.com/chart?cht=p&chd=t:6,5,3&chs=300x180&chl=Chicago|Indy|New%20York&.jpg
|
66
|
+
|
67
|
+
In a pie chart, there are two kinds of required data per slice: A value (how many people live in each city) and a label (what is the name of the city). meta points the two generic requirements (label and value) to specific methods in the returned objects (or key names, if an array of Hash objects are returned). ALC will effectively call "item.city" for each label.
|
68
|
+
|
69
|
+
If your query does not return data which maps to a specific method or key, meta can also map requirements to procedure blocks (Proc.new, proc, or lambda) which procedurally extracts values from each item in the returned list of data values.
|
70
|
+
|
71
|
+
chart :pie do
|
72
|
+
data do
|
73
|
+
Person.count(:group => :gender)
|
74
|
+
end
|
75
|
+
meta :label => proc{|item| item[0].blank? ? 'Unknown' : item[0] }, :value => 1
|
76
|
+
end
|
77
|
+
|
78
|
+
ALC will effectively call "item[0].blank? ? 'Unknown' : item[0]" for each label.
|
79
|
+
|
80
|
+
=== View Chart
|
81
|
+
|
82
|
+
Note that we have yet to define the type of charting engine we wish to use. Since that is a view-level decision, we will place that in the view. Before continuing, let us assume you wish to display a report page with a table of people information - alongside the pie chart which displays the count of people per city.
|
83
|
+
|
84
|
+
To start, create a "def index" in the PeopleController which returns the list of people, as you normally would.
|
85
|
+
|
86
|
+
class PeopleController < ApplicationController
|
87
|
+
a_la_chart
|
88
|
+
|
89
|
+
chart :pie do
|
90
|
+
data do
|
91
|
+
Person.all(:select => 'city, COUNT(people.*) as total', :group => 'city')
|
92
|
+
end
|
93
|
+
meta :label => :city, :value => :total
|
94
|
+
end
|
95
|
+
|
96
|
+
def index
|
97
|
+
@people = People.all
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
The corresponding index.html.haml (or index.html.erb, or whatever) will look something like this
|
102
|
+
|
103
|
+
= chart_tag :google, :pie
|
104
|
+
%table
|
105
|
+
%tr
|
106
|
+
%th Name
|
107
|
+
%th City
|
108
|
+
- @people.each do |person|
|
109
|
+
%tr
|
110
|
+
%td&= person.name
|
111
|
+
%td&= person.city
|
112
|
+
|
113
|
+
Except for the chart_tag, it's pretty normal. Here we pass in the minimum required fields for chart_tag: the chart implementation (:google, :fusion, :raphael are the first to be supported, more to come), and the chart type (dependent on the implementation, see a_la_chart/configs/*/config.yml for the lists). Certain configs are supported by all implementations, such as :width and :height.
|
114
|
+
|
115
|
+
= chart_tag :fusion, :angular, :height => 300, :width => 300, :title => 'My Angular Chart'
|
116
|
+
|
117
|
+
More to come...
|
118
|
+
|
23
119
|
== LICENSE:
|
24
120
|
|
25
121
|
Copyright (c) 2009 Mobi, released under the MIT license
|
@@ -1,11 +1,12 @@
|
|
1
1
|
xml.instruct!
|
2
2
|
xml.chart(chart_options.merge(:upperLimit => params[:upperLimit] || 100, :caption => params[:title])) do
|
3
|
+
the_case = params[:case]
|
3
4
|
xml.colorRange do
|
4
5
|
xml.color :minValue => '0', :maxValue => '2', :code => 'FF654F'
|
5
6
|
xml.color :minValue => '2', :maxValue => '4', :code => 'F6BD0F'
|
6
7
|
xml.color :minValue => '4', :maxValue => '5', :code => '8BBA00'
|
7
8
|
end
|
8
9
|
xml.dials do
|
9
|
-
xml.dial :value => value(data, :value)
|
10
|
+
xml.dial :value => value(data, :value, the_case)
|
10
11
|
end
|
11
12
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
xml.instruct!
|
2
2
|
xml.chart(chart_options.merge(:caption => params[:title])) do
|
3
|
-
|
4
|
-
|
3
|
+
the_case = params[:case]
|
4
|
+
data(the_case).each do |record|
|
5
|
+
xml.set :value => value(record, :value, the_case), :label => value(record, :label, the_case), :color => next_color
|
5
6
|
end
|
6
7
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
xml.instruct!
|
2
2
|
xml.chart(chart_options.merge(:caption => params[:title])) do
|
3
|
-
|
4
|
-
|
3
|
+
the_case = params[:case]
|
4
|
+
data(the_case).each do |record|
|
5
|
+
xml.set :value => value(record, :value, the_case), :label => value(record, :label, the_case), :link => value(record, :link, the_case), :color => next_color
|
5
6
|
end
|
6
7
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
xml.instruct!
|
2
2
|
xml.chart(chart_options.merge(:caption => params[:title])) do
|
3
|
-
|
4
|
-
|
3
|
+
the_case = params[:case]
|
4
|
+
data(the_case).each do |record|
|
5
|
+
xml.set :value => value(record, :value, the_case), :label => value(record, :label, the_case), :color => next_color
|
5
6
|
end
|
6
7
|
end
|
data/configs/fusion/config.yml
CHANGED
data/configs/raphael/config.yml
CHANGED
@@ -1,26 +1,32 @@
|
|
1
1
|
module ALaChart
|
2
2
|
|
3
3
|
module HelperMethods
|
4
|
-
def meta
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
def meta(the_case=nil)
|
5
|
+
if the_case.blank?
|
6
|
+
@metadata ||= defined?(get_meta) ? get_meta : {}
|
7
|
+
else
|
8
|
+
@metadata ||= respond_to?("get_meta_#{the_case.to_s}") ? send("get_meta_#{the_case.to_s}") : []
|
9
|
+
end
|
10
10
|
end
|
11
|
-
|
12
|
-
def data
|
13
|
-
|
11
|
+
|
12
|
+
def data(the_case=nil)
|
13
|
+
if the_case.blank?
|
14
|
+
@data ||= defined?(get_data) ? get_data : []
|
15
|
+
else
|
16
|
+
@data ||= respond_to?("get_data_#{the_case.to_s}") ? send("get_data_#{the_case.to_s}") : []
|
17
|
+
end
|
14
18
|
end
|
15
|
-
|
16
|
-
def value(object, key)
|
17
|
-
return '' if object.blank?
|
18
19
|
|
20
|
+
def value(object, key, the_case=nil)
|
21
|
+
return '' if object.blank?
|
22
|
+
|
19
23
|
# if there is a meta map, use it. else try the key itself
|
20
|
-
key_field = meta[key] || key
|
21
|
-
|
24
|
+
key_field = meta(the_case)[key] || key
|
25
|
+
|
22
26
|
if key_field.class == Proc
|
23
27
|
val = key_field.call(object)
|
28
|
+
elsif key_field.class == Fixnum
|
29
|
+
val = object[key_field] if object.respond_to?('[]')
|
24
30
|
else
|
25
31
|
val = object.respond_to?(key_field) ? object.send(key_field) : nil
|
26
32
|
val = object[key_field] if object.respond_to?('[]')
|
@@ -52,8 +58,8 @@ module ALaChart
|
|
52
58
|
include HelperMethods
|
53
59
|
|
54
60
|
def provide_chart_data
|
55
|
-
chart_make = params[:
|
56
|
-
chart_type = params[:
|
61
|
+
chart_make = params[:cm]
|
62
|
+
chart_type = params[:ct]
|
57
63
|
|
58
64
|
chart_type_config, chart_make_version = nil, nil
|
59
65
|
if !chart_make.nil? && (chart_make_config = a_la_chart_config[chart_make.to_sym])
|
@@ -65,9 +71,9 @@ module ALaChart
|
|
65
71
|
return if chart_type_config.nil? || !respond_to?("set_chart_#{chart_type}")
|
66
72
|
|
67
73
|
respond_to do |format|
|
68
|
-
|
69
|
-
format.
|
70
|
-
format.
|
74
|
+
format.chartxml { render_style(chart_make, chart_type, chart_make_version, chart_type_config) }
|
75
|
+
format.chartjs { render_style(chart_make, chart_type, chart_make_version, chart_type_config) }
|
76
|
+
format.chartjson { render_style(chart_make, chart_type, chart_make_version, chart_type_config) }
|
71
77
|
end
|
72
78
|
end
|
73
79
|
|
@@ -100,7 +106,10 @@ module ALaChart
|
|
100
106
|
def a_la_chart
|
101
107
|
include ALaChart::InstanceMethods
|
102
108
|
|
103
|
-
|
109
|
+
# TODO: We should ensure this is for inherited_resources
|
110
|
+
self.respond_to(:chartxml, :chartjs, :chartjson) if defined?(self.respond_to)
|
111
|
+
|
112
|
+
self.before_filter(:provide_chart_data, :only => [:index, :show])
|
104
113
|
|
105
114
|
# Namespace this stuff??
|
106
115
|
[:data, :fields, :meta, :value, :a_la_chart_config, :set_chart].each do |method|
|
@@ -120,19 +129,44 @@ module ALaChart
|
|
120
129
|
end
|
121
130
|
end
|
122
131
|
|
123
|
-
def data(&block)
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
132
|
+
def data(*attrs, &block)
|
133
|
+
if attrs.size == 1
|
134
|
+
attrs = attrs[0]
|
135
|
+
if attrs.class == Hash
|
136
|
+
options = attrs
|
137
|
+
elsif attrs.class == Symbol || attrs.class == String
|
138
|
+
cases = [attrs]
|
139
|
+
end
|
140
|
+
elsif attrs.size > 1
|
141
|
+
cases = attrs[0...-1]
|
142
|
+
options = attrs[-1]
|
128
143
|
end
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
144
|
+
|
145
|
+
cases ||= []
|
146
|
+
options ||= {}
|
147
|
+
|
148
|
+
if cases.blank?
|
149
|
+
define_method("get_data") do
|
150
|
+
# note: instance_eval binds scope variables, call does not
|
151
|
+
instance_eval(&block) || []
|
152
|
+
# block.call(binding)
|
153
|
+
end
|
154
|
+
unless options.blank?
|
155
|
+
define_method("get_meta") do
|
156
|
+
options
|
157
|
+
end
|
158
|
+
end
|
159
|
+
else
|
160
|
+
cases.each { |caze|
|
161
|
+
define_method("get_data_#{caze}") do
|
162
|
+
# note: instance_eval binds scope variables, call does not
|
163
|
+
instance_eval(&block) || []
|
164
|
+
# block.call(binding)
|
165
|
+
end
|
166
|
+
define_method("get_meta_#{caze}") do
|
167
|
+
options
|
168
|
+
end
|
169
|
+
}
|
136
170
|
end
|
137
171
|
end
|
138
172
|
|
@@ -20,16 +20,18 @@ module ALaChartHelper
|
|
20
20
|
chart_type = chart_style.to_s if chart_type.blank?
|
21
21
|
inline_template = chart_type_config['inline']
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
url
|
23
|
+
data_format = chart_type_config['format'] || chart_make_config['format']
|
24
|
+
|
25
|
+
append_url = chart_type_config['url'] || ".chart#{data_format}"
|
26
|
+
url += "#{append_url}?"
|
26
27
|
|
27
28
|
explicit_args = args[:args].present? ? params.merge(args[:args]) : params
|
28
29
|
args.reject!{ |k,v| [:name,:width,:height,:base_url].include?(k) }
|
29
30
|
args.merge!(explicit_args)
|
30
31
|
# Can I do this? Or does it make me look like...
|
31
32
|
args.reject!{ |k,v| ['action','controller'].include?(k) }
|
32
|
-
args[:
|
33
|
+
args[:cm] = chart_make.to_s
|
34
|
+
args[:ct] = chart_style.to_s
|
33
35
|
|
34
36
|
url += args.to_param
|
35
37
|
|
@@ -38,7 +40,8 @@ module ALaChartHelper
|
|
38
40
|
inline = ERB.new(File.read(File.join(File.dirname(__FILE__), '..', '..', 'configs', chart_make.to_s, chart_make_version.to_s, inline_template)))
|
39
41
|
inline.result(binding)
|
40
42
|
end
|
41
|
-
|
43
|
+
|
44
|
+
# TODO: REMOVE all of this stuff... make this all external configurations
|
42
45
|
def color_palette
|
43
46
|
["7BB465","B2B4B6","FEC35A","65A4B5","9E65B5","B57765","F7DF65","8F866C","B6A65F","C99D60","C1727A","8AABB9","65B584"]
|
44
47
|
end
|
data/lib/a_la_chart.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module ALaChart
|
2
|
-
VERSION = '0.0.
|
2
|
+
VERSION = '0.0.3'
|
3
3
|
end
|
4
4
|
|
5
5
|
require File.join(File.dirname(__FILE__), 'a_la_chart', 'a_la_chart')
|
@@ -9,3 +9,7 @@ ActionController::Base.class_eval do
|
|
9
9
|
include ALaChart
|
10
10
|
end
|
11
11
|
ActionView::Base.send :include, ALaChartHelper
|
12
|
+
|
13
|
+
Mime::Type.register "application/xml", :chartxml, %w( text/xml application/x-xml ) unless defined?(Mime::CHARTXML)
|
14
|
+
Mime::Type.register "text/javascript", :chartjs, %w( application/javascript application/x-javascript ) unless defined?(Mime::CHARTJS)
|
15
|
+
Mime::Type.register "application/json", :chartjson, %w( text/x-json application/jsonrequest ) unless defined?(Mime::CHARTJSON)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: a_la_chart
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Redmond
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-12-
|
12
|
+
date: 2009-12-18 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -22,7 +22,7 @@ dependencies:
|
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: 2.3.3
|
24
24
|
version:
|
25
|
-
description:
|
25
|
+
description: "'a la Chart' (ALC) is a framework for managing various chart implementations - from grabbing the data, to declaring how those values are mapped to the desired type of chart (pie, line, bar, etc), and finally rendering them. Note: ALC is very much in alpha development and not recommended for public use."
|
26
26
|
email:
|
27
27
|
- eric.redmond@gmail.com
|
28
28
|
executables: []
|
@@ -89,7 +89,7 @@ rubyforge_project: a_la_chart
|
|
89
89
|
rubygems_version: 1.3.5
|
90
90
|
signing_key:
|
91
91
|
specification_version: 3
|
92
|
-
summary:
|
92
|
+
summary: "'a la Chart' (ALC) is a framework for managing various chart implementations - from grabbing the data, to declaring how those values are mapped to the desired type of chart (pie, line, bar, etc), and finally rendering them"
|
93
93
|
test_files:
|
94
94
|
- test/test_a_la_chart.rb
|
95
95
|
- test/test_helper.rb
|