garb 0.9.1 → 0.9.7

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 (175) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.travis.yml +7 -0
  4. data/CHANGELOG.md +95 -0
  5. data/Gemfile +15 -0
  6. data/README.md +47 -25
  7. data/Rakefile +8 -34
  8. data/garb.gemspec +22 -0
  9. data/lib/garb.rb +71 -48
  10. data/lib/garb/attributes.rb +35 -0
  11. data/lib/garb/core_ext/array.rb +13 -0
  12. data/lib/garb/core_ext/string.rb +48 -0
  13. data/lib/garb/core_ext/symbol.rb +59 -0
  14. data/lib/garb/destination.rb +5 -6
  15. data/lib/garb/errors.rb +20 -0
  16. data/lib/garb/filter_parameters.rb +20 -29
  17. data/lib/garb/management/account.rb +10 -16
  18. data/lib/garb/management/feed.rb +4 -5
  19. data/lib/garb/management/goal.rb +10 -15
  20. data/lib/garb/management/profile.rb +11 -20
  21. data/lib/garb/management/segment.rb +9 -15
  22. data/lib/garb/management/web_property.rb +11 -16
  23. data/lib/garb/model.rb +37 -13
  24. data/lib/garb/path_attribute.rb +7 -0
  25. data/lib/garb/report_parameter.rb +5 -5
  26. data/lib/garb/report_response.rb +29 -28
  27. data/lib/garb/reports/bounces.rb +1 -1
  28. data/lib/garb/reports/exits.rb +1 -1
  29. data/lib/garb/reports/pageviews.rb +1 -1
  30. data/lib/garb/reports/unique_pageviews.rb +1 -1
  31. data/lib/garb/reports/visits.rb +1 -1
  32. data/lib/garb/request/authentication.rb +54 -0
  33. data/lib/garb/request/data.rb +98 -0
  34. data/lib/garb/result_set.rb +23 -9
  35. data/lib/garb/session.rb +3 -3
  36. data/lib/garb/step.rb +4 -4
  37. data/lib/garb/support.rb +6 -0
  38. data/lib/garb/version.rb +3 -3
  39. data/test/fixtures/account_management.json +1 -0
  40. data/test/fixtures/ga_profile_management.json +1 -0
  41. data/test/fixtures/ga_segment_management.json +1 -0
  42. data/test/fixtures/ga_webproperty_management.json +1 -0
  43. data/test/fixtures/goal_management.json +1 -0
  44. data/test/fixtures/profile_feed.json +1 -0
  45. data/test/fixtures/report_feed.json +75 -0
  46. data/test/test_helper.rb +6 -10
  47. data/test/unit/garb/filter_parameters_test.rb +15 -46
  48. data/test/unit/garb/management/account_test.rb +8 -16
  49. data/test/unit/garb/management/feed_test.rb +5 -10
  50. data/test/unit/garb/management/goal_test.rb +7 -21
  51. data/test/unit/garb/management/profile_test.rb +11 -26
  52. data/test/unit/garb/management/segment_test.rb +7 -6
  53. data/test/unit/garb/management/web_property_test.rb +10 -12
  54. data/test/unit/garb/model_test.rb +70 -32
  55. data/test/unit/garb/report_parameter_test.rb +6 -6
  56. data/test/unit/garb/report_response_test.rb +23 -13
  57. data/test/unit/garb/request/authentication_test.rb +152 -0
  58. data/test/unit/garb/request/data_test.rb +157 -0
  59. data/test/unit/garb/session_test.rb +13 -13
  60. data/test/unit/garb_test.rb +4 -11
  61. data/test/unit/symbol_operator_test.rb +3 -3
  62. metadata +90 -292
  63. data/lib/garb/account.rb +0 -22
  64. data/lib/garb/account_feed_request.rb +0 -25
  65. data/lib/garb/authentication_request.rb +0 -53
  66. data/lib/garb/data_request.rb +0 -42
  67. data/lib/garb/goal.rb +0 -20
  68. data/lib/garb/profile.rb +0 -33
  69. data/lib/garb/report.rb +0 -28
  70. data/lib/garb/resource.rb +0 -115
  71. data/lib/support.rb +0 -40
  72. data/test/unit/garb/account_feed_request_test.rb +0 -42
  73. data/test/unit/garb/account_test.rb +0 -53
  74. data/test/unit/garb/authentication_request_test.rb +0 -121
  75. data/test/unit/garb/coverage/assets/0.3.9/app.js +0 -66
  76. data/test/unit/garb/coverage/assets/0.3.9/fancybox/blank.gif +0 -0
  77. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancy_close.png +0 -0
  78. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancy_loading.png +0 -0
  79. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancy_nav_left.png +0 -0
  80. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancy_nav_right.png +0 -0
  81. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancy_shadow_e.png +0 -0
  82. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancy_shadow_n.png +0 -0
  83. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancy_shadow_ne.png +0 -0
  84. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancy_shadow_nw.png +0 -0
  85. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancy_shadow_s.png +0 -0
  86. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancy_shadow_se.png +0 -0
  87. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancy_shadow_sw.png +0 -0
  88. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancy_shadow_w.png +0 -0
  89. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancy_title_left.png +0 -0
  90. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancy_title_main.png +0 -0
  91. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancy_title_over.png +0 -0
  92. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancy_title_right.png +0 -0
  93. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancybox-x.png +0 -0
  94. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancybox-y.png +0 -0
  95. data/test/unit/garb/coverage/assets/0.3.9/fancybox/fancybox.png +0 -0
  96. data/test/unit/garb/coverage/assets/0.3.9/fancybox/jquery.fancybox-1.3.1.css +0 -363
  97. data/test/unit/garb/coverage/assets/0.3.9/fancybox/jquery.fancybox-1.3.1.pack.js +0 -44
  98. data/test/unit/garb/coverage/assets/0.3.9/favicon.png +0 -0
  99. data/test/unit/garb/coverage/assets/0.3.9/jquery-1.4.2.min.js +0 -155
  100. data/test/unit/garb/coverage/assets/0.3.9/jquery.dataTables.min.js +0 -152
  101. data/test/unit/garb/coverage/assets/0.3.9/jquery.timeago.js +0 -141
  102. data/test/unit/garb/coverage/assets/0.3.9/jquery.url.js +0 -174
  103. data/test/unit/garb/coverage/assets/0.3.9/loading.gif +0 -0
  104. data/test/unit/garb/coverage/assets/0.3.9/magnify.png +0 -0
  105. data/test/unit/garb/coverage/assets/0.3.9/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  106. data/test/unit/garb/coverage/assets/0.3.9/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  107. data/test/unit/garb/coverage/assets/0.3.9/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  108. data/test/unit/garb/coverage/assets/0.3.9/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  109. data/test/unit/garb/coverage/assets/0.3.9/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  110. data/test/unit/garb/coverage/assets/0.3.9/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  111. data/test/unit/garb/coverage/assets/0.3.9/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  112. data/test/unit/garb/coverage/assets/0.3.9/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  113. data/test/unit/garb/coverage/assets/0.3.9/smoothness/images/ui-icons_222222_256x240.png +0 -0
  114. data/test/unit/garb/coverage/assets/0.3.9/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  115. data/test/unit/garb/coverage/assets/0.3.9/smoothness/images/ui-icons_454545_256x240.png +0 -0
  116. data/test/unit/garb/coverage/assets/0.3.9/smoothness/images/ui-icons_888888_256x240.png +0 -0
  117. data/test/unit/garb/coverage/assets/0.3.9/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  118. data/test/unit/garb/coverage/assets/0.3.9/smoothness/jquery-ui-1.8.4.custom.css +0 -295
  119. data/test/unit/garb/coverage/assets/0.3.9/stylesheet.css +0 -341
  120. data/test/unit/garb/coverage/index.html +0 -100
  121. data/test/unit/garb/coverage/resultset.yml +0 -5
  122. data/test/unit/garb/data_request_test.rb +0 -107
  123. data/test/unit/garb/destination_test.rb +0 -28
  124. data/test/unit/garb/goal_test.rb +0 -24
  125. data/test/unit/garb/management/coverage/assets/0.3.9/app.js +0 -66
  126. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/blank.gif +0 -0
  127. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancy_close.png +0 -0
  128. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancy_loading.png +0 -0
  129. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancy_nav_left.png +0 -0
  130. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancy_nav_right.png +0 -0
  131. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancy_shadow_e.png +0 -0
  132. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancy_shadow_n.png +0 -0
  133. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancy_shadow_ne.png +0 -0
  134. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancy_shadow_nw.png +0 -0
  135. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancy_shadow_s.png +0 -0
  136. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancy_shadow_se.png +0 -0
  137. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancy_shadow_sw.png +0 -0
  138. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancy_shadow_w.png +0 -0
  139. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancy_title_left.png +0 -0
  140. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancy_title_main.png +0 -0
  141. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancy_title_over.png +0 -0
  142. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancy_title_right.png +0 -0
  143. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancybox-x.png +0 -0
  144. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancybox-y.png +0 -0
  145. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/fancybox.png +0 -0
  146. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/jquery.fancybox-1.3.1.css +0 -363
  147. data/test/unit/garb/management/coverage/assets/0.3.9/fancybox/jquery.fancybox-1.3.1.pack.js +0 -44
  148. data/test/unit/garb/management/coverage/assets/0.3.9/favicon.png +0 -0
  149. data/test/unit/garb/management/coverage/assets/0.3.9/jquery-1.4.2.min.js +0 -155
  150. data/test/unit/garb/management/coverage/assets/0.3.9/jquery.dataTables.min.js +0 -152
  151. data/test/unit/garb/management/coverage/assets/0.3.9/jquery.timeago.js +0 -141
  152. data/test/unit/garb/management/coverage/assets/0.3.9/jquery.url.js +0 -174
  153. data/test/unit/garb/management/coverage/assets/0.3.9/loading.gif +0 -0
  154. data/test/unit/garb/management/coverage/assets/0.3.9/magnify.png +0 -0
  155. data/test/unit/garb/management/coverage/assets/0.3.9/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  156. data/test/unit/garb/management/coverage/assets/0.3.9/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  157. data/test/unit/garb/management/coverage/assets/0.3.9/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  158. data/test/unit/garb/management/coverage/assets/0.3.9/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  159. data/test/unit/garb/management/coverage/assets/0.3.9/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  160. data/test/unit/garb/management/coverage/assets/0.3.9/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  161. data/test/unit/garb/management/coverage/assets/0.3.9/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  162. data/test/unit/garb/management/coverage/assets/0.3.9/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  163. data/test/unit/garb/management/coverage/assets/0.3.9/smoothness/images/ui-icons_222222_256x240.png +0 -0
  164. data/test/unit/garb/management/coverage/assets/0.3.9/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  165. data/test/unit/garb/management/coverage/assets/0.3.9/smoothness/images/ui-icons_454545_256x240.png +0 -0
  166. data/test/unit/garb/management/coverage/assets/0.3.9/smoothness/images/ui-icons_888888_256x240.png +0 -0
  167. data/test/unit/garb/management/coverage/assets/0.3.9/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  168. data/test/unit/garb/management/coverage/assets/0.3.9/smoothness/jquery-ui-1.8.4.custom.css +0 -295
  169. data/test/unit/garb/management/coverage/assets/0.3.9/stylesheet.css +0 -341
  170. data/test/unit/garb/management/coverage/index.html +0 -100
  171. data/test/unit/garb/management/coverage/resultset.yml +0 -5
  172. data/test/unit/garb/profile_test.rb +0 -77
  173. data/test/unit/garb/report_test.rb +0 -99
  174. data/test/unit/garb/resource_test.rb +0 -50
  175. data/test/unit/garb/step_test.rb +0 -15
