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 CHANGED
@@ -1,3 +1,8 @@
1
+ === 0.0.3 2009-12-18
2
+
3
+ * 1 major enhancement:
4
+ * replaced paths to include special formats (chartxml, chartjs, chartjson) for remote data
5
+
1
6
  === 0.0.2 2009-12-17
2
7
 
3
8
  * 1 major enhancement:
data/README.rdoc CHANGED
@@ -4,22 +4,118 @@
4
4
 
5
5
  == DESCRIPTION:
6
6
 
7
- This is a general framework for inserting various type of charting implementations - from grabbing the data, to declaring how those values are mapped to the desired type of chart (pie, line, bar, etc).
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
- * Fusion Charts (partial)
14
- * Google Charts (partial)
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
- To install this plugin:
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
- data.each do |record|
4
- xml.set :value => value(record, :value), :label => value(record, :label), :color => next_color
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
- data.each do |record|
4
- xml.set :value => value(record, :value), :label => value(record, :label), :link => value(record, :link), :color => next_color
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,7 +1,8 @@
1
1
  xml.instruct!
2
2
  xml.chart(chart_options.merge(:caption => params[:title])) do
3
+ the_case = params[:case]
3
4
  xml.dataset do |dataset|
4
- data.each do |set|
5
+ data(the_case).each do |set|
5
6
  xml.set :value => set
6
7
  end
7
8
  end
@@ -1,6 +1,7 @@
1
1
  xml.instruct!
2
2
  xml.chart(chart_options.merge(:caption => params[:title])) do
3
- data.each do |record|
4
- xml.set :value => value(record, :value), :label => value(record, :label), :color => next_color
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,5 +1,6 @@
1
1
  default: 3.0
2
2
  3.0:
3
+ format: xml
3
4
  angular:
4
5
  data: 'angular.xml.builder'
5
6
  chart_type: AngularGauge
@@ -1,8 +1,9 @@
1
1
  default: 1.2
2
2
  1.2:
3
+ format: js
3
4
  impact:
4
5
  data: 'impact.js.erb'
5
6
  inline: 'inline.html.erb'
6
- url: 'impact.js'
7
+ # url: '.chartjs'
7
8
  dot:
8
9
  inline: 'dot.html.erb'
@@ -1,26 +1,32 @@
1
1
  module ALaChart
2
2
 
3
3
  module HelperMethods
4
- def meta
5
- @metadata ||= defined?(get_meta) ? get_meta : {}
6
- end
7
-
8
- def fields
9
- @fields ||= defined?(get_fields) ? get_fields : []
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
- @data ||= defined?(get_data) ? get_data : []
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[:chart_make]
56
- chart_type = params[:id]
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
- # format.html # index.html.erb
69
- format.xml { render_style chart_make, chart_type, chart_make_version, chart_type_config }
70
- format.js { render_style chart_make, chart_type, chart_make_version, chart_type_config }
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
- self.before_filter(:provide_chart_data, :only => [:show])
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
- define_method("get_data") do
125
- # note: instance_eval binds scope variables, call does not
126
- instance_eval(&block) || []
127
- # block.call(binding)
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
- end
130
-
131
- def fields(*attrs)
132
- field_ary = attrs.map{|d| d.to_sym }
133
-
134
- define_method("get_fields") do
135
- field_ary
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
- append_url = chart_type_config['url']
24
- append_url = "#{chart_style.to_s}.xml" if append_url.blank?
25
- url += "/#{append_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[:chart_make] = chart_make
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'
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.2
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-17 00:00:00 -05:00
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: This is a general framework for inserting various type of charting implementations - from grabbing the data, to declaring how those values are mapped to the desired type of chart (pie, line, bar, etc).
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: This is a general framework for inserting various type of charting implementations - from grabbing the data, to declaring how those values are mapped to the desired type of chart (pie, line, bar, etc).
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