garb 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,37 +1,12 @@
1
- garb
1
+ Garb
2
2
  ====
3
3
 
4
- by Tony Pitale with much help from Justin Marney, Patrick Reagan and others at Viget Labs
5
-
6
4
  http://github.com/vigetlabs/garb
7
5
 
8
6
  Important Changes
9
7
  =================
10
8
 
11
- Version 0.5.0
12
-
13
- * Filters now have a new DSL so that I could toss Symbol operators (which conflict with DataMapper and MongoMapper)
14
- * The method of passing a hash to filters no longer works, at all
15
-
16
- Version 0.4.0
17
-
18
- * Changes the api for filters and sort making it consistent with metrics/dimensions
19
- * If you wish to clear the defaults defined on a class, you may use clear_(filters/sort/metrics/dimensions)
20
- * To make a custom class using Garb::Resource, you must now extend instead of include the module
21
-
22
- Version 0.3.2
23
-
24
- * adds Profile.first which can be used to get the first profile with a table id, or web property id (UA number)
25
-
26
- Version 0.2.4
27
-
28
- * requires happymapper from rubygems, version 0.2.5. Be sure to update.
29
-
30
- Version 0.2.0
31
-
32
- * makes major changes (compared to 0.1.0) to the way garb is used to build reports.
33
- * There is now both a module that gets included for generating defined classes,
34
- * slight changes to the way that the Report class can be used.
9
+ Please read CHANGELOG
35
10
 
36
11
  Description
37
12
  -----------
@@ -43,13 +18,19 @@ Description
43
18
  Basic Usage
44
19
  ===========
45
20
 
46
- Login
47
- -----
21
+ Single User Login
22
+ -----------------
48
23
 
49
24
  > Garb::Session.login(username, password)
25
+
26
+ OAuth Access Token
27
+ ------------------
28
+
29
+ > Garb::Session.access_token = access_token # assign from oauth gem
50
30
 
51
31
  Accounts
52
32
  --------
33
+
53
34
  > Garb::Account.all
54
35
 
55
36
  Profiles
@@ -62,8 +43,8 @@ Profiles
62
43
  > Garb::Profile.all
63
44
  > profile = Garb::Profile.all.first
64
45
 
65
- Define a Report Class and Get Results
66
- -------------------------------------
46
+ Define a Report Class
47
+ ---------------------
67
48
 
68
49
  class Exits
69
50
  extend Garb::Resource
@@ -75,8 +56,20 @@ Define a Report Class and Get Results
75
56
  filters do
76
57
  eql(:page_path, 'season')
77
58
  end
59
+
60
+ # alternative:
61
+ # filters :page_path.eql => 10
78
62
  end
79
63
 
64
+ Get the Results
65
+ ---------------
66
+
67
+ > Exits.results(profile)
68
+
69
+ OR shorthand
70
+
71
+ > profile.exits
72
+
80
73
  Other Parameters
81
74
  ----------------
82
75
 
@@ -118,6 +111,9 @@ Building a Report
118
111
  contains(:page_path, 'season')
119
112
  gt(:exits, 100)
120
113
  end
114
+
115
+ # or with a hash
116
+ # filters :page_path.contains => 'season', :exits.gt => 100
121
117
  end
122
118
 
123
119
  reports will be an array of OpenStructs with methods for the metrics and dimensions returned.
@@ -135,6 +131,9 @@ Build a One-Off Report
135
131
  gte(:exits, 10)
136
132
  and
137
133
 
134
+ # or with a hash
135
+ # report.filters :page_path.contains => 'season', :exits.gt => 100
136
+
138
137
  report.results
139
138
 
140
139
  Filtering
@@ -152,21 +151,21 @@ Filtering
152
151
 
153
152
  Operators on metrics:
154
153
 