@@ -1,29 +1,24 @@
1
1
  module Garb
2
2
  module Management
3
3
  class WebProperty
4
- attr_accessor :session, :path
5
- attr_accessor :id, :account_id
4
+ extend Attributes
5
+ include PathAttribute
6
6
 
7
- def self.all(session = Session, path='/accounts/~all/webproperties')
7
+ attr_reader :session, :entry
8
+ ga_attribute :id, :name, :account_id, :website_url
9
+
10
+ def self.all(session = Session, path = '/accounts/~all/webproperties')
8
11
  feed = Feed.new(session, path)
9
- feed.entries.map {|entry| new_from_entry(entry, session)}
12
+ feed.entries.map { |entry| new(entry, session) }
10
13
  end
11
14
 
12
15
  def self.for_account(account)
13
- all(account.session, account.path+'/webproperties')
14
- end
15
-
16
- def self.new_from_entry(entry, session)
17
- web_property = new
18
- web_property.session = session
19
- web_property.path = Garb.parse_link(entry, "self").gsub(Feed::BASE_URL, '')
20
- web_property.properties = Garb.parse_properties(entry)
21
- web_property
16
+ all(account.session, account.path + '/webproperties')
22
17
  end
23
18
 
24
- def properties=(properties)
25
- self.id = properties["web_property_id"]
26
- self.account_id = properties["account_id"]
19
+ def initialize(entry, session)
20
+ @entry = entry
21
+ @session = session
27
22
  end
