titanous-garb 0.8.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/README.md +262 -0
  2. data/Rakefile +56 -0
  3. data/lib/garb.rb +69 -0
  4. data/lib/garb/account.rb +21 -0
  5. data/lib/garb/account_feed_request.rb +25 -0
  6. data/lib/garb/authentication_request.rb +53 -0
  7. data/lib/garb/data_request.rb +42 -0
  8. data/lib/garb/destination.rb +20 -0
  9. data/lib/garb/filter_parameters.rb +41 -0
  10. data/lib/garb/goal.rb +20 -0
  11. data/lib/garb/management/account.rb +32 -0
  12. data/lib/garb/management/feed.rb +26 -0
  13. data/lib/garb/management/goal.rb +20 -0
  14. data/lib/garb/management/profile.rb +39 -0
  15. data/lib/garb/management/web_property.rb +30 -0
  16. data/lib/garb/model.rb +89 -0
  17. data/lib/garb/profile.rb +33 -0
  18. data/lib/garb/profile_reports.rb +16 -0
  19. data/lib/garb/report.rb +28 -0
  20. data/lib/garb/report_parameter.rb +25 -0
  21. data/lib/garb/report_response.rb +34 -0
  22. data/lib/garb/reports.rb +5 -0
  23. data/lib/garb/reports/bounces.rb +5 -0
  24. data/lib/garb/reports/exits.rb +5 -0
  25. data/lib/garb/reports/pageviews.rb +5 -0
  26. data/lib/garb/reports/unique_pageviews.rb +5 -0
  27. data/lib/garb/reports/visits.rb +5 -0
  28. data/lib/garb/resource.rb +115 -0
  29. data/lib/garb/session.rb +35 -0
  30. data/lib/garb/step.rb +13 -0
  31. data/lib/garb/version.rb +13 -0
  32. data/lib/support.rb +40 -0
  33. data/test/fixtures/cacert.pem +67 -0
  34. data/test/fixtures/profile_feed.xml +72 -0
  35. data/test/fixtures/report_feed.xml +46 -0
  36. data/test/test_helper.rb +37 -0
  37. data/test/unit/garb/account_feed_request_test.rb +9 -0
  38. data/test/unit/garb/account_test.rb +53 -0
  39. data/test/unit/garb/authentication_request_test.rb +121 -0
  40. data/test/unit/garb/data_request_test.rb +120 -0
  41. data/test/unit/garb/destination_test.rb +28 -0
  42. data/test/unit/garb/filter_parameters_test.rb +59 -0
  43. data/test/unit/garb/goal_test.rb +24 -0
  44. data/test/unit/garb/management/account_test.rb +54 -0
  45. data/test/unit/garb/management/profile_test.rb +59 -0
  46. data/test/unit/garb/management/web_property_test.rb +58 -0
  47. data/test/unit/garb/model_test.rb +134 -0
  48. data/test/unit/garb/oauth_session_test.rb +11 -0
  49. data/test/unit/garb/profile_reports_test.rb +29 -0
  50. data/test/unit/garb/profile_test.rb +77 -0
  51. data/test/unit/garb/report_parameter_test.rb +43 -0
  52. data/test/unit/garb/report_response_test.rb +37 -0
  53. data/test/unit/garb/report_test.rb +99 -0
  54. data/test/unit/garb/resource_test.rb +49 -0
  55. data/test/unit/garb/session_test.rb +91 -0
  56. data/test/unit/garb/step_test.rb +15 -0
  57. data/test/unit/garb_test.rb +26 -0
  58. data/test/unit/symbol_operator_test.rb +37 -0
  59. metadata +180 -0
