google-analytics 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +1 -0
- data/LICENSE +0 -0
- data/Manifest +17 -0
- data/README.textile +3 -0
- data/Rakefile +12 -0
- data/google-analytics.gemspec +40 -0
- data/lib/google_analytics.rb +9 -0
- data/lib/google_analytics/account.rb +25 -0
- data/lib/google_analytics/client.rb +98 -0
- data/lib/google_analytics/report.rb +93 -0
- data/lib/google_analytics/user.rb +31 -0
- data/test.rb +11 -0
- data/test/google_analytics/account_test.rb +4 -0
- data/test/google_analytics/client_test.rb +58 -0
- data/test/google_analytics/report_test.rb +4 -0
- data/test/google_analytics/user_test.rb +4 -0
- data/test/test_helper.rb +8 -0
- metadata +116 -0
data/CHANGELOG
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
v0.1.0 Nothing here yet
|
data/LICENSE
ADDED
File without changes
|
data/Manifest
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
LICENSE
|
3
|
+
Manifest
|
4
|
+
README.textile
|
5
|
+
Rakefile
|
6
|
+
google-analytics.gemspec
|
7
|
+
lib/google_analytics.rb
|
8
|
+
lib/google_analytics/account.rb
|
9
|
+
lib/google_analytics/client.rb
|
10
|
+
lib/google_analytics/report.rb
|
11
|
+
lib/google_analytics/user.rb
|
12
|
+
test.rb
|
13
|
+
test/google_analytics/account_test.rb
|
14
|
+
test/google_analytics/client_test.rb
|
15
|
+
test/google_analytics/report_test.rb
|
16
|
+
test/google_analytics/user_test.rb
|
17
|
+
test/test_helper.rb
|
data/README.textile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'echoe'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
Echoe.new("google-analytics") do |p|
|
5
|
+
p.author = "Dan Webb"
|
6
|
+
p.email = 'dan@danwebb.net'
|
7
|
+
p.summary = "Wrapper around the Google Analytics API"
|
8
|
+
p.runtime_dependencies = ['nokogiri']
|
9
|
+
p.development_dependencies = ['shoulda', 'fakeweb']
|
10
|
+
p.retain_gemspec = true
|
11
|
+
p.rcov_options = "--exclude 'gems/*'"
|
12
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{google-analytics}
|
5
|
+
s.version = "0.1.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Dan Webb"]
|
9
|
+
s.date = %q{2009-10-12}
|
10
|
+
s.description = %q{Wrapper around the Google Analytics API}
|
11
|
+
s.email = %q{dan@danwebb.net}
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README.textile", "lib/google_analytics.rb", "lib/google_analytics/account.rb", "lib/google_analytics/client.rb", "lib/google_analytics/report.rb", "lib/google_analytics/user.rb"]
|
13
|
+
s.files = ["CHANGELOG", "LICENSE", "Manifest", "README.textile", "Rakefile", "google-analytics.gemspec", "lib/google_analytics.rb", "lib/google_analytics/account.rb", "lib/google_analytics/client.rb", "lib/google_analytics/report.rb", "lib/google_analytics/user.rb", "test.rb", "test/google_analytics/account_test.rb", "test/google_analytics/client_test.rb", "test/google_analytics/report_test.rb", "test/google_analytics/user_test.rb", "test/test_helper.rb"]
|
14
|
+
s.homepage = %q{}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Google-analytics", "--main", "README.textile"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{google-analytics}
|
18
|
+
s.rubygems_version = %q{1.3.5}
|
19
|
+
s.summary = %q{Wrapper around the Google Analytics API}
|
20
|
+
s.test_files = ["test/google_analytics/account_test.rb", "test/google_analytics/client_test.rb", "test/google_analytics/report_test.rb", "test/google_analytics/user_test.rb", "test/test_helper.rb"]
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 3
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
|
28
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
29
|
+
s.add_development_dependency(%q<fakeweb>, [">= 0"])
|
30
|
+
else
|
31
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
32
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
33
|
+
s.add_dependency(%q<fakeweb>, [">= 0"])
|
34
|
+
end
|
35
|
+
else
|
36
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
37
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
38
|
+
s.add_dependency(%q<fakeweb>, [">= 0"])
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module GoogleAnalytics
|
2
|
+
autoload :Client, 'google_analytics/client'
|
3
|
+
autoload :User, 'google_analytics/user'
|
4
|
+
autoload :Account, 'google_analytics/account'
|
5
|
+
autoload :Profile, 'google_analytics/profile'
|
6
|
+
autoload :Report, 'google_analytics/report'
|
7
|
+
|
8
|
+
DXP_NAMESPACE = 'http://schemas.google.com/analytics/2009'
|
9
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module GoogleAnalytics
|
2
|
+
|
3
|
+
class Account
|
4
|
+
attr_reader :url, :title, :id, :profile_id, :client
|
5
|
+
|
6
|
+
def self.from_node(client, node)
|
7
|
+
new client,
|
8
|
+
node.xpath('.//xmlns:id').first.content,
|
9
|
+
node.xpath('.//dxp:property[@name="ga:accountName"]', 'dxp' => DXP_NAMESPACE).first.attributes['value'].to_s,
|
10
|
+
node.xpath('.//dxp:property[@name="ga:accountId"]', 'dxp' => DXP_NAMESPACE).first.attributes['value'].to_s,
|
11
|
+
node.xpath('.//dxp:property[@name="ga:profileId"]', 'dxp' => DXP_NAMESPACE).first.attributes['value'].to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(client, url, title, id, profile_id)
|
15
|
+
@client, @url, @title = client, url, title
|
16
|
+
@id, @profile_id = id, profile_id
|
17
|
+
end
|
18
|
+
|
19
|
+
def report(options={})
|
20
|
+
Report.new(self, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
require 'uri'
|
4
|
+
require 'nokogiri'
|
5
|
+
|
6
|
+
module GoogleAnalytics
|
7
|
+
|
8
|
+
AUTH_URL = 'https://www.google.com/accounts/ClientLogin'
|
9
|
+
BASE_URL = 'https://www.google.com/analytics'
|
10
|
+
|
11
|
+
class NotAuthorized < StandardError; end
|
12
|
+
class ResponseError < StandardError; end
|
13
|
+
|
14
|
+
class Client
|
15
|
+
|
16
|
+
def initialize(username, password)
|
17
|
+
@username, @password = username, password
|
18
|
+
end
|
19
|
+
|
20
|
+
def login!
|
21
|
+
data = {
|
22
|
+
:accountType => 'GOOGLE_OR_HOSTED',
|
23
|
+
:service => 'analytics',
|
24
|
+
:Email => @username,
|
25
|
+
:Passwd => @password
|
26
|
+
}
|
27
|
+
|
28
|
+
begin
|
29
|
+
resp = request(URI.parse(AUTH_URL), :post, data)
|
30
|
+
|
31
|
+
if resp =~ /^Auth\=(.+)$/
|
32
|
+
@auth = $1
|
33
|
+
else
|
34
|
+
raise NotAuthorized, 'you cannot access this google account'
|
35
|
+
end
|
36
|
+
rescue GoogleAnalytics::ResponseError
|
37
|
+
raise NotAuthorized, 'you cannot access this google account'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def get(path, args=nil)
|
42
|
+
api_request(path, :get, args)
|
43
|
+
end
|
44
|
+
|
45
|
+
def post(path, args=nil)
|
46
|
+
api_request(path, :post, args)
|
47
|
+
end
|
48
|
+
|
49
|
+
def api_request(path, method, args)
|
50
|
+
login! unless @auth
|
51
|
+
url = URI.parse([BASE_URL, path].join)
|
52
|
+
|
53
|
+
resp = request(url, method, args)
|
54
|
+
|
55
|
+
Nokogiri::XML(resp)
|
56
|
+
end
|
57
|
+
|
58
|
+
def request(url, method=:get, data=nil)
|
59
|
+
case method
|
60
|
+
when :get
|
61
|
+
url.query = encode(data) if data
|
62
|
+
req = Net::HTTP::Get.new(url.request_uri)
|
63
|
+
when :post
|
64
|
+
req = Net::HTTP::Post.new(url.request_uri)
|
65
|
+
req.body = encode(data) if data
|
66
|
+
req.content_type = 'application/x-www-form-urlencoded'
|
67
|
+
end
|
68
|
+
|
69
|
+
req['Authorization'] = "GoogleLogin auth=#{@auth}" if @auth
|
70
|
+
|
71
|
+
puts url.to_s
|
72
|
+
|
73
|
+
http = Net::HTTP.new(url.host, url.port)
|
74
|
+
http.use_ssl = (url.port == 443)
|
75
|
+
|
76
|
+
res = http.start() { |conn| conn.request(req) }
|
77
|
+
|
78
|
+
raise ResponseError, "#{res.code} #{res.message}: #{res.body}" unless res.code == '200'
|
79
|
+
|
80
|
+
res.body
|
81
|
+
end
|
82
|
+
|
83
|
+
protected
|
84
|
+
|
85
|
+
def encode(data)
|
86
|
+
data.map do |k,v|
|
87
|
+
value = v.is_a?(Array) ? v.join(',') : v.to_s
|
88
|
+
("%s=%s" % [uri_encode(k), uri_encode(value)]) unless value.empty?
|
89
|
+
end.compact.join("&")
|
90
|
+
end
|
91
|
+
|
92
|
+
def uri_encode(item)
|
93
|
+
encoded = URI.encode(item.to_s)
|
94
|
+
encoded.gsub(/\=/, '%3D')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module GoogleAnalytics
|
2
|
+
class Report
|
3
|
+
|
4
|
+
def initialize(account, options={})
|
5
|
+
@account = account
|
6
|
+
@options = {
|
7
|
+
:ids => "ga:#{account.profile_id}",
|
8
|
+
:metrics => ga_prefix(options.delete(:metrics)),
|
9
|
+
:dimensions => ga_prefix(options.delete(:dimensions)),
|
10
|
+
:'start-date' => date_format(options.delete(:start) || one_month_ago),
|
11
|
+
:'end-date' => date_format(options.delete(:end) || Time.now)
|
12
|
+
}.merge(options)
|
13
|
+
|
14
|
+
if filters = options.delete(:filters)
|
15
|
+
@options[:filters] = ga_prefix(filters)
|
16
|
+
end
|
17
|
+
|
18
|
+
if sort = options.delete(:sort)
|
19
|
+
@options[:sort] = ga_prefix(sort)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def each
|
24
|
+
page = 1
|
25
|
+
items = rows(page)
|
26
|
+
|
27
|
+
while !items.empty?
|
28
|
+
yield items.pop
|
29
|
+
|
30
|
+
items = rows(page+=1) if items.empty?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def rows(page=1, per_page=10000)
|
35
|
+
parse_rows(get(
|
36
|
+
:"start-index" => start_index(page, per_page),
|
37
|
+
:'max-results' => per_page
|
38
|
+
))
|
39
|
+
end
|
40
|
+
|
41
|
+
def get(options={})
|
42
|
+
@account.client.get('/feeds/data', @options.merge(options))
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def start_index(page, per_page)
|
48
|
+
((page - 1) * per_page) + 1
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse_rows(data)
|
52
|
+
rows = []
|
53
|
+
data.xpath('.//xmlns:entry').each do |node|
|
54
|
+
row = {}
|
55
|
+
|
56
|
+
node.xpath('./dxp:dimension').each do |dim|
|
57
|
+
row[strip_prefix(dim.attributes['name'])] = dim.attributes['value'].to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
node.xpath('./dxp:metric').each do |dim|
|
61
|
+
row[strip_prefix(dim.attributes['name'])] = dim.attributes['value'].to_s
|
62
|
+
end
|
63
|
+
|
64
|
+
rows << row
|
65
|
+
end
|
66
|
+
return rows
|
67
|
+
end
|
68
|
+
|
69
|
+
def strip_prefix(name)
|
70
|
+
name.to_s.gsub(/^ga\:/, '')
|
71
|
+
end
|
72
|
+
|
73
|
+
def ga_prefix(data)
|
74
|
+
if data.is_a?(Array)
|
75
|
+
data.collect { |d| d.gsub(/(-?)(\w+)/, "\\1ga:\\2") }
|
76
|
+
elsif !data.nil?
|
77
|
+
d.gsub(/(-?)(\w+)/, "\\1ga:\\2")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def date_format(date)
|
82
|
+
if date.nil?
|
83
|
+
nil
|
84
|
+
else
|
85
|
+
date.strftime('%Y-%m-%d')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def one_month_ago
|
90
|
+
Time.now - (28 * 24 * 60 * 60)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module GoogleAnalytics
|
2
|
+
|
3
|
+
class User
|
4
|
+
|
5
|
+
def initialize(username, password)
|
6
|
+
@client = Client.new username, password
|
7
|
+
end
|
8
|
+
|
9
|
+
def accounts
|
10
|
+
return @accounts if @accounts
|
11
|
+
xml = @client.get('/feeds/accounts/default')
|
12
|
+
@accounts = xml.xpath('//xmlns:entry').collect do |account_node|
|
13
|
+
Account.from_node(@client, account_node)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def account(id)
|
18
|
+
accounts.find do |account|
|
19
|
+
account if account.id == id
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid?
|
24
|
+
@client.login!
|
25
|
+
true
|
26
|
+
rescue NotAuthorized
|
27
|
+
false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/test.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'google_analytics'
|
3
|
+
|
4
|
+
include GoogleAnalytics
|
5
|
+
|
6
|
+
u = User.new 'dan@danwebb.net', 'poortheory'
|
7
|
+
|
8
|
+
r = u.accounts[1].report :dimensions => ['keyword'],
|
9
|
+
:metrics => ['visits', 'bounces', 'newVisits', 'timeOnSite']
|
10
|
+
|
11
|
+
puts r.rows(1).inspect
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '../test_helper')
|
2
|
+
|
3
|
+
class ClientTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context 'given an instance of Client' do
|
6
|
+
setup do
|
7
|
+
@client = GoogleAnalytics::Client.new('user', 'password')
|
8
|
+
end
|
9
|
+
|
10
|
+
should 'turn a hash into a url encoded string when encode called' do
|
11
|
+
res = @client.send :encode, { :thing => 12, :thong => 'something with spaces' }
|
12
|
+
assert_match /thing\=12/, res
|
13
|
+
assert_match /thong\=something\%20with\%20spaces/, res
|
14
|
+
end
|
15
|
+
|
16
|
+
should 'should comma seperate arrays when encode called' do
|
17
|
+
res = @client.send :encode, { :thong => [:blurg, :blargh] }
|
18
|
+
assert_match /thong\=blurg,blargh/, res
|
19
|
+
end
|
20
|
+
|
21
|
+
should 'send request to correct url with username and password' do
|
22
|
+
@client.expects(:request).with(
|
23
|
+
URI.parse(GoogleAnalytics::AUTH_URL),
|
24
|
+
:post,
|
25
|
+
{
|
26
|
+
:Email => 'user', :Passwd => 'password',
|
27
|
+
:accountType => 'GOOGLE_OR_HOSTED', :service => 'analytics'
|
28
|
+
}
|
29
|
+
).returns('Auth=thing')
|
30
|
+
|
31
|
+
@client.login!
|
32
|
+
end
|
33
|
+
|
34
|
+
should 'raise not authorized if failed to log in' do
|
35
|
+
@client.expects(:request).with(
|
36
|
+
URI.parse(GoogleAnalytics::AUTH_URL),
|
37
|
+
:post,
|
38
|
+
{
|
39
|
+
:Email => 'user', :Passwd => 'password',
|
40
|
+
:accountType => 'GOOGLE_OR_HOSTED', :service => 'analytics'
|
41
|
+
}
|
42
|
+
).returns('fail')
|
43
|
+
|
44
|
+
assert_raises GoogleAnalytics::NotAuthorized do
|
45
|
+
@client.login!
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
should 'login if auth is not set before api_request' do
|
50
|
+
@client.instance_variable_set(:@auth, nil)
|
51
|
+
@client.expects(:login!)
|
52
|
+
@client.expects(:request).returns('')
|
53
|
+
|
54
|
+
@client.api_request('/', :get, {})
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: google-analytics
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dan Webb
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-12 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: nokogiri
|
17
|
+
type: :runtime
|
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: shoulda
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: fakeweb
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
description: Wrapper around the Google Analytics API
|
46
|
+
email: dan@danwebb.net
|
47
|
+
executables: []
|
48
|
+
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
extra_rdoc_files:
|
52
|
+
- CHANGELOG
|
53
|
+
- LICENSE
|
54
|
+
- README.textile
|
55
|
+
- lib/google_analytics.rb
|
56
|
+
- lib/google_analytics/account.rb
|
57
|
+
- lib/google_analytics/client.rb
|
58
|
+
- lib/google_analytics/report.rb
|
59
|
+
- lib/google_analytics/user.rb
|
60
|
+
files:
|
61
|
+
- CHANGELOG
|
62
|
+
- LICENSE
|
63
|
+
- Manifest
|
64
|
+
- README.textile
|
65
|
+
- Rakefile
|
66
|
+
- google-analytics.gemspec
|
67
|
+
- lib/google_analytics.rb
|
68
|
+
- lib/google_analytics/account.rb
|
69
|
+
- lib/google_analytics/client.rb
|
70
|
+
- lib/google_analytics/report.rb
|
71
|
+
- lib/google_analytics/user.rb
|
72
|
+
- test.rb
|
73
|
+
- test/google_analytics/account_test.rb
|
74
|
+
- test/google_analytics/client_test.rb
|
75
|
+
- test/google_analytics/report_test.rb
|
76
|
+
- test/google_analytics/user_test.rb
|
77
|
+
- test/test_helper.rb
|
78
|
+
has_rdoc: true
|
79
|
+
homepage: ""
|
80
|
+
licenses: []
|
81
|
+
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options:
|
84
|
+
- --line-numbers
|
85
|
+
- --inline-source
|
86
|
+
- --title
|
87
|
+
- Google-analytics
|
88
|
+
- --main
|
89
|
+
- README.textile
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: "0"
|
97
|
+
version:
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: "1.2"
|
103
|
+
version:
|
104
|
+
requirements: []
|
105
|
+
|
106
|
+
rubyforge_project: google-analytics
|
107
|
+
rubygems_version: 1.3.5
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: Wrapper around the Google Analytics API
|
111
|
+
test_files:
|
112
|
+
- test/google_analytics/account_test.rb
|
113
|
+
- test/google_analytics/client_test.rb
|
114
|
+
- test/google_analytics/report_test.rb
|
115
|
+
- test/google_analytics/user_test.rb
|
116
|
+
- test/test_helper.rb
|