28
23
 
29
24
  def profiles
@@ -1,10 +1,11 @@
1
1
  module Garb
2
2
  module Model
3
3
  MONTH = 2592000
4
- URL = "https://www.google.com/analytics/feeds/data"
4
+ URL = 'https://www.googleapis.com/analytics/v3/data/ga'
5
5
 
6
6
  def self.extended(base)
7
7
  ProfileReports.add_report_method(base)
8
+ # base.set_instance_klass(base)
8
9
  end
9
10
 
10
11
  def metrics(*fields)
@@ -26,8 +27,10 @@ module Garb
26
27
  end
27
28
 
28
29
  def results(profile, options = {})
29
- start_date = options.fetch(:start_date, Time.now - MONTH)
30
- end_date = options.fetch(:end_date, Time.now)
30
+ return all_results(profile, options) if options.delete(:all)
31
+
32
+ start_date = options[:start_date] || Time.now - MONTH
33
+ end_date = options[:end_date] || Time.now
31
34
  default_params = build_default_params(profile, start_date, end_date)
32
35
 
33
36
  param_set = [
@@ -39,31 +42,49 @@ module Garb
39
42
  parse_sort(options).to_params,
40
43
  build_page_params(options)
41
44
  ]
42
-
43
45
  data = send_request_for_data(profile, build_params(param_set))