155
- :eql => '==',
156
- :not_eql => '!=',
157
- :gt => '>',
158
- :gte => '>=',
159
- :lt => '<',
160
- :lte => '<='
154
+ eql => '==',
155
+ not_eql => '!=',
156
+ gt => '>',
157
+ gte => '>=',
158
+ lt => '<',
159
+ lte => '<='
161
160
 
162
161
  Operators on dimensions:
163
162
 
164
- :matches => '==',
165
- :does_not_match => '!=',
166
- :contains => '=~',
167
- :does_not_contain => '!~',
168
- :substring => '=@',
169
- :not_substring => '!@'
163
+ matches => '==',
164
+ does_not_match => '!=',
165
+ contains => '=~',
166
+ does_not_contain => '!~',
167
+ substring => '=@',
168
+ not_substring => '!@'
170
169
 
171
170
  Given the previous example one-off report, we can add a line for filter:
172
171
 
@@ -174,10 +173,14 @@ Filtering
174
173
  eql(:page_path, '/extend/effectively-using-git-with-subversion/')
175
174
  end
176
175
 
176
+ Or, if you're comfortable using symbol operators:
177
+
178
+ report.filters :page_path.eql => '/extend/effectively-using-git-with-subversion/'
179
+
177
180
  SSL
178
181
  ---
179
182
 
180
- Version 0.2.3 includes support for real ssl encryption for authentication. First do:
183
+ Version 0.2.3 includes support for real ssl encryption for SINGLE USER authentication. First do:
181
184
 
182
185
  Garb::Session.login(username, password, :secure => true)
183
186
 
@@ -191,27 +194,40 @@ TODOS
191
194
  -----
192
195
 
193
196
  * Sessions are currently global, which isn't awesome
194
- * Single user login is the only supported method currently.
195
- Intend to add hooks for using OAuth
196
197
  * Read opensearch header in results
197
198
 
198
199
  Requirements
199
200
  ------------
200
201
 
201
- happymapper >= 0.3.0 (should also install libxml)
202
- active_support >= 2.3.0
202
+ * happymapper >= 0.3.0 (should also install libxml)
203
+ * active_support >= 2.3.0
204
+
205
+ Requirements for Testing
206
+ ------------------------
207
+
208
+ * jferris-mocha
209
+ * tpitale-shoulda (works with minitest)
203
210
 
204
211
  Install
205
212
  -------
206
213
 
207
214
  sudo gem install garb
208
215
 
216
+ Contributors
217
+ ------------
218
+
219
+ Many Thanks, for all their help, goes to:
220
+
221
+ * Patrick Reagan
222
+ * Justin Marney
223
+ * Nick Plante
224
+
209
225
  License
210
226
  -------
211
227
 
212
228
  (The MIT License)
213
229
 
214
- Copyright (c) 2008 Viget Labs
230
+ Copyright (c) 2010 Viget Labs
215
231
 
216
232
  Permission is hereby granted, free of charge, to any person obtaining
217
233
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ spec = Gem::Specification.new do |s|
12
12
  s.has_rdoc = false
13
13
  s.rubyforge_project = 'viget'
14
14
  s.summary = "Google Analytics API Ruby Wrapper"
15
- s.authors = ['Tony Pitale', 'Patrick Reagan']
15
+ s.authors = ['Tony Pitale']
16
16
  s.email = 'tony.pitale@viget.com'
17
17
  s.homepage = 'http://github.com/vigetlabs/garb'
18
18
  s.files = %w(README.md Rakefile) + Dir.glob("lib/**/*")
data/lib/garb.rb CHANGED
@@ -9,10 +9,10 @@ require 'happymapper'
9
9
  require 'active_support'
10
10
 
11
11
  require 'garb/version'
12
- require 'garb/profile_array'
13
12
  require 'garb/authentication_request'
14
13
  require 'garb/data_request'
15
14
  require 'garb/session'
15
+ require 'garb/profile_reports'
16
16
  require 'garb/profile'
17
17
  require 'garb/account'
