garb 0.2.1

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,28 @@
1
+ module Garb
2
+ class DataRequest
3
+
4
+ def initialize(base_url, parameters={})
5
+ @base_url = base_url
6
+ @parameters = parameters
7
+ end
8
+
9
+ def query_string
10
+ parameter_list = @parameters.map {|k,v| "#{k}=#{v}" }
11
+ parameter_list.empty? ? '' : "?#{parameter_list.join('&')}"
12
+ end
13
+
14
+ def uri
15
+ URI.parse(@base_url)
16
+ end
17
+
18
+ def send_request
19
+ http = Net::HTTP.new(uri.host, uri.port)
20
+ http.use_ssl = true
21
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
22
+ response = http.get("#{uri.path}#{query_string}", 'Authorization' => "GoogleLogin auth=#{Session.auth_token}")
23
+ raise response.body.inspect unless response.is_a?(Net::HTTPOK)
24
+ response
25
+ end
26
+
27
+ end
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
@@ -0,0 +1,45 @@
1
+ module Garb
2
+ class Profile
3
+
4
+ attr_reader :table_id, :title, :account_name
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)
30
+ @title = entry.title
31
+ @table_id = entry.tableId
32
+ @account_name = entry.properties.detect{|p| p.name == 'ga:accountName'}.value
33
+ end
34
+
35
+ def id
36
+ @table_id.sub(/^ga:/, '')
37
+ end
38
+
39
+ def self.all
40
+ url = "https://www.google.com/analytics/feeds/accounts/#{Session.email}"
41
+ response = DataRequest.new(url).send_request
42
+ Entry.parse(response.body).map {|e| Garb::Profile.new(e)}
43
+ end
44
+ end
45
+ 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).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).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
@@ -0,0 +1,19 @@
1
+ module Garb
2
+ class Session
3
+
4
+ def self.login(email, password)
5
+ @email = email
6
+ auth_request = AuthenticationRequest.new(email, password)
7
+ @auth_token = auth_request.auth_token
8
+ end
9
+
10
+ def self.auth_token
11
+ @auth_token
12
+ end
13
+
14
+ def self.email
15
+ @email
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module Garb
2
+ module Version
3
+
4
+ MAJOR = 0
5
+ MINOR = 2
6
+ TINY = 1
7
+
8
+ def self.to_s # :nodoc:
9
+ [MAJOR, MINOR, TINY].join('.')
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,33 @@
1
+ <?xml version='1.0' encoding='UTF-8'?>
2
+ <feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:dxp='http://schemas.google.com/analytics/2009'>
3
+ <id>http://www.google.com/analytics/feeds/accounts/email@example.com</id>
4
+ <updated>2009-03-27T08:14:28.000-07:00</updated>
5
+ <title type='text'>Profile list for email@example.com</title>
6
+ <link rel='self' type='application/atom+xml' href='http://www.google.com/analytics/feeds/accounts/email@example.com'/>
7
+ <author><name>Google Analytics</name></author>
8
+ <generator version='1.0'>Google Analytics</generator>
9
+ <openSearch:totalResults>2</openSearch:totalResults>
10
+ <openSearch:startIndex>1</openSearch:startIndex>
11
+ <openSearch:itemsPerPage>2</openSearch:itemsPerPage>
12
+ <entry>
13
+ <id>http://www.google.com/analytics/feeds/accounts/ga:12345</id>
14
+ <updated>2008-07-21T14:05:57.000-07:00</updated>
15
+ <title type='text'>Historical</title>
16
+ <dxp:tableId>ga:12345</dxp:tableId>
17
+ <dxp:property name='ga:accountId' value='1111'/>
18
+ <dxp:property name='ga:accountName' value='Blog Beta'/>
19
+ <dxp:property name='ga:profileId' value='1212'/>
20
+ <dxp:property name='ga:webPropertyId' value='UA-1111-1'/>
21
+ </entry>
22
+ <entry>
23
+ <id>http://www.google.com/analytics/feeds/accounts/ga:12346</id>
24
+ <updated>2008-11-24T11:51:07.000-08:00</updated>
25
+ <title type='text'>Presently</title>
26
+ <dxp:tableId>ga:12346</dxp:tableId>
27
+ <dxp:property name='ga:accountId' value='1111'/>
28
+ <dxp:property name='ga:accountName' value='Blog Beta'/>
29
+ <dxp:property name='ga:profileId' value='1213'/>
30
+ <dxp:property name='ga:webPropertyId' value='UA-1111-2'/>
31
+ </entry>
32
+ </feed>
33
+
@@ -0,0 +1,46 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <feed xmlns='http://www.w3.org/2005/Atom'
3
+ xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'
4
+ xmlns:dxp='http://schemas.google.com/dataexport/2009'
5
+ xmlns:ga='http://schemas.google.com/analytics/2008'>
6
+ <id>http://www.google.com/analytics/feeds/data?ids=ga:983247&amp;dimensions=ga:country,ga:city&amp;metrics=ga:pageViews&amp;start-date=2008-01-01&amp;end-date=2008-01-02</id>
7
+ <updated>2008-01-02T15:59:59.999-08:00 </updated>
8
+ <title type="text">Google Analytics Data for Profile 983247</title>
9
+ <link href="http://www.google.com/analytics/feeds/data" rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml"/>
10
+ <link href="http://www.google.com/analytics/feeds/data?end-date=2008-01-02&amp;start-date=2008-01-01&amp;metrics=ga%3ApageViews&amp;ids=ga%3A983247&amp;dimensions=ga%3Acountry%2Cga%3Acity" rel="self" type="application/atom+xml"/>
11
+ <link href="http://www.google.com/analytics/feeds/data?start-index=1001&amp;max-results=1000&amp;end-date=2008-01-02&amp;start-date=2008-01-01&amp;metrics=ga%3ApageViews&amp;ids=ga%3A983247&amp;dimensions=ga%3Acountry%2Cga%3Acity" rel="next" type="application/atom+xml"/>
12
+ <author>
13
+ <name>Google Analytics</name>
14
+ </author>
15
+ <openSearch:startIndex>3</openSearch:startIndex>
16
+ <openSearch:itemsPerPage>4</openSearch:itemsPerPage>
17
+ <ga:webPropertyID>UA-983247-67</ga:webPropertyID>
18
+ <ga:start-date>2008-01-01</ga:start-date>
19
+ <ga:end-date>2008-01-02</ga:end-date>
20
+
21
+ <entry>
22
+ <id> http://www.google.com/analytics/feeds/data?ids=ga:1174&amp;ga:country=%28not%20set%29&amp;ga:city=%28not%20set%29&amp;start-date=2008-01-01&amp;end-date=2008-01-02 </id>
23
+ <updated> 2008-01-01T16:00:00.001-08:00 </updated>
24
+ <title type="text"> ga:country=(not set) | ga:city=(not set) </title>
25
+ <link href="http://www.google.com/analytics/feeds/data" rel="self" type="application/atom+xml"/>
26
+ <dxp:dimension name="ga:country" value="(not set)" />
27
+ <dxp:dimension name="ga:city" value="(not set)" />
28
+ <dxp:metric name="ga:pageviews" value="33" />
29
+ </entry>
30
+ <entry>
31
+ <id> http://www.google.com/analytics/feeds/data?ids=ga:1174&amp;ga:country=Afghanistan&amp;ga:city=Kabul&amp;start-date=2008-01-01&amp;end-date=2008-01-02 </id>
32
+ <updated> 2008-01-01T16:00:00.001-08:00 </updated>
33
+ <title type="text"> ga:country=Afghanistan | ga:city=Kabul </title>
34
+ <dxp:dimension name="ga:country" value="Afghanistan" />
35
+ <dxp:dimension name="ga:city" value="Kabul" />
36
+ <dxp:metric name="ga:pageviews" value="2" />
37
+ </entry>
38
+ <entry>
39
+ <id> http://www.google.com/analytics/feeds/data?ids=ga:1174&amp;ga:country=Albania&amp;ga:city=Tirana&amp;start-date=2008-01-01&amp;end-date=2008-01-02 </id>
40
+ <updated> 2008-01-01T16:00:00.001-08:00 </updated>
41
+ <title type="text"> ga:country=Albania | ga:city=Tirana </title>
42
+ <dxp:dimension name="ga:country" value="Albania" />
43
+ <dxp:dimension name="ga:city" value="Tirana" />
44
+ <dxp:metric name="ga:pageviews" value="1" />
45
+ </entry>
46
+ </feed>
@@ -0,0 +1,16 @@
1
+ $:.reject! { |e| e.include? 'TextMate' }
2
+
3
+ require 'rubygems'
4
+ require 'test/unit'
5
+ require 'shoulda'
6
+ require 'mocha'
7
+
8
+ require File.dirname(__FILE__) + '/../lib/garb'
9
+
10
+ class Test::Unit::TestCase
11
+
12
+ def read_fixture(filename)
13
+ File.read(File.dirname(__FILE__) + "/fixtures/#{filename}")
14
+ end
15
+
16
+ end