garb 0.9.0 → 0.9.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.
data/README.md CHANGED
@@ -48,7 +48,7 @@ Accounts, WebProperties, Profiles, and Goals
48
48
  Profiles for a UA- Number (a WebProperty)
49
49
  --------
50
50
 
51
- > profile = profile = Garb::Management::Profile.all.detect {|p| p.web_property_id == 'UA-XXXXXXX-X'}
51
+ > profile = Garb::Management::Profile.all.detect {|p| p.web_property_id == 'UA-XXXXXXX-X'}
52
52
 
53
53
  Define a Report Class
54
54
  ---------------------
@@ -82,14 +82,13 @@ Other Parameters
82
82
  Metrics & Dimensions
83
83
  --------------------
84
84
 
85
- **Metrics and Dimensions are very complex because of the ways in which the can and cannot be combined.**
85
+ **Metrics and Dimensions are very complex because of the ways in which they can and cannot be combined.**
86
86
 
87
87
  I suggest reading the google documentation to familiarize yourself with this.
88
88
 
89
89
  http://code.google.com/apis/analytics/docs/gdata/gdataReferenceDimensionsMetrics.html#bounceRate
90
90
 
91
- When you've returned, you can pass the appropriate combinations (up to 50 metrics and 2 dimenstions)
92
- to garb, as an array, of symbols. Or you can simply push a symbol into the array.
91
+ When you've returned, you can pass the appropriate combinations to Garb, as symbols.
93
92
 
94
93
  Filtering
95
94
  ---------
@@ -98,11 +97,8 @@ Filtering
98
97
 
99
98
  http://code.google.com/apis/analytics/docs/gdata/gdataReference.html#filtering
100
99
 