18
18
  require 'garb/filter_parameters'
data/lib/garb/account.rb CHANGED
@@ -9,7 +9,21 @@ module Garb
9
9
  end
10
10
 
11
11
  def self.all
12
- Profile.all.group_to_array{|p| p.account_id}.map{|profiles| new(profiles)}
12
+ # Profile.all.group_to_array{|p| p.account_id}.map{|profiles| new(profiles)}
13
+
14
+ profile_groups = Profile.all.inject({}) do |hash, profile|
15
+ key = profile.account_id
16
+
17
+ if hash.has_key?(key)
18
+ hash[key] << profile
19
+ else
20
+ hash[key] = [profile]
21
+ end
22
+
23
+ hash
24
+ end
25
+
26
+ profile_groups.map {|k,v| v}.map {|profiles| new(profiles)}
13
27
  end
14
28
  end
15
29
  end
@@ -1,5 +1,6 @@
1
1
  module Garb
2
2
  class DataRequest
3
+ class ClientError < StandardError; end
3
4
 
4
5
  def initialize(base_url, parameters={})
5
6
  @base_url = base_url
@@ -16,13 +17,25 @@ module Garb
16
17
  end
17
18
 
18
19
  def send_request
20
+ response = if Session.single_user?
21
+ single_user_request
22
+ elsif Session.oauth_user?
23
+ oauth_user_request
24
+ end
25
+
26
+ raise ClientError, response.body.inspect unless response.kind_of?(Net::HTTPSuccess)
27
+ response
28
+ end
29
+
30
+ def single_user_request
19
31
  http = Net::HTTP.new(uri.host, uri.port)
20
32
  http.use_ssl = true
21
33
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
22
- response = http.get("#{uri.path}#{query_string}", 'Authorization' => "GoogleLogin auth=#{Session.auth_token}")
23
- raise response.body.inspect unless response.is_a?(Net::HTTPOK)
24
- response
34
+ http.get("#{uri.path}#{query_string}", 'Authorization' => "GoogleLogin auth=#{Session.auth_token}")
25
35
  end
26
36
 
37
+ def oauth_user_request
38
+ Session.access_token.get("#{uri}#{query_string}")
39
+ end
27
40
  end
28
- end
41
+ end
data/lib/garb/profile.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  module Garb
2
2
  class Profile
3
-
3
+
4
+ include ProfileReports
5
+
4
6
  attr_reader :table_id, :title, :account_name, :account_id, :web_property_id
5
7
 
6
8
  class Property
@@ -44,8 +46,7 @@ module Garb
44
46
  def self.all
45
47
  url = "https://www.google.com/analytics/feeds/accounts/default"
46
48
  response = DataRequest.new(url).send_request
47
- profiles = Entry.parse(response.body).map {|entry| new(entry)}
48
- ProfileArray.new(profiles)
49
+ Entry.parse(response.body).map {|entry| new(entry)}
49
50
  end
50
51
 
51
52
  def self.first(id)
@@ -0,0 +1,15 @@
1
+ module Garb
2
+ module ProfileReports
3
+ def self.add_report_method(klass)
4
+ # demodulize leaves potential to redefine
5
+ # these methods given different namespaces
6
+ method_name = klass.to_s.demodulize.underscore
7
+
8
+ class_eval <<-CODE
9
+ def #{method_name}(opts = {}, &block)
10
+ #{klass}.results(self, opts, &block)
11
+ end
12
+ CODE
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ require 'reports/exits'
2
+ require 'reports/visits'
3
+ require 'reports/bounces'
4
+ require 'reports/pageviews'
5
+ require 'reports/unique_pageviews'
@@ -0,0 +1,5 @@
1
+ class Bounces
2
+ extend Garb::Resource
3
+
4
+ metric :bounces
5
+ end
@@ -0,0 +1,5 @@
1
+ class Exits
2
+ extend Garb::Resource
3
+
4
+ metric :exits
5
+ end
@@ -0,0 +1,5 @@
1
+ class Pageviews
2
+ extend Garb::Resource
3
+
4
+ metric :pageviews
5
+ end
@@ -0,0 +1,5 @@
1
+ class UniquePageviews
2
+ extend Garb::Resource
3
+
4
+ metric :unique_pageviews
5
+ end
@@ -0,0 +1,5 @@
1
+ class Visits
2
+ extend Garb::Resource
3
+
4
+ metric :visits
5
+ end
data/lib/garb/resource.rb CHANGED
@@ -3,6 +3,15 @@ module Garb
3
3
  MONTH = 2592000
