jonuts-garb 0.2.3

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.
@@ -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
@@ -0,0 +1,47 @@
1
+ module Garb
2
+ class Profile
3
+
4
+ attr_reader :table_id, :title, :account_name, :email
5
+
6
+ class Property
7
+ include HappyMapper
8
+
9
+ tag 'property'
10
+ namespace 'dxp'
11
+
12
+ attribute :name, String
13
+ attribute :value, String
14
+ end
15
+
16
+ class Entry
17
+ include HappyMapper
18
+
19
+ tag 'entry'
20
+
21
+ element :id, Integer
22
+ element :title, String
23
+ element :tableId, String, :namespace => 'dxp'
24
+
25
+ # has_one :table_id, TableId
26
+ has_many :properties, Property
27
+ end
28
+
29
+ def initialize(entry, email=nil)
30
+ @email = Session.email(email)
31
+ @title = entry.title
32
+ @table_id = entry.tableId
33
+ @account_name = entry.properties.detect{|p| p.name == 'ga:accountName'}.value
34
+ end
35
+
36
+ def id
37
+ @table_id.sub(/^ga:/, '')
38
+ end
39
+
40
+ def self.all(email=nil)
41
+ email = Session.email(email)
42
+ url = "https://www.google.com/analytics/feeds/accounts/#{email}"
43
+ response = DataRequest.new(url).send_request(email)
44
+ Entry.parse(response.body).map {|e| Garb::Profile.new(e, email)}
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,31 @@
1
+ module Garb
2
+ class Report
3
+ include Resource::ResourceMethods
4
+
5
+ MONTH = 2592000
6
+ URL = "https://www.google.com/analytics/feeds/data"
7
+
8
+ def initialize(profile, opts={})
9
+ @profile = profile
10
+
11
+ @start_date = opts.fetch(:start_date, Time.now - MONTH)
12
+ @end_date = opts.fetch(:end_date, Time.now)
13
+ @limit = opts.fetch(:limit, nil)
14
+ @offset = opts.fetch(:offset, nil)
15
+
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, [])
24
+ end
25
+
26
+ def results
27
+ ReportResponse.new(send_request_for_body(@profile.email)).results
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,36 @@
1
+ module Garb
2
+ class ReportParameter
3
+
4
+ attr_reader :elements
5
+
6
+ def initialize(name)
7
+ @name = name
8
+ @elements = []
9
+ end
10
+
11
+ def name
12
+ @name.to_s
13
+ end
14
+
15
+ def <<(element)
16
+ (@elements += [element].flatten).compact!
17
+ self
18
+ end
19
+
20
+ def to_params
21
+ params = self.elements.map do |elem|
22
+ case elem
23
+ when Hash
24
+ elem.collect do |k,v|
25
+ next unless k.is_a?(Operator)
26
+ "#{k.target}#{URI.encode(k.operator.to_s, /[=<>]/)}#{CGI::escape(v.to_s)}"
27
+ end.join(';')
28
+ else
29
+ elem.to_ga
30
+ end
31
+ end.join(',')
32
+
33
+ params.empty? ? {} : {self.name => params}
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,62 @@
1
+ module Garb
2
+ class ReportResponse
3
+ # include Enumerable
4
+
5
+ def initialize(response_body)
6
+ @xml = response_body
7
+ end
8
+
9
+ def parse
10
+ entries = Entry.parse(@xml)
11
+
12
+ @results = entries.collect do |entry|
13
+ hash = {}
14
+
15
+ entry.metrics.each do |m|
16
+ name = m.name.sub(/^ga\:/,'').underscored
17
+ hash.merge!({name => m.value})
18
+ end
19
+
20
+ entry.dimensions.each do |d|
21
+ name = d.name.sub(/^ga\:/,'').underscored
22
+ hash.merge!({name => d.value})
23
+ end
24
+
25
+ OpenStruct.new(hash)
26
+ end
27
+ end
28
+
29
+ def results
30
+ @results || parse
31
+ end
32
+
33
+ class Metric
34
+ include HappyMapper
35
+
36
+ tag 'metric'
37
+ namespace 'dxp'
38
+
39
+ attribute :name, String
40
+ attribute :value, String
41
+ end
42
+
43
+ class Dimension
44
+ include HappyMapper
45
+
46
+ tag 'dimension'
47
+ namespace 'dxp'
48
+
49
+ attribute :name, String
50
+ attribute :value, String
51
+ end
52
+
53
+ class Entry
54
+ include HappyMapper
55
+
56
+ tag 'entry'
57
+
58
+ has_many :metrics, Metric
59
+ has_many :dimensions, Dimension
60
+ end
61
+ end
62
+ end
@@ -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(profile.email)).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(email)
82
+ request = DataRequest.new(URL, params)
83
+ response = request.send_request(email)
84
+ response.body
85
+ end
86
+
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,51 @@
1
+ module Garb
2
+ class Session
3
+ class << self; attr_reader :sessions end
4
+
5
+ @sessions ||= []
6
+
7
+ def self.login(email, password, opts={})
8
+ auth_request = AuthenticationRequest.new(email, password)
9
+ creds = { :email => email, :auth_token => auth_request.auth_token(opts) }
10
+ update_or_create(creds)
11
+ end
12
+
13
+ def self.auth_token(email=nil)
14
+ email ? find_session(email).auth_token : default.auth_token
15
+ end
16
+
17
+ def self.email(email=nil)
18
+ email || default.email
19
+ end
20
+
21
+ def self.find_session(email)
22
+ @sessions.find {|session| session.email == email}
23
+ end
24
+
25
+ def self.default
26
+ @sessions.first
27
+ end
28
+
29
+ def self.update_or_create(creds)
30
+ if session = find_session(creds[:email])
31
+ session.auth_token = creds[:auth_token]
32
+ else
33
+ @sessions << session = new(creds)
34
+ end
35
+ session
36
+ end
37
+
38
+ def initialize(creds={})
39
+ @email = creds[:email]
40
+ @auth_token = creds[:auth_token]
41
+ end
42
+
43
+ attr_accessor :auth_token
44
+ attr_reader :email
45
+
46
+ def profiles
47
+ Profile.all(@email)
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,13 @@
1
+ module Garb
2
+ module Version
3
+
4
+ MAJOR = 0
5
+ MINOR = 2
6
+ TINY = 3
7
+
8
+ def self.to_s # :nodoc:
9
+ [MAJOR, MINOR, TINY].join('.')
10
+ end
11
+
12
+ end
13
+ end
data/lib/garb.rb ADDED
@@ -0,0 +1,70 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__))
2
+
3
+ require 'net/http'
4
+ require 'net/https'
5
+ require 'rubygems'
6
+ require 'cgi'
7
+ require 'ostruct'
8
+ require 'happymapper'
9
+
10
+ require 'garb/version'
11
+ require 'garb/authentication_request'
12
+ require 'garb/data_request'
13
+ require 'garb/session'
14
+ require 'garb/profile'
15
+ require 'garb/report_parameter'
16
+ require 'garb/report_response'
17
+ require 'garb/resource'
18
+ require 'garb/report'
19
+
20
+ require 'extensions/string'
21
+ require 'extensions/operator'
22
+ require 'extensions/symbol'
23
+ require 'extensions/happymapper'
24
+
25
+ module Garb
26
+ # :stopdoc:
27
+ GA = "http://schemas.google.com/analytics/2008"
28
+
29
+ VERSION = '0.1.2'
30
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
31
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
32
+ # :startdoc:
33
+
34
+ # Returns the version string for the library.
35
+ #
36
+ def self.version
37
+ VERSION
38
+ end
39
+
40
+ # Returns the library path for the module. If any arguments are given,
41
+ # they will be joined to the end of the libray path using
42
+ # <tt>File.join</tt>.
43
+ #
44
+ def self.libpath( *args )
45
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
46
+ end
47
+
48
+ # Returns the lpath for the module. If any arguments are given,
49
+ # they will be joined to the end of the path using
50
+ # <tt>File.join</tt>.
51
+ #
52
+ def self.path( *args )
53
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
54
+ end
55
+
56
+ # Utility method used to rquire all files ending in .rb that lie in the
57
+ # directory below this file that has the same name as the filename passed
58
+ # in. Optionally, a specific _directory_ name can be passed in such that
59
+ # the _filename_ does not have to be equivalent to the directory.
60
+ #
61
+ def self.require_all_libs_relative_to( fname, dir = nil )
62
+ dir ||= ::File.basename(fname, '.*')
63
+ search_me = ::File.expand_path(
64
+ ::File.join(::File.dirname(fname), dir, '*', '*.rb'))
65
+
66
+ Dir.glob(search_me).sort.each {|rb| require rb}
67
+ end
68
+ end # module Garb
69
+
70
+ # EOF
@@ -0,0 +1,67 @@
1
+ ##
2
+ ## cacert.pem-foo -- Bundle of CA Root Certificates
3
+ ##
4
+ ## Converted at: Thu Mar 26 21:23:06 2009 UTC
5
+ ##
6
+ ## This is a bundle of X.509 certificates of public Certificate Authorities
7
+ ## (CA). These were automatically extracted from Mozilla's root certificates
8
+ ## file (certdata.txt). This file can be found in the mozilla source tree:
9
+ ## '/mozilla/security/nss/lib/ckfw/builtins/certdata.txt'
10
+ ##
11
+ ## It contains the certificates in PEM format and therefore
12
+ ## can be directly used with curl / libcurl / php_curl, or with
13
+ ## an Apache+mod_ssl webserver for SSL client authentication.
14
+ ## Just configure this file as the SSLCACertificateFile.
15
+ ##
16
+
17
+ # ***** BEGIN LICENSE BLOCK *****
18
+ # Version: MPL 1.1/GPL 2.0/LGPL 2.1
19
+ #
20
+ # The contents of this file are subject to the Mozilla Public License Version
21
+ # 1.1 (the "License"); you may not use this file except in compliance with
22
+ # the License. You may obtain a copy of the License at
23
+ # http://www.mozilla.org/MPL/
24
+ #
25
+ # Software distributed under the License is distributed on an "AS IS" basis,
26
+ # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
27
+ # for the specific language governing rights and limitations under the
28
+ # License.
29
+ #
30
+ # The Original Code is the Netscape security libraries.
31
+ #
32
+ # The Initial Developer of the Original Code is
33
+ # Netscape Communications Corporation.
34
+ # Portions created by the Initial Developer are Copyright (C) 1994-2000
35
+ # the Initial Developer. All Rights Reserved.
36
+ #
37
+ # Contributor(s):
38
+ #
39
+ # Alternatively, the contents of this file may be used under the terms of
40
+ # either the GNU General Public License Version 2 or later (the "GPL"), or
41
+ # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
42
+ # in which case the provisions of the GPL or the LGPL are applicable instead
43
+ # of those above. If you wish to allow use of your version of this file only
44
+ # under the terms of either the GPL or the LGPL, and not to allow others to
45
+ # use your version of this file under the terms of the MPL, indicate your
46
+ # decision by deleting the provisions above and replace them with the notice
47
+ # and other provisions required by the GPL or the LGPL. If you do not delete
48
+ # the provisions above, a recipient may use your version of this file under
49
+ # the terms of any one of the MPL, the GPL or the LGPL.
50
+ #
51
+ # ***** END LICENSE BLOCK *****
52
+ # @(#) $RCSfile: certdata.txt,v $ $Revision: 1.51 $ $Date: 2009/01/15 22:35:15 $
53
+
54
+ Verisign/RSA Secure Server CA
55
+ =============================
56
+ -----BEGIN CERTIFICATE-----
57
+ MIICNDCCAaECEAKtZn5ORf5eV288mBle3cAwDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMx
58
+ IDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYDVQQLEyVTZWN1cmUgU2VydmVy
59
+ IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk0MTEwOTAwMDAwMFoXDTEwMDEwNzIzNTk1OVow
60
+ XzELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYDVQQL
61
+ EyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGbMA0GCSqGSIb3DQEBAQUA
62
+ A4GJADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6OLDfO6zV4ZFQD5YRAUcm/jwjiioII0haGN1Xp
63
+ sSECrXZogZoFokvJSyVmIlZsiAeP94FZbYQHZXATcXY+m3dM41CJVphIuR2nKRoTLkoRWZweFdVJ
64
+ VCxzOmmCsZc5nG1wZ0jl3S3WyB57AgMBAAEwDQYJKoZIhvcNAQECBQADfgBl3X7hsuyw4jrg7HFG
65
+ mhkRuNPHoLQDQCYCPgmc4RKz0Vr2N6W3YQO2WxZpO8ZECAyIUwxrl0nHPjXcbLm7qt9cuzovk2C2
66
+ qUtN8iD3zV9/ZHuO3ABc1/p3yjkWWW8O6tO1g39NTUJWdrTJXwT4OPjr0l91X817/OWOgHz8UA==
67
+ -----END CERTIFICATE-----