google_api 1.0.0.beta
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/.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
|