4
4
  URL = "https://www.google.com/analytics/feeds/data"
5
5
 
6
+ def self.extended(base)
7
+ # define a method on a module that gets included into profile
8
+ # Exits would make:
9
+ # to enable profile.exits(options_hash, &block)
10
+ # returns Exits.results(self, options_hash, &block)
11
+ # every class defined which extends Resource will add to the module
12
+ ProfileReports.add_report_method(base)
13
+ end
14
+
6
15
  %w(metrics dimensions sort).each do |parameter|
7
16
  class_eval <<-CODE
8
17
  def #{parameter}(*fields)
data/lib/garb/session.rb CHANGED
@@ -2,12 +2,21 @@ module Garb
2
2
  module Session
3
3
  extend self
4
4
 
5
- attr_accessor :auth_token, :email
6
-
5
+ attr_accessor :auth_token, :access_token, :email
6
+
7
+ # use only for single user authentication
7
8
  def login(email, password, opts={})
8
9
  self.email = email
9
10
  auth_request = AuthenticationRequest.new(email, password, opts)
10
11
  self.auth_token = auth_request.auth_token(opts)
11
12
  end
13
+
14
+ def single_user?
15
+ auth_token && auth_token.is_a?(String)
16
+ end
17
+
18
+ def oauth_user?
19
+ !access_token.nil?
20
+ end
12
21
  end
13
22
  end
data/lib/garb/version.rb CHANGED
@@ -2,8 +2,8 @@ module Garb
2
2
  module Version
3
3
 
4
4
  MAJOR = 0
5
- MINOR = 5
6
- TINY = 1
5
+ MINOR = 6
6
+ TINY = 0
7
7
 
8
8
  def self.to_s # :nodoc:
9
9
  [MAJOR, MINOR, TINY].join('.')
@@ -6,10 +6,14 @@ module Garb
6
6
  should "have an array of accounts with all profiles" do
7
7
  p1 = stub(:account_id => '1111', :account_name => 'Blog 1')
8
8
  p2 = stub(:account_id => '1112', :account_name => 'Blog 2')
9
- Profile.stubs(:all).returns(ProfileArray.new([p1,p2,p1,p2]))
10
- Account.expects(:new).with([p1,p1]).returns('account1')
11
- Account.expects(:new).with([p2,p2]).returns('account2')
9
+
10
+ Profile.stubs(:all).returns([p1,p2,p1,p2])
11
+ Account.stubs(:new).returns('account1', 'account2')
12
+
12
13
  assert_equal ['account1','account2'], Account.all
14
+ assert_received(Profile, :all)
15
+ assert_received(Account, :new) {|e| e.with([p1,p1])}
16
+ assert_received(Account, :new) {|e| e.with([p2,p2])}
13
17
  end
14
18
  end
15
19
 
@@ -30,23 +30,73 @@ module Garb
30
30
  assert_equal expected, DataRequest.new(url).uri
31
31
  end
32
32
 