data/README.md ADDED
@@ -0,0 +1,262 @@
1
+ Garb
2
+ ====
3
+
4
+ http://github.com/vigetlabs/garb
5
+
6
+ Important Changes
7
+ =================
8
+
9
+ I've started to work on a new module for reporting. You can see that work
10
+ in lib/garb/model.rb. This will be the API to replace both Garb::Resource
11
+ and Garb::Report. However, it isn't complete yet, so I would advise against
12
+ using it right now. Resource and Report will be deprecated around 0.9.0 and
13
+ I'd like to remove them for 1.0.0. I will start this process after I've
14
+ updated the documentation and I feel that Garb::Model is in a solid place.
15
+
16
+ Please read CHANGELOG
17
+
18
+ Description
19
+ -----------
20
+
21
+ Provides a Ruby API to the Google Analytics API.
22
+
23
+ http://code.google.com/apis/analytics/docs/gdata/gdataDeveloperGuide.html
24
+
25
+ Basic Usage
26
+ ===========
27
+
28
+ Single User Login
29
+ -----------------
30
+
31
+ > Garb::Session.login(username, password)
32
+
33
+ OAuth Access Token
34
+ ------------------
35
+
36
+ > Garb::Session.access_token = access_token # assign from oauth gem
37
+
38
+ AuthSub Token
39
+ -------------
40
+
41
+ > Garb::Session.auth_sub(token) # assign by AuthSub system
42
+
43
+ Accounts
44
+ --------
45
+
46
+ > Garb::Account.all
47
+
48
+ Profiles
49
+ --------
50
+
51
+ > Garb::Account.first.profiles
52
+
53
+ > Garb::Profile.first('UA-XXXX-XX')
54
+
55
+ > Garb::Profile.all
56
+ > profile = Garb::Profile.all.first
57
+
58
+ Define a Report Class
59
+ ---------------------
60
+
61
+ class Exits
62
+ extend Garb::Resource
63
+
64
+ metrics :exits, :pageviews, :exit_rate
65
+ dimensions :page_path
66
+ sort :exits
67
+
68
+ filters do
69
+ eql(:page_path, 'season')
70
+ end
71
+
72
+ # alternative:
73
+ # filters :page_path.eql => 10
74
+ end
75
+
76
+ Get the Results
77
+ ---------------
78
+
79
+ > Exits.results(profile)
80
+
81
+ OR shorthand
82
+
83
+ > profile.exits
84
+
85
+ Other Parameters
86
+ ----------------
87
+
88
+ * start_date: The date of the period you would like this report to start
89
+ * end_date: The date to end, inclusive
90
+ * limit: The maximum number of results to be returned
91
+ * offset: The starting index
92
+
93
+ Metrics & Dimensions
94
+ --------------------
95
+
96
+ Metrics and Dimensions are very complex because of the ways in which the can and cannot be combined.
97
+
98
+ I suggest reading the google documentation to familiarize yourself with this.
99
+
100
+ http://code.google.com/apis/analytics/docs/gdata/gdataReferenceDimensionsMetrics.html#bounceRate
101
+
102
+ When you've returned, you can pass the appropriate combinations (up to 50 metrics and 2 dimenstions)
103
+ to garb, as an array, of symbols. Or you can simply push a symbol into the array.
104
+
105
+ Sorting
106
+ -------
107
+
108
+ Sorting can be done on any metric or dimension defined in the request, with .desc reversing the sort.
109
+
110
+ Building a Report
111
+ -----------------
112
+
113
+ Given the class, session, and profile from above we can do:
114
+
115
+ Exits.results(profile, :limit => 10, :offset => 19)
116
+
117
+ Or, with sorting and filters:
118
+
119
+ Exits.results(profile, :limit => 10, :offset => 19) do
120
+ sort :exits
121
+
122
+ filters do
123
+ contains(:page_path, 'season')
124
+ gt(:exits, 100)
125
+ end
126
+
127
+ # or with a hash
128
+ # filters :page_path.contains => 'season', :exits.gt => 100
129
+ end
130
+
131
+ reports will be an array of OpenStructs with methods for the metrics and dimensions returned.
132
+
133
+ Build a One-Off Report
134
+ ----------------------
135
+
136
+ report = Garb::Report.new(profile)
137
+ report.metrics :pageviews, :exits
138
+ report.dimensions :page_path
139
+ report.sort :exits
140
+
141
+ report.filters do
142
+ contains(:page_path, 'season')
143
+ gte(:exits, 10)
144
+ and
145
+
146
+ # or with a hash
147
+ # report.filters :page_path.contains => 'season', :exits.gt => 100
148
+
149
+ report.results
150
+
151
+ Filtering
152
+ ---------
153
+
154
+ Google Analytics supports a significant number of filtering options.
155
+
156
+ http://code.google.com/apis/analytics/docs/gdata/gdataReference.html#filtering
157
+
158
+ We handle filtering as an array of hashes that you can push into,
159
+ which will be joined together (AND'd)
160
+
161
+ Here is what we can do currently:
162
+ (the operator is a method on a symbol metric or dimension)
163
+
164
+ Operators on metrics:
165
+
166
+ eql => '==',
167
+ not_eql => '!=',
168
+ gt => '>',
169
+ gte => '>=',
170
+ lt => '<',
171
+ lte => '<='
172
+
173
+ Operators on dimensions:
174
+
175
+ matches => '==',
176
+ does_not_match => '!=',
177
+ contains => '=~',
178
+ does_not_contain => '!~',
179
+ substring => '=@',
180
+ not_substring => '!@'
181
+
182
+ Given the previous example one-off report, we can add a line for filter:
183
+
184
+ report.filters do
185
+ eql(:page_path, '/extend/effectively-using-git-with-subversion/')
186
+ end
187
+
188
+ Or, if you're comfortable using symbol operators:
189
+
190
+ report.filters :page_path.eql => '/extend/effectively-using-git-with-subversion/'
191
+
192
+ SSL
193
+ ---
194
+
195
+ Version 0.2.3 includes support for real ssl encryption for SINGLE USER authentication. First do:
196
+
197
+ Garb::Session.login(username, password, :secure => true)
198
+
199
+ Next, be sure to download http://curl.haxx.se/ca/cacert.pem into your application somewhere.
200
+ Then, define a constant CA_CERT_FILE and point to that file.
201
+
202
+ For whatever reason, simply creating a new certificate store and setting the defaults would
203
+ not validate the google ssl certificate as authentic.
204
+
205
+ TODOS
206
+ -----
207
+
208
+ * Read opensearch header in results
209
+ * Investigate new features from GA to see if they're in the API, implement if so
210
+ * clarify AND/OR filtering behavior in code and documentation
211
+
212
+ Requirements
213
+ ------------
214
+
215
+ * crack >= 0.1.6
216
+ * active_support >= 2.2.0
217
+
218
+ Requirements for Testing
219
+ ------------------------
220
+
221
+ * jferris-mocha
222
+ * tpitale-shoulda (works with minitest)
223
+
224
+ Install
225
+ -------
226
+
227
+ sudo gem install garb
228
+
229
+ Contributors
230
+ ------------
231
+
232
+ Many Thanks, for all their help, goes to:
233
+
234
+ * Patrick Reagan
235
+ * Justin Marney
236
+ * Nick Plante
237
+
238
+ License
239
+ -------
240
+
241
+ (The MIT License)
242
+
243
+ Copyright (c) 2010 Viget Labs
244
+
245
+ Permission is hereby granted, free of charge, to any person obtaining
246
+ a copy of this software and associated documentation files (the
247
+ 'Software'), to deal in the Software without restriction, including
248
+ without limitation the rights to use, copy, modify, merge, publish,
249
+ distribute, sublicense, and/or sell copies of the Software, and to
250
+ permit persons to whom the Software is furnished to do so, subject to
251
+ the following conditions:
252
+
253
+ The above copyright notice and this permission notice shall be
254
+ included in all copies or substantial portions of the Software.
255
+
256
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
257
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
258
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
259
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
260
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
261
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
262
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,56 @@
1
+ require 'rake/gempackagetask'
2
+ require 'rake/testtask'
3
+
4
+ $:.unshift File.expand_path('../lib', __FILE__)
5
+ require 'garb'
6
+
7
+ task :default => :test
8
+
9
+ spec = Gem::Specification.new do |s|
10
+ s.name = 'titanous-garb'
11
+ s.version = Garb::Version.to_s
12
+ s.has_rdoc = false
13
+ s.summary = "Google Analytics API Ruby Wrapper"
14
+ s.authors = ['Tony Pitale']
15
+ s.email = 'tony.pitale@viget.com'
16
+ s.homepage = 'http://github.com/vigetlabs/garb'
17
+ s.files = %w(README.md Rakefile) + Dir.glob("lib/**/*")
18
+ s.test_files = Dir.glob("test/**/*")
19
+
20
+ s.add_dependency("crack", [">= 0.1.6"])
21
+ s.add_dependency("activesupport", [">= 2.2.0"])
22
+ end
23
+
24
+ Rake::GemPackageTask.new(spec) do |pkg|
25
+ pkg.gem_spec = spec
26
+ end
27
+
28
+ Rake::TestTask.new do |t|
29
+ t.libs << 'test'
30
+ t.test_files = FileList["test/**/*_test.rb"]
31
+ t.verbose = true
32
+ end
33
+
34
+ desc 'Generate the gemspec to serve this Gem from Github'
35
+ task :github do
36
+ file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
37
+ File.open(file, 'w') {|f| f << spec.to_ruby }
38
+ puts "Created gemspec: #{file}"
39
+ end
40
+
41
+ begin
42
+ require 'rcov/rcovtask'
43
+
44
+ desc "Generate RCov coverage report"
45
+ Rcov::RcovTask.new(:rcov) do |t|
46
+ t.libs << "test"
47
+ t.test_files = FileList['test/**/*_test.rb']
48
+ t.rcov_opts << "-x \"test/*,gems/*,/Library/Ruby/*,config/*\" -x lib/garb.rb -x lib/garb/version.rb --rails"
49
+ end
50
+ rescue LoadError
51
+ nil
52
+ end
53
+
54
+ task :default => 'test'
55
+
56
+ # EOF
data/lib/garb.rb ADDED
@@ -0,0 +1,69 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+
4
+ require 'cgi'
5
+ require 'ostruct'
6
+ require 'crack'
7
+
8
+ begin
9
+ require 'active_support/inflector'
10
+ require 'active_support/deprecation'
11
+ rescue LoadError
12
+ require 'active_support'
13
+ end
14
+
15
+ require 'garb/version'
16
+ require 'garb/authentication_request'
17
+ require 'garb/data_request'
18
+ require 'garb/account_feed_request'
19
+ require 'garb/session'
20
+ require 'garb/profile_reports'
21
+ require 'garb/step'
22
+ require 'garb/destination'
23
+ require 'garb/goal'
24
+ require 'garb/profile'
25
+ require 'garb/account'
26
+ require 'garb/filter_parameters'
27
+ require 'garb/report_parameter'
28
+ require 'garb/report_response'
29
+ require 'garb/resource'
30
+ require 'garb/report'
31
+
32
+ require 'garb/model'
33
+
34
+ # management
35
+ require 'garb/management/feed'
36
+ require 'garb/management/account'
37
+ require 'garb/management/web_property'
38
+ require 'garb/management/profile'
39
+
40
+ require 'support'
41
+
42
+ module Garb
43
+ GA = "http://schemas.google.com/analytics/2008"
44
+
45
+ extend self
46
+
47
+ def to_google_analytics(thing)
48
+ return thing.to_google_analytics if thing.respond_to?(:to_google_analytics)
49
+
50
+ "ga:#{thing.to_s.camelize(:lower)}"
51
+ end
52
+ alias :to_ga :to_google_analytics
53
+
54
+ def from_google_analytics(thing)
55
+ thing.to_s.gsub(/^ga\:/, '').underscore
56
+ end
57
+ alias :from_ga :from_google_analytics
58
+
59
+ def parse_properties(entry)
60
+ entry['dxp:property'].inject({}) do |hash, p|
61
+ hash[Garb.from_ga(p['name'])] = p['value']
62
+ hash
63
+ end
64
+ end
65
+
66
+ def parse_link(entry, rel)
67
+ entry['link'].detect {|link| link["rel"] == rel}['href']
68
+ end
69
+ end
@@ -0,0 +1,21 @@
1
+ module Garb
2
+ class Account
3
+ attr_reader :id, :name, :profiles
4
+
5
+ def initialize(profiles)
6
+ @id = profiles.first.account_id
7
+ @name = profiles.first.account_name
8
+ @profiles = profiles
9
+ end
10
+
11
+ def self.all(session = Session)
12
+ profiles = {}
13
+
14
+ Profile.all(session).each do |profile|
15
+ (profiles[profile.account_id] ||= []) << profile
16
+ end
17
+
18
+ profiles.map {|k,v| v}.map {|profiles| new(profiles)}
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ module Garb
2
+ class AccountFeedRequest
3
+ URL = "https://www.google.com/analytics/feeds/accounts/default"
4
+
5
+ def initialize(session = Session)
6
+ @request = DataRequest.new(session, URL)
7
+ end
8
+
9
+ def response
10
+ @response ||= @request.send_request
11
+ end
12
+
13
+ def parsed_response
14
+ @parsed_response ||= Crack::XML.parse(response.body)
15
+ end
16
+
17
+ def entries
18
+ parsed_response ? [parsed_response['feed']['entry']].flatten : []
19
+ end
20
+
21
+ def segments
22
+ parsed_response ? [parsed_response['feed']['dxp:segment']].flatten : []
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,53 @@
1
+ module Garb
2
+ class AuthenticationRequest
3
+ class AuthError < StandardError;end
4
+
5
+ URL = 'https://www.google.com/accounts/ClientLogin'
6
+
7
+ def initialize(email, password, opts={})
8
+ @email = email
9
+ @password = password
10
+ @account_type = opts.fetch(:account_type, 'HOSTED_OR_GOOGLE')
11
+ end
12
+
13
+ def parameters
14
+ {
15
+ 'Email' => @email,
16
+ 'Passwd' => @password,
17
+ 'accountType' => @account_type,
18
+ 'service' => 'analytics',
19
+ 'source' => 'vigetLabs-garb-001'
20
+ }
21
+ end
22
+
23
+ def uri
24
+ URI.parse(URL)
25
+ end
26
+
27
+ def send_request(ssl_mode)
28
+ http = Net::HTTP.new(uri.host, uri.port)
29
+ http.use_ssl = true
30
+ http.verify_mode = ssl_mode
31
+
32
+ if ssl_mode == OpenSSL::SSL::VERIFY_PEER
33
+ http.ca_file = CA_CERT_FILE
34
+ end
35
+
36
+ http.request(build_request) do |response|
37
+ raise AuthError unless response.is_a?(Net::HTTPOK)
38
+ end
39
+ end
40
+
41
+ def build_request
42
+ post = Net::HTTP::Post.new(uri.path)
43
+ post.set_form_data(parameters)
44
+ post
45
+ end
46
+
47
+ def auth_token(opts={})
48
+ ssl_mode = opts[:secure] ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
49
+ send_request(ssl_mode).body.match(/^Auth=(.*)$/)[1]
50
+ end
51
+
52
+ end
53
+ end