garb-authsub 0.7.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.
Files changed (41) hide show
  1. data/README.md +250 -0
  2. data/Rakefile +56 -0
  3. data/lib/garb.rb +38 -0
  4. data/lib/garb/account.rb +29 -0
  5. data/lib/garb/authentication_request.rb +53 -0
  6. data/lib/garb/data_request.rb +51 -0
  7. data/lib/garb/filter_parameters.rb +37 -0
  8. data/lib/garb/profile.rb +57 -0
  9. data/lib/garb/profile_reports.rb +15 -0
  10. data/lib/garb/report.rb +26 -0
  11. data/lib/garb/report_parameter.rb +25 -0
  12. data/lib/garb/report_response.rb +62 -0
  13. data/lib/garb/reports.rb +5 -0
  14. data/lib/garb/reports/bounces.rb +5 -0
  15. data/lib/garb/reports/exits.rb +5 -0
  16. data/lib/garb/reports/pageviews.rb +5 -0
  17. data/lib/garb/reports/unique_pageviews.rb +5 -0
  18. data/lib/garb/reports/visits.rb +5 -0
  19. data/lib/garb/resource.rb +92 -0
  20. data/lib/garb/session.rb +29 -0
  21. data/lib/garb/version.rb +13 -0
  22. data/lib/support.rb +39 -0
  23. data/test/fixtures/cacert.pem +67 -0
  24. data/test/fixtures/profile_feed.xml +40 -0
  25. data/test/fixtures/report_feed.xml +46 -0
  26. data/test/test_helper.rb +18 -0
  27. data/test/unit/garb/account_test.rb +53 -0
  28. data/test/unit/garb/authentication_request_test.rb +121 -0
  29. data/test/unit/garb/data_request_test.rb +106 -0
  30. data/test/unit/garb/filter_parameters_test.rb +57 -0
  31. data/test/unit/garb/oauth_session_test.rb +11 -0
  32. data/test/unit/garb/profile_reports_test.rb +29 -0
  33. data/test/unit/garb/profile_test.rb +87 -0
  34. data/test/unit/garb/report_parameter_test.rb +43 -0
  35. data/test/unit/garb/report_response_test.rb +29 -0
  36. data/test/unit/garb/report_test.rb +91 -0
  37. data/test/unit/garb/resource_test.rb +38 -0
  38. data/test/unit/garb/session_test.rb +84 -0
  39. data/test/unit/garb_test.rb +14 -0
  40. data/test/unit/symbol_operator_test.rb +37 -0
  41. metadata +131 -0