33
- should "be able to make a request to the GAAPI" do
33
+ should "be able to send a request for a single user" do
34
+ Session.stubs(:single_user?).returns(true)
35
+ response = mock('Net::HTTPOK') do |m|
36
+ m.expects(:kind_of?).with(Net::HTTPSuccess).returns(true)
37
+ end
38
+
39
+ data_request = DataRequest.new('https://example.com/data', 'key' => 'value')
40
+ data_request.stubs(:single_user_request).returns(response)
41
+ data_request.send_request
42
+
43
+ assert_received(data_request, :single_user_request)
44
+ end
45
+
46
+ should "be able to send a request for an oauth user" do
47
+ Session.stubs(:single_user?).returns(false)
48
+ Session.stubs(:oauth_user?).returns(true)
49
+ response = mock('Net::HTTPOK') do |m|
50
+ m.expects(:kind_of?).with(Net::HTTPSuccess).returns(true)
51
+ end
52
+
53
+ data_request = DataRequest.new('https://example.com/data', 'key' => 'value')
54
+ data_request.stubs(:oauth_user_request).returns(response)
55
+ data_request.send_request
56
+
57
+ assert_received(data_request, :oauth_user_request)
58
+ end
59
+
60
+ should "raise if the request is unauthorized" do
61
+ Session.stubs(:single_user?).returns(false)
62
+ Session.stubs(:oauth_user?).returns(true)
63
+ response = mock('Net::HTTPUnauthorized', :body => 'Error')
64
+
65
+ data_request = DataRequest.new('https://example.com/data', 'key' => 'value')
66
+ data_request.stubs(:oauth_user_request).returns(response)
67
+
68
+ assert_raises(Garb::DataRequest::ClientError) do
69
+ data_request.send_request
70
+ end
71
+ end
72
+
73
+ should "be able to request via the ouath access token" do
74
+ access_token = stub(:get => "responseobject")
75
+ Session.stubs(:access_token).returns(access_token)
76
+
77
+ data_request = DataRequest.new('https://example.com/data', 'key' => 'value')
78
+ assert_equal 'responseobject', data_request.oauth_user_request
79
+
80
+ assert_received(Session, :access_token)
81
+ assert_received(access_token, :get) {|e| e.with('https://example.com/data?key=value')}
82
+ end
83
+
84
+ should "be able to request via http with an auth token" do
34
85
  Session.expects(:auth_token).with().returns('toke')
35
86
  response = mock
36
- response.expects(:is_a?).with(Net::HTTPOK).returns(true)
37
-
87
+
38
88
  http = mock do |m|
39
89
  m.expects(:use_ssl=).with(true)
40
90
  m.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
41
91
  m.expects(:get).with('/data?key=value', 'Authorization' => 'GoogleLogin auth=toke').returns(response)
42
92
  end
43
-
93
+
44
94
  Net::HTTP.expects(:new).with('example.com', 443).returns(http)
45
-
95
+
46
96
  data_request = DataRequest.new('https://example.com/data', 'key' => 'value')
47
- assert_equal response, data_request.send_request
97
+ assert_equal response, data_request.single_user_request
48
98
  end
49
99
  end
50
100
 
51
101
  end
52
- end
102
+ end
@@ -0,0 +1,29 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', '/test_helper')
2
+
3
+ module Garb
4
+
5
+ Exits = Class.new
6
+
7
+ class FakeProfile
8
+ include ProfileReports
9
+ end
10
+
11
+ class ProfileReportsTest < MiniTest::Unit::TestCase
12
+ context "The ProfileReports module" do
13
+ should "define a new method when given a class" do
14
+ ProfileReports.add_report_method(Exits)
15
+ assert_equal true, FakeProfile.new.respond_to?(:exits)
16
+ end
17
+
18
+ should "return results from the given class with options" do
19
+ results = [1,2,3]
20
+ Exits.stubs(:results).returns(results)
21
+ ProfileReports.add_report_method(Exits)
22
+
23
+ profile = FakeProfile.new
24
+ assert_equal results, profile.exits(:start => "now")
25
+ assert_received(Exits, :results) {|e| e.with(profile, :start => "now")}
26
+ end
27
+ end
28
+ end
29
+ end
@@ -28,7 +28,16 @@ module Garb
28
28
  Session.login('email', 'password')