101
- We handle filtering as an array of hashes that you can push into,
102
- which will be joined together (AND'd)
103
-
104
100
  Here is what we can do currently:
105
- (the operator is a method on a symbol metric or dimension)
101
+ (the operator is a method on a symbol for the appropriate metric or dimension)
106
102
 
107
103
  Operators on metrics:
108
104
 
@@ -122,7 +118,7 @@ Filtering
122
118
  substring => '=@',
123
119
  not_substring => '!@'
124
120
 
125
- Given the previous Exits example report, we can add an options for filter:
121
+ Given the previous Exits example report in shorthand, we can add an option for filter:
126
122
 
127
123
  profile.exits(:filters => {:page_path.eql => '/extend/effectively-using-git-with-subversion/')
128
124
 
@@ -25,6 +25,7 @@ require 'garb/profile'
25
25
  require 'garb/account'
26
26
  require 'garb/filter_parameters'
27
27
  require 'garb/report_parameter'
28
+ require 'garb/result_set'
28
29
  require 'garb/report_response'
29
30
  require 'garb/resource'
30
31
  require 'garb/report'
@@ -31,7 +31,8 @@ module Garb
31
31
  value = self.parameters.map do |param|
32
32
  param.map do |k,v|
33
33
  next unless k.is_a?(SymbolOperator)
34
- "#{URI.encode(k.to_google_analytics, /[=<>]/)}#{CGI::escape(v.to_s)}"
34
+ escaped_v = v.to_s.gsub(/([,;\\])/) {|c| '\\'+c}
35
+ "#{URI.encode(k.to_google_analytics, /[=<>]/)}#{CGI::escape(escaped_v)}"
35
36
  end.join('%3B') # Hash AND (no duplicate keys), escape char for ';' fixes oauth
36
37
  end.join(',') # Array OR
37
38
 
@@ -15,7 +15,7 @@ module Garb
15
15
 
16
16
  def entries
17
17
  # possible to have nil entries, yuck
18
- parsed_response ? Array(parsed_response['feed']['entry']).flatten.compact : []
18
+ parsed_response ? [parsed_response['feed']['entry']].flatten.compact : []
19
19
  end
20
20
 
21
21
  def response
@@ -26,7 +26,9 @@ module Garb
26
26
  end
27
27
 
28
28
  def results(profile, options = {})
29
- default_params = build_default_params(profile)
29
+ start_date = options.fetch(:start_date, Time.now - MONTH)
30
+ end_date = options.fetch(:end_date, Time.now)
31
+ default_params = build_default_params(profile, start_date, end_date)
30
32
 
31
33
  param_set = [
32
34
  default_params,
@@ -70,11 +72,11 @@ module Garb
70
72
  sort
71
73
  end
72
74
 
73
- def build_default_params(profile)
75
+ def build_default_params(profile, start_date, end_date)
74
76
  {
75
77
  'ids' => Garb.to_ga(profile.id),
76
- 'start-date' => format_time(Time.now - Model::MONTH),
77
- 'end-date' => format_time(Time.now)
78
+ 'start-date' => format_time(start_date),
79
+ 'end-date' => format_time(end_date)
78
80
  }
79
81
  end
80
82
 
@@ -8,7 +8,16 @@ module Garb
8
8
  end
9
9
 
10
10
  def results
11
- @results ||= parse
11
+ if @results.nil?
12
+ @results = ResultSet.new(parse)
13
+ @results.total_results = parse_total_results
14
+ @results.sampled = parse_sampled_flag
15
+ end
16
+
17
+ @results
18
+ end
19
+
20
+ def sampled?
12
21
  end
13
22
 
14
23
  private
@@ -21,8 +30,23 @@ module Garb
21
30
  end
22
31
 
23
32
  def entries
24
- entry_hash = Crack::XML.parse(@xml)
25
- entry_hash ? [entry_hash['feed']['entry']].flatten.compact : []
33
+ feed? ? [parsed_xml['feed']['entry']].flatten.compact : []
34
+ end
35
+
36
+ def parse_total_results
37
+ feed? ? parsed_xml['feed']['openSearch:totalResults'].to_i : 0
38
+ end
39
+
40
+ def parse_sampled_flag
41
+ feed? ? (parsed_xml['feed']['dxp:containsSampledData'] == 'true') : false
42
+ end
43
+
44
+ def parsed_xml
45
+ @parsed_xml ||= Crack::XML.parse(@xml)
46
+ end
47
+
48
+ def feed?
49
+ !parsed_xml['feed'].nil?
26
50
  end
27
51
 
28
52
  def values_for(entry)
@@ -0,0 +1,21 @@
1
+ module Garb
2
+ class ResultSet
3
+ include Enumerable
4
+
5
+ attr_accessor :total_results, :sampled
6
+
7
+ alias :sampled? :sampled
8
+
9
+ def initialize(results)
10
+ @results = results
11
+ end
12
+
13
+ def each(&block)
14
+ @results.each(&block)
15
+ end
16
+
17
+ def to_a
18
+ @results
19
+ end
20
+ end
21
+ end
@@ -3,7 +3,7 @@ module Garb
3
3
 
4
4
  MAJOR = 0
5
5
  MINOR = 9
6
- TINY = 0
6
+ TINY = 1
7
7
 
8
8
  def self.to_s # :nodoc:
9
9
  [MAJOR, MINOR, TINY].join('.')
@@ -14,6 +14,8 @@
14
14
  </author>
15
15
  <openSearch:startIndex>3</openSearch:startIndex>
16
16
  <openSearch:itemsPerPage>4</openSearch:itemsPerPage>
17
+ <openSearch:totalResults>18</openSearch:totalResults>
18
+ <dxp:containsSampledData>true</dxp:containsSampledData>
17
19
  <ga:webPropertyID>UA-983247-67</ga:webPropertyID>
18
20
  <ga:start-date>2008-01-01</ga:start-date>
19
21
  <ga:end-date>2008-01-02</ga:end-date>
@@ -21,7 +21,7 @@
21
21
  <img src="./assets/0.3.9/loading.gif" alt="loading"/>
22
22
  </div>
23
23
  <div id="wrapper" style="display:none;">
24
- <div class="timestamp">Generated <abbr class="timeago" title="2010-12-09T15:19:41-05:00">2010-12-09T15:19:41-05:00</abbr></div>
24
+ <div class="timestamp">Generated <abbr class="timeago" title="2011-01-08T20:21:01-05:00">2011-01-08T20:21:01-05:00</abbr></div>
25
25
  <ul class="group_tabs"></ul>
26
26
 
27
27
  <div id="content">
@@ -2,4 +2,4 @@
2
2
  Unit Tests:
3
3
  :original_result: {}
4
4
 
5
- :created_at: 2010-12-09 15:19:41.584017 -05:00
5
+ :created_at: 2011-01-08 20:21:01.493065 -05:00
@@ -53,6 +53,15 @@ module Garb
53
53
  params = {'filters' => 'ga:pagePath%3D~New+York'}
54
54
  assert_equal params, @filter_parameters.to_params
55
55
  end
56
+
57
+ should "escape comma, semicolon, and backslash in values" do
58
+ @filter_parameters.filters do
59
+ eql(:url, 'this;that,thing\other')
60
+ end
61
+
62
+ params = {'filters' => 'ga:url%3D%3Dthis%5C%3Bthat%5C%2Cthing%5C%5Cother'}
63
+ assert_equal params, @filter_parameters.to_params
64
+ end
56
65
  end
57
66
  end
58
67
  end
@@ -21,7 +21,7 @@
21
21
  <img src="./assets/0.3.9/loading.gif" alt="loading"/>
22
22
  </div>
23
23
  <div id="wrapper" style="display:none;">
24
- <div class="timestamp">Generated <abbr class="timeago" title="2010-12-09T17:47:44-05:00">2010-12-09T17:47:44-05:00</abbr></div>
24
+ <div class="timestamp">Generated <abbr class="timeago" title="2011-01-08T19:38:09-05:00">2011-01-08T19:38:09-05:00</abbr></div>
25
25
  <ul class="group_tabs"></ul>
26
26
 
27
27
  <div id="content">
@@ -2,4 +2,4 @@
2
2
  Unit Tests:
3
3
  :original_result: {}
4
4
 
5
- :created_at: 2010-12-09 17:47:44.425037 -05:00
5
+ :created_at: 2011-01-08 19:38:09.152093 -05:00
@@ -23,6 +23,11 @@ module Garb
23
23
  assert_equal ['entry1', 'entry2'], @feed.entries
24
24
  end
25
25
 
26
+ should "handle case of a single entry" do
27
+ @feed.stubs(:parsed_response).returns({'feed' => {'entry' => {'profile_id' => '12345'}}})
28
+ assert_equal [{'profile_id' => '12345'}], @feed.entries
29
+ end
30
+
26
31
  should "have an empty array for entries without a response" do
27
32
  @feed.stubs(:parsed_response).returns(nil)
28
33
  assert_equal [], @feed.entries
@@ -64,7 +64,7 @@ module Garb
64
64
  @test_model.stubs(:dimensions).returns(stub(:to_params => {'dimensions' => 'ga:pagePath'}))
65
65
 
66
66
  now = Time.now
67
- Time.stubs(:new).returns(now)
67
+ Time.stubs(:now).returns(now)
68
68
 
69
69
  # p @profile.id
70
70
 
@@ -115,7 +115,13 @@ module Garb
115
115
  assert_data_params(@params.merge({'start-index' => 10}))
116
116
  end
117
117
 
118
- # should "be able to shift the date range"
118
+ should "be able to shift the date range" do
119
+ start_date = (Time.now - 1296000)
120
+ end_date = Time.now
121
+
122
+ assert_equal ['result'], @test_model.results(@profile, :start_date => start_date, :end_date => end_date)
123
+ assert_data_params(@params.merge({'start-date' => start_date.strftime('%Y-%m-%d'), 'end-date' => end_date.strftime('%Y-%m-%d')}))
124
+ end
119
125
 
120
126
  should "return a set of results in the defined class" do
121
127
  @test_model.stubs(:instance_klass).returns(ResultKlass)
@@ -125,7 +131,6 @@ module Garb
125
131
  end
126
132
  end
127
133
 
128
- # should "have a block syntax for filtering results"
129
134
  # should "return results as an array of the class it belongs to, if that class is an ActiveRecord descendant"
130
135
  # should "return results as an array of the class it belongs to, if that class is a DataMapper descendant"
131
136
  # should "return results as an array of the class it belongs to, if that class is a MongoMapper descendant"
@@ -7,30 +7,40 @@ module Garb
7
7
  context "A ReportResponse" do
8
8
  context "with a report feed" do
9
9
  setup do
10
- @file = File.read(File.join(File.dirname(__FILE__), '..', '..', "/fixtures/report_feed.xml"))
10
+ @xml = File.read(File.join(File.dirname(__FILE__), '..', '..', "/fixtures/report_feed.xml"))
11
11
  end
12
12
 
13
13
  should "parse results from atom xml" do
14
- response = ReportResponse.new(@file)
14
+ response = ReportResponse.new(@xml)
15
15
  assert_equal ['33', '2', '1'], response.results.map(&:pageviews)
16
16
  end
17
17
 
18
18
  should "default to returning an array of OpenStruct objects" do
19
- response = ReportResponse.new(@file)
19
+ response = ReportResponse.new(@xml)
20
20
  assert_equal [OpenStruct, OpenStruct, OpenStruct], response.results.map(&:class)
21
21
  end
22
22
 
23
23
  should "return an array of instances of a specified class" do
24
- response = ReportResponse.new(@file, SpecialKlass)
24
+ response = ReportResponse.new(@xml, SpecialKlass)
25
25
  assert_equal [SpecialKlass, SpecialKlass, SpecialKlass], response.results.map(&:class)
26
26
  end
27
+
28
+ should "know the total number of results" do
29
+ response = ReportResponse.new(@xml)
30
+ assert_equal 18, response.results.total_results
31
+ end
32
+
33
+ should "know if the data has been sampled" do
34
+ response = ReportResponse.new(@xml)
35
+ assert_equal true, response.results.sampled?
36
+ end
27
37
  end
28
38
 
29
39
  should "return an empty array if there are no results" do
30
40
  response = ReportResponse.new("result xml")
31
41
  Crack::XML.stubs(:parse).with("result xml").returns({'feed' => {'entry' => nil}})
32
42
 
33
- assert_equal [], response.results
43
+ assert_equal [], response.results.to_a
34
44
  end
35
45
  end
36
46
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 9
8
- - 0
9
- version: 0.9.0
8
+ - 1
9
+ version: 0.9.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Tony Pitale
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-12-10 00:00:00 -05:00
17
+ date: 2011-01-08 00:00:00 -05:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -84,6 +84,7 @@ files:
84
84
  - lib/garb/reports/visits.rb
85
85
  - lib/garb/reports.rb
86
86
  - lib/garb/resource.rb
87
+ - lib/garb/result_set.rb
87
88
  - lib/garb/session.rb
88
89
  - lib/garb/step.rb
89
90
  - lib/garb/version.rb