titanous-garb 0.8.5

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 (59) hide show
  1. data/README.md +262 -0
  2. data/Rakefile +56 -0
  3. data/lib/garb.rb +69 -0
  4. data/lib/garb/account.rb +21 -0
  5. data/lib/garb/account_feed_request.rb +25 -0
  6. data/lib/garb/authentication_request.rb +53 -0
  7. data/lib/garb/data_request.rb +42 -0
  8. data/lib/garb/destination.rb +20 -0
  9. data/lib/garb/filter_parameters.rb +41 -0
  10. data/lib/garb/goal.rb +20 -0
  11. data/lib/garb/management/account.rb +32 -0
  12. data/lib/garb/management/feed.rb +26 -0
  13. data/lib/garb/management/goal.rb +20 -0
  14. data/lib/garb/management/profile.rb +39 -0
  15. data/lib/garb/management/web_property.rb +30 -0
  16. data/lib/garb/model.rb +89 -0
  17. data/lib/garb/profile.rb +33 -0
  18. data/lib/garb/profile_reports.rb +16 -0
  19. data/lib/garb/report.rb +28 -0
  20. data/lib/garb/report_parameter.rb +25 -0
  21. data/lib/garb/report_response.rb +34 -0
  22. data/lib/garb/reports.rb +5 -0
  23. data/lib/garb/reports/bounces.rb +5 -0
  24. data/lib/garb/reports/exits.rb +5 -0
  25. data/lib/garb/reports/pageviews.rb +5 -0
  26. data/lib/garb/reports/unique_pageviews.rb +5 -0
  27. data/lib/garb/reports/visits.rb +5 -0
  28. data/lib/garb/resource.rb +115 -0
  29. data/lib/garb/session.rb +35 -0
  30. data/lib/garb/step.rb +13 -0
  31. data/lib/garb/version.rb +13 -0
  32. data/lib/support.rb +40 -0
  33. data/test/fixtures/cacert.pem +67 -0
  34. data/test/fixtures/profile_feed.xml +72 -0
  35. data/test/fixtures/report_feed.xml +46 -0
  36. data/test/test_helper.rb +37 -0
  37. data/test/unit/garb/account_feed_request_test.rb +9 -0
  38. data/test/unit/garb/account_test.rb +53 -0
  39. data/test/unit/garb/authentication_request_test.rb +121 -0
  40. data/test/unit/garb/data_request_test.rb +120 -0
  41. data/test/unit/garb/destination_test.rb +28 -0
  42. data/test/unit/garb/filter_parameters_test.rb +59 -0
  43. data/test/unit/garb/goal_test.rb +24 -0
  44. data/test/unit/garb/management/account_test.rb +54 -0
  45. data/test/unit/garb/management/profile_test.rb +59 -0
  46. data/test/unit/garb/management/web_property_test.rb +58 -0
  47. data/test/unit/garb/model_test.rb +134 -0
  48. data/test/unit/garb/oauth_session_test.rb +11 -0
  49. data/test/unit/garb/profile_reports_test.rb +29 -0
  50. data/test/unit/garb/profile_test.rb +77 -0
  51. data/test/unit/garb/report_parameter_test.rb +43 -0
  52. data/test/unit/garb/report_response_test.rb +37 -0
  53. data/test/unit/garb/report_test.rb +99 -0
  54. data/test/unit/garb/resource_test.rb +49 -0
  55. data/test/unit/garb/session_test.rb +91 -0
  56. data/test/unit/garb/step_test.rb +15 -0
  57. data/test/unit/garb_test.rb +26 -0
  58. data/test/unit/symbol_operator_test.rb +37 -0
  59. metadata +180 -0