29
29
  assert_equal 'email', Session.email
30
30
  end
31
-
31
+
32
+ should "know if the Session is for a single user" do
33
+ Session.auth_token = "abcdefg1234567"
34
+ assert_equal true, Session.single_user?
35
+ end
36
+
37
+ should "know if the Session is for oauth" do
38
+ Session.access_token = 'some_oauth_access_token'
39
+ assert_equal true, Session.oauth_user?
40
+ end
32
41
  end
33
42
 
34
43
  end
metadata CHANGED
@@ -1,16 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: garb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Pitale
8
- - Patrick Reagan
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
11
 
13
- date: 2010-01-14 00:00:00 -05:00
12
+ date: 2010-02-10 00:00:00 -05:00
14
13
  default_executable:
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
@@ -48,12 +47,17 @@ files:
48
47
  - lib/garb/authentication_request.rb
49
48
  - lib/garb/data_request.rb
50
49
  - lib/garb/filter_parameters.rb
51
- - lib/garb/oauth_session.rb
52
50
  - lib/garb/profile.rb
53
- - lib/garb/profile_array.rb
51
+ - lib/garb/profile_reports.rb
54
52
  - lib/garb/report.rb
55
53
  - lib/garb/report_parameter.rb
56
54
  - lib/garb/report_response.rb
55
+ - lib/garb/reports/bounces.rb
56
+ - lib/garb/reports/exits.rb
57
+ - lib/garb/reports/pageviews.rb
58
+ - lib/garb/reports/unique_pageviews.rb
59
+ - lib/garb/reports/visits.rb
60
+ - lib/garb/reports.rb
57
61
  - lib/garb/resource.rb
58
62
  - lib/garb/session.rb
59
63
  - lib/garb/version.rb
@@ -68,6 +72,7 @@ files:
68
72
  - test/unit/garb/data_request_test.rb
69
73
  - test/unit/garb/filter_parameters_test.rb
70
74
  - test/unit/garb/oauth_session_test.rb
75
+ - test/unit/garb/profile_reports_test.rb
71
76
  - test/unit/garb/profile_test.rb
72
77
  - test/unit/garb/report_parameter_test.rb
73
78
  - test/unit/garb/report_response_test.rb
@@ -114,6 +119,7 @@ test_files:
114
119
  - test/unit/garb/data_request_test.rb
115
120
  - test/unit/garb/filter_parameters_test.rb
116
121
  - test/unit/garb/oauth_session_test.rb
122
+ - test/unit/garb/profile_reports_test.rb
117
123
  - test/unit/garb/profile_test.rb
118
124
  - test/unit/garb/report_parameter_test.rb
119
125
  - test/unit/garb/report_response_test.rb
@@ -1,21 +0,0 @@
1
- module Garb
2
- class OAuthSession
3
- attr_accessor :access_token
4
-
5
- OAuthGetRequestToken = "https://www.google.com/accounts/OAuthGetRequestToken"
6
- OAuthAuthorizeToken = "https://www.google.com/accounts/OAuthAuthorizeToken"
7
- OAuthGetAccessToken = "https://www.google.com/accounts/OAuthGetAccessToken"
8
-
9
- def get_request_token
10
-
11
- end
12
-
13
- def authorization_request
14
-
15
- end
16
-
17
- def get_access_token
18
-
19
- end
20
- end
21
- end
@@ -1,18 +0,0 @@
1
- module Garb
2
- class ProfileArray < Array
3
- def group_to_array
4
- h = Hash.new
5
-
6
- each do |element|
7
- key = yield(element)
8
- if h.has_key?(key)
9
- h[key] << element
10
- else
11
- h[key] = [element]
12
- end
13
- end
14
-
15
- h.map{|k,v| v}
16
- end
17
- end
18
- end