44
46
  ReportResponse.new(data, instance_klass).results
45
47
  end
46
48
 
49
+ def all_results(profile, options = {})
50
+ options = options.dup
51
+ limit = options.delete(:limit)
52
+ options[:limit] = 10_000 # maximum allowed
53
+ results = nil
54
+ while ((rs = results(profile, options)) && !rs.empty?)
55
+ results = results ? results + rs : rs
56
+ options[:offset] = results.size + 1
57
+
58
+ break if limit && results.size >= limit
59
+ break if results.size >= results.total_results
60
+ end
61
+ limit && results ? results[0...limit] : results
62
+ end
63
+
47
64
  private
48
65
  def send_request_for_data(profile, params)
49
- request = DataRequest.new(profile.session, URL, params)
66
+ request = Request::Data.new(profile.session, URL, params)
50
67
  response = request.send_request
51
68
  response.body
52
69
  end
53
70
 
54
71
  def build_params(param_set)
55
- param_set.inject({}) {|p,i| p.merge(i)}.reject{|k,v| v.nil?}
72
+ param_set.inject({}) { |p,i| p.merge i }.reject { |k,v| v.nil? }
56
73
  end
57
74
 
58
75
  def parse_filters(options)
59
- filters = FilterParameters.new
60
- filters.parameters << options[:filters] if options.has_key?(:filters)
61
- filters
76
+ FilterParameters.new(options[:filters])
62
77
  end