@@ -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/analytics/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,37 @@
1
+ begin
2
+ require 'simplecov'
3
+ SimpleCov.start 'rails'
4
+ rescue LoadError
5
+ puts "Install simplecov if you use 1.9 and want coverage metrics"
6
+ end
7
+
8
+ $:.reject! { |e| e.include? 'TextMate' }
9
+
10
+ require 'rubygems'
11
+ require 'bundler'
12
+ Bundler.setup(:default, :test)
13
+
14
+ require 'shoulda'
15
+ require 'minitest/unit'
16
+ require 'mocha'
17
+
18
+ $:.unshift File.expand_path('../../lib', __FILE__)
19
+ require 'garb'
20
+
21
+ class MiniTest::Unit::TestCase
22
+ include Shoulda::InstanceMethods
23
+ extend Shoulda::ClassMethods
24
+ include Shoulda::Assertions
25
+ extend Shoulda::Macros
26
+ include Shoulda::Helpers
27
+
28
+ def read_fixture(filename)
29
+ File.read(File.dirname(__FILE__) + "/fixtures/#{filename}")
30
+ end
31
+
32
+ def assert_data_params(expected)
33
+ assert_received(Garb::DataRequest, :new) {|e| e.with(Garb::Session, Garb::Model::URL, expected)}
34
+ end
35
+ end
36
+
37
+ MiniTest::Unit.autorun
@@ -0,0 +1,9 @@
1
+ require 'test_helper'
2
+
3
+ module Garb
4
+ class AccountFeedRequestTest < MiniTest::Unit::TestCase
5
+ context "An AccountFeedRequest" do
6
+
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,53 @@
1
+ require 'test_helper'
2
+
3
+ module Garb
4
+ class AccountTest < MiniTest::Unit::TestCase
5
+ context "The Account class" do
6
+ should "have an array of accounts with all profiles" do
7
+ p1 = stub(:account_id => '1111', :account_name => 'Blog 1')
8
+ p2 = stub(:account_id => '1112', :account_name => 'Blog 2')
9
+
10
+ Profile.stubs(:all).returns([p1,p2,p1,p2])
11
+ Account.stubs(:new).returns('account1', 'account2')
12
+
13
+ assert_equal ['account1','account2'], Account.all
14
+ assert_received(Profile, :all)
15
+ assert_received(Account, :new) {|e| e.with([p1,p1])}
16
+ assert_received(Account, :new) {|e| e.with([p2,p2])}
17
+ end
18
+ end
19
+
20
+ context "An instance of the Account class" do
21
+ setup do
22
+ profile = stub(:account_id => '1111', :account_name => 'Blog 1')
23
+ @profiles = [profile,profile]
24
+ @account = Account.new(@profiles)
25
+ end
26
+
27
+ context "all" do
28
+ should "use an optional user session" do
29
+ session = Session.new
30
+ Garb::Profile.expects(:all).with(session).returns(@profiles)
31
+
32
+ accounts = Account.all(session)
33
+ assert_equal 1, accounts.size
34
+ assert_equal @profiles, accounts.first.profiles
35
+ end
36
+ end
37
+
38
+ context "when creating a new account from an array of profiles" do
39
+ should "take the account id from the first profile" do
40
+ assert_equal @profiles.first.account_id, @account.id
41
+ end
42
+
43
+ should "take the account name from the first profile" do
44
+ assert_equal @profiles.first.account_name, @account.name
45
+ end
46
+
47
+ should "store the array of profiles" do
48
+ assert_equal @profiles, @account.profiles
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,121 @@
1
+ require 'test_helper'
2
+
3
+ CA_CERT_FILE = File.join(File.dirname(__FILE__), '..', '/cacert.pem')
4
+
5
+ module Garb
6
+ class AuthenticationRequestTest < MiniTest::Unit::TestCase
7
+
8
+ context "An instance of the AuthenticationRequest class" do
9
+
10
+ setup { @request = AuthenticationRequest.new('email', 'password') }
11
+
12
+ should "have a collection of parameters that include the email and password" do
13
+ expected =
14
+ {
15
+ 'Email' => 'user@example.com',
16
+ 'Passwd' => 'fuzzybunnies',
17
+ 'accountType' => 'HOSTED_OR_GOOGLE',
18
+ 'service' => 'analytics',
19
+ 'source' => 'vigetLabs-garb-001'
20
+ }
21
+
22
+ request = AuthenticationRequest.new('user@example.com', 'fuzzybunnies')
23
+ assert_equal expected, request.parameters
24
+ end
25
+
26
+ should "have a URI" do
27
+ assert_equal URI.parse('https://www.google.com/accounts/ClientLogin'), @request.uri
28
+ end
29
+
30
+ should "be able to send a request to the GAAPI service with proper ssl" do
31
+ @request.expects(:build_request).returns('post')
32
+
33
+ response = mock {|m| m.expects(:is_a?).with(Net::HTTPOK).returns(true) }
34
+
35
+ http = mock do |m|
36
+ m.expects(:use_ssl=).with(true)
37
+ m.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
38
+ m.expects(:ca_file=).with(CA_CERT_FILE)
39
+ m.expects(:request).with('post').yields(response)
40
+ end
41
+
42
+ Net::HTTP.expects(:new).with('www.google.com', 443).returns(http)
43
+
44
+ @request.send_request(OpenSSL::SSL::VERIFY_PEER)
45
+ end
46
+
47
+ should "be able to send a request to the GAAPI service with ignoring ssl" do
48
+ @request.expects(:build_request).returns('post')
49
+
50
+ response = mock {|m| m.expects(:is_a?).with(Net::HTTPOK).returns(true) }
51
+
52
+ http = mock do |m|
53
+ m.expects(:use_ssl=).with(true)
54
+ m.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
55
+ m.expects(:request).with('post').yields(response)
56
+ end
57
+
58
+ Net::HTTP.expects(:new).with('www.google.com', 443).returns(http)
59
+
60
+ @request.send_request(OpenSSL::SSL::VERIFY_NONE)
61
+ end
62
+
63
+ should "be able to build a request for the GAAPI service" do
64
+ params = "param"
65
+ @request.expects(:parameters).with().returns(params)
66
+
67
+ post = mock
68
+ post.expects(:set_form_data).with(params)
69
+
70
+ Net::HTTP::Post.expects(:new).with('/accounts/ClientLogin').returns(post)
71
+
72
+ @request.build_request
73
+ end
74
+
75
+ should "be able to retrieve an auth_token from the body" do
76
+ response_data =
77
+ "SID=mysid\n" +
78
+ "LSID=mylsid\n" +
79
+ "Auth=auth_token\n"
80
+
81
+ @request.expects(:send_request).with(OpenSSL::SSL::VERIFY_NONE).returns(stub(:body => response_data))
82
+
83
+ assert_equal 'auth_token', @request.auth_token
84
+ end
85
+
86
+ should "use VERIFY_PEER if auth_token needs to be secure" do
87
+ response_data =
88
+ "SID=mysid\n" +
89
+ "LSID=mylsid\n" +
90
+ "Auth=auth_token\n"
91
+
92
+ @request.expects(:send_request).with(OpenSSL::SSL::VERIFY_PEER).returns(stub(:body => response_data))
93
+
94
+ assert_equal 'auth_token', @request.auth_token(:secure => true)
95
+ end
96
+
97
+ should "raise an exception when requesting an auth_token when the authorization fails" do
98
+ @request.stubs(:build_request)
99
+ response = mock do |m|
100
+ m.expects(:is_a?).with(Net::HTTPOK).returns(false)
101
+ end
102
+
103
+ http = stub do |s|
104
+ s.stubs(:use_ssl=)
105
+ s.stubs(:verify_mode=)
106
+ s.stubs(:request).yields(response)
107
+ end
108
+
109
+ Net::HTTP.stubs(:new).with('www.google.com', 443).returns(http)
110
+
111
+ assert_raises(Garb::AuthenticationRequest::AuthError) do
112
+ @request.send_request(OpenSSL::SSL::VERIFY_NONE)
113
+ end
114
+ end
115
+
116
+ end
117
+
118
+
119
+
120
+ end
121
+ end
@@ -0,0 +1,120 @@
1
+ require 'test_helper'
2
+
3
+ module Garb
4
+ class DataRequestTest < MiniTest::Unit::TestCase
5
+
6
+ context "An instance of the DataRequest class" do
7
+ setup do
8
+ @session = Session.new
9
+ @session.auth_token = 'abcdefg123456'
10
+ end
11
+
12
+ should "be able to build the query string from parameters" do
13
+ parameters = {'ids' => '12345', 'metrics' => 'country'}
14
+ data_request = DataRequest.new(@session, "", parameters)
15
+
16
+ query_string = data_request.query_string
17
+
18
+ assert_match(/^\?/, query_string)
19
+
20
+ query_string.sub!(/^\?/, '')
21
+
22
+ assert_equal ["ids=12345", "metrics=country"], query_string.split('&').sort
23
+ end
24
+
25
+ should "return an empty query string if parameters are empty" do
26
+ data_request = DataRequest.new(@session, "")
27
+ assert_equal "", data_request.query_string
28
+ end
29
+
30
+ should "be able to build a uri" do
31
+ url = 'http://example.com'
32
+ assert_equal URI.parse(url), DataRequest.new(@session, url).uri
33
+ end
34
+
35
+ should "be able to send a request for a single user" do
36
+ @session.stubs(:single_user?).returns(true)
37
+ response = mock('Net::HTTPOK') do |m|
38
+ m.expects(:kind_of?).with(Net::HTTPSuccess).returns(true)
39
+ end
40
+
41
+ data_request = DataRequest.new(@session, 'https://example.com/data', 'key' => 'value')
42
+ data_request.expects(:single_user_request).with('GoogleLogin').returns(response)
43
+ data_request.send_request
44
+
45
+ assert_received(data_request, :single_user_request)
46
+ end
47
+
48
+ should "be able to send a request for an auth_sub" do
49
+ @session.stubs(:auth_sub?).returns(true)
50
+ response = mock('Net::HTTPOK') do |m|
51
+ m.expects(:kind_of?).with(Net::HTTPSuccess).returns(true)
52
+ end
53
+
54
+ data_request = DataRequest.new(@session, 'https://example.com/data', 'key' => 'value')
55
+ data_request.expects(:single_user_request).with('AuthSub').returns(response)
56
+ data_request.send_request
57
+
58
+ assert_received(data_request, :single_user_request)
59
+ end
60
+
61
+ should "be able to send a request for an oauth user" do
62
+ @session.stubs(:single_user?).returns(false)
63
+ @session.stubs(:oauth_user?).returns(true)
64
+ response = mock('Net::HTTPOK') do |m|
65
+ m.expects(:kind_of?).with(Net::HTTPSuccess).returns(true)
66
+ end
67
+
68
+ data_request = DataRequest.new(@session, 'https://example.com/data', 'key' => 'value')
69
+ data_request.stubs(:oauth_user_request).returns(response)
70
+ data_request.send_request
71
+
72
+ assert_received(data_request, :oauth_user_request)
73
+ end
74
+
75
+ should "raise if the request is unauthorized" do
76
+ @session.stubs(:single_user?).returns(false)
77
+ @session.stubs(:oauth_user?).returns(true)
78
+ response = mock('Net::HTTPUnauthorized', :body => 'Error')
79
+
80
+ data_request = DataRequest.new(@session, 'https://example.com/data', 'key' => 'value')
81
+ data_request.stubs(:oauth_user_request).returns(response)
82
+
83
+ assert_raises(Garb::DataRequest::ClientError) do
84
+ data_request.send_request
85
+ end
86
+ end
87
+
88
+ should "be able to request via the ouath access token" do
89
+ access_token = stub(:get => "responseobject")
90
+ @session.stubs(:access_token).returns(access_token)
91
+
92
+ data_request = DataRequest.new(@session, 'https://example.com/data', 'key' => 'value')
93
+ assert_equal 'responseobject', data_request.oauth_user_request
94
+
95
+ assert_received(@session, :access_token)
96
+ assert_received(access_token, :get) {|e| e.with('https://example.com/data?key=value', {'GData-Version' => '2'})}
97
+ end
98
+
99
+ should "be able to request via http with an auth token" do
100
+ @session.expects(:auth_token).with().returns('toke')
101
+ response = mock
102
+
103
+ http = mock do |m|
104
+ m.expects(:use_ssl=).with(true)
105
+ m.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
106
+ m.expects(:get).with('/data?key=value', {
107
+ 'Authorization' => 'GoogleLogin auth=toke',
108
+ 'GData-Version' => '2'
109
+ }).returns(response)
110
+ end
111
+
112
+ Net::HTTP.expects(:new).with('example.com', 443).returns(http)
113
+
114
+ data_request = DataRequest.new(@session, 'https://example.com/data', 'key' => 'value')
115
+ assert_equal response, data_request.single_user_request
116
+ end
117
+ end
118
+
119
+ end
120
+ end
@@ -0,0 +1,28 @@
1
+ require 'test_helper'
2
+
3
+ module Garb
4
+ class DestinationTest < MiniTest::Unit::TestCase
5
+ context 'A Destination' do
6
+ should "have a match_type and an expression" do
7
+ destination = Destination.new({'matchType' => 'exact', 'expression' => '/contact.html', 'caseSensitive' => 'false'})
8
+ assert_equal 'exact', destination.match_type
9
+ assert_equal '/contact.html', destination.expression
10
+ end
11
+
12
+ should "know if it's case sensitive" do
13
+ destination = Destination.new({'matchType' => 'exact', 'expression' => '/contact.html', 'caseSensitive' => 'true'})
14
+ assert_equal true, destination.case_sensitive?
15
+ end
16
+
17
+ should "know if it's not case sensitive" do
18
+ destination = Destination.new({'matchType' => 'exact', 'expression' => '/contact.html', 'caseSensitive' => 'false'})
19
+ assert_equal false, destination.case_sensitive?
20
+ end
21
+
22
+ should "have steps" do
23
+ destination = Destination.new({'ga:step' => {'name' => 'Contact', 'number' => '1', 'path' => '/'}})
24
+ assert_equal ['Contact'], destination.steps.map(&:name)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,59 @@
1
+ require 'test_helper'
2
+
3
+ module Garb
4
+ class FilterParametersTest < MiniTest::Unit::TestCase
5
+ def self.should_define_operators(*operators)
6
+ operators.each do |operator|
7
+ should "create an operator and add to parameters for the #{operator} method" do
8
+ new_operator = stub
9
+ symbol = :foo
10
+
11
+ SymbolOperator.expects(:new).with(:bar, operator).returns(new_operator)
12
+ @filter_parameters.filters do
13
+ send(operator.to_sym, :bar, 100)
14
+ end
15
+
16
+ parameter = {new_operator => 100}
17
+ assert_equal parameter, @filter_parameters.parameters.last
18
+ end
19
+ end
20
+ end
21
+
22
+ context "A FilterParameters" do
23
+ setup do
24
+ @filter_parameters = FilterParameters.new
25
+ end
26
+
27
+ should_define_operators :eql, :not_eql, :gt, :gte, :lt, :lte,
28
+ :matches, :does_not_match, :contains, :does_not_contain, :substring, :not_substring
29
+
30
+ should "instance eval for filters" do
31
+ blk = lambda {"in a block"}
32
+
33
+ @filter_parameters.expects(:instance_eval)
34
+ @filter_parameters.filters(&blk)
35
+ end
36
+
37
+ context "when converting parameters hash into query string parameters" do
38
+ should "parameterize hash operators and join elements with AND" do
39
+ @filter_parameters.filters do
40
+ eql(:city, 'New York City')
41
+ eql(:state, 'New York')
42
+ end
43
+
44
+ params = ['ga:city%3D%3DNew+York+City', 'ga:state%3D%3DNew+York']
45
+ assert_equal params, @filter_parameters.to_params['filters'].split('%3B').sort
46
+ end
47
+
48
+ should "properly encode operators" do
49
+ @filter_parameters.filters do
50
+ contains(:page_path, 'New York')
51
+ end
52
+
53
+ params = {'filters' => 'ga:pagePath%3D~New+York'}
54
+ assert_equal params, @filter_parameters.to_params
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end