chrisle-gattica 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +6 -0
- data/Gemfile.lock +21 -0
- data/LICENSE +22 -0
- data/README.md +506 -0
- data/Rakefile +24 -0
- data/VERSION.yml +5 -0
- data/chrisle-gattica.gemspec +68 -0
- data/lib/gattica.rb +35 -0
- data/lib/gattica/account.rb +54 -0
- data/lib/gattica/auth.rb +47 -0
- data/lib/gattica/convertible.rb +39 -0
- data/lib/gattica/data_point.rb +73 -0
- data/lib/gattica/data_set.rb +57 -0
- data/lib/gattica/engine.rb +295 -0
- data/lib/gattica/exceptions.rb +21 -0
- data/lib/gattica/goals.rb +33 -0
- data/lib/gattica/hash_extensions.rb +20 -0
- data/lib/gattica/profiles.rb +33 -0
- data/lib/gattica/segment.rb +17 -0
- data/lib/gattica/settings.rb +34 -0
- data/lib/gattica/user.rb +31 -0
- data/test/helper.rb +14 -0
- data/test/settings.rb +28 -0
- data/test/suite.rb +6 -0
- data/test/test_engine.rb +48 -0
- data/test/test_results.rb +23 -0
- data/test/test_user.rb +24 -0
- metadata +109 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
module GatticaError
|
2
|
+
# user errors
|
3
|
+
class InvalidEmail < StandardError; end;
|
4
|
+
class InvalidPassword < StandardError; end;
|
5
|
+
# authentication errors
|
6
|
+
class CouldNotAuthenticate < StandardError; end;
|
7
|
+
class NoLoginOrToken < StandardError; end;
|
8
|
+
class InvalidToken < StandardError; end;
|
9
|
+
# profile errors
|
10
|
+
class InvalidProfileId < StandardError; end;
|
11
|
+
# search errors
|
12
|
+
class TooManyDimensions < StandardError; end;
|
13
|
+
class TooManyMetrics < StandardError; end;
|
14
|
+
class InvalidSort < StandardError; end;
|
15
|
+
class InvalidFilter < StandardError; end;
|
16
|
+
class MissingStartDate < StandardError; end;
|
17
|
+
class MissingEndDate < StandardError; end;
|
18
|
+
# errors from Analytics
|
19
|
+
class AnalyticsError < StandardError; end;
|
20
|
+
class UnknownAnalyticsError < StandardError; end;
|
21
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hpricot'
|
3
|
+
|
4
|
+
module Gattica
|
5
|
+
class Goals
|
6
|
+
include Convertible
|
7
|
+
|
8
|
+
attr_reader :id, :updated, :title, :table_id, :account_id, :account_name,
|
9
|
+
:profile_id, :web_property_id, :goals
|
10
|
+
|
11
|
+
|
12
|
+
def initialize(xml)
|
13
|
+
@id = xml.at(:id).inner_html
|
14
|
+
@updated = DateTime.parse(xml.at(:updated).inner_html)
|
15
|
+
@account_id = xml.at("dxp:property[@name='ga:accountId']").attributes['value'].to_i
|
16
|
+
@account_name = xml.at("dxp:property[@name='ga:accountName']").attributes['value']
|
17
|
+
|
18
|
+
@title = xml.at("dxp:property[@name='ga:profileName']").attributes['value']
|
19
|
+
@table_id = xml.at("dxp:property[@name='dxp:tableId']").attributes['value']
|
20
|
+
@profile_id = xml.at("dxp:property[@name='ga:profileId']").attributes['value'].to_i
|
21
|
+
@web_property_id = xml.at("dxp:property[@name='ga:webPropertyId']").attributes['value']
|
22
|
+
|
23
|
+
# @goals = xml.search('ga:goal').collect do |goal| {
|
24
|
+
# :active => goal.attributes['active'],
|
25
|
+
# :name => goal.attributes['name'],
|
26
|
+
# :number => goal.attributes['number'].to_i,
|
27
|
+
# :value => goal.attributes['value'].to_f,
|
28
|
+
# }
|
29
|
+
# end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Gattica
|
2
|
+
module HashExtensions
|
3
|
+
|
4
|
+
def to_query
|
5
|
+
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
|
6
|
+
self.collect do |key, value|
|
7
|
+
"#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
|
8
|
+
end.sort * '&'
|
9
|
+
end
|
10
|
+
|
11
|
+
def key
|
12
|
+
self.keys.first if self.length == 1
|
13
|
+
end
|
14
|
+
|
15
|
+
def value
|
16
|
+
self.values.first if self.length == 1
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hpricot'
|
3
|
+
|
4
|
+
module Gattica
|
5
|
+
class Profiles
|
6
|
+
include Convertible
|
7
|
+
|
8
|
+
attr_reader :id, :updated, :title, :table_id, :account_id, :account_name,
|
9
|
+
:profile_id, :web_property_id, :goals
|
10
|
+
|
11
|
+
|
12
|
+
def initialize(xml)
|
13
|
+
@id = xml.at(:id).inner_html
|
14
|
+
@updated = DateTime.parse(xml.at(:updated).inner_html)
|
15
|
+
@account_id = xml.at("dxp:property[@name='ga:accountId']").attributes['value'].to_i
|
16
|
+
@account_name = xml.at("dxp:property[@name='ga:accountName']").attributes['value']
|
17
|
+
|
18
|
+
@title = xml.at("dxp:property[@name='ga:profileName']").attributes['value']
|
19
|
+
@table_id = xml.at("dxp:property[@name='dxp:tableId']").attributes['value']
|
20
|
+
@profile_id = xml.at("dxp:property[@name='ga:profileId']").attributes['value'].to_i
|
21
|
+
@web_property_id = xml.at("dxp:property[@name='ga:webPropertyId']").attributes['value']
|
22
|
+
|
23
|
+
# @goals = xml.search('ga:goal').collect do |goal| {
|
24
|
+
# :active => goal.attributes['active'],
|
25
|
+
# :name => goal.attributes['name'],
|
26
|
+
# :number => goal.attributes['number'].to_i,
|
27
|
+
# :value => goal.attributes['value'].to_f,
|
28
|
+
# }
|
29
|
+
# end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hpricot'
|
3
|
+
|
4
|
+
module Gattica
|
5
|
+
class Segment
|
6
|
+
include Convertible
|
7
|
+
|
8
|
+
attr_reader :id, :name, :definition
|
9
|
+
|
10
|
+
def initialize(xml)
|
11
|
+
@id = xml.attributes['id']
|
12
|
+
@name = xml.attributes['name']
|
13
|
+
@definition = xml.at("dxp:definition").inner_html
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Gattica
|
2
|
+
module Settings
|
3
|
+
|
4
|
+
USE_SSL = true
|
5
|
+
SSL_PORT = 443
|
6
|
+
NON_SSL_PORT = 80
|
7
|
+
|
8
|
+
TIMEOUT = 100
|
9
|
+
|
10
|
+
DEFAULT_ARGS = {
|
11
|
+
:start_date => nil,
|
12
|
+
:end_date => nil,
|
13
|
+
:dimensions => [],
|
14
|
+
:metrics => [],
|
15
|
+
:filters => [],
|
16
|
+
:sort => []
|
17
|
+
}
|
18
|
+
|
19
|
+
DEFAULT_OPTIONS = {
|
20
|
+
:email => nil, # eg: 'email@gmail.com'
|
21
|
+
:password => nil, # eg: '$up3r_$ekret'
|
22
|
+
:token => nil,
|
23
|
+
:profile_id => nil,
|
24
|
+
:debug => false,
|
25
|
+
:headers => {},
|
26
|
+
:logger => Logger.new(STDOUT)
|
27
|
+
}
|
28
|
+
|
29
|
+
FILTER_METRIC_OPERATORS = %w{ == != > < >= <= }
|
30
|
+
FILTER_DIMENSION_OPERATORS = %w{ == != =~ !~ =@ ~@ }
|
31
|
+
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
data/lib/gattica/user.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Gattica
|
2
|
+
|
3
|
+
# Represents a user to be authenticated by GA
|
4
|
+
|
5
|
+
class User
|
6
|
+
|
7
|
+
include Convertible
|
8
|
+
|
9
|
+
attr_accessor :email, :password
|
10
|
+
|
11
|
+
def initialize(email,password)
|
12
|
+
@email = email
|
13
|
+
@password = password
|
14
|
+
validate
|
15
|
+
end
|
16
|
+
|
17
|
+
# User gets a special +to_h+ because Google expects +Email+ and +Passwd+ instead of our nicer internal names
|
18
|
+
def to_h
|
19
|
+
{ :Email => @email,
|
20
|
+
:Passwd => @password }
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
# Determine whether or not this is a valid user
|
25
|
+
def validate
|
26
|
+
raise GatticaError::InvalidEmail, "The email address '#{@email}' is not valid" if not @email.match(/^(?:[_a-z0-9-]+)(\.[_a-z0-9-]+)*@([a-z0-9-]+)(\.[a-zA-Z0-9\-\.]+)*(\.[a-z]{2,4})$/i)
|
27
|
+
raise GatticaError::InvalidPassword, "The password cannot be blank" if @password.empty? || @password.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. lib gattica])
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
# include Gattica
|
7
|
+
|
8
|
+
def fixture(name)
|
9
|
+
File.read(File.join(File.dirname(__FILE__), 'fixtures', name))
|
10
|
+
end
|
11
|
+
|
12
|
+
def absolute_project_path
|
13
|
+
File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
14
|
+
end
|
data/test/settings.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module GatticaTest
|
2
|
+
|
3
|
+
DEFAULT_AUTH = {
|
4
|
+
:email => 'name@email.com',
|
5
|
+
:password => 'password',
|
6
|
+
:debug => true
|
7
|
+
}
|
8
|
+
DEFAULT_QUERY = {
|
9
|
+
:start_date => '2010-01-01',
|
10
|
+
:end_date => '2011-01-01',
|
11
|
+
:dimensions => ['date'],
|
12
|
+
:metrics => ['visits']
|
13
|
+
}
|
14
|
+
PROFILE_ID = 23987717
|
15
|
+
|
16
|
+
def self.ga(options={}, profile_id=PROFILE_ID)
|
17
|
+
unless defined? @ga
|
18
|
+
@ga = Gattica.new(DEFAULT_AUTH)
|
19
|
+
@ga.profile_id = profile_id
|
20
|
+
end
|
21
|
+
@ga
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.get(options={}, profile_id=PROFILE_ID)
|
25
|
+
ga.get(DEFAULT_QUERY.merge(options))
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/test/suite.rb
ADDED
data/test/test_engine.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
require File.expand_path('../settings', __FILE__)
|
3
|
+
|
4
|
+
class TestEngine < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_login_with_good_password
|
7
|
+
assert Gattica.new(GatticaTest::DEFAULT_AUTH),
|
8
|
+
"should have been able to login"
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_login_with_bad_user_password
|
12
|
+
assert_raise GatticaError::CouldNotAuthenticate do
|
13
|
+
Gattica.new({ :email => 'bad-email@gmail.com',
|
14
|
+
:password => 'bad-password' })
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_accounts
|
19
|
+
ga = Gattica.new(GatticaTest::DEFAULT_AUTH)
|
20
|
+
accounts = ga.accounts
|
21
|
+
assert accounts.count > 900, "should have gotten at least 900 accounts"
|
22
|
+
end
|
23
|
+
|
24
|
+
# def test_timeout_too_short
|
25
|
+
# ga = Gattica.new(GatticaTest::DEFAULT_AUTH.merge!(:timeout => 0))
|
26
|
+
# assert_raise Timeout::Error do
|
27
|
+
# ga.accounts
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
|
31
|
+
def test_setting_timeout
|
32
|
+
ga = Gattica.new((GatticaTest::DEFAULT_AUTH).merge!(:timeout => 300))
|
33
|
+
http = ga.instance_variable_get('@http')
|
34
|
+
assert http.read_timeout == 300, "http timeout should be 300"
|
35
|
+
end
|
36
|
+
|
37
|
+
# def test_raise_error_when_no_user_pass_or_token_specified
|
38
|
+
# assert_raise GatticaError::NoLoginOrToken do
|
39
|
+
# Gattica.new
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# def test_use_an_existing_token
|
44
|
+
# token = Gattica.new(@auth).token
|
45
|
+
# assert Gattica.new({ :token => token })
|
46
|
+
# end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
require File.expand_path('../settings', __FILE__)
|
3
|
+
|
4
|
+
class TestResults < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@response = GatticaTest::get({ :start_index => 5, :max_results => 5 })
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_max_results
|
11
|
+
assert @response.points.count == 5, "should only return 5 results"
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_start_index
|
15
|
+
assert @response.points.first.title == "ga:date=20100105", "should start on the 5th"
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_conversions
|
19
|
+
assert @response.class.inspect == 'Gattica::DataSet', "should be a Gattica:DataSet"
|
20
|
+
assert @response.to_h.class.inspect == 'Hash', "Should be a hash"
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/test/test_user.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
|
3
|
+
class TestUser < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_can_create_user
|
9
|
+
assert Gattica::User.new('anonymous@anon.com','none')
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_invalid_email
|
13
|
+
assert_raise GatticaError::InvalidEmail do Gattica::User.new('','') end
|
14
|
+
assert_raise ArgumentError do Gattica::User.new('') end
|
15
|
+
assert_raise GatticaError::InvalidEmail do Gattica::User.new('anonymous','none') end
|
16
|
+
assert_raise GatticaError::InvalidEmail do Gattica::User.new('anonymous@asdfcom','none') end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_invalid_password
|
20
|
+
assert_raise GatticaError::InvalidPassword do Gattica::User.new('anonymous@anon.com','') end
|
21
|
+
assert_raise ArgumentError do Gattica::User.new('anonymous@anon.com') end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: chrisle-gattica
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Christopher Le, et all
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: test-unit
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: hpricot
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: Gattica is a easy to use Ruby Gem for getting data from the Google Analytics
|
47
|
+
API. It supports metrics, dimensions, sort, filters, goals, and segments. It can
|
48
|
+
handle accounts with 1000+ profiles, and can return data in CSV, Hash, or JSON
|
49
|
+
email: chrisl@seerinteractive.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files:
|
53
|
+
- LICENSE
|
54
|
+
- README.md
|
55
|
+
files:
|
56
|
+
- Gemfile
|
57
|
+
- Gemfile.lock
|
58
|
+
- LICENSE
|
59
|
+
- README.md
|
60
|
+
- Rakefile
|
61
|
+
- VERSION.yml
|
62
|
+
- chrisle-gattica.gemspec
|
63
|
+
- lib/gattica.rb
|
64
|
+
- lib/gattica/account.rb
|
65
|
+
- lib/gattica/auth.rb
|
66
|
+
- lib/gattica/convertible.rb
|
67
|
+
- lib/gattica/data_point.rb
|
68
|
+
- lib/gattica/data_set.rb
|
69
|
+
- lib/gattica/engine.rb
|
70
|
+
- lib/gattica/exceptions.rb
|
71
|
+
- lib/gattica/goals.rb
|
72
|
+
- lib/gattica/hash_extensions.rb
|
73
|
+
- lib/gattica/profiles.rb
|
74
|
+
- lib/gattica/segment.rb
|
75
|
+
- lib/gattica/settings.rb
|
76
|
+
- lib/gattica/user.rb
|
77
|
+
- test/helper.rb
|
78
|
+
- test/settings.rb
|
79
|
+
- test/suite.rb
|
80
|
+
- test/test_engine.rb
|
81
|
+
- test/test_results.rb
|
82
|
+
- test/test_user.rb
|
83
|
+
homepage: http://github.com/chrisle/gattica
|
84
|
+
licenses: []
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ! '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubyforge_project:
|
103
|
+
rubygems_version: 1.8.24
|
104
|
+
signing_key:
|
105
|
+
specification_version: 3
|
106
|
+
summary: Gattica is a easy to use Ruby Gem for getting data from the Google Analytics
|
107
|
+
API.
|
108
|
+
test_files: []
|
109
|
+
has_rdoc:
|