google_otg 1.0.9

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ *~
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 esilverberg
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,66 @@
1
+ = google_otg
2
+
3
+ Author:: Eric Silverberg (http://www.ericsilverberg.com)
4
+ Copyright:: Copyright (c) 2009 Eric Silverberg
5
+ License:: MIT (Go Beavers!)
6
+ Git:: http://github.com/esilverberg/google_otg/tree/master
7
+
8
+ This plugin adds helpers to include Google's pretty over time line graph in your rails application. You will
9
+ recognize this line graph from Google Analytics.
10
+
11
+ Many features are missing, including support for multiple lines and colors. Feel free to add.
12
+
13
+ == Requirements
14
+ You must be able to generate arrays of objects that respond to "count" and "created_at". The X-axis is presumed to be dates. You can control time step of the x-axis.
15
+
16
+ == Example Usage
17
+ In your controller:
18
+
19
+ @hits_last_week = Hits.find_by_sql(["
20
+ SELECT
21
+ DAYOFYEAR(hits.created_at) as d,
22
+ DATE(hits.created_at) as created_at,
23
+ count(*) as count
24
+ FROM hits
25
+ WHERE hits.created_at > UTC_TIMESTAMP() - INTERVAL 7 DAY
26
+ GROUP BY d
27
+ ORDER BY created_at"])
28
+
29
+ In your view:
30
+
31
+ <%= over_time_graph(@hits_last_week) %>
32
+ or
33
+ <%= over_time_graph(@hits_last_week, :label => "Hits", :range => 1440, :x_label_format => "%A, %B %d", :src => "/google/OverTimeGraph.swf") %>
34
+
35
+ == +over_time_graph+
36
+
37
+ Some of the options available:
38
+
39
+ <tt>:label</tt>:: The label of quantity being measured
40
+ <tt>:range</tt>:: The time step, in minutes
41
+ <tt>:x_label_format</tt>:: The time format for the x label
42
+ <tt>:src</tt>:: An optional local source to serve this widget (otherwise will serve from Google)
43
+
44
+ == +google_line_graph+
45
+
46
+ Example:
47
+
48
+ Some of the options available:
49
+
50
+ google_line_graph(
51
+ [@impressions, @conversions],
52
+ :x_label_format => "%a, %b %d",
53
+ :time_zone => @time_zone,
54
+ :range => 1440,
55
+ :title => @company_name,
56
+ :legend => ['Impressions','Conversions'])
57
+
58
+ <tt>:title</tt>:: The title of this graph
59
+ <tt>:legend</tt>:: The graph legend
60
+ <tt>:title_color</tt>:: The title color
61
+ <tt>:title_size</tt>:: Title font size
62
+ <tt>:grid_lines</tt>:: Grid lines on the graph
63
+
64
+ == Copyright
65
+
66
+ Copyright (c) 2009 esilverberg. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "google_otg"
8
+ gem.summary = %Q{Google's amazing over-time graph, in your rails app}
9
+ gem.description = %Q{Include Google's Over Time Graph in your app}
10
+ gem.email = "eric@ericsilverberg.com"
11
+ gem.homepage = "http://github.com/esilverberg/google_otg"
12
+ gem.authors = ["esilverberg"]
13
+ gem.add_development_dependency "thoughtbot-shoulda"
14
+ gem.add_dependency "mattetti-googlecharts"
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
20
+ end
21
+
22
+ require 'rake/testtask'
23
+ Rake::TestTask.new(:test) do |test|
24
+ test.libs << 'lib' << 'test'
25
+ test.pattern = 'test/**/*_test.rb'
26
+ test.verbose = true
27
+ end
28
+
29
+ begin
30
+ require 'rcov/rcovtask'
31
+ Rcov::RcovTask.new do |test|
32
+ test.libs << 'test'
33
+ test.pattern = 'test/**/*_test.rb'
34
+ test.verbose = true
35
+ end
36
+ rescue LoadError
37
+ task :rcov do
38
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
39
+ end
40
+ end
41
+
42
+ task :test => :check_dependencies
43
+
44
+ task :default => :test
45
+
46
+ require 'rake/rdoctask'
47
+ Rake::RDocTask.new do |rdoc|
48
+ if File.exist?('VERSION')
49
+ version = File.read('VERSION')
50
+ else
51
+ version = ""
52
+ end
53
+
54
+ rdoc.rdoc_dir = 'rdoc'
55
+ rdoc.title = "google_otg #{version}"
56
+ rdoc.rdoc_files.include('README*')
57
+ rdoc.rdoc_files.include('lib/**/*.rb')
58
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.9
@@ -0,0 +1,58 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{google_otg}
8
+ s.version = "1.0.9"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["esilverberg"]
12
+ s.date = %q{2009-10-01}
13
+ s.description = %q{Include Google's Over Time Graph in your app}
14
+ s.email = %q{eric@ericsilverberg.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "google_otg.gemspec",
27
+ "lib/gchart_mod.rb",
28
+ "lib/google_otg.rb",
29
+ "rails/init.rb",
30
+ "test/google_otg_test.rb",
31
+ "test/test_helper.rb"
32
+ ]
33
+ s.homepage = %q{http://github.com/esilverberg/google_otg}
34
+ s.rdoc_options = ["--charset=UTF-8"]
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = %q{1.3.5}
37
+ s.summary = %q{Google's amazing over-time graph, in your rails app}
38
+ s.test_files = [
39
+ "test/google_otg_test.rb",
40
+ "test/test_helper.rb"
41
+ ]
42
+
43
+ if s.respond_to? :specification_version then
44
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
45
+ s.specification_version = 3
46
+
47
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
48
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
49
+ s.add_runtime_dependency(%q<mattetti-googlecharts>, [">= 0"])
50
+ else
51
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
52
+ s.add_dependency(%q<mattetti-googlecharts>, [">= 0"])
53
+ end
54
+ else
55
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
56
+ s.add_dependency(%q<mattetti-googlecharts>, [">= 0"])
57
+ end
58
+ end
data/lib/gchart_mod.rb ADDED
@@ -0,0 +1,105 @@
1
+ require 'gchart'
2
+
3
+ class Gchart
4
+ attr_accessor :grid_lines, :shape_markers
5
+
6
+ def set_shape_markers
7
+ shape_markers_values = case @shape_markers
8
+ when String
9
+ @shape_markers
10
+ when Array
11
+ if @shape_markers[0].is_a?(Array)
12
+ @shape_markers.map{|sm|sm.join(",")}.join("|")
13
+ else
14
+ @shape_markers.join("|")
15
+ end
16
+ when Hash
17
+ marker_type = @shape_markers[:marker_type]
18
+ color = @shape_markers[:color]
19
+ data_set_index = @shape_markers[:data_set_index]
20
+ data_point = @shape_markers[:data_point]
21
+ size = @shape_markers[:size]
22
+ priority = @shape_markers[:priority]
23
+ [marker_type,color,data_set_index,data_point,size,priority].join(",")
24
+ else
25
+ @shape_makers.to_s
26
+ end
27
+ "chm=#{shape_markers_values}"
28
+ end
29
+
30
+ def set_grid_lines
31
+ grid_lines_values = case @grid_lines
32
+ when String
33
+ @grid_lines
34
+ when Array
35
+ @grid_lines.join(",")
36
+ when Hash
37
+ x_step = @grid_lines[:x_step]
38
+ y_step = @grid_lines[:y_step]
39
+ line_length = @grid_lines[:line_length]
40
+ blank_length = @grid_lines[:blank_length]
41
+ x_offset = @grid_lines[:x_offset]
42
+ y_offset = @grid_lines[:y_offset]
43
+ [x_step,y_step,line_length,blank_length,x_offset,y_offset].join(",")
44
+ else
45
+ @grid_lines.to_s
46
+ end
47
+ "chg=#{grid_lines_values}"
48
+ end
49
+
50
+ def query_builder(options="")
51
+ dataset
52
+ query_params = instance_variables.map do |var|
53
+ case var
54
+ when '@data'
55
+ set_data unless @data == []
56
+ # Set the graph size
57
+ when '@width'
58
+ set_size unless @width.nil? || @height.nil?
59
+ when '@type'
60
+ set_type
61
+ when '@title'
62
+ set_title unless @title.nil?
63
+ when '@legend'
64
+ set_legend unless @legend.nil?
65
+ when '@bg_color'
66
+ set_colors
67
+ when '@chart_color'
68
+ set_colors if @bg_color.nil?
69
+ when '@bar_colors'
70
+ set_bar_colors
71
+ when '@bar_width_and_spacing'
72
+ set_bar_width_and_spacing
73
+ when '@axis_with_labels'
74
+ set_axis_with_labels
75
+ when '@axis_range'
76
+ set_axis_range if dataset
77
+ when '@axis_labels'
78
+ set_axis_labels
79
+ when '@range_markers'
80
+ set_range_markers
81
+ when '@geographical_area'
82
+ set_geographical_area
83
+ when '@country_codes'
84
+ set_country_codes
85
+ when '@grid_lines'
86
+ set_grid_lines
87
+ when '@shape_markers'
88
+ set_shape_markers
89
+ when '@custom'
90
+ @custom
91
+ end
92
+ end.compact
93
+
94
+ # Use ampersand as default delimiter
95
+ unless options == :html
96
+ delimiter = '&'
97
+ # Escape ampersand for html image tags
98
+ else
99
+ delimiter = '&amp;'
100
+ end
101
+
102
+ jstize(@@url + query_params.join(delimiter))
103
+ end
104
+
105
+ end
data/lib/google_otg.rb ADDED
@@ -0,0 +1,337 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+ require 'gchart_mod'
3
+ require 'uri'
4
+
5
+ module GoogleOtg
6
+
7
+ DEFAULT_RANGE = 30 # 30 min
8
+
9
+ def google_line_graph(hits, args = {})
10
+
11
+ raise ArgumentError, "Invalid hits" unless hits && hits.length > 0
12
+
13
+ size = args.has_key?(:size) ? args[:size] : '800x200'
14
+ title = args.has_key?(:title) ? args[:title] : "Graph"
15
+ title_color = args.has_key?(:title_color) ? args[:title_color] : '000000'
16
+ title_size = args.has_key?(:title_size) ? args[:title_size] : '20'
17
+ grid_lines = args.has_key?(:grid_lines) ? args[:grid_lines] : [25,50]
18
+ legend = args.has_key?(:legend) ? args[:legend] : nil
19
+
20
+ x_labels = []
21
+ y_labels = [0]
22
+ data = []
23
+
24
+ if hits[0].is_a?(Array)
25
+ shape_markers = [['o','0000ff',0,'-1.0',6],['o','FF6600',1,'-1.0',6]]
26
+ line_colors = ['6699CC','FF9933']
27
+
28
+ hits.map{|h|
29
+ converted = hits_to_gchart_range(h, args)
30
+ data.push(converted[:points])
31
+ x_labels = converted[:x_labels] if converted[:x_labels].length > x_labels.length
32
+ y_labels = converted[:y_labels] if converted[:y_labels].max > y_labels.max
33
+ }
34
+
35
+ else
36
+ shape_markers = ['o','0000ff',0,'-1.0',6]
37
+ line_colors = '6699CC'
38
+
39
+ converted = hits_to_gchart_range(hits, args)
40
+ data.push(converted[:points])
41
+ x_labels = converted[:x_labels]
42
+ y_labels = converted[:y_labels]
43
+
44
+ end
45
+
46
+ axis_with_labels = 'x,y'
47
+ axis_labels = [x_labels,y_labels]
48
+
49
+ return Gchart.line(
50
+ :size => size,
51
+ :title => title,
52
+ :title_color => title_color,
53
+ :title_size => title_size,
54
+ :grid_lines => grid_lines,
55
+ :shape_markers => shape_markers,
56
+ :data => data,
57
+ :axis_with_labels => axis_with_labels,
58
+ :legend => legend,
59
+ :axis_labels => axis_labels,
60
+ :line_colors => line_colors)
61
+
62
+ end
63
+
64
+ def over_time_graph(hits, args = {})
65
+ height = args.has_key?(:height) ? args[:height] : 125
66
+ src = args.has_key?(:src) ? args[:src] : "http://www.google.com/analytics/static/flash/OverTimeGraph.swf"
67
+
68
+ range = hits_to_otg_range(hits, args)
69
+ vars = range_to_flashvars(range)
70
+
71
+ html = <<-eos
72
+ <embed width="100%" height="#{height}"
73
+ wmode="opaque" salign="tl" scale="noScale" quality="high" bgcolor="#FFFFFF"
74
+ flashvars="input=#{vars}"
75
+ pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash"
76
+ src="#{src}"/>
77
+ eos
78
+
79
+ return html
80
+
81
+ end
82
+
83
+ def google_pie(hits, label_fn, args = {})
84
+ height = args.has_key?(:height) ? args[:height] : 125
85
+ width = args.has_key?(:width) ? args[:width] : 125
86
+ pie_values = extract_pct_values(hits, label_fn, args)
87
+ vars = pie_to_flashvars(pie_values, args)
88
+ src = args.has_key?(:src) ? args[:src] : "http://www.google.com/analytics/static/flash/pie.swf"
89
+
90
+ html = <<-eos
91
+ <embed
92
+ width="#{width}"
93
+ height="#{height}"
94
+ salign="tl"
95
+ scale="noScale"
96
+ quality="high"
97
+ bgcolor="#FFFFFF"
98
+ flashvars="input=#{vars}&amp;locale=en-US"
99
+ pluginspage="http://www.macromedia.com/go/getflashplayer"
100
+ type="application/x-shockwave-flash"
101
+ src="#{src}"/>
102
+ eos
103
+ return html
104
+
105
+ end
106
+
107
+ def pie_to_flashvars(args = {})
108
+
109
+ labels = args[:labels]
110
+ raw_values = args[:raw_values]
111
+ percent_values = args[:percent_values]
112
+
113
+ options = {
114
+ :Pie => {
115
+ :Id => "Pie",
116
+ :Compare => false,
117
+ :HasOtherSlice => false,
118
+ :RawValues => raw_values,
119
+ :Format => "DASHBOARD",
120
+ :PercentValues => percent_values
121
+ }
122
+ }
123
+
124
+ return URI::encode(options.to_json)
125
+
126
+ end
127
+ protected :pie_to_flashvars
128
+
129
+ def extract_pct_values(hits, label_fn, args = {})
130
+
131
+ limit = args.has_key?(:limit) ? args[:limit] : 0.0
132
+
133
+ total = 0.0
134
+ other = 0.0
135
+ percent_values = []
136
+ raw_values = []
137
+ labels = []
138
+ values = []
139
+ hits.each{|hit|
140
+ total += hit.count.to_f
141
+ }
142
+ hits.each{|hit|
143
+ ct = hit.count.to_f
144
+ pct = (ct / total)
145
+
146
+ if pct > limit
147
+ percent_values.push([pct, sprintf("%.2f%%", pct * 100)])
148
+ raw_values.push([ct, ct])
149
+
150
+ label = label_fn.call(hit)
151
+ meta = args.has_key?(:meta) ? args[:meta].call(hit) : nil
152
+
153
+ labels.push(label)
154
+ values.push({:label => label, :meta => meta, :percent_value => [pct, sprintf("%.2f%%", pct * 100)], :raw_value => ct})
155
+ else
156
+ other += ct
157
+ end
158
+ }
159
+ if other > 0.0
160
+ pct = other / total
161
+ percent_values.push([pct, sprintf("%.2f%%", pct * 100)])
162
+ raw_values.push([other, other])
163
+ labels.push("Other")
164
+ values.push({:label => "Other", :percent_value => [pct, sprintf("%.2f%%", pct * 100)], :raw_value => other})
165
+ end
166
+
167
+ return {:labels => labels, :raw_values => raw_values, :percent_values => percent_values, :values => values}
168
+
169
+ end
170
+ protected :extract_pct_values
171
+
172
+ def flto10(val)
173
+ return ((val / 10) * 10).to_i
174
+ end
175
+ protected :flto10
176
+
177
+ def hits_to_otg_range(hits, args = {})
178
+ return hits_to_range(hits, lambda {|count, date_key, date_value|
179
+ {:Value => [count, count], :Label => [date_key, date_value]}
180
+ }, lambda{|mid, top|
181
+ [[mid,mid],[top,top]]
182
+ }, lambda{|hit, hit_date_key, hit_date_value|
183
+ [hit_date_key, hit_date_value]
184
+ },args)
185
+ end
186
+
187
+ def hits_to_gchart_range(hits, args = {})
188
+ return hits_to_range(hits, lambda {|count, date_key, date_value|
189
+ count
190
+ }, lambda {|mid, top|
191
+ [0,top/2,top]
192
+ },lambda{|hit, hit_date_key, hit_date_value|
193
+ hit_date_value
194
+ }, args)
195
+ end
196
+
197
+ def hits_to_range(hits, points_fn, y_label_fn, x_label_fn, args = {})
198
+
199
+ return nil unless hits
200
+
201
+ hits.map{|h|
202
+ if !h.respond_to?("created_at") || !h.respond_to?("count")
203
+ raise ArgumentError, "Invalid object type. All objects must respond to 'count' and 'created_at'"
204
+ end
205
+ }
206
+
207
+ tz = args.has_key?(:time_zone) ? args[:time_zone] : ActiveSupport::TimeZone['UTC']
208
+ Time.zone = tz
209
+ label = args.has_key?(:label) ? args[:label] : "Value"
210
+ time_fn = args.has_key?(:time_fn) ? args[:time_fn] : lambda {|h| h.created_at }
211
+ range = args.has_key?(:range) ? args[:range] : DEFAULT_RANGE
212
+ x_label_format = args.has_key?(:x_label_format) ? args[:x_label_format] : "%A %I:%M%p"
213
+
214
+ max_y = 0
215
+ hits_dict = {}
216
+ hits.each { |h|
217
+ hits_dict[time_fn.call(h)] = h
218
+ }
219
+
220
+ total = 0
221
+
222
+ points = []
223
+ point_dates = []
224
+
225
+ now_days = Time.now # use this get the right year, month and day
226
+ now_minutes = Time.at((Time.now.to_i/(60*range))*(60*range)).gmtime
227
+ now_floored = Time.local(now_days.year, now_days.month, now_days.day,
228
+ now_minutes.hour, now_minutes.min, now_minutes.sec)
229
+
230
+ current = hits.length > 0 ? time_fn.call(hits[0]) : now_floored
231
+
232
+ while (current < now_floored + range.minutes && range > 0) do
233
+ if hits_dict[current]
234
+ count = hits_dict[current].count.to_i
235
+ max_y = count if count > max_y
236
+
237
+ date = time_fn.call(hits_dict[current])
238
+ date_key = date.to_i
239
+ date_value = date.strftime(x_label_format)
240
+
241
+ points.push(points_fn.call(count, date_key, date_value))
242
+ total += count
243
+ else
244
+
245
+ date = current
246
+ date_key = date.to_i
247
+ date_value = date.strftime(x_label_format)
248
+
249
+ points.push(points_fn.call(0, date_key, date_value))
250
+ end
251
+ # Save the date for the x labels later
252
+ point_dates.push({:key => date_key, :value => date_value})
253
+ current = current + range.minutes
254
+ break if points.length > 100
255
+ end
256
+
257
+ ## Setup Y axis labels ##
258
+ max_y = args.has_key?(:max_y) ? (args[:max_y] > max_y ? args[:max_y] : max_y) : max_y
259
+
260
+ top_y = self.flto10(max_y) + 10
261
+ mid_y = self.flto10(top_y / 2)
262
+ y_labels = y_label_fn.call(mid_y, top_y)
263
+ ## end y axis labels ##
264
+
265
+ ## Setup X axis labels
266
+ x_labels = []
267
+ max_x_label_count = args.has_key?(:max_x_label_count) ? args[:max_x_label_count] : points.length
268
+
269
+ if points.length > 0
270
+ step = points.length / max_x_label_count
271
+ idx = 0
272
+
273
+ while idx < points.length
274
+ point = points[idx]
275
+ date = point_dates[idx]
276
+ x_labels.push(x_label_fn.call(point, date[:key], date[:value]))
277
+ idx += step
278
+ end
279
+ end
280
+
281
+ ## End x axis labels ##
282
+
283
+ return {:x_labels => x_labels, :y_labels => y_labels, :label => label, :points => points, :total => total}
284
+
285
+ end
286
+ protected :hits_to_range
287
+
288
+ def range_to_flashvars(args = {})
289
+ x_labels = args[:x_labels]
290
+ y_labels = args[:y_labels]
291
+ label = args[:label]
292
+ points = args[:points]
293
+
294
+ raise ArgumentError unless x_labels
295
+ raise ArgumentError unless y_labels
296
+ raise ArgumentError unless label
297
+ raise ArgumentError unless points
298
+
299
+ # this is the structure necessary to support the Google Analytics OTG
300
+
301
+ options = {:Graph => {
302
+ :Id => "Graph",
303
+ :ShowHover => true,
304
+ :Format => "NORMAL",
305
+ :XAxisTitle => "Day",
306
+ :Compare => false,
307
+ :XAxisLabels => x_labels,
308
+ :HoverType => "primary_compare",
309
+ :SelectedSeries => ["primary", "compare"],
310
+ :Series => [
311
+ {
312
+ :SelectionStartIndex => 0,
313
+ :SelectionEndIndex => points.length,
314
+ :Style =>
315
+ {
316
+ :PointShape => "CIRCLE",
317
+ :PointRadius => 9,
318
+ :FillColor => 30668,
319
+ :FillAlpha => 10,
320
+ :LineThickness => 4,
321
+ :ActiveColor => 30668,
322
+ :InactiveColor => 11654895
323
+ },
324
+ :Label => label,
325
+ :Id => "primary",
326
+ :YLabels => y_labels,
327
+ :ValueCategory => "visits",
328
+ :Points => points
329
+ }]
330
+ } # end graph
331
+ } # end options
332
+
333
+ return URI::encode(options.to_json)
334
+ end
335
+ protected :range_to_flashvars
336
+
337
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'google_otg.rb'
2
+ ActionView::Base.send :include, GoogleOtg
3
+
4
+
5
+
@@ -0,0 +1,52 @@
1
+ require 'test_helper'
2
+
3
+ class GoogleOtgTest < ActiveSupport::TestCase
4
+ include GoogleOtg
5
+
6
+ context "GoogleOtg" do
7
+ context "when passed in bad data" do
8
+ should "throw ArgumentError" do
9
+ e = nil
10
+ begin
11
+ over_time_graph([:foo => "bar"])
12
+ rescue Exception => e
13
+ end
14
+
15
+ assert_instance_of(ArgumentError, e)
16
+ end
17
+ end
18
+
19
+ context "when passed in simple data" do
20
+ should "print out html" do
21
+ hits = Hit.find(:all, :order => "created_at" )
22
+ output = over_time_graph(hits)
23
+ md = output.strip!.match(/^\<embed/)
24
+ assert(md != nil)
25
+ end
26
+ end
27
+
28
+ context "actionview is modified" do
29
+ should "have over_time_graph method" do
30
+ assert(ActionView::Base.instance_methods.include?("over_time_graph"))
31
+ end
32
+ end
33
+
34
+ context "google chart" do
35
+ should "when passed in simple data" do
36
+ hits = Hit.find(:all, :order => "created_at" )
37
+ output = google_line_graph(hits)
38
+ md = output.match(/^http\:\/\/chart\.apis\.google\.com\/chart\?/)
39
+ assert(md != nil)
40
+ end
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ class ApplicationHelperTest < ActionView::TestCase
47
+ def test_helper_method
48
+ #hits = Hit.find(:all, :order => "created_at" )
49
+ #tag = over_time_graph(hits)
50
+ #assert_tag_in(tag, :embed)#, :attributes => {:href => edit_account_path})
51
+ end
52
+ end
@@ -0,0 +1,42 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+
8
+ require 'active_record'
9
+ require 'action_controller'
10
+ require 'active_record/fixtures'
11
+ require 'active_support'
12
+ require 'action_pack'
13
+ require 'action_view'
14
+ require 'rails/init'
15
+ require 'action_view/test_case'
16
+
17
+ require 'google_otg'
18
+
19
+ class Test::Unit::TestCase
20
+ end
21
+
22
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
23
+
24
+ ActiveRecord::Schema.define(:version => 1) do
25
+ create_table :hits do |t|
26
+ t.integer :count
27
+ t.datetime :created_at
28
+ end
29
+ end
30
+
31
+ class Hit < ActiveRecord::Base
32
+ validates_presence_of :count
33
+ end
34
+
35
+ (1..3).each{|idx|
36
+ ca = (3 - idx).hours.ago
37
+ range = GoogleOtg::DEFAULT_RANGE
38
+ ca = Time.at((ca.to_i/(60*range))*(60*range))
39
+
40
+ Hit.create(:count => rand(65535), :created_at => ca)
41
+ }
42
+
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: google_otg
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.9
5
+ platform: ruby
6
+ authors:
7
+ - esilverberg
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-01 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: thoughtbot-shoulda
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: mattetti-googlecharts
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ description: Include Google's Over Time Graph in your app
36
+ email: eric@ericsilverberg.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.rdoc
44
+ files:
45
+ - .document
46
+ - .gitignore
47
+ - LICENSE
48
+ - README.rdoc
49
+ - Rakefile
50
+ - VERSION
51
+ - google_otg.gemspec
52
+ - lib/gchart_mod.rb
53
+ - lib/google_otg.rb
54
+ - rails/init.rb
55
+ - test/google_otg_test.rb
56
+ - test/test_helper.rb
57
+ has_rdoc: true
58
+ homepage: http://github.com/esilverberg/google_otg
59
+ licenses: []
60
+
61
+ post_install_message:
62
+ rdoc_options:
63
+ - --charset=UTF-8
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ version:
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: "0"
77
+ version:
78
+ requirements: []
79
+
80
+ rubyforge_project:
81
+ rubygems_version: 1.3.5
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: Google's amazing over-time graph, in your rails app
85
+ test_files:
86
+ - test/google_otg_test.rb
87
+ - test/test_helper.rb