vigetlabs-garb 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +27 -24
- data/Rakefile +44 -20
- data/lib/garb/data_request.rb +5 -5
- data/lib/garb/oauth_session.rb +21 -0
- data/lib/garb/report.rb +17 -197
- data/lib/garb/report_parameter.rb +3 -3
- data/lib/garb/report_response.rb +7 -1
- data/lib/garb/resource.rb +89 -0
- data/lib/garb/version.rb +13 -0
- data/lib/garb.rb +4 -1
- data/test/test_helper.rb +6 -3
- data/test/{authentication_request_test.rb → unit/authentication_request_test.rb} +1 -1
- data/test/{data_request_test.rb → unit/data_request_test.rb} +1 -1
- data/test/{garb_test.rb → unit/garb_test.rb} +1 -1
- data/test/unit/oauth_session_test.rb +11 -0
- data/test/{operator_test.rb → unit/operator_test.rb} +1 -1
- data/test/{profile_test.rb → unit/profile_test.rb} +1 -1
- data/test/{report_parameter_test.rb → unit/report_parameter_test.rb} +9 -2
- data/test/{report_response_test.rb → unit/report_response_test.rb} +7 -2
- data/test/unit/report_test.rb +71 -0
- data/test/unit/resource_test.rb +19 -0
- data/test/{session_test.rb → unit/session_test.rb} +1 -1
- data/test/unit/string_test.rb +9 -0
- data/test/{symbol_test.rb → unit/symbol_test.rb} +1 -1
- metadata +49 -21
- data/History.txt +0 -0
- data/garb.gemspec +0 -42
- data/test/report_test.rb +0 -124
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
garb
|
2
2
|
====
|
3
3
|
|
4
|
-
by Tony Pitale
|
4
|
+
by Tony Pitale with much help from Justin Marney, Patrick Reagan and others at Viget Labs
|
5
5
|
|
6
6
|
http://github.com/vigetlabs/garb
|
7
7
|
|
@@ -26,20 +26,14 @@ Profiles
|
|
26
26
|
> Garb::Profile.all
|
27
27
|
> profile = Garb::Profile.all.first
|
28
28
|
|
29
|
-
Define a Report Class
|
30
|
-
|
31
|
-
|
32
|
-
class
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
config.metrics << [:exits, :pageviews, :exit_rate]
|
38
|
-
config.dimensions << :request_uri
|
39
|
-
config.sort << :exits.desc
|
40
|
-
config.max_results = 10
|
41
|
-
end
|
42
|
-
end
|
29
|
+
Define a Report Class and Get Results
|
30
|
+
-------------------------------------
|
31
|
+
|
32
|
+
class Exits
|
33
|
+
include Garb::Resource
|
34
|
+
|
35
|
+
metrics :exits, :pageviews, :exit_rate
|
36
|
+
dimensions :request_uri
|
43
37
|
end
|
44
38
|
|
45
39
|
Parameters
|
@@ -47,7 +41,8 @@ Parameters
|
|
47
41
|
|
48
42
|
* start_date: The date of the period you would like this report to start
|
49
43
|
* end_date: The date to end, inclusive
|
50
|
-
*
|
44
|
+
* limit: The maximum number of results to be returned
|
45
|
+
* offset: The starting index
|
51
46
|
|
52
47
|
Metrics & Dimensions
|
53
48
|
--------------------
|
@@ -65,13 +60,20 @@ Sorting
|
|
65
60
|
-------
|
66
61
|
|
67
62
|
Sorting can be done on any metric or dimension defined in the request, with .desc reversing the sort.
|
68
|
-
|
63
|
+
|
69
64
|
Building a Report
|
70
65
|
-----------------
|
71
66
|
|
72
|
-
Given the class, session, and profile from above:
|
67
|
+
Given the class, session, and profile from above we can do:
|
73
68
|
|
74
|
-
|
69
|
+
Exits.results(profile, :limit => 10, :offset => 19)
|
70
|
+
|
71
|
+
Or, with sorting and filters:
|
72
|
+
|
73
|
+
Exits.results(profile, :limit => 10, :offset => 19) do
|
74
|
+
filter :request_uri.contains => 'season', :exits.gt => 100
|
75
|
+
sort :exits
|
76
|
+
end
|
75
77
|
|
76
78
|
reports will be an array of OpenStructs with methods for the metrics and dimensions returned.
|
77
79
|
|
@@ -79,10 +81,13 @@ Build a One-Off Report
|
|
79
81
|
----------------------
|
80
82
|
|
81
83
|
report = Garb::Report.new(profile)
|
82
|
-
report.metrics
|
83
|
-
report.dimensions
|
84
|
+
report.metrics :pageviews
|
85
|
+
report.dimensions :request_uri
|
86
|
+
|
87
|
+
report.filter :request_uri.contains => 'season', :exits.gte => 10
|
88
|
+
report.sort :exits
|
84
89
|
|
85
|
-
report.
|
90
|
+
report.results
|
86
91
|
|
87
92
|
Filtering
|
88
93
|
---------
|
@@ -125,8 +130,6 @@ TODOS
|
|
125
130
|
* Sessions are currently global, which isn't awesome
|
126
131
|
* Single user login is the only supported method currently.
|
127
132
|
Intend to add hooks for using OAuth
|
128
|
-
* Intend to make defined report classes before more like AR
|
129
|
-
* Support start-index
|
130
133
|
* Read opensearch header in results
|
131
134
|
* OR joining filter parameters
|
132
135
|
|
data/Rakefile
CHANGED
@@ -1,26 +1,50 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rcov/rcovtask'
|
5
|
+
|
6
|
+
require 'lib/garb'
|
7
|
+
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
spec = Gem::Specification.new do |s|
|
11
|
+
s.name = 'garb'
|
12
|
+
s.version = Garb::Version.to_s
|
13
|
+
s.has_rdoc = false
|
14
|
+
s.summary = "Google Analytics API Ruby Wrapper"
|
15
|
+
s.authors = ['Tony Pitale','Justin Marney', 'Patrick Reagan']
|
16
|
+
s.email = 'tony.pitale@viget.com'
|
17
|
+
s.homepage = 'http://github.com/vigetlabs/garb'
|
18
|
+
s.files = %w(README.md Rakefile) + Dir.glob("lib/**/*")
|
19
|
+
s.test_files = Dir.glob("test/**/*")
|
20
|
+
|
21
|
+
s.add_dependency("jnunemaker-happymapper", [">= 0.2.2"])
|
22
|
+
s.add_dependency("libxml-ruby", [">= 0.9.8"])
|
10
23
|
end
|
11
24
|
|
12
|
-
|
13
|
-
|
25
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
26
|
+
pkg.gem_spec = spec
|
27
|
+
end
|
14
28
|
|
15
|
-
|
29
|
+
Rake::TestTask.new do |t|
|
30
|
+
t.libs << 'test'
|
31
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
32
|
+
t.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'Generate the gemspec to serve this Gem from Github'
|
36
|
+
task :github do
|
37
|
+
file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
|
38
|
+
File.open(file, 'w') {|f| f << spec.to_ruby }
|
39
|
+
puts "Created gemspec: #{file}"
|
40
|
+
end
|
16
41
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
PROJ.spec.opts << '--color'
|
42
|
+
desc "Generate RCov coverage report"
|
43
|
+
Rcov::RcovTask.new(:rcov) do |t|
|
44
|
+
t.test_files = FileList['test/**/*_test.rb']
|
45
|
+
t.rcov_opts << "-x lib/garb.rb -x lib/garb/version.rb"
|
46
|
+
end
|
47
|
+
|
48
|
+
task :default => 'test'
|
25
49
|
|
26
50
|
# EOF
|
data/lib/garb/data_request.rb
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
module Garb
|
2
2
|
class DataRequest
|
3
|
-
|
3
|
+
|
4
4
|
def initialize(base_url, parameters={})
|
5
5
|
@base_url = base_url
|
6
6
|
@parameters = parameters
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
def query_string
|
10
10
|
parameter_list = @parameters.map {|k,v| "#{k}=#{v}" }
|
11
11
|
parameter_list.empty? ? '' : "?#{parameter_list.join('&')}"
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def uri
|
15
15
|
URI.parse(@base_url)
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def send_request
|
19
19
|
http = Net::HTTP.new(uri.host, uri.port)
|
20
20
|
http.use_ssl = true
|
@@ -23,6 +23,6 @@ module Garb
|
|
23
23
|
raise response.body.inspect unless response.is_a?(Net::HTTPOK)
|
24
24
|
response
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
end
|
28
28
|
end
|
@@ -0,0 +1,21 @@
|
|
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
|
data/lib/garb/report.rb
CHANGED
@@ -1,211 +1,31 @@
|
|
1
|
-
require 'ostruct'
|
2
|
-
|
3
1
|
module Garb
|
4
2
|
class Report
|
3
|
+
include Resource::ResourceMethods
|
4
|
+
|
5
5
|
MONTH = 2592000
|
6
6
|
URL = "https://www.google.com/analytics/feeds/data"
|
7
7
|
|
8
|
-
METRICS = {
|
9
|
-
:visitor => [
|
10
|
-
:avgPageviews,
|
11
|
-
:avgSessionTime,
|
12
|
-
:bounces,
|
13
|
-
:bounceRate,
|
14
|
-
:entrances,
|
15
|
-
:exits,
|
16
|
-
:exitRate,
|
17
|
-
:newVisitors,
|
18
|
-
:pageDuration,
|
19
|
-
:pageviews,
|
20
|
-
:visitDuration,
|
21
|
-
:visitors,
|
22
|
-
:visits
|
23
|
-
],
|
24
|
-
:campaign => [
|
25
|
-
:cost,
|
26
|
-
:clicks,
|
27
|
-
:clickThroughRate,
|
28
|
-
:costPerConversion,
|
29
|
-
:costPerGoalConversion,
|
30
|
-
:costPerMilleImpressions,
|
31
|
-
:costPerTransaction,
|
32
|
-
:impressions
|
33
|
-
],
|
34
|
-
:content => [
|
35
|
-
:uniquePageviews
|
36
|
-
],
|
37
|
-
:ecommerce => [
|
38
|
-
:productPurchases,
|
39
|
-
:productRevenue,
|
40
|
-
:products,
|
41
|
-
:revenue,
|
42
|
-
:revenuePerClick,
|
43
|
-
:revenuePerTransaction,
|
44
|
-
:revenuePerVisit,
|
45
|
-
:shipping,
|
46
|
-
:tax,
|
47
|
-
:transactions
|
48
|
-
],
|
49
|
-
:internal_search => [
|
50
|
-
:searchDepth,
|
51
|
-
:searchDuration,
|
52
|
-
:searchExits,
|
53
|
-
:searchTransitions,
|
54
|
-
:uniqueInternalSearches,
|
55
|
-
:visitsWithSearches
|
56
|
-
],
|
57
|
-
:goals => [
|
58
|
-
:goalCompletions1,
|
59
|
-
:goalCompletions2,
|
60
|
-
:goalCompletions3,
|
61
|
-
:goalCompletions4,
|
62
|
-
:goalCompletionsAll,
|
63
|
-
:goalConversionRate,
|
64
|
-
:goalStarts1,
|
65
|
-
:goalStarts2,
|
66
|
-
:goalStarts3,
|
67
|
-
:goalStarts4,
|
68
|
-
:goalStartsAll,
|
69
|
-
:goalValue1,
|
70
|
-
:goalValue2,
|
71
|
-
:goalValue3,
|
72
|
-
:goalValue4,
|
73
|
-
:goalValueAll,
|
74
|
-
:goalValuePerVisit
|
75
|
-
]
|
76
|
-
}
|
77
|
-
|
78
|
-
DIMENSIONS = {
|
79
|
-
:visitor => [
|
80
|
-
:browser,
|
81
|
-
:browserVersion,
|
82
|
-
:city,
|
83
|
-
:connectionSpeed,
|
84
|
-
:continent,
|
85
|
-
:country,
|
86
|
-
:daysSinceLastVisit,
|
87
|
-
:domain,
|
88
|
-
:flashVersion,
|
89
|
-
:hostname,
|
90
|
-
:hour,
|
91
|
-
:javaEnabled,
|
92
|
-
:languqage,
|
93
|
-
:medium,
|
94
|
-
:organization,
|
95
|
-
:pageDepth,
|
96
|
-
:platform,
|
97
|
-
:platformVersion,
|
98
|
-
:referralPath,
|
99
|
-
:region,
|
100
|
-
:screenColors,
|
101
|
-
:screenResolution,
|
102
|
-
:subContinentRegion,
|
103
|
-
:userDefinedValue,
|
104
|
-
:visitNumber,
|
105
|
-
:visitorType
|
106
|
-
],
|
107
|
-
:campaign => [
|
108
|
-
:adGroup,
|
109
|
-
:adSlot,
|
110
|
-
:adSlotPosition,
|
111
|
-
:campaign,
|
112
|
-
:content,
|
113
|
-
:keyword,
|
114
|
-
:source,
|
115
|
-
:sourceMedium
|
116
|
-
],
|
117
|
-
:content => [
|
118
|
-
:pageTitle,
|
119
|
-
:requestUri,
|
120
|
-
:requestUri1,
|
121
|
-
:requestUriLast
|
122
|
-
],
|
123
|
-
:ecommerce => [
|
124
|
-
:affiliation,
|
125
|
-
:daysToTransaction,
|
126
|
-
:productCode,
|
127
|
-
:productName,
|
128
|
-
:productVariation,
|
129
|
-
:transactionId,
|
130
|
-
:visitsToTransaction
|
131
|
-
],
|
132
|
-
:internal_search => [
|
133
|
-
:hasInternalSearch,
|
134
|
-
:internalSearchKeyword,
|
135
|
-
:internalSearchNext,
|
136
|
-
:internalSearchType
|
137
|
-
]
|
138
|
-
}
|
139
|
-
|
140
|
-
attr_accessor :metrics, :dimensions, :sort, :filters,
|
141
|
-
:start_date, :max_results, :end_date
|
142
|
-
|
143
|
-
attr_reader :profile
|
144
|
-
|
145
|
-
# def self.element_id(property_name)
|
146
|
-
# property_name.is_a?(Operator) ? property_name.to_s : property_name.to_ga.lower_camelized
|
147
|
-
# end
|
148
|
-
#
|
149
|
-
# def self.property_value(entry, property_name)
|
150
|
-
# (entry/property_name).first.inner_text
|
151
|
-
# end
|
152
|
-
#
|
153
|
-
# def self.property_values(entry, property_names)
|
154
|
-
# hash = property_names.inject({}) do |hash, property_name|
|
155
|
-
# hash.merge({property_name => property_value(entry, property_name.to_s.lower_camelized)})
|
156
|
-
# end
|
157
|
-
# OpenStruct.new hash
|
158
|
-
# end
|
159
|
-
#
|
160
|
-
def self.format_time(t)
|
161
|
-
t.strftime('%Y-%m-%d')
|
162
|
-
end
|
163
|
-
|
164
8
|
def initialize(profile, opts={})
|
165
9
|
@profile = profile
|
166
10
|
|
167
|
-
@sort = ReportParameter.new(:sort) << opts.fetch(:sort, [])
|
168
|
-
@filters = ReportParameter.new(:filters) << opts.fetch(:filters, [])
|
169
|
-
@metrics = ReportParameter.new(:metrics) << opts.fetch(:metrics, [])
|
170
|
-
@dimensions = ReportParameter.new(:dimensions) << opts.fetch(:dimensions, [])
|
171
|
-
|
172
11
|
@start_date = opts.fetch(:start_date, Time.now - MONTH)
|
173
12
|
@end_date = opts.fetch(:end_date, Time.now)
|
174
|
-
|
175
|
-
|
176
|
-
end
|
13
|
+
@limit = opts.fetch(:limit, nil)
|
14
|
+
@offset = opts.fetch(:offset, nil)
|
177
15
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
end
|
187
|
-
|
188
|
-
def params
|
189
|
-
[
|
190
|
-
metrics.to_params,
|
191
|
-
dimensions.to_params,
|
192
|
-
sort.to_params,
|
193
|
-
filters.to_params,
|
194
|
-
page_params
|
195
|
-
].inject(default_params) do |p, i|
|
196
|
-
p.merge(i)
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
def send_request_for_body
|
201
|
-
request = DataRequest.new(URL, params)
|
202
|
-
response = request.send_request
|
203
|
-
response.body
|
16
|
+
# clear filters and sort
|
17
|
+
@filters = ReportParameter.new(:filters)
|
18
|
+
@sorts = ReportParameter.new(:sort)
|
19
|
+
|
20
|
+
metrics opts.fetch(:metrics, [])
|
21
|
+
dimensions opts.fetch(:dimensions, [])
|
22
|
+
filter opts.fetch(:filter, [])
|
23
|
+
sort opts.fetch(:sort, [])
|
204
24
|
end
|
205
|
-
|
206
|
-
def
|
207
|
-
|
25
|
+
|
26
|
+
def results
|
27
|
+
ReportResponse.new(send_request_for_body).results
|
208
28
|
end
|
209
|
-
|
29
|
+
|
210
30
|
end
|
211
|
-
end
|
31
|
+
end
|
@@ -13,7 +13,7 @@ module Garb
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def <<(element)
|
16
|
-
@elements += [element].flatten
|
16
|
+
(@elements += [element].flatten).compact!
|
17
17
|
self
|
18
18
|
end
|
19
19
|
|
@@ -23,7 +23,7 @@ module Garb
|
|
23
23
|
when Hash
|
24
24
|
elem.collect do |k,v|
|
25
25
|
next unless k.is_a?(Operator)
|
26
|
-
"#{k.target}#{
|
26
|
+
"#{k.target}#{URI.encode(k.operator.to_s, /[=<>]/)}#{CGI::escape(v.to_s)}"
|
27
27
|
end.join(';')
|
28
28
|
else
|
29
29
|
elem.to_ga
|
@@ -31,6 +31,6 @@ module Garb
|
|
31
31
|
end.join(',')
|
32
32
|
|
33
33
|
params.empty? ? {} : {self.name => params}
|
34
|
-
end
|
34
|
+
end
|
35
35
|
end
|
36
36
|
end
|
data/lib/garb/report_response.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Garb
|
2
2
|
class ReportResponse
|
3
|
+
# include Enumerable
|
4
|
+
|
3
5
|
def initialize(response_body)
|
4
6
|
@xml = response_body
|
5
7
|
end
|
@@ -7,7 +9,7 @@ module Garb
|
|
7
9
|
def parse
|
8
10
|
entries = Entry.parse(@xml)
|
9
11
|
|
10
|
-
entries.collect do |entry|
|
12
|
+
@results = entries.collect do |entry|
|
11
13
|
hash = {}
|
12
14
|
|
13
15
|
entry.metrics.each do |m|
|
@@ -23,6 +25,10 @@ module Garb
|
|
23
25
|
OpenStruct.new(hash)
|
24
26
|
end
|
25
27
|
end
|
28
|
+
|
29
|
+
def results
|
30
|
+
@results || parse
|
31
|
+
end
|
26
32
|
|
27
33
|
class Metric
|
28
34
|
include HappyMapper
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Garb
|
2
|
+
module Resource
|
3
|
+
MONTH = 2592000
|
4
|
+
URL = "https://www.google.com/analytics/feeds/data"
|
5
|
+
|
6
|
+
def self.included(report)
|
7
|
+
report.extend(ResourceMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ResourceMethods
|
11
|
+
|
12
|
+
def metrics(*fields)
|
13
|
+
@metrics ||= ReportParameter.new(:metrics)
|
14
|
+
@metrics << fields
|
15
|
+
end
|
16
|
+
|
17
|
+
def dimensions(*fields)
|
18
|
+
@dimensions ||= ReportParameter.new(:dimensions)
|
19
|
+
@dimensions << fields
|
20
|
+
end
|
21
|
+
|
22
|
+
def filter(*hash)
|
23
|
+
@filters << hash
|
24
|
+
end
|
25
|
+
|
26
|
+
def filters
|
27
|
+
@filters ||= ReportParameter.new(:filters)
|
28
|
+
end
|
29
|
+
|
30
|
+
def sort(*fields)
|
31
|
+
@sorts << fields
|
32
|
+
end
|
33
|
+
|
34
|
+
def sorts
|
35
|
+
@sorts ||= ReportParameter.new(:sort)
|
36
|
+
end
|
37
|
+
|
38
|
+
def results(profile, opts = {}, &block)
|
39
|
+
@profile = profile
|
40
|
+
|
41
|
+
# clear filters and sort
|
42
|
+
@filters = ReportParameter.new(:filters)
|
43
|
+
@sorts = ReportParameter.new(:sort)
|
44
|
+
|
45
|
+
@start_date = opts.fetch(:start_date, Time.now - MONTH)
|
46
|
+
@end_date = opts.fetch(:end_date, Time.now)
|
47
|
+
@limit = opts.fetch(:limit, nil)
|
48
|
+
@offset = opts.fetch(:offset, nil)
|
49
|
+
|
50
|
+
instance_eval(&block) if block_given?
|
51
|
+
|
52
|
+
ReportResponse.new(send_request_for_body).results
|
53
|
+
end
|
54
|
+
|
55
|
+
def page_params
|
56
|
+
{'max-results' => @limit, 'start-index' => @offset}.reject{|k,v| v.nil?}
|
57
|
+
end
|
58
|
+
|
59
|
+
def default_params
|
60
|
+
{'ids' => @profile.table_id,
|
61
|
+
'start-date' => format_time(@start_date),
|
62
|
+
'end-date' => format_time(@end_date)}
|
63
|
+
end
|
64
|
+
|
65
|
+
def params
|
66
|
+
[
|
67
|
+
metrics.to_params,
|
68
|
+
dimensions.to_params,
|
69
|
+
sorts.to_params,
|
70
|
+
filters.to_params,
|
71
|
+
page_params
|
72
|
+
].inject(default_params) do |p, i|
|
73
|
+
p.merge(i)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def format_time(t)
|
78
|
+
t.strftime('%Y-%m-%d')
|
79
|
+
end
|
80
|
+
|
81
|
+
def send_request_for_body
|
82
|
+
request = DataRequest.new(URL, params)
|
83
|
+
response = request.send_request
|
84
|
+
response.body
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/garb/version.rb
ADDED
data/lib/garb.rb
CHANGED
@@ -4,15 +4,18 @@ require 'net/http'
|
|
4
4
|
require 'net/https'
|
5
5
|
require 'rubygems'
|
6
6
|
require 'cgi'
|
7
|
+
require 'ostruct'
|
7
8
|
require 'happymapper'
|
8
9
|
|
10
|
+
require 'garb/version'
|
9
11
|
require 'garb/authentication_request'
|
10
12
|
require 'garb/data_request'
|
11
13
|
require 'garb/session'
|
12
14
|
require 'garb/profile'
|
13
|
-
require 'garb/report'
|
14
15
|
require 'garb/report_parameter'
|
15
16
|
require 'garb/report_response'
|
17
|
+
require 'garb/resource'
|
18
|
+
require 'garb/report'
|
16
19
|
|
17
20
|
require 'extensions/string'
|
18
21
|
require 'extensions/operator'
|
data/test/test_helper.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
-
|
1
|
+
$:.reject! { |e| e.include? 'TextMate' }
|
2
|
+
|
2
3
|
require 'rubygems'
|
3
|
-
require '
|
4
|
+
require 'test/unit'
|
4
5
|
require 'shoulda'
|
5
|
-
require '
|
6
|
+
require 'mocha'
|
7
|
+
|
8
|
+
require File.dirname(__FILE__) + '/../lib/garb'
|
6
9
|
|
7
10
|
class Test::Unit::TestCase
|
8
11
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), 'test_helper')
|
1
|
+
require File.join(File.dirname(__FILE__), '..', '/test_helper')
|
2
2
|
|
3
3
|
module Garb
|
4
4
|
class ReportParameterTest < Test::Unit::TestCase
|
@@ -45,7 +45,14 @@ module Garb
|
|
45
45
|
should "parameterize hash operators and join elements" do
|
46
46
|
@metrics << {:city.eql => 'New York'}
|
47
47
|
params = {'metrics' => 'ga:city%3D%3DNew+York'}
|
48
|
-
|
48
|
+
|
49
|
+
assert_equal params, @metrics.to_params
|
50
|
+
end
|
51
|
+
|
52
|
+
should "properly encode operators" do
|
53
|
+
@metrics << {:request_uri.contains => 'New York'}
|
54
|
+
params = {'metrics' => 'ga:requestUri%3D~New+York'}
|
55
|
+
|
49
56
|
assert_equal params, @metrics.to_params
|
50
57
|
end
|
51
58
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), 'test_helper')
|
1
|
+
require File.join(File.dirname(__FILE__), '..', '/test_helper')
|
2
2
|
|
3
3
|
module Garb
|
4
4
|
class ReportResponseTest < Test::Unit::TestCase
|
5
5
|
context "An instance of the ReportResponse class" do
|
6
6
|
setup do
|
7
|
-
@xml = File.read(File.dirname(__FILE__)
|
7
|
+
@xml = File.read(File.join(File.dirname(__FILE__), '..', "/fixtures/report_feed.xml"))
|
8
8
|
@response = ReportResponse.new(@xml)
|
9
9
|
end
|
10
10
|
|
@@ -19,6 +19,11 @@ module Garb
|
|
19
19
|
|
20
20
|
assert_equal(['entry1', 'entry2', 'entry3'], @response.parse)
|
21
21
|
end
|
22
|
+
|
23
|
+
should "have results or parse them" do
|
24
|
+
@response.expects(:parse)
|
25
|
+
@response.results
|
26
|
+
end
|
22
27
|
end
|
23
28
|
end
|
24
29
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', '/test_helper')
|
2
|
+
|
3
|
+
module Garb
|
4
|
+
# Also tests Garb::Resource, which is the basis for Garb::Report
|
5
|
+
class ReportTest < Test::Unit::TestCase
|
6
|
+
context "An instance of the Report class" do
|
7
|
+
setup do
|
8
|
+
@now = Time.now
|
9
|
+
Time.stubs(:now).returns(@now)
|
10
|
+
@profile = stub(:table_id => 'ga:1234')
|
11
|
+
@report = Report.new(@profile)
|
12
|
+
end
|
13
|
+
|
14
|
+
%w(metrics dimensions filters sorts).each do |param|
|
15
|
+
should "have parameters for #{param}" do
|
16
|
+
assert @report.send(:"#{param}").is_a?(ReportParameter)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
should "have default parameters" do
|
21
|
+
@report.stubs(:format_time).returns('2009-08-01')
|
22
|
+
params = {'ids' => 'ga:1234', 'start-date' => '2009-08-01', 'end-date' => '2009-08-01'}
|
23
|
+
assert_equal params, @report.default_params
|
24
|
+
end
|
25
|
+
|
26
|
+
should "collect params from metrics, dimensions, filters, sort, and defaults" do
|
27
|
+
@report.stubs(:metrics).returns(stub(:to_params => {'metrics' => 6}))
|
28
|
+
@report.stubs(:dimensions).returns(stub(:to_params => {'dimensions' => 5}))
|
29
|
+
@report.stubs(:filters).returns(stub(:to_params => {'filters' => 4}))
|
30
|
+
@report.stubs(:sorts).returns(stub(:to_params => {'sort' => 3}))
|
31
|
+
@report.stubs(:page_params).returns({'page_params' => 2})
|
32
|
+
@report.stubs(:default_params).returns({'default_params' => 1})
|
33
|
+
|
34
|
+
params = {'metrics' => 6, 'dimensions' => 5, 'filters' => 4, 'sort' => 3, 'page_params' => 2, 'default_params' => 1}
|
35
|
+
assert_equal params, @report.params
|
36
|
+
end
|
37
|
+
|
38
|
+
should "format time" do
|
39
|
+
assert_equal @now.strftime('%Y-%m-%d'), @report.format_time(@now)
|
40
|
+
end
|
41
|
+
|
42
|
+
should "send a data request to GA" do
|
43
|
+
response = mock {|m| m.expects(:body).returns('response body') }
|
44
|
+
request = mock {|m| m.expects(:send_request).returns(response) }
|
45
|
+
@report.expects(:params).returns('params')
|
46
|
+
|
47
|
+
DataRequest.expects(:new).with(Garb::Report::URL, 'params').returns(request)
|
48
|
+
assert_equal 'response body', @report.send_request_for_body
|
49
|
+
end
|
50
|
+
|
51
|
+
should "fetch and parse results from GA" do
|
52
|
+
@report.expects(:send_request_for_body).with().returns('xml')
|
53
|
+
ReportResponse.expects(:new).with('xml').returns(mock(:results => ['entry']))
|
54
|
+
assert_equal ['entry'], @report.results
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "An instance of the Report class with initial options" do
|
59
|
+
setup do
|
60
|
+
@profile = stub(:table_id => 'ga:1234')
|
61
|
+
@report = Report.new(@profile, :limit => 10, :offset => 20)
|
62
|
+
end
|
63
|
+
|
64
|
+
should "have page paramaters" do
|
65
|
+
params = {'max-results' => 10, 'start-index' => 20}
|
66
|
+
assert_equal params, @report.page_params
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', '/test_helper')
|
2
|
+
|
3
|
+
class TestReport
|
4
|
+
include Garb::Resource
|
5
|
+
end
|
6
|
+
|
7
|
+
# Most of the resource testing is done as a part of ReportTest
|
8
|
+
class ResourceTest < Test::Unit::TestCase
|
9
|
+
|
10
|
+
context "A class with Garb::Resource mixed in" do
|
11
|
+
should "get results from GA" do
|
12
|
+
profile = stub
|
13
|
+
TestReport.expects(:send_request_for_body).returns('xml')
|
14
|
+
Garb::ReportResponse.expects(:new).with('xml').returns(mock(:results => 'analytics'))
|
15
|
+
|
16
|
+
assert_equal 'analytics', TestReport.results(profile)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vigetlabs-garb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Pitale
|
8
|
+
- Justin Marney
|
9
|
+
- Patrick Reagan
|
8
10
|
autorequire:
|
9
11
|
bindir: bin
|
10
12
|
cert_chain: []
|
11
13
|
|
12
|
-
date: 2009-04-
|
14
|
+
date: 2009-04-29 00:00:00 -07:00
|
13
15
|
default_executable:
|
14
16
|
dependencies:
|
15
17
|
- !ruby/object:Gem::Dependency
|
@@ -32,7 +34,7 @@ dependencies:
|
|
32
34
|
- !ruby/object:Gem::Version
|
33
35
|
version: 0.9.8
|
34
36
|
version:
|
35
|
-
description:
|
37
|
+
description:
|
36
38
|
email: tony.pitale@viget.com
|
37
39
|
executables: []
|
38
40
|
|
@@ -41,22 +43,43 @@ extensions: []
|
|
41
43
|
extra_rdoc_files: []
|
42
44
|
|
43
45
|
files:
|
44
|
-
- History.txt
|
45
46
|
- README.md
|
46
47
|
- Rakefile
|
47
|
-
-
|
48
|
-
- lib/
|
48
|
+
- lib/extensions
|
49
|
+
- lib/extensions/happymapper.rb
|
50
|
+
- lib/extensions/operator.rb
|
51
|
+
- lib/extensions/string.rb
|
52
|
+
- lib/extensions/symbol.rb
|
53
|
+
- lib/garb
|
49
54
|
- lib/garb/authentication_request.rb
|
50
55
|
- lib/garb/data_request.rb
|
56
|
+
- lib/garb/oauth_session.rb
|
51
57
|
- lib/garb/profile.rb
|
52
58
|
- lib/garb/report.rb
|
53
59
|
- lib/garb/report_parameter.rb
|
54
60
|
- lib/garb/report_response.rb
|
61
|
+
- lib/garb/resource.rb
|
55
62
|
- lib/garb/session.rb
|
56
|
-
- lib/
|
57
|
-
- lib/
|
58
|
-
-
|
59
|
-
-
|
63
|
+
- lib/garb/version.rb
|
64
|
+
- lib/garb.rb
|
65
|
+
- test/fixtures
|
66
|
+
- test/fixtures/profile_feed.xml
|
67
|
+
- test/fixtures/report_feed.xml
|
68
|
+
- test/test_helper.rb
|
69
|
+
- test/unit
|
70
|
+
- test/unit/authentication_request_test.rb
|
71
|
+
- test/unit/data_request_test.rb
|
72
|
+
- test/unit/garb_test.rb
|
73
|
+
- test/unit/oauth_session_test.rb
|
74
|
+
- test/unit/operator_test.rb
|
75
|
+
- test/unit/profile_test.rb
|
76
|
+
- test/unit/report_parameter_test.rb
|
77
|
+
- test/unit/report_response_test.rb
|
78
|
+
- test/unit/report_test.rb
|
79
|
+
- test/unit/resource_test.rb
|
80
|
+
- test/unit/session_test.rb
|
81
|
+
- test/unit/string_test.rb
|
82
|
+
- test/unit/symbol_test.rb
|
60
83
|
has_rdoc: false
|
61
84
|
homepage: http://github.com/vigetlabs/garb
|
62
85
|
post_install_message:
|
@@ -84,16 +107,21 @@ signing_key:
|
|
84
107
|
specification_version: 2
|
85
108
|
summary: Google Analytics API Ruby Wrapper
|
86
109
|
test_files:
|
87
|
-
- test/
|
88
|
-
- test/data_request_test.rb
|
89
|
-
- test/garb_test.rb
|
90
|
-
- test/operator_test.rb
|
91
|
-
- test/profile_test.rb
|
92
|
-
- test/report_parameter_test.rb
|
93
|
-
- test/report_response_test.rb
|
94
|
-
- test/report_test.rb
|
95
|
-
- test/session_test.rb
|
96
|
-
- test/symbol_test.rb
|
97
|
-
- test/test_helper.rb
|
110
|
+
- test/fixtures
|
98
111
|
- test/fixtures/profile_feed.xml
|
99
112
|
- test/fixtures/report_feed.xml
|
113
|
+
- test/test_helper.rb
|
114
|
+
- test/unit
|
115
|
+
- test/unit/authentication_request_test.rb
|
116
|
+
- test/unit/data_request_test.rb
|
117
|
+
- test/unit/garb_test.rb
|
118
|
+
- test/unit/oauth_session_test.rb
|
119
|
+
- test/unit/operator_test.rb
|
120
|
+
- test/unit/profile_test.rb
|
121
|
+
- test/unit/report_parameter_test.rb
|
122
|
+
- test/unit/report_response_test.rb
|
123
|
+
- test/unit/report_test.rb
|
124
|
+
- test/unit/resource_test.rb
|
125
|
+
- test/unit/session_test.rb
|
126
|
+
- test/unit/string_test.rb
|
127
|
+
- test/unit/symbol_test.rb
|
data/History.txt
DELETED
File without changes
|
data/garb.gemspec
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
Gem::Specification.new do |s|
|
2
|
-
s.name = "garb"
|
3
|
-
s.version = "0.1.2"
|
4
|
-
s.date = "2009-04-22"
|
5
|
-
s.summary = "Google Analytics API Ruby Wrapper"
|
6
|
-
s.email = "tony.pitale@viget.com"
|
7
|
-
s.homepage = "http://github.com/vigetlabs/garb"
|
8
|
-
s.description = "A ruby gem to aid in the use of the Google Analytics API"
|
9
|
-
s.has_rdoc = false
|
10
|
-
s.authors = ["Tony Pitale"]
|
11
|
-
s.files = ["History.txt",
|
12
|
-
"README.md",
|
13
|
-
"Rakefile",
|
14
|
-
"garb.gemspec",
|
15
|
-
"lib/garb.rb",
|
16
|
-
"lib/garb/authentication_request.rb",
|
17
|
-
"lib/garb/data_request.rb",
|
18
|
-
"lib/garb/profile.rb",
|
19
|
-
"lib/garb/report.rb",
|
20
|
-
"lib/garb/report_parameter.rb",
|
21
|
-
"lib/garb/report_response.rb",
|
22
|
-
"lib/garb/session.rb",
|
23
|
-
"lib/extensions/symbol.rb",
|
24
|
-
"lib/extensions/string.rb",
|
25
|
-
"lib/extensions/operator.rb",
|
26
|
-
"lib/extensions/happymapper.rb"]
|
27
|
-
s.test_files = ['test/authentication_request_test.rb',
|
28
|
-
'test/data_request_test.rb',
|
29
|
-
'test/garb_test.rb',
|
30
|
-
'test/operator_test.rb',
|
31
|
-
'test/profile_test.rb',
|
32
|
-
'test/report_parameter_test.rb',
|
33
|
-
'test/report_response_test.rb',
|
34
|
-
'test/report_test.rb',
|
35
|
-
'test/session_test.rb',
|
36
|
-
'test/symbol_test.rb',
|
37
|
-
'test/test_helper.rb',
|
38
|
-
'test/fixtures/profile_feed.xml',
|
39
|
-
'test/fixtures/report_feed.xml']
|
40
|
-
s.add_dependency("jnunemaker-happymapper", [">= 0.2.2"])
|
41
|
-
s.add_dependency("libxml-ruby", [">= 0.9.8"])
|
42
|
-
end
|
data/test/report_test.rb
DELETED
@@ -1,124 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
-
|
3
|
-
module Garb
|
4
|
-
class ReportTest < Test::Unit::TestCase
|
5
|
-
|
6
|
-
context "An instance of the Report class" do
|
7
|
-
setup do
|
8
|
-
@now = Time.now
|
9
|
-
Time.stubs(:now).returns(@now)
|
10
|
-
@profile = stub(:table_id => 'ga:1234')
|
11
|
-
@report = Report.new(@profile)
|
12
|
-
end
|
13
|
-
|
14
|
-
%w(metrics dimensions sort filters).each do |param|
|
15
|
-
should "have parameters for #{param}" do
|
16
|
-
assert @report.send(:"#{param}").is_a?(ReportParameter)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
should "have a start_date" do
|
21
|
-
report = Report.new(@profile, :start_date => @now)
|
22
|
-
assert_equal @now, report.start_date
|
23
|
-
end
|
24
|
-
|
25
|
-
should "have a default start_date of one month ago" do
|
26
|
-
month_ago = @now - Report::MONTH
|
27
|
-
assert_equal month_ago, @report.start_date
|
28
|
-
end
|
29
|
-
|
30
|
-
should "have an end_date" do
|
31
|
-
report = Report.new(@profile, :end_date => @now)
|
32
|
-
assert_equal @now, report.end_date
|
33
|
-
end
|
34
|
-
|
35
|
-
should "have a default end_date of now" do
|
36
|
-
assert_equal @now, @report.end_date
|
37
|
-
end
|
38
|
-
|
39
|
-
should "have max results if set" do
|
40
|
-
@report.max_results = 5
|
41
|
-
assert_equal({'max-results' => 5}, @report.page_params)
|
42
|
-
end
|
43
|
-
|
44
|
-
should "have an empty hash if max results not set" do
|
45
|
-
assert_equal({}, @report.page_params)
|
46
|
-
end
|
47
|
-
|
48
|
-
should "have a profile" do
|
49
|
-
assert_equal @profile, @report.profile
|
50
|
-
end
|
51
|
-
|
52
|
-
should "return default params with no options" do
|
53
|
-
Report.expects(:format_time).with(@report.start_date).returns('start')
|
54
|
-
Report.expects(:format_time).with(@report.end_date).returns('end')
|
55
|
-
|
56
|
-
params = {'ids' => 'ga:1234', 'start-date' => 'start', 'end-date' => 'end'}
|
57
|
-
assert_equal params, @report.default_params
|
58
|
-
end
|
59
|
-
|
60
|
-
should "combine parameters for request" do
|
61
|
-
@report.sort.expects(:to_params).returns({'sort' => 'value'})
|
62
|
-
@report.filters.expects(:to_params).returns({'filters' => 'value'})
|
63
|
-
@report.metrics.expects(:to_params).returns({'metrics' => 'value'})
|
64
|
-
@report.dimensions.expects(:to_params).returns({'dimensions' => 'value'})
|
65
|
-
|
66
|
-
@report.expects(:page_params).returns({})
|
67
|
-
@report.expects(:default_params).returns({'ids' => 'ga:1234'})
|
68
|
-
|
69
|
-
params = {'ids' => 'ga:1234', 'metrics' => 'value', 'dimensions' => 'value', 'sort' => 'value', 'filters' => 'value'}
|
70
|
-
|
71
|
-
assert_equal params, @report.params
|
72
|
-
end
|
73
|
-
|
74
|
-
should "send a request and get the response body" do
|
75
|
-
response = stub(:body => 'feed')
|
76
|
-
data_request = mock
|
77
|
-
data_request.expects(:send_request).with().returns(response)
|
78
|
-
|
79
|
-
@report.stubs(:params).returns({'key' => 'value'})
|
80
|
-
DataRequest.expects(:new).with(Report::URL, {'key' => 'value'}).returns(data_request)
|
81
|
-
|
82
|
-
assert_equal 'feed', @report.send_request_for_body
|
83
|
-
end
|
84
|
-
|
85
|
-
should "fetch and parse all entries" do
|
86
|
-
@report.expects(:send_request_for_body).with().returns('xml')
|
87
|
-
ReportResponse.expects(:new).with('xml').returns(mock(:parse => ['entry']))
|
88
|
-
assert_equal ['entry'], @report.all
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
context "The Report class" do
|
93
|
-
|
94
|
-
# should "get the value for a given entry and property" do
|
95
|
-
# # entry = mock do |m|
|
96
|
-
# # m.expects(:/).with().returns(['balding'])
|
97
|
-
# # end
|
98
|
-
# # assert_equal 'balding', Report.property_value(entry, Report.element_id(:balding))
|
99
|
-
# end
|
100
|
-
#
|
101
|
-
# should "get the values for a given entry and array of properties" do
|
102
|
-
# entry = stub
|
103
|
-
# Report.stubs(:property_value).with(entry, "balding").returns('balding')
|
104
|
-
# Report.stubs(:property_value).with(entry, "spaulding").returns('spaulding')
|
105
|
-
#
|
106
|
-
# data = Report.property_values(entry, [:balding, :spaulding])
|
107
|
-
# assert_equal 'balding', data.balding
|
108
|
-
# assert_equal 'spaulding', data.spaulding
|
109
|
-
# end
|
110
|
-
#
|
111
|
-
# should "return an ostruct for an entry" do
|
112
|
-
# entry = stub
|
113
|
-
# Report.stubs(:property_value).with(entry, "balding").returns('balding')
|
114
|
-
# assert_equal true, Report.property_values(entry, [:balding]).is_a?(OpenStruct)
|
115
|
-
# end
|
116
|
-
|
117
|
-
should "format time" do
|
118
|
-
t = Time.now
|
119
|
-
assert_equal t.strftime('%Y-%m-%d'), Report.format_time(t)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
end
|
124
|
-
end
|