esilverberg-google_otg 1.0.0

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,46 @@
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
+ == Copyright
45
+
46
+ Copyright (c) 2009 esilverberg. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,56 @@
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 is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
18
+ end
19
+
20
+ require 'rake/testtask'
21
+ Rake::TestTask.new(:test) do |test|
22
+ test.libs << 'lib' << 'test'
23
+ test.pattern = 'test/**/*_test.rb'
24
+ test.verbose = true
25
+ end
26
+
27
+ begin
28
+ require 'rcov/rcovtask'
29
+ Rcov::RcovTask.new do |test|
30
+ test.libs << 'test'
31
+ test.pattern = 'test/**/*_test.rb'
32
+ test.verbose = true
33
+ end
34
+ rescue LoadError
35
+ task :rcov do
36
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
37
+ end
38
+ end
39
+
40
+ task :test => :check_dependencies
41
+
42
+ task :default => :test
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ if File.exist?('VERSION')
47
+ version = File.read('VERSION')
48
+ else
49
+ version = ""
50
+ end
51
+
52
+ rdoc.rdoc_dir = 'rdoc'
53
+ rdoc.title = "google_otg #{version}"
54
+ rdoc.rdoc_files.include('README*')
55
+ rdoc.rdoc_files.include('lib/**/*.rb')
56
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,55 @@
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.0"
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-09-03}
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/google_otg.rb",
28
+ "rails/init.rb",
29
+ "test/google_otg_test.rb",
30
+ "test/test_helper.rb"
31
+ ]
32
+ s.has_rdoc = true
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.1}
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 = 2
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
+ else
50
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
51
+ end
52
+ else
53
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
54
+ end
55
+ end
data/lib/google_otg.rb ADDED
@@ -0,0 +1,157 @@
1
+ require 'uri'
2
+
3
+ module GoogleOtg
4
+
5
+ DEFAULT_RANGE = 30 # 30 min
6
+
7
+ def over_time_graph(hits, args = {})
8
+ height = args.has_key?(:height) ? args[:height] : 125
9
+ src = args.has_key?(:src) ? args[:src] : "http://www.google.com/analytics/static/flash/OverTimeGraph.swf"
10
+
11
+ range = hits_to_range(hits, args)
12
+ vars = range_to_flashvars(range)
13
+
14
+ html = <<-eos
15
+ <embed width="100%" height="#{height}"
16
+ wmode="opaque" salign="tl" scale="noScale" quality="high" bgcolor="#FFFFFF"
17
+ flashvars="input=#{vars}"
18
+ pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash"
19
+ src="#{src}"/>
20
+ eos
21
+
22
+ end
23
+
24
+ def flto10(val)
25
+ return ((val / 10) * 10).to_i
26
+ end
27
+ protected :flto10
28
+
29
+ def hits_to_range(hits, args = {})
30
+
31
+ return nil unless hits
32
+
33
+ hits.map{|h|
34
+ if !h.respond_to?("created_at") || !h.respond_to?("count")
35
+ raise ArgumentError, "Invalid object type. All objects must respond to 'count' and 'created_at'"
36
+ end
37
+ }
38
+
39
+ label = args.has_key?(:label) ? args[:label] : "Value"
40
+ time_fn = args.has_key?(:time_fn) ? args[:time_fn] : lambda {|h| h.created_at }
41
+ range = args.has_key?(:range) ? args[:range] : DEFAULT_RANGE
42
+ x_label_format = args.has_key?(:x_label_format) ? args[:x_label_format] : "%A %I:%M%p"
43
+
44
+ max_y = 0
45
+ tz = args.has_key?(:time_zone) ? args[:time_zone] : ActiveSupport::TimeZone['UTC']
46
+ hits_dict = {}
47
+ hits.each { |h|
48
+ hits_dict[time_fn.call(h)] = h
49
+ }
50
+
51
+ total = 0
52
+
53
+ points = []
54
+ now_floored = Time.at((Time.now.to_i/(60*range))*(60*range)).gmtime
55
+ current = hits.length > 0 ? time_fn.call(hits[0]) : now_floored
56
+
57
+ while (current <= now_floored && range > 0) do
58
+ if hits_dict[current]
59
+ count = hits_dict[current].count.to_i
60
+ max_y = count if count > max_y
61
+
62
+ date = tz.utc_to_local(time_fn.call(hits_dict[current]))
63
+ date_key = date.to_i
64
+ date_value = date.strftime(x_label_format)
65
+
66
+ points.push({:Value => [count, count], :Label => [date_key, date_value]})
67
+ total += count
68
+ else
69
+
70
+ date = tz.utc_to_local(current)
71
+ date_key = date.to_i
72
+ date_value = date.strftime(x_label_format)
73
+
74
+ points.push({:Value => [0, 0], :Label => [date_key, date_value]})
75
+ end
76
+ current = current + range.minutes
77
+ if points.length > 100
78
+ break
79
+ end
80
+ end
81
+
82
+
83
+ mid_y = self.flto10(max_y / 2)
84
+ top_y = self.flto10(max_y)
85
+ if (top_y == 0)
86
+ mid_y = max_y / 2
87
+ top_y = max_y
88
+ end
89
+
90
+ y_labels = [ [mid_y, mid_y], [top_y, top_y] ]
91
+
92
+ x_labels = []
93
+
94
+ if points.length > 0
95
+ for i in 0..3
96
+ hit = points[i * (points.length / 4)]
97
+
98
+ date_key = hit[:Label][0]
99
+ date_value = hit[:Label][1]
100
+ x_labels.push([date_key, date_value])
101
+ end
102
+ end
103
+ return {:x_labels => x_labels, :y_labels => y_labels, :label => label, :points => points, :total => total}
104
+
105
+ end
106
+ protected :hits_to_range
107
+
108
+ def range_to_flashvars(args = {})
109
+ x_labels = args[:x_labels]
110
+ y_labels = args[:y_labels]
111
+ label = args[:label]
112
+ points = args[:points]
113
+
114
+ raise ArgumentError unless x_labels
115
+ raise ArgumentError unless y_labels
116
+ raise ArgumentError unless label
117
+ raise ArgumentError unless points
118
+
119
+ # this is the structure necessary to support the Google Analytics OTG
120
+
121
+ options = {:Graph => {
122
+ :Id => "Graph",
123
+ :ShowHover => true,
124
+ :Format => "NORMAL",
125
+ :XAxisTitle => "Day",
126
+ :Compare => false,
127
+ :XAxisLabels => x_labels,
128
+ :HoverType => "primary_compare",
129
+ :SelectedSeries => ["primary", "compare"],
130
+ :Series => [
131
+ {
132
+ :SelectionStartIndex => 0,
133
+ :SelectionEndIndex => points.length,
134
+ :Style =>
135
+ {
136
+ :PointShape => "CIRCLE",
137
+ :PointRadius => 9,
138
+ :FillColor => 30668,
139
+ :FillAlpha => 10,
140
+ :LineThickness => 4,
141
+ :ActiveColor => 30668,
142
+ :InactiveColor => 11654895
143
+ },
144
+ :Label => label,
145
+ :Id => "primary",
146
+ :YLabels => y_labels,
147
+ :ValueCategory => "visits",
148
+ :Points => points
149
+ }]
150
+ } # end graph
151
+ } # end options
152
+
153
+ return URI::encode(options.to_json)
154
+ end
155
+ protected :range_to_flashvars
156
+
157
+ 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,43 @@
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
+ end
34
+
35
+ end
36
+
37
+ class ApplicationHelperTest < ActionView::TestCase
38
+ def test_helper_method
39
+ hits = Hit.find(:all, :order => "created_at" )
40
+ tag = over_time_graph(hits)
41
+ assert_tag_in(tag, :embed)#, :attributes => {:href => edit_account_path})
42
+ end
43
+ 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,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: esilverberg-google_otg
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - esilverberg
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-09-03 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
+ description: Include Google's Over Time Graph in your app
26
+ email: eric@ericsilverberg.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.rdoc
34
+ files:
35
+ - .document
36
+ - .gitignore
37
+ - LICENSE
38
+ - README.rdoc
39
+ - Rakefile
40
+ - VERSION
41
+ - google_otg.gemspec
42
+ - lib/google_otg.rb
43
+ - rails/init.rb
44
+ - test/google_otg_test.rb
45
+ - test/test_helper.rb
46
+ has_rdoc: true
47
+ homepage: http://github.com/esilverberg/google_otg
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --charset=UTF-8
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ requirements: []
66
+
67
+ rubyforge_project:
68
+ rubygems_version: 1.2.0
69
+ signing_key:
70
+ specification_version: 2
71
+ summary: Google's amazing over-time graph, in your rails app
72
+ test_files:
73
+ - test/google_otg_test.rb
74
+ - test/test_helper.rb