google_api 1.0.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +453 -0
- data/Rakefile +9 -0
- data/google_api.gemspec +22 -0
- data/lib/google_api.rb +37 -0
- data/lib/google_api/cache.rb +49 -0
- data/lib/google_api/configuration.rb +37 -0
- data/lib/google_api/core_ext/hash.rb +11 -0
- data/lib/google_api/core_ext/string.rb +13 -0
- data/lib/google_api/date.rb +59 -0
- data/lib/google_api/ga.rb +52 -0
- data/lib/google_api/ga/data.rb +335 -0
- data/lib/google_api/ga/data/data_dsl.rb +66 -0
- data/lib/google_api/ga/data/filters_dsl.rb +11 -0
- data/lib/google_api/ga/data/segment_dsl.rb +11 -0
- data/lib/google_api/ga/helper.rb +41 -0
- data/lib/google_api/ga/management/account.rb +37 -0
- data/lib/google_api/ga/management/goal.rb +58 -0
- data/lib/google_api/ga/management/management.rb +36 -0
- data/lib/google_api/ga/management/profile.rb +64 -0
- data/lib/google_api/ga/management/segment.rb +41 -0
- data/lib/google_api/ga/management/webproperty.rb +52 -0
- data/lib/google_api/ga/session.rb +165 -0
- data/lib/google_api/version.rb +3 -0
- data/spec/lib/google_api_spec.rb +63 -0
- data/spec/spec_helper.rb +8 -0
- metadata +125 -0
data/Rakefile
ADDED
data/google_api.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/google_api/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Ondřej Moravčík"]
|
6
|
+
gem.email = ["moravcik.ondrej@gmail.com"]
|
7
|
+
gem.description = %q{Simple Google Api. Include google analytics.}
|
8
|
+
gem.summary = %q{Simple Google Api. Include google analytics.}
|
9
|
+
gem.homepage = "https://github.com/ondra-m/google_api"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "google_api"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = GoogleApi::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'google-api-client'
|
19
|
+
|
20
|
+
gem.add_development_dependency 'rake'
|
21
|
+
gem.add_development_dependency 'rspec'
|
22
|
+
end
|
data/lib/google_api.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require "google/api_client"
|
2
|
+
|
3
|
+
require "google_api/core_ext/hash"
|
4
|
+
require "google_api/configuration"
|
5
|
+
require "google_api/version"
|
6
|
+
|
7
|
+
module GoogleApi
|
8
|
+
|
9
|
+
autoload :Cache, 'google_api/cache'
|
10
|
+
|
11
|
+
autoload :Ga, 'google_api/ga'
|
12
|
+
|
13
|
+
class SessionError < StandardError; end
|
14
|
+
class GaError < StandardError; end
|
15
|
+
class DateError < StandardError; end
|
16
|
+
class TypeError < StandardError; end
|
17
|
+
|
18
|
+
CONFIGURATION = {
|
19
|
+
client_id: nil,
|
20
|
+
client_secret: nil,
|
21
|
+
client_developer_email: nil,
|
22
|
+
client_cert_file: nil,
|
23
|
+
key_secret: 'notasecret',
|
24
|
+
redirect_uri: nil,
|
25
|
+
|
26
|
+
ga: Configuration.new(Ga::CONFIGURATION)
|
27
|
+
}
|
28
|
+
|
29
|
+
def self.config
|
30
|
+
@config ||= Configuration.new(CONFIGURATION)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.configure(&block)
|
34
|
+
config.instance_eval(&block)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module GoogleApi
|
2
|
+
class Cache
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@cache = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def write(key, value, expire = 0)
|
9
|
+
expire = expire == 0 ? 0 : Time.now + (expire * 60)
|
10
|
+
|
11
|
+
@cache[key] = [value, expire]
|
12
|
+
end
|
13
|
+
|
14
|
+
def read(key)
|
15
|
+
if exists?(key)
|
16
|
+
return @cache[key][0]
|
17
|
+
end
|
18
|
+
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def exists?(key)
|
23
|
+
@cache.has_key?(key) && !expire?(key)
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete(key)
|
27
|
+
@cache.delete(key)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def expire?(key)
|
33
|
+
expire = @cache[key][1]
|
34
|
+
|
35
|
+
if expire == 0
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
|
39
|
+
if expire < Time.now
|
40
|
+
delete(key)
|
41
|
+
|
42
|
+
return true
|
43
|
+
end
|
44
|
+
|
45
|
+
false
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module GoogleApi
|
2
|
+
class Configuration
|
3
|
+
|
4
|
+
def initialize(config = {})
|
5
|
+
config.each do |key, value|
|
6
|
+
eval <<-METHOD
|
7
|
+
def #{key}=(value)
|
8
|
+
@#{key} = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def #{key}(value = nil, &block)
|
12
|
+
if block_given?
|
13
|
+
@#{key}.instance_eval(&block)
|
14
|
+
end
|
15
|
+
|
16
|
+
if value.nil?
|
17
|
+
return @#{key}
|
18
|
+
end
|
19
|
+
|
20
|
+
self.#{key} = value
|
21
|
+
end
|
22
|
+
METHOD
|
23
|
+
|
24
|
+
self.send("#{key}=", value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def configure(&block)
|
29
|
+
if block_given?
|
30
|
+
yield self
|
31
|
+
end
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "date"
|
2
|
+
|
3
|
+
module GoogleApi
|
4
|
+
module Date
|
5
|
+
|
6
|
+
DATE_FORMAT = /\A[0-9]{4}-[0-9]{2}-[0-9]{2}\Z/
|
7
|
+
|
8
|
+
def to_date(date)
|
9
|
+
if date.is_a?(String)
|
10
|
+
unless date =~ DATE_FORMAT
|
11
|
+
raise GoogleApi::DateError, "Date: #{date} must match with #{DATE_FORMAT}."
|
12
|
+
end
|
13
|
+
|
14
|
+
date = Date.parse(date)
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
date.to_date
|
19
|
+
end
|
20
|
+
|
21
|
+
def now
|
22
|
+
Date.today
|
23
|
+
end
|
24
|
+
|
25
|
+
def prev_month
|
26
|
+
Date.today.prev_month
|
27
|
+
end
|
28
|
+
|
29
|
+
def more_months?(d1, d2)
|
30
|
+
|
31
|
+
d1 = Date.parse(d1) if d1.is_a?(String)
|
32
|
+
d2 = Date.parse(d2) if d2.is_a?(String)
|
33
|
+
|
34
|
+
d1.month != d2.month
|
35
|
+
end
|
36
|
+
|
37
|
+
def more_years?(d1, d2)
|
38
|
+
|
39
|
+
d1 = Date.parse(d1) if d1.is_a?(String)
|
40
|
+
d2 = Date.parse(d2) if d2.is_a?(String)
|
41
|
+
|
42
|
+
d1.year != d2.year
|
43
|
+
end
|
44
|
+
|
45
|
+
def day_older?(date)
|
46
|
+
|
47
|
+
if date.is_a?(String)
|
48
|
+
date = Date.parse(date)
|
49
|
+
end
|
50
|
+
|
51
|
+
if (Date.today <=> date.to_date) > 0
|
52
|
+
return true
|
53
|
+
end
|
54
|
+
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module GoogleApi
|
2
|
+
module Ga
|
3
|
+
|
4
|
+
CONFIGURATION = {
|
5
|
+
client_id: nil,
|
6
|
+
client_secret: nil,
|
7
|
+
client_developer_email: nil,
|
8
|
+
client_cert_file: nil,
|
9
|
+
key_secret: 'notasecret',
|
10
|
+
redirect_uri: nil,
|
11
|
+
|
12
|
+
cache: GoogleApi::Cache.new
|
13
|
+
}
|
14
|
+
|
15
|
+
# Session
|
16
|
+
autoload :Session, 'google_api/ga/session'
|
17
|
+
|
18
|
+
# Management
|
19
|
+
autoload :Management, 'google_api/ga/management/management'
|
20
|
+
autoload :Account, 'google_api/ga/management/account'
|
21
|
+
autoload :Webproperty, 'google_api/ga/management/webproperty'
|
22
|
+
autoload :Profile, 'google_api/ga/management/profile'
|
23
|
+
autoload :Goal, 'google_api/ga/management/goal'
|
24
|
+
autoload :Segment, 'google_api/ga/management/segment'
|
25
|
+
|
26
|
+
# Helper
|
27
|
+
autoload :Helper, 'google_api/ga/helper'
|
28
|
+
|
29
|
+
# Data
|
30
|
+
autoload :Data, 'google_api/ga/data'
|
31
|
+
autoload :DataDsl, 'google_api/ga/data/data_dsl'
|
32
|
+
autoload :FiltersDsl, 'google_api/ga/data/filters_dsl'
|
33
|
+
autoload :SegmentDsl, 'google_api/ga/data/segment_dsl'
|
34
|
+
|
35
|
+
extend GoogleApi::Ga::Helper
|
36
|
+
|
37
|
+
@@id = 0
|
38
|
+
|
39
|
+
def self.id(id=nil)
|
40
|
+
if id.nil?
|
41
|
+
return @@id
|
42
|
+
end
|
43
|
+
|
44
|
+
@@id = id.to_i
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.cache
|
48
|
+
GoogleApi.config.ga.cache
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,335 @@
|
|
1
|
+
module GoogleApi
|
2
|
+
module Ga
|
3
|
+
class Data
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@ids, @cache = nil, nil
|
7
|
+
@start_date, @end_date = Date.today, Date.today
|
8
|
+
@metrics, @dimensions, @sort = [], [], []
|
9
|
+
@filters, @segment = nil, nil
|
10
|
+
@start_index, @max_results = nil, nil
|
11
|
+
end
|
12
|
+
|
13
|
+
# Auto initialize data
|
14
|
+
def self.method_missing(method, *args, &block)
|
15
|
+
if block_given?
|
16
|
+
new.send(method, &block)
|
17
|
+
else
|
18
|
+
new.send(method, *args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Convert string, DateTime and Time to date
|
23
|
+
DATE_FORMAT = /\A[0-9]{4}-[0-9]{2}-[0-9]{2}\Z/
|
24
|
+
|
25
|
+
def to_date(date)
|
26
|
+
if date.is_a?(String)
|
27
|
+
unless date =~ DATE_FORMAT
|
28
|
+
raise GoogleApi::DateError, "Date: #{date} must match with #{DATE_FORMAT}."
|
29
|
+
end
|
30
|
+
|
31
|
+
date = Date.parse(date)
|
32
|
+
end
|
33
|
+
|
34
|
+
date.to_date
|
35
|
+
end
|
36
|
+
|
37
|
+
# Add prefix ga: to symbol in Array
|
38
|
+
def build_param(value)
|
39
|
+
type?(value, Array)
|
40
|
+
|
41
|
+
value.flatten.collect { |v| v.is_a?(Symbol) ? "ga:#{v}" : v }
|
42
|
+
end
|
43
|
+
|
44
|
+
# Check type
|
45
|
+
def type?(value, type)
|
46
|
+
unless value.is_a?(type)
|
47
|
+
raise GoogleApi::TypeError, "Value: #{value} must be #{type}."
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Clear value
|
52
|
+
def clear
|
53
|
+
@header = nil
|
54
|
+
@parameters = nil
|
55
|
+
@data = nil
|
56
|
+
@all = nil
|
57
|
+
end
|
58
|
+
|
59
|
+
# -----------------------------------------------------------------------------------
|
60
|
+
# Type 1, all are integer
|
61
|
+
#
|
62
|
+
# Ids, Cache, Start index, Max results
|
63
|
+
#
|
64
|
+
# name, alias
|
65
|
+
#
|
66
|
+
TYPE_1 = { ids: :id,
|
67
|
+
cache: nil,
|
68
|
+
start_index: :offset,
|
69
|
+
max_results: :limit }
|
70
|
+
|
71
|
+
TYPE_1.each do |key, value|
|
72
|
+
eval <<-METHOD
|
73
|
+
def #{key}(value = nil)
|
74
|
+
if value.nil?
|
75
|
+
return @#{key}
|
76
|
+
end
|
77
|
+
|
78
|
+
self.#{key} = value
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
def #{key}=(value)
|
83
|
+
type?(value, Integer)
|
84
|
+
|
85
|
+
@#{key} = value
|
86
|
+
end
|
87
|
+
METHOD
|
88
|
+
|
89
|
+
unless value.nil?
|
90
|
+
eval <<-METHOD
|
91
|
+
alias :#{value} :#{key}
|
92
|
+
alias :#{value}= :#{key}=
|
93
|
+
METHOD
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# -----------------------------------------------------------------------------------
|
98
|
+
# Type 2, date
|
99
|
+
#
|
100
|
+
# Start date, End date
|
101
|
+
#
|
102
|
+
# name, alias
|
103
|
+
#
|
104
|
+
TYPE_2 = { start_date: :from,
|
105
|
+
end_date: :to }
|
106
|
+
|
107
|
+
TYPE_2.each do |key, value|
|
108
|
+
eval <<-METHOD
|
109
|
+
def #{key}(date = nil)
|
110
|
+
if date.nil?
|
111
|
+
return @#{key}
|
112
|
+
end
|
113
|
+
|
114
|
+
self.#{key} = date
|
115
|
+
self
|
116
|
+
end
|
117
|
+
|
118
|
+
def #{key}=(date)
|
119
|
+
if date.is_a?(Integer)
|
120
|
+
@#{key} += date
|
121
|
+
else
|
122
|
+
@#{key} = to_date(date)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
METHOD
|
126
|
+
|
127
|
+
unless value.nil?
|
128
|
+
eval <<-METHOD
|
129
|
+
alias :#{value} :#{key}
|
130
|
+
alias :#{value}= :#{key}=
|
131
|
+
METHOD
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# -----------------------------------------------------------------------------------
|
136
|
+
# Type 3
|
137
|
+
#
|
138
|
+
# Metrics, Dimensions, Sort
|
139
|
+
#
|
140
|
+
# name, alias
|
141
|
+
#
|
142
|
+
TYPE_3 = { metrics: :select,
|
143
|
+
dimensions: :with,
|
144
|
+
sort: nil }
|
145
|
+
|
146
|
+
TYPE_3.each do |key, value|
|
147
|
+
eval <<-METHOD
|
148
|
+
def #{key}(*args)
|
149
|
+
if args.size == 0
|
150
|
+
return @#{key}
|
151
|
+
end
|
152
|
+
|
153
|
+
self.#{key} = args
|
154
|
+
self
|
155
|
+
end
|
156
|
+
|
157
|
+
def #{key}=(value)
|
158
|
+
clear
|
159
|
+
@#{key} = build_param(value)
|
160
|
+
end
|
161
|
+
|
162
|
+
def #{key}_add(*args)
|
163
|
+
if args.size == 0
|
164
|
+
return @#{key}
|
165
|
+
end
|
166
|
+
|
167
|
+
self.#{key}_add = args
|
168
|
+
self
|
169
|
+
end
|
170
|
+
|
171
|
+
def #{key}_add=(value)
|
172
|
+
clear
|
173
|
+
@#{key} += build_param(value)
|
174
|
+
end
|
175
|
+
|
176
|
+
def #{key}_sub(*args)
|
177
|
+
if args.size == 0
|
178
|
+
return @#{key}
|
179
|
+
end
|
180
|
+
|
181
|
+
self.#{key}_sub = args
|
182
|
+
self
|
183
|
+
end
|
184
|
+
|
185
|
+
def #{key}_sub=(value)
|
186
|
+
clear
|
187
|
+
@#{key} -= build_param(value)
|
188
|
+
end
|
189
|
+
METHOD
|
190
|
+
|
191
|
+
unless value.nil?
|
192
|
+
eval <<-METHOD
|
193
|
+
alias :#{value} :#{key}
|
194
|
+
alias :#{value}= :#{key}=
|
195
|
+
|
196
|
+
alias :#{value}_add :#{key}_add
|
197
|
+
alias :#{value}_add= :#{key}_add=
|
198
|
+
|
199
|
+
alias :#{value}_sub :#{key}_sub
|
200
|
+
alias :#{value}_sub= :#{key}_sub=
|
201
|
+
METHOD
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# -----------------------------------------------------------------------------------
|
206
|
+
# Type 4
|
207
|
+
#
|
208
|
+
# Filters, Segment
|
209
|
+
#
|
210
|
+
# name, alias
|
211
|
+
#
|
212
|
+
TYPE_4 = { filters: :where,
|
213
|
+
segment: nil }
|
214
|
+
|
215
|
+
TYPE_4.each do |key, value|
|
216
|
+
eval <<-METHOD
|
217
|
+
def #{key}(value = nil, &block)
|
218
|
+
if !block_given? && value.nil?
|
219
|
+
return @#{key}
|
220
|
+
end
|
221
|
+
|
222
|
+
if block_given?
|
223
|
+
@#{key} = #{key.to_s.capitalize}Dsl.new.instance_eval(&block).join
|
224
|
+
else
|
225
|
+
@#{key} = value
|
226
|
+
end
|
227
|
+
self
|
228
|
+
end
|
229
|
+
METHOD
|
230
|
+
|
231
|
+
unless value.nil?
|
232
|
+
eval <<-METHOD
|
233
|
+
alias :#{value} :#{key}
|
234
|
+
METHOD
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Add row!, header!, all!, count!. First clear and run method .
|
239
|
+
[:rows, :header, :all, :count].each do |value|
|
240
|
+
eval <<-METHOD
|
241
|
+
def #{value}!
|
242
|
+
clear
|
243
|
+
#{value}
|
244
|
+
end
|
245
|
+
METHOD
|
246
|
+
end
|
247
|
+
|
248
|
+
def rows
|
249
|
+
data.rows
|
250
|
+
end
|
251
|
+
|
252
|
+
def header
|
253
|
+
@header ||= data.column_headers.map { |c| c.name }
|
254
|
+
end
|
255
|
+
|
256
|
+
def count
|
257
|
+
data.total_results
|
258
|
+
end
|
259
|
+
|
260
|
+
def all
|
261
|
+
@all ||= [header, rows]
|
262
|
+
end
|
263
|
+
|
264
|
+
def each(&block)
|
265
|
+
if block.arity == 1 # each
|
266
|
+
rows.each do |row|
|
267
|
+
yield(row)
|
268
|
+
end
|
269
|
+
else # each with index
|
270
|
+
i = -1
|
271
|
+
rows.each do |row|
|
272
|
+
i += 1
|
273
|
+
yield(i, row)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def each!(&block)
|
279
|
+
clear
|
280
|
+
each(block)
|
281
|
+
end
|
282
|
+
|
283
|
+
private
|
284
|
+
|
285
|
+
def data
|
286
|
+
@data ||= get.data
|
287
|
+
end
|
288
|
+
|
289
|
+
def parameters
|
290
|
+
return @parameters if @parameters
|
291
|
+
|
292
|
+
if @ids.nil?
|
293
|
+
self.ids = Ga.id
|
294
|
+
end
|
295
|
+
|
296
|
+
@parameters = {}
|
297
|
+
|
298
|
+
@parameters['ids'] = "ga:#{@ids}"
|
299
|
+
@parameters['start-date'] = @start_date.to_s
|
300
|
+
@parameters['end-date'] = @end_date.to_s
|
301
|
+
@parameters['metrics'] = @metrics.join(',')
|
302
|
+
|
303
|
+
@parameters['dimensions'] = @dimensions.join(',') unless @dimensions.empty?
|
304
|
+
@parameters['sort'] = @sort.join(',') unless @sort.empty?
|
305
|
+
@parameters['filters'] = @filters unless @filters.nil?
|
306
|
+
@parameters['start-index'] = @start_index unless @start_index.nil?
|
307
|
+
@parameters['max-results'] = @max_results unless @max_results.nil?
|
308
|
+
|
309
|
+
@parameters
|
310
|
+
end
|
311
|
+
|
312
|
+
def _cache
|
313
|
+
GoogleApi::Ga.cache
|
314
|
+
end
|
315
|
+
|
316
|
+
def _session
|
317
|
+
Session
|
318
|
+
end
|
319
|
+
|
320
|
+
def get
|
321
|
+
if @cache && _cache.exists?(parameters)
|
322
|
+
return _cache.read(parameters)
|
323
|
+
end
|
324
|
+
|
325
|
+
result = _session.client.execute( api_method: _session.api.data.ga.get,
|
326
|
+
parameters: parameters )
|
327
|
+
|
328
|
+
_cache.write(parameters, result, @cache) if @cache.is_a?(Integer)
|
329
|
+
|
330
|
+
result
|
331
|
+
end
|
332
|
+
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|