google_otg 1.0.9

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/.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