63
78
 
64
79
  def parse_segment(options)
65
- segment_id = "gaid::#{options[:segment_id].to_i}" if options.has_key?(:segment_id)
66
- {'segment' => segment_id}
80
+ # dirty hack to support dynamic segments
81
+ if options.has_key?(:segment_id)
82
+ segment = "gaid::#{options[:segment_id]}"
83
+ elsif options.has_key?(:dynamic_segment)
84
+ filters = FilterParameters.new(options[:dynamic_segment])
85
+ segment = "dynamic::#{filters.to_params['filters']}"
86
+ end
87
+ {'segment' => segment}
67
88
  end
68
89
 
69
90
  def parse_sort(options)
@@ -81,11 +102,14 @@ module Garb
81
102
  end
82
103
 
83
104
  def build_page_params(options)
84
- {'max-results' => options[:limit], 'start-index' => options[:offset]}
105
+ params = {}
106
+ params['max-results'] = options[:limit] if options.has_key? :limit
107
+ params['start-index'] = options[:offset] if options.has_key? :offset
108
+ params
85
109
  end
86
110
 
87
111
  def format_time(t)
88
112
  t.strftime('%Y-%m-%d')
89
113
  end
90
114
  end
91
- end
115
+ end
@@ -0,0 +1,7 @@
1
+ module Garb
2
+ module PathAttribute
3
+ def path
4
+ @path ||= @entry['selfLink'].gsub(Management::Feed::BASE_URL, '')
5
+ end
6
+ end
7
+ end
@@ -2,23 +2,23 @@ module Garb
2
2
  class ReportParameter
3
3
 
4
4
  attr_reader :elements
5
-
5
+
6
6
  def initialize(name)
7
7
  @name = name
8
8
  @elements = []
9
9
  end
10
-
10
+
11
11
  def name
12
12
  @name.to_s
13
13
  end
14
-
14
+
15
15
  def <<(element)
16
16
  (@elements += [element].flatten).compact!
17
17
  self
18
18
  end
19
-
19
+
20
20
  def to_params
21
- value = self.elements.map{|param| Garb.to_google_analytics(param)}.join(',')
21
+ value = self.elements.map { |param| Garb.to_google_analytics(param) }.join(',')
22
22
  value.empty? ? {} : {self.name => value}
23
23
  end
24
24
  end
@@ -1,56 +1,57 @@
1
- module Garb
1
+ module Garb
2
2
  class ReportResponse
3
- KEYS = ['dxp:metric', 'dxp:dimension']
4
3
 
5
4
  def initialize(response_body, instance_klass = OpenStruct)
6
- @xml = response_body
5
+ @response_body = response_body
7
6
  @instance_klass = instance_klass
8
7
  end
9
8
 
10
9
  def results
11
10
  if @results.nil?
12
11
  @results = ResultSet.new(parse)
13
- @results.total_results = parse_total_results
14
- @results.sampled = parse_sampled_flag
12
+ @results.total_results = total_results
13
+ @results.sampled = sampled?
15
14
  end
16
-
17
15
  @results
18
16
  end
19
17
 
20
- def sampled?
21
- end
22
-
23
- private
24
- def parse
25
- entries.map do |entry|
26
- @instance_klass.new(Hash[
27
- values_for(entry).map {|v| [Garb.from_ga(v['name']), v['value']]}
28
- ])
29
- end
18
+ def total_results
19
+ data[:total_results]
30
20
  end