@@ -0,0 +1,37 @@
1
+ module Garb
2
+ class FilterParameters
3
+ def self.define_operators(*methods)
4
+ methods.each do |method|
5
+ class_eval <<-CODE
6
+ def #{method}(field, value)
7
+ self.parameters << {SymbolOperator.new(field, :#{method}) => value}
8
+ end
9
+ CODE
10
+ end
11
+ end
12
+
13
+ define_operators :eql, :not_eql, :gt, :gte, :lt, :lte, :matches,
14
+ :does_not_match, :contains, :does_not_contain, :substring, :not_substring
15
+
16
+ attr_accessor :parameters
17
+
18
+ def initialize
19
+ self.parameters = []
20
+ end
21
+
22
+ def filters(&block)
23
+ instance_eval &block
24
+ end
25
+
26
+ def to_params
27
+ value = self.parameters.map do |param|
28
+ param.map do |k,v|
29
+ next unless k.is_a?(SymbolOperator)
30
+ "#{URI.encode(k.to_google_analytics, /[=<>]/)}#{CGI::escape(v.to_s)}"
31
+ end.join(',') # Hash AND
32
+ end.join(';') # Array OR
33
+
34
+ value.empty? ? {} : {'filters' => value}
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,57 @@
1
+ module Garb
2
+ class Profile
3
+
4
+ include ProfileReports
5
+
6
+ attr_reader :session, :table_id, :title, :account_name, :account_id, :web_property_id
7
+
8
+ class Property
9
+ include HappyMapper
10
+
11
+ tag 'property'
12
+ namespace 'http://schemas.google.com/analytics/2009'
13
+
14
+ attribute :name, String
15
+ attribute :value, String
16
+
17
+ def instance_name
18
+ Garb.from_google_analytics(name)
19
+ end
20
+ end
21
+
22
+ class Entry
23
+ include HappyMapper
24
+
25
+ tag 'entry'
26
+
27
+ element :title, String
28
+ element :tableId, String, :namespace => 'http://schemas.google.com/analytics/2009'
29
+
30
+ has_many :properties, Property
31
+ end
32
+
33
+ def initialize(entry, session)
34
+ @session = session
35
+ @title = entry.title
36
+ @table_id = entry.tableId
37
+
38
+ entry.properties.each do |p|
39
+ instance_variable_set :"@#{p.instance_name}", p.value
40
+ end
41
+ end
42
+
43
+ def id
44
+ Garb.from_google_analytics(@table_id)
45
+ end
46
+
47
+ def self.all(session = Session)
48
+ url = "https://www.google.com/analytics/feeds/accounts/default"
49
+ response = DataRequest.new(session, url).send_request
50
+ Entry.parse(response.body).map {|entry| new(entry, session)}
51
+ end
52
+
53
+ def self.first(id, session = Session)
54
+ all(session).detect {|profile| profile.id == id || profile.web_property_id == id }
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,15 @@
1
+ module Garb
2
+ module ProfileReports
3
+ def self.add_report_method(klass)
4
+ # demodulize leaves potential to redefine
5
+ # these methods given different namespaces
6
+ method_name = klass.to_s.demodulize.underscore
7
+
8
+ class_eval <<-CODE
9
+ def #{method_name}(opts = {}, &block)
10
+ #{klass}.results(self, opts, &block)
11
+ end
12
+ CODE
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ module Garb
2
+ class Report
3
+ include Resource
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
+ metrics opts.fetch(:metrics, [])
17
+ dimensions opts.fetch(:dimensions, [])
18
+ sort opts.fetch(:sort, [])
19
+ end
20
+
21
+ def results
22
+ ReportResponse.new(send_request_for_body).results
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,25 @@
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
+ value = self.elements.map{|param| Garb.to_google_analytics(param)}.join(',')
22
+ value.empty? ? {} : {self.name => value}
23
+ end
24
+ end
25
+ 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\:/,'').underscore
17
+ hash.merge!({name => m.value})
18
+ end
19
+
20
+ entry.dimensions.each do |d|
21
+ name = d.name.sub(/^ga\:/,'').underscore
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 'http://schemas.google.com/analytics/2009'
38
+
39
+ attribute :name, String
40
+ attribute :value, String
41
+ end
42
+
43
+ class Dimension
44
+ include HappyMapper
45
+
46
+ tag 'dimension'
47
+ namespace 'http://schemas.google.com/analytics/2009'
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,5 @@
1
+ require 'reports/exits'
2
+ require 'reports/visits'
3
+ require 'reports/bounces'
4
+ require 'reports/pageviews'
5
+ require 'reports/unique_pageviews'
@@ -0,0 +1,5 @@
1
+ class Bounces
2
+ extend Garb::Resource
3
+
4
+ metric :bounces
5
+ end
@@ -0,0 +1,5 @@
1
+ class Exits
2
+ extend Garb::Resource
3
+
4
+ metric :exits
5
+ end
@@ -0,0 +1,5 @@
1
+ class Pageviews
2
+ extend Garb::Resource
3
+
4
+ metric :pageviews
5
+ end
@@ -0,0 +1,5 @@
1
+ class UniquePageviews
2
+ extend Garb::Resource
3
+
4
+ metric :unique_pageviews
5
+ end
@@ -0,0 +1,5 @@
1
+ class Visits
2
+ extend Garb::Resource
3
+
4
+ metric :visits
5
+ end
@@ -0,0 +1,92 @@
1
+ module Garb
2
+ module Resource
3
+ MONTH = 2592000
4
+ URL = "https://www.google.com/analytics/feeds/data"
5
+
6
+ def self.extended(base)
7
+ # define a method on a module that gets included into profile
8
+ # Exits would make:
9
+ # to enable profile.exits(options_hash, &block)
10
+ # returns Exits.results(self, options_hash, &block)
11
+ # every class defined which extends Resource will add to the module
12
+ ProfileReports.add_report_method(base)
13
+ end
14
+
15
+ %w(metrics dimensions sort).each do |parameter|
16
+ class_eval <<-CODE
17
+ def #{parameter}(*fields)
18
+ @#{parameter} ||= ReportParameter.new(:#{parameter})
19
+ @#{parameter} << fields
20
+ end
21
+
22
+ def clear_#{parameter}
23
+ @#{parameter} = ReportParameter.new(:#{parameter})
24
+ end
25
+ CODE
26
+ end
27
+
28
+ def filters(*hashes, &block)
29
+ @filter_parameters ||= FilterParameters.new
30
+
31
+ hashes.each do |hash|
32
+ @filter_parameters.parameters << hash
33
+ end
34
+
35
+ @filter_parameters.filters(&block) if block_given?
36
+ @filter_parameters
37
+ end
38
+
39
+ def clear_filters
40
+ @filter_parameters = FilterParameters.new
41
+ end
42
+
43
+ def results(profile, opts = {}, &block)
44
+ @profile = profile.is_a?(Profile) ? profile : Profile.first(profile, opts.fetch(:session, Session))
45
+
46
+ if @profile
47
+ @start_date = opts.fetch(:start_date, Time.now - MONTH)
48
+ @end_date = opts.fetch(:end_date, Time.now)
49
+ @limit = opts.fetch(:limit, nil)
50
+ @offset = opts.fetch(:offset, nil)
51
+
52
+ instance_eval(&block) if block_given?
53
+
54
+ ReportResponse.new(send_request_for_body).results
55
+ else
56
+ []
57
+ end
58
+ end
59
+
60
+ def page_params
61
+ {'max-results' => @limit, 'start-index' => @offset}.reject{|k,v| v.nil?}
62
+ end
63
+
64
+ def default_params
65
+ {'ids' => @profile.table_id,
66
+ 'start-date' => format_time(@start_date),
67
+ 'end-date' => format_time(@end_date)}
68
+ end
69
+
70
+ def params
71
+ [
72
+ metrics.to_params,
73
+ dimensions.to_params,
74
+ sort.to_params,
75
+ filters.to_params,
76
+ page_params
77
+ ].inject(default_params) do |p, i|
78
+ p.merge(i)
79
+ end
80
+ end
81
+
82
+ def format_time(t)
83
+ t.strftime('%Y-%m-%d')
84
+ end
85
+
86
+ def send_request_for_body
87
+ request = DataRequest.new(@profile.session, URL, params)
88
+ response = request.send_request
89
+ response.body
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,29 @@
1
+ module Garb
2
+ class Session
3
+ module Methods
4
+ attr_accessor :auth_token, :access_token, :authsub_token, :email
5
+
6
+ # use only for single user authentication
7
+ def login(email, password, opts={})
8
+ self.email = email
9
+ auth_request = AuthenticationRequest.new(email, password, opts)
10
+ self.auth_token = auth_request.auth_token(opts)
11
+ end
12
+
13
+ def single_user?
14
+ auth_token && auth_token.is_a?(String)
15
+ end
16
+
17
+ def oauth_user?
18
+ !access_token.nil?
19
+ end
20
+
21
+ def authsub_user?
22
+ !authsub_token.nil?
23
+ end
24
+ end
25
+
26
+ include Methods
27
+ extend Methods
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ module Garb
2
+ module Version
3
+
4
+ MAJOR = 0
5
+ MINOR = 7
6
+ TINY = 0
7
+
8
+ def self.to_s # :nodoc:
9
+ [MAJOR, MINOR, TINY].join('.')
10
+ end
11
+
12
+ end
13
+ end
data/lib/support.rb ADDED
@@ -0,0 +1,39 @@
1
+ class SymbolOperator
2
+ def initialize(field, operator)
3
+ @field, @operator = field, operator
4
+ end unless method_defined?(:initialize)
5
+
6
+ def to_google_analytics
7
+ operators = {
8
+ :eql => '==',
9
+ :not_eql => '!=',
10
+ :gt => '>',
11
+ :gte => '>=',
12
+ :lt => '<',
13
+ :lte => '<=',
14
+ :matches => '==',
15
+ :does_not_match => '!=',
16
+ :contains => '=~',
17
+ :does_not_contain => '!~',
18
+ :substring => '=@',
19
+ :not_substring => '!@',
20
+ :desc => '-'
21
+ }
22
+
23
+ target = Garb.to_google_analytics(@field)
24
+ operator = operators[@operator]
25
+
26
+ @operator == :desc ? "#{operator}#{target}" : "#{target}#{operator}"
27
+ end
28
+ end
29
+
30
+ class Symbol
31
+ [:eql, :not_eql, :gt, :gte, :lt, :lte, :desc,
32
+ :matches, :does_not_match, :contains, :does_not_contain,
33
+ :substring, :not_substring].each do |operator|
34
+
35
+ define_method(operator) do
36
+ SymbolOperator.new(self, operator)
37
+ end unless method_defined?(operator)
38
+ end
39
+ end