cannikin-gattica 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,10 @@
1
+ == 0.2.0 / 2009-04-27
2
+ * Changed initialization format: pass a hash of options rather than individual email, password and profile_id
3
+ * Can initialize with a valid token and use that instead of requiring email/password each time
4
+ * Can initialize with your own logger object instead of having to use the default (useful if you're using with Rails, initialize with RAILS_DEFAULT_LOGGER)
5
+ * Show error if token is invalid or expired (Google returns a 401 on any HTTP call)
6
+ * Started tests
7
+
1
8
  == 0.1.4 / 2009-04-22
2
9
  * Another attempt at getting the gem to build on github
3
10
 
data/README.rdoc CHANGED
@@ -11,25 +11,23 @@ When you want to require, you just use 'gattica' as the gem name:
11
11
  require 'gattica'
12
12
 
13
13
  = Introduction
14
+ It's a good idea to familiarize yourself with the Google API docs: http://code.google.com/apis/analytics/docs/gdata/gdataDeveloperGuide.html
15
+
16
+ In particular there are some very specific combinations of Metrics and Dimensions that
17
+ you are restricted to and those are explained in this document: http://code.google.com/apis/analytics/docs/gdata/gdataReferenceDimensionsMetrics.html
18
+
19
+ See examples/example.rb for some code that should work automatically if you replace the email/password with your own
20
+
14
21
  There are generally three steps to getting info from the GA API:
15
22
 
16
23
  1. Authenticate
17
24
  2. Get a profile id
18
25
  3. Get the data you really want
19
26
 
20
- It's a good idea to familiarize yourself with the Google API docs:
21
-
22
- http://code.google.com/apis/analytics/docs/gdata/gdataDeveloperGuide.html
23
-
24
- In particular there are some very specific combinations of Metrics and Dimensions that
25
- you are restricted to and those are explained in this document:
26
-
27
- http://code.google.com/apis/analytics/docs/gdata/gdataReferenceDimensionsMetrics.html
28
-
29
27
  = Usage
30
28
  This library does all three. A typical transaction will look like this:
31
29
 
32
- gs = Gattica.new('johndoe@google.com','password',123456)
30
+ gs = Gattica.new({:email => 'johndoe@google.com', :password => 'password', profile_id => 123456})
33
31
  results = gs.get({ :start_date => '2008-01-01',
34
32
  :end_date => '2008-02-01',
35
33
  :dimensions => 'browser',
@@ -45,7 +43,7 @@ page views.
45
43
 
46
44
  If you don't know the profile_id you want to get data for, call +accounts+
47
45
 
48
- gs = Gattica.new('johndoe@google.com','password')
46
+ gs = Gattica.new({:email => 'johndoe@google.com', :password => 'password'})
49
47
  accounts = gs.accounts
50
48
 
51
49
  This returns all of the accounts and profiles that the user has access to. Note that if you
@@ -59,9 +57,7 @@ use this method to get profiles, you need to manually set the profile before you
59
57
  :sort => '-pageviews'})
60
58
 
61
59
  When you put in the names for the dimensions and metrics you want, refer to this doc for the
62
- available names:
63
-
64
- http://code.google.com/apis/analytics/docs/gdata/gdataReferenceDimensionsMetrics.html
60
+ available names: http://code.google.com/apis/analytics/docs/gdata/gdataReferenceDimensionsMetrics.html
65
61
 
66
62
  Note that you do *not* use the 'ga:' prefix when you tell Gattica which ones you want. Gattica
67
63
  adds that for you automatically.
@@ -74,7 +70,35 @@ also pass in single values as arrays too, if you wish):
74
70
  :dimensions => ['browser','browserVersion'],
75
71
  :metrics => ['pageviews','visits'],
76
72
  :sort => ['-pageviews']})
77
-
73
+
74
+ == Notes on Authentication
75
+ === Authentication Token
76
+ Google recommends not re-authenticating each time you do a request against the API. To accomplish
77
+ this you should save the authorization token you receive from Google and use that for future
78
+ requests:
79
+
80
+ ga.token => 'DSasdf94...' (some huge long string)
81
+
82
+ You can now initialize Gattica with this token for future requests:
83
+
84
+ ga = Gattica.new({:token => 'DSasdf94...'})
85
+
86
+ (You enter the full token, of course). I'm not sure how long a token from the Google's ClientLogin
87
+ system remains active, but if/when I do I'll add that to the docs here.
88
+
89
+ === Headers
90
+ Google expects a special header in all HTTP requests called 'Authorization'. This contains your
91
+ token:
92
+
93
+ Authorization = GoogleLogin auth=DSasdf94...
94
+
95
+ This header is generated automatically. If you have your own headers you'd like to add, you can
96
+ pass them in when you initialize:
97
+
98
+ ga = Gattica.new({:token => 'DSasdf94...', :headers => {'My-Special-Header':'my_custom_value'}})
99
+
100
+ And they'll be sent with every request you make.
101
+
78
102
  = Output