31
21
 
32
- def entries
33
- feed? ? [parsed_xml['feed']['entry']].flatten.compact : []
22
+ def sampled?
23
+ data[:contains_sampled_data]
34
24
  end
35
25
 
36
- def parse_total_results
37
- feed? ? parsed_xml['feed']['openSearch:totalResults'].to_i : 0
26
+ private
27
+ def keys
28
+ @keys ||= column_headers.map { |header| Garb.from_ga header['name'] }
38
29
  end
39
30
 
40
- def parse_sampled_flag
41
- feed? ? (parsed_xml['feed']['dxp:containsSampledData'] == 'true') : false
31
+ def parse
32
+ rows.map do |row|
33
+ @instance_klass.new(Hash[*keys.zip(row).flatten])
34
+ end
42
35
  end
43
36
 
44
- def parsed_xml
45
- @parsed_xml ||= Crack::XML.parse(@xml)
37
+ def column_headers
38
+ data[:column_headers] || []
46
39
  end
47
40
 
48
- def feed?
49
- !parsed_xml['feed'].nil?
41
+ def rows
42
+ data[:rows] || []
50
43
  end
51
44
 
52
- def values_for(entry)
53
- KEYS.map {|k| entry[k]}.flatten.compact
45
+ def data
46
+ unless @data
47
+ @data = MultiJson.load @response_body
48
+ @data = @data.inject({}) do |data, pair|
49
+ key, value = pair
50
+ data[key.underscore.to_sym] = value
51
+ data
52
+ end
53
+ end
54
+ @data
54
55
  end
55
56
  end
56
57
  end
@@ -1,5 +1,5 @@
1
1
  class Bounces
2
- extend Garb::Resource
2
+ extend Garb::Model
3
3
 
4
4
  metrics :bounces
5
5
  end
@@ -1,5 +1,5 @@
1
1
  class Exits
2
- extend Garb::Resource
2
+ extend Garb::Model
3
3
 
4
4
  metrics :exits
5
5
  end
@@ -1,5 +1,5 @@
1
1
  class Pageviews
2
- extend Garb::Resource
2
+ extend Garb::Model
3
3
 
4
4
  metrics :pageviews
5
5
  end
@@ -1,5 +1,5 @@
1
1
  class UniquePageviews
2
- extend Garb::Resource
2
+ extend Garb::Model
3
3
 
4
4
  metrics :unique_pageviews
5
5
  end
@@ -1,5 +1,5 @@
1
1
  class Visits
2
- extend Garb::Resource
2
+ extend Garb::Model
3
3
 
4
4
  metrics :visits
5
5
  end