79
103
  When Gattica was originally created it was intended to take the data returned and put it into
80
104
  Excel for someone else to crunch through the numbers. Thus, Gattica has great built-in support
@@ -122,9 +146,9 @@ the API what number to begin at and it will return the next 1000. Gattica does n
122
146
  support this, but it's in the plan for the very next version.
123
147
 
124
148
  The GA API support filtering, so you can say things like "only show me the pageviews for pages
125
- whose URL meets the regular expression ^/saf.*?/$". Gattica can pass in filters, but you'll need
126
- to know how to format them correctly (refer to the GA API), it won't let you do any kind of pretty
127
- syntax and convert it for you. I plan to add this soon.
149
+ whose URL meets the regular expression ^/saf.*?/$". Support for filters have begun, but according
150
+ to one report, the DataSet object that returns doesn't parse the results correctly. So for now,
151
+ avoid using filters.
128
152
 
129
153
  = The Future
130
154
  A couple of things I have planned:
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gemspec|
8
+ gemspec.name = "gattica"
9
+ gemspec.summary = "Gattica is a Ruby library for extracting data from the Google Analytics API."
10
+ gemspec.email = "cannikinn@gmail.com"
11
+ gemspec.homepage = "http://github.com/cannikin/gattica"
12
+ gemspec.description = "Gattica is a Ruby library for extracting data from the Google Analytics API."
13
+ gemspec.authors = ["Rob Cameron"]
14
+ end
15
+ rescue LoadError
16
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
17
+ end
18
+
19
+ Rake::TestTask.new do |t|
20
+ t.libs << 'lib'
21
+ t.pattern = 'test/**/test_*.rb'
22
+ t.verbose = false
23
+ end
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
- ---
1
+ ---
2
2
  :major: 0
3
- :minor: 1
4
- :patch: 4
3
+ :minor: 2
4
+ :patch: 0
data/examples/example.rb CHANGED
@@ -1,7 +1,10 @@
1
1
  require '../lib/gattica'
2
2
 
3
- # authenticate with the API
4
- ga = Gattica.new('username@gmail.com','password')
3
+ # authenticate with the API via email/password
4
+ ga = Gattica.new({:email => 'username@gmail.com', :password => 'password'})
5
+
6
+ # or, initialize via a pre-existing token (does not authenticate, but will throw an error on subsequent calls [like ga.accounts] if the token is invalid)
7
+ # ga = Gattica.new({:token => 'DQAAAJYAAACN-JMelka5I0Fs-T6lF53eUSfUooeHgcKc1iEdc0wkDS3w8GaXY7LjuUB_4vmzDB94HpScrULiweW_xQsU8yyUgdInDIX7ZnHm8_o0knf6FWSR90IoAZGsphpqteOjZ3O0NlNt603GgG7ylvGWRSeHl1ybD38nysMsKJR-dj0LYgIyPMvtgXLrqr_20oTTEExYbrDSg5_q84PkoLHUcODZ' })
5
8
 
6
9
  # get the list of accounts you have access to with that username and password
7
10
  accounts = ga.accounts
@@ -11,6 +14,8 @@ accounts = ga.accounts
11
14
  # property you're most used to seeing in GA, looks like UA123456-1)
12
15
  ga.profile_id = accounts.first.profile_id
13
16
 
17
+ # puts ga.token
18
+
14
19
  # now get the number of page views by browser for Janurary 2009
15
20
  # note that as of right now, Gattica does not support filtering
16
21
  data = ga.get({ :start_date => '2009-01-01',
data/lib/gattica.rb CHANGED
@@ -20,57 +20,22 @@ require 'gattica/data_point'
20
20
 
21
21
  # Gattica is a Ruby library for talking to the Google Analytics API.
22
22
  #
23
- # = Introduction
24
- # There are generally three steps to getting info from the GA API:
25
- #
26
- # 1. Authenticate
27
- # 2. Get a profile id
28
- # 3. Get the data you really want
29
- #
30
- # = Usage
31
- # This library does all three. A typical transaction will look like this:
32
- #
33
- # gs = Gattica.new('johndoe@google.com','password',123456)
34
- # results = gs.get({ :start_date => '2008-01-01',
35
- # :end_date => '2008-02-01',
36
- # :dimensions => 'browser',
37
- # :metrics => 'pageviews',
38
- # :sort => 'pageviews'})
39
- #
40
- # So we instantiate a copy of Gattica and pass it a Google Account email address and password.
41
- # The third parameter is the profile_id that we want to access data for. (If you don't know what
42
- # your profile_id is [and you probably don't since GA doesn't tell you except through this API]
43
- # then check out Gattica::Engine#accounts).
44
- #
45
- # Then we call +get+ with the parameters we want to shape our data with. In this case we want
46
- # total page views, broken down by browser, from Jan 1 2008 to Feb 1 2008, sorted by page views.
47
- #
48
- # If you don't know the profile_id you want to get data for, call +accounts+
49
- #
50
- # gs = Gattica.new('johndoe@google.com','password')
51
- # accounts = gs.accounts
52
- #
53
- # This returns all of the accounts and profiles that the user has access to. Note that if you
54
- # use this method to get profiles, you need to manually set the profile before you can call +get+
55
- #
56
- # gs.profile_id = 123456
57
- # results = gs.get({ :start_date => '2008-01-01',
58
- # :end_date => '2008-02-01',
59
- # :dimensions => 'browser',
60
- # :metrics => 'pageviews',
61
- # :sort => 'pageviews'})
62
-
23
+ # Please see the README for usage docs.
63
24
 
64
25
  module Gattica
65
26
 
66
- VERSION = '0.1.4'
67
- LOGGER = Logger.new(STDOUT)
68
-
27
+ VERSION = '0.2.0'
28
+
29
+ # Creates a new instance of Gattica::Engine and gets us going. Please see the README for usage docs.
30
+ #
31
+ # ga = Gattica.new({:email => 'anonymous@anon.com', :password => 'password, :profile_id => 123456 })
32
+
69
33
  def self.new(*args)
70
34
  Engine.new(*args)
71
35
  end
72
36
 
73
- # The real meat of Gattica, deals with talking to GA, returning and parsing results.
37
+ # The real meat of Gattica, deals with talking to GA, returning and parsing results. You automatically
38
+ # get an instance of this when you go Gattica.new()
74
39
 
75
40
  class Engine
76
41
 
@@ -78,33 +43,48 @@ module Gattica
78
43
  PORT = 443
79
44
  SECURE = true
80
45
  DEFAULT_ARGS = { :start_date => nil, :end_date => nil, :dimensions => [], :metrics => [], :filters => [], :sort => [] }
46
+ DEFAULT_OPTIONS = { :email => nil, :password => nil, :token => nil, :profile_id => nil, :debug => false, :headers => {}, :logger => Logger.new(STDOUT) }
81
47
 
82
- attr_reader :user, :token
83
- attr_accessor :profile_id
48
+ attr_reader :user
49
+ attr_accessor :profile_id, :token
84
50
 
85
51
  # Create a user, and get them authorized.
86
- # If you're making a web app you're going to want to save the token that's returned by this
87
- # method so that you can use it later (without having to re-authenticate the user each time)
52
+ # If you're making a web app you're going to want to save the token that's retrieved by Gattica
53
+ # so that you can use it later (Google recommends not re-authenticating the user for each and every request)
88
54
  #
89
- # ga = Gattica.new('johndoe@google.com','password',123456)
55
+ # ga = Gattica.new({:email => 'johndoe@google.com', :password => 'password', :profile_id => 123456})
90
56
  # ga.token => 'DW9N00wenl23R0...' (really long string)
57
+ #
58
+ # Or if you already have the token (because you authenticated previously and now want to reuse that session):
59
+ #
60
+ # ga = Gattica.new({:token => '23ohda09hw...', :profile_id => 123456})
91
61
 
92
- def initialize(email,password,profile_id=0,debug=false)
93
- LOGGER.datetime_format = '' if LOGGER.respond_to? 'datetime_format'
62
+ def initialize(options={})
63
+ @options = DEFAULT_OPTIONS.merge(options)
64
+ @logger = @options[:logger]
94
65
 
95
- @profile_id = profile_id
96
- @user_accounts = nil
66
+ @profile_id = @options[:profile_id] # if you don't include the profile_id now, you'll have to set it manually later via Gattica::Engine#profile_id=
67
+ @user_accounts = nil # filled in later if the user ever calls Gattica::Engine#accounts
68
+ @headers = {} # headers used for any HTTP requests (Google requires a special 'Authorization' header)
97
69
 
98
70
  # save an http connection for everyone to use
99
71
  @http = Net::HTTP.new(SERVER, PORT)
100
72
  @http.use_ssl = SECURE
101
- @http.set_debug_output $stdout if debug
73
+ @http.set_debug_output $stdout if @options[:debug]
102
74
 
103
- # create a user and authenticate them
104
- @user = User.new(email, password)
105
- @auth = Auth.new(@http, user, { :source => 'active-gattica-0.1' }, { 'User-Agent' => 'ruby 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0] Net::HTTP' })
106
- @token = @auth.tokens[:auth]
107
- @headers = { 'Authorization' => "GoogleLogin auth=#{@token}" }
75
+ # authenticate
76
+ if @options[:email] && @options[:password] # email and password: authenticate and get a token from Google's ClientLogin
77
+ @user = User.new(@options[:email], @options[:password])
78
+ @auth = Auth.new(@http, user, { :source => 'gattica-'+VERSION }, { 'User-Agent' => 'Ruby Net::HTTP' })
79
+ self.token = @auth.tokens[:auth]
80
+ elsif @options[:token] # use an existing token (this also sets the headers for any HTTP requests we make)
81
+ self.token = @options[:token]
82
+ else # no login or token, you can't do anything
83
+ raise GatticaError::NoLoginOrToken, 'You must provide an email and password, or authentication token'
84
+ end
85
+
86
+ # the user can provide their own additional headers - merge them into the ones that Gattica requires
87
+ @headers = @headers.merge(@options[:headers])
108
88
 
109
89
  # TODO: check that the user has access to the specified profile and show an error here rather than wait for Google to respond with a message
110
90
  end
@@ -115,7 +95,7 @@ module Gattica
115
95
  # don't know the profile_id then use this method to get a list of all them. Then set the profile_id of your
116
96
  # instance and you can make regular calls from then on.
117
97
  #
118
- # ga = Gattica.new('johndoe@google.com','password')
98
+ # ga = Gattica.new({:email => 'johndoe@google.com', :password => 'password'})
119
99
  # ga.get_accounts
120
100
  # # you parse through the accounts to find the profile_id you need
121
101
  # ga.profile_id = 12345678
@@ -129,7 +109,11 @@ module Gattica
129
109
  def accounts
130
110
  # if we haven't retrieved the user's accounts yet, get them now and save them
131
111
  if @accts.nil?
112
+ # TODO: move these calls into their own method so we can encapsulate all the errors in one place
132
113
  response, data = @http.get('/analytics/feeds/accounts/default', @headers)
114
+ if response.code == '401'
115
+ raise GatticaError::InvalidToken, 'The token you have provided is invalid or has expired'
116
+ end
133
117
  xml = Hpricot(data)
134
118
  @user_accounts = xml.search(:entry).collect { |entry| Account.new(entry) }
135
119
  end
@@ -141,7 +125,7 @@ module Gattica
141
125
  #
142
126
  # == Usage
143
127
  #
144
- # gs = Gattica.new('johndoe@google.com','password',123456)
128
+ # gs = Gattica.new({:email => 'johndoe@google.com', :password => 'password', :profile_id => 123456})
145
129
  # gs.get({ :start_date => '2008-01-01',
146
130
  # :end_date => '2008-02-01',
147
131
  # :dimensions => 'browser',
@@ -174,8 +158,12 @@ module Gattica
174
158
  def get(args={})
175
159
  args = validate_and_clean(DEFAULT_ARGS.merge(args))
176
160
  query_string = build_query_string(args,@profile_id)
177
- LOGGER.debug(query_string)
161
+ @logger.debug(query_string)
162
+ # TODO: move these calls into their own method so we can encapsulate all the errors in one place
178
163
  response, data = @http.get("/analytics/feeds/data?#{query_string}", @headers)
164
+ if response.code == '401'
165
+ raise GatticaError::InvalidToken, 'The token you have provided is invalid or has expired'
166
+ end
179
167
  begin
180
168
  response.value
181
169
  rescue Net::HTTPServerException => e
@@ -185,7 +173,24 @@ module Gattica
185
173
  end
186
174
 
187
175
 
176
+ # Since google wants the token to appear in any HTTP call's header, we have to set that header
177
+ # again any time @token is changed
178
+
179
+ def token=(token)
180
+ @token = token
181
+ set_http_headers
182
+ end
183
+
184
+
188
185
  private
186
+
187
+ # Sets up the HTTP headers that Google expects (this is called any time @token is set either by Gattica
188
+ # or manually by the user since the header must include the token)
189
+ def set_http_headers
190
+ @headers['Authorization'] = "GoogleLogin auth=#{@token}"
191
+ end
192
+
193
+
189
194
  # Creates a valid query string for GA
190
195
  def build_query_string(args,profile)
191
196
  output = "ids=ga:#{profile}&start-date=#{args[:start_date]}&end-date=#{args[:end_date]}"
@@ -4,6 +4,8 @@ module GatticaError
4
4
  class InvalidPassword < StandardError; end;
5
5
  # authentication errors
6
6
  class CouldNotAuthenticate < StandardError; end;
7
+ class NoLoginOrToken < StandardError; end;
8
+ class InvalidToken < StandardError; end;
7
9
  # profile errors
8
10
  class InvalidProfileId < StandardError; end;
9
11
  # search errors
data/test/helper.rb CHANGED
@@ -4,7 +4,7 @@ require 'rubygems'
4
4
  require 'test/unit'
5
5
  require 'mocha'
6
6
 
7
- include Gattica
7
+ # include Gattica
8
8
 
9
9
  def fixture(name)
10
10
  File.read(File.join(File.dirname(__FILE__), 'fixtures', name))
@@ -1,13 +1,12 @@
1
1
  require File.dirname(__FILE__) + '/helper'
2
2
 
3
- class TestActor < Test::Unit::TestCase
3
+ class TestAuth < Test::Unit::TestCase
4
4
  def setup
5
5
 
6
6
  end
7
7
 
8
- # from_string
9
-
10
8
  def test_truth
11
9
  assert true
12
10
  end
13
- end
11
+
12
+ end
@@ -0,0 +1,14 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestEngine < Test::Unit::TestCase
4
+ def setup
5
+
6
+ end
7
+
8
+ def test_initialization
9
+ assert Gattica.new({:email => 'anonymous@anon.com', :password => 'none'}) # you can initialize with a potentially invalid email and password
10
+ assert Gattica.new({:token => 'test'}) # you can initialize with a potentially invalid token
11
+ assert_raise GatticaError::NoLoginOrToken do Gattica.new() end # but, you must initialize with one or the other
12
+ end
13
+
14
+ end
data/test/test_user.rb ADDED
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__) + '/helper'
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 CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cannikin-gattica
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Cameron
@@ -9,34 +9,26 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-22 00:00:00 -07:00
12
+ date: 2009-04-27 00:00:00 -07:00
13
13
  default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: hpricot
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: 0.8.1
24
- version:
14
+ dependencies: []
15
+
25
16
  description: Gattica is a Ruby library for extracting data from the Google Analytics API.
26
17
  email: cannikinn@gmail.com
27
18
  executables: []
28
19
 
29
20
  extensions: []
30
21
 
31
- extra_rdoc_files: []
32
-
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.rdoc
33
25
  files:
34
26
  - History.txt
35
- - README.rdoc
36
27
  - LICENSE
28
+ - README.rdoc
29
+ - Rakefile
37
30
  - VERSION.yml
38
31
  - examples/example.rb
39
- - lib/gattica
40
32
  - lib/gattica.rb
41
33
  - lib/gattica/account.rb
42
34
  - lib/gattica/auth.rb
@@ -48,12 +40,13 @@ files:
48
40
  - lib/gattica/user.rb
49
41
  - test/helper.rb
50
42
  - test/suite.rb
51
- - test/test_sample.rb
43
+ - test/test_auth.rb
44
+ - test/test_engine.rb
45
+ - test/test_user.rb
52
46
  has_rdoc: true
53
47
  homepage: http://github.com/cannikin/gattica
54
48
  post_install_message:
55
49
  rdoc_options:
56
- - --inline-source
57
50
  - --charset=UTF-8
58
51
  require_paths:
59
52
  - lib
@@ -69,13 +62,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
62
  - !ruby/object:Gem::Version
70
63
  version: "0"
71
64
  version:
72
- requirements:
73
- - A Google Analytics Account
74
- - One or more Profiles that are being tracked in your GA account
65
+ requirements: []
66
+
75
67
  rubyforge_project:
76
68
  rubygems_version: 1.2.0
77
69
  signing_key:
78
70
  specification_version: 2
79
71
  summary: Gattica is a Ruby library for extracting data from the Google Analytics API.
80
- test_files: []
81
-
72
+ test_files:
73
+ - test/helper.rb
74
+ - test/suite.rb
75
+ - test/test_auth.rb
76
+ - test/test_engine.rb
77
+ - test/test_user.rb
78
+ - examples/example.rb