@@ -0,0 +1,54 @@
1
+ module Garb
2
+ module Request
3
+ class Authentication
4
+ URL = 'https://www.google.com/accounts/ClientLogin'
5
+
6
+ def initialize(email, password, opts = {})
7
+ @email = email
8
+ @password = password
9
+ @account_type = opts[:account_type] || 'HOSTED_OR_GOOGLE'
10
+ end
11
+
12
+ def parameters
13
+ {
14
+ 'Email' => @email,
15
+ 'Passwd' => @password,
16
+ 'accountType' => @account_type,
17
+ 'service' => 'analytics',
18
+ 'source' => "sija-garb-v#{Garb::VERSION}"
19
+ }
20
+ end
21
+
22
+ def uri
23
+ @uri ||= URI.parse(URL)
24
+ end
25
+
26
+ def send_request(ssl_mode)
27
+ http = Net::HTTP.new(uri.host, uri.port, Garb.proxy_address, Garb.proxy_port)
28
+ http.open_timeout = Garb.open_timeout
29
+ http.read_timeout = Garb.read_timeout
30
+ http.use_ssl = true
31
+ http.verify_mode = ssl_mode
32
+
33
+ if ssl_mode == OpenSSL::SSL::VERIFY_PEER
34
+ http.ca_file = Garb.ca_cert_file
35
+ end
36
+
37
+ http.request(build_request) do |response|
38
+ raise AuthError unless response.is_a?(Net::HTTPOK)
39
+ end
40
+ end
41
+
42
+ def build_request
43
+ post = Net::HTTP::Post.new(uri.path)
44
+ post.set_form_data(parameters)
45
+ post
46
+ end
47
+
48
+ def auth_token(opts = {})
49
+ ssl_mode = opts[:secure] ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
50
+ send_request(ssl_mode).body.match(/^Auth=(.*)$/)[1]
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,98 @@
1
+ module Garb
2
+ module Request
3
+ class Data
4
+ def initialize(session, base_url, parameters = {})
5
+ @session, @base_url, @parameters = session, base_url, parameters
6
+ end
7
+
8
+ def parameters
9
+ @parameters ||= {}
10
+ @parameters['key'] ||= @session.api_key unless @session.api_key.nil?
11
+ @parameters
12
+ end
13
+
14
+ def query_string
15
+ params_list = parameters.map { |k, v| "#{k}=#{v}" }.join('&')
16
+ params_list.empty? ? '' : "?#{params_list}"
17
+ end
18
+
19
+ def uri
20
+ @uri ||= URI.parse(@base_url)
21
+ end
22
+
23
+ def absolute_uri
24
+ uri.to_s + query_string
25
+ end
26
+
27
+ def relative_uri
28
+ uri.path.to_s + query_string
29
+ end
30
+
31
+ def send_request
32
+ Garb.log "Garb::Request -> #{relative_uri}"
33
+
34
+ response = if @session.single_user?
35
+ if Garb.use_fibers
36
+ single_user_request_evented
37
+ else
38
+ single_user_request
39
+ end
40
+ elsif @session.oauth_user?
41
+ oauth_user_request
42
+ end
43
+
44
+ Garb.log "Garb::Response -> #{response.inspect}"
45
+ handle_response response
46
+ end
47
+
48
+ def handle_response(response)
49
+ unless response.kind_of?(Net::HTTPSuccess) || (response.respond_to?(:status) && response.status == 200)
50
+ body, parsed = response.body, MultiJson.load(response.body) rescue nil
51
+ if parsed and error = parsed['error']
52
+ klass = case error['code']
53
+ when 400 then BadRequestError
54
+ when 401 then InvalidCredentialsError
55
+ when 403 then InsufficientPermissionsError
56
+ when 503 then BackendError
57
+ else ClientError
58
+ end
59
+ raise klass.new(absolute_uri, error['message'], error['code'], error['errors'])
60
+ else
61
+ raise ClientError.new(absolute_uri, body)
62
+ end
63
+ end
64
+ response
65
+ end
66
+
67
+ def single_user_request_evented
68
+ f = Fiber.current
69
+ fiber = Fiber.new do
70
+ f.resume single_user_request
71
+ end
72
+ fiber.resume
73
+ Fiber.yield
74
+ end
75
+
76
+ def single_user_request
77
+ http = Net::HTTP.new(uri.host, uri.port, Garb.proxy_address, Garb.proxy_port)
78
+ http.open_timeout = Garb.open_timeout
79
+ http.read_timeout = Garb.read_timeout
80
+ http.use_ssl = true
81
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
82
+
83
+ unless @session.access_token.nil?
84
+ http.request_get(relative_uri, {'Authorization' => "Bearer #{@session.access_token.token}"})
85
+ else
86
+ http.request_get(relative_uri, {
87
+ 'Authorization' => "GoogleLogin auth=#{@session.auth_token}",
88
+ 'GData-Version' => '3'
89
+ })
90
+ end
91
+ end
92
+
93
+ def oauth_user_request
94
+ @session.access_token.get(absolute_uri, {'GData-Version' => '3'})
95
+ end
96
+ end
97
+ end
98
+ end