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
@@ -0,0 +1,35 @@
1
+ module Garb
2
+ module Attributes
3
+ def self.extended(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def ga_attribute(*keys)
9
+ hashes, symbols = keys.partition { |k| k.is_a? Hash }
10
+ symbols.each { |key| define_a_method_for key }
11
+ hashes.each { |hash| map_values_for hash }
12
+ end
13
+
14
+ private
15
+ def define_a_method_for(key, custom_key = nil)
16
+ custom_key ||= key.to_s.camelize(:lower)
17
+ define_method(key) do
18
+ var = "@#{key}"
19
+ if instance_variable_defined? var
20
+ instance_variable_get var
21
+ else
22
+ hash_key = (custom_key || key).to_s
23
+ instance_variable_set var, @entry[hash_key]
24
+ end
25
+ end #unless method_defined?(key)
26
+ end
27
+
28
+ def map_values_for(hash)
29
+ hash.each_pair do |key, value|
30
+ define_a_method_for key, value
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,13 @@
1
+ unless Object.const_defined?('ActiveSupport')
2
+ class Array
3
+ def self.wrap(object)
4
+ if object.nil?
5
+ []
6
+ elsif object.respond_to?(:to_ary)
7
+ object.to_ary
8
+ else
9
+ [object]
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,48 @@
1
+ # Pull in some AS String utilities (not loaded if AS is available)
2
+ unless Object.const_defined?('ActiveSupport')
3
+ class String
4
+ def camelize(first_letter = :upper)
5
+ case first_letter
6
+ when :upper then Garb::Inflector.camelize(self, true)
7
+ when :lower then Garb::Inflector.camelize(self, false)
8
+ end
9
+ end
10
+ alias_method :camelcase, :camelize
11
+
12
+ def underscore
13
+ Garb::Inflector.underscore(self)
14
+ end
15
+
16
+ def demodulize
17
+ Garb::Inflector.demodulize(self)
18
+ end
19
+ end
20
+
21
+ module Garb
22
+ module Inflector
23
+ extend self
24
+
25
+ def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
26
+ if first_letter_in_uppercase
27
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
28
+ else
29
+ lower_case_and_underscored_word.to_s[0].chr.downcase + camelize(lower_case_and_underscored_word)[1..-1]
30
+ end
31
+ end
32
+
33
+ def underscore(camel_cased_word)
34
+ word = camel_cased_word.to_s.dup
35
+ word.gsub!(/::/, '/')
36
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
37
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
38
+ word.tr!("-", "_")
39
+ word.downcase!
40
+ word
41
+ end
42
+
43
+ def demodulize(class_name_in_module)
44
+ class_name_in_module.to_s.gsub(/^.*::/, '')
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,59 @@
1
+ module SymbolOperatorMethods
2
+ OPERATORS = {
3
+ :eql => '==',
4
+ :not_eql => '!=',
5
+ :gt => '>',
6
+ :gte => '>=',
7
+ :lt => '<',
8
+ :lte => '<=',
9
+ :matches => '==',
10
+
11
+ :does_not_match => '!=',
12
+ :contains => '=~',
13
+ :does_not_contain => '!~',
14
+ :substring => '=@',
15
+ :not_substring => '!@',
16
+
17
+ :desc => '-',
18
+ :descending => '-'
19
+ }
20
+ SLUGS = OPERATORS.keys.freeze
21
+
22
+ def to_google_analytics
23
+ t = Garb.to_google_analytics @field || @target
24
+ o = OPERATORS[@operator]
25
+
26
+ [:desc, :descending].include?(@operator) ? "#{o}#{t}" : "#{t}#{o}"
27
+ end
28
+ end
29
+
30
+ class SymbolOperator
31
+ include SymbolOperatorMethods
32
+
33
+ def initialize(field, operator)
34
+ @field, @operator = field, operator
35
+ end unless method_defined? :initialize
36
+ end
37
+
38
+ symbol_slugs = if Object.const_defined?('DataMapper')
39
+ # make sure the class is defined
40
+ require 'dm-core/core_ext/symbol'
41
+
42
+ # add to_google_analytics to DM's Opeartor
43
+ class DataMapper::Query::Operator
44
+ include SymbolOperatorMethods
45
+ end
46
+
47
+ SymbolOperatorMethods::SLUGS - DataMapper::Query::Conditions::Comparison.slugs
48
+ else
49
+ SymbolOperatorMethods::SLUGS
50
+ end
51
+
52
+ # define the remaining symbol operators
53
+ symbol_slugs.each do |operator|
54
+ Symbol.class_eval <<-RUBY
55
+ def #{operator}
56
+ SymbolOperator.new self, :#{operator}
57
+ end unless method_defined? :#{operator}
58
+ RUBY
59
+ end
@@ -1,18 +1,17 @@
1
1
  module Garb
2
2
  class Destination
3
- attr_reader :match_type, :expression, :steps, :case_sensitive
3
+ attr_reader :match_type, :url, :steps, :case_sensitive
4
4
 
5
5
  alias :case_sensitive? :case_sensitive
6
6
 
7
7
  def initialize(attributes)
8
- return unless attributes.is_a?(Hash)
8
+ return unless attributes.is_a? Hash
9
9
 
10
10
  @match_type = attributes['matchType']
11
- @expression = attributes['expression']
12
- @case_sensitive = (attributes['caseSensitive'] == 'true')
11
+ @url = attributes['url'] # TODO was @expression
12
+ @case_sensitive = attributes['caseSensitive']
13
13
 
14
- step_attributes = attributes[Garb.to_ga('step')]
15
- @steps = Array(step_attributes.is_a?(Hash) ? [step_attributes] : step_attributes).map {|s| Step.new(s)}
14
+ @steps = attributes['steps'] && attributes['steps'].map { |s| Step.new(s) }
16
15
  end
17
16
  end
18
17
  end
@@ -0,0 +1,20 @@
1
+ module Garb
2
+ class Error < StandardError; end
3
+ class MissingCertFileError < Error; end
4
+ class AuthError < Error; end
5
+ class ClientError < Error
6
+ attr_reader :code, :message, :errors, :uri
7
+
8
+ def initialize(uri, message, code = nil, errors = [])
9
+ @code, @message, @errors, @uri = code, message, errors, uri
10
+ end
11
+
12
+ def to_s
13
+ "#{code ? "[#{code}] #{message}" : message} : #{uri}"
14
+ end
15
+ end
16
+ class BadRequestError < ClientError; end
17
+ class InvalidCredentialsError < ClientError; end
18
+ class InsufficientPermissionsError < ClientError; end
19
+ class BackendError < ClientError; end
20
+ end
@@ -1,42 +1,33 @@
1
1
  module Garb
2
2
  class FilterParameters
3
- def self.define_operators(*methods)
4
- methods.each do |method|
5
- class_eval <<-CODE
6
- def #{method}(field, value)
7
- @filter_hash.merge!({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
3
  attr_accessor :parameters
17
4
 
18
- def initialize
19
- self.parameters = []
5
+ def initialize(parameters)
6
+ self.parameters = (Array.wrap(parameters) || []).compact
20
7
  end
21
8
 
22
- def filters(&block)
23
- @filter_hash = {}
24
-
25
- instance_eval &block
26
-
27
- self.parameters << @filter_hash
9
+ def to_params
10
+ value = array_to_params(self.parameters)
11
+ value.empty? ? {} : {'filters' => value}
28
12
  end
29
13
 
30
- def to_params
31
- value = self.parameters.map do |param|
32
- param.map do |k,v|
33
- next unless k.is_a?(SymbolOperator)
34
- escaped_v = v.to_s.gsub(/([,;\\])/) {|c| '\\'+c}
35
- "#{URI.encode(k.to_google_analytics, /[=<>]/)}#{CGI::escape(escaped_v)}"
36
- end.join('%3B') # Hash AND (no duplicate keys), escape char for ';' fixes oauth
14
+ private
15
+ def array_to_params(arr)
16
+ arr.map do |param|
17
+ case param
18
+ when Hash then hash_to_params(param)
19
+ when Array then array_to_params(param)
20
+ end
37
21
  end.join(',') # Array OR
22
+ end
38
23
 
39
- value.empty? ? {} : {'filters' => value}
24
+ def hash_to_params(hsh)
25
+ hsh.map do |k, v|
26
+ next unless k.is_a?(SymbolOperatorMethods)
27
+
28
+ escaped_v = v.to_s.gsub(/([,;])/) { |c| '\\' + c }
29
+ "#{URI.encode(k.to_google_analytics, /[=<>]/)}#{CGI::escape(escaped_v)}"
30
+ end.join('%3B') # Hash AND (no duplicate keys), escape char for ';' fixes oauth
40
31
  end
41
32
  end
42
33
  end
@@ -1,26 +1,20 @@
1
1
  module Garb
2
2
  module Management
3
3
  class Account
4
- attr_accessor :session, :path
5
- attr_accessor :id, :title, :name
4
+ extend Garb::Attributes
5
+ include PathAttribute
6
6
 
7
- def self.all(session = Session)
8
- feed = Feed.new(session, '/accounts')
9
- feed.entries.map {|entry| new_from_entry(entry, session)}
10
- end
7
+ attr_reader :session
8
+ ga_attribute :id, :name
11
9
 
12
- def self.new_from_entry(entry, session)
13
- account = new
14
- account.session = session
15
- account.path = Garb.parse_link(entry, "self").gsub(Feed::BASE_URL, '')
16
- account.title = entry['title'].gsub('Google Analytics Account ', '') # can we get this in properties=?
17
- account.properties = Garb.parse_properties(entry)
18
- account
10
+ def initialize(entry, session)
11
+ @entry = entry
12
+ @session = session
19
13
  end
20
14
 
21
- def properties=(properties)
22
- self.id = properties["account_id"]
23
- self.name = properties["account_name"]
15
+ def self.all(session = Session)
16
+ feed = Feed.new(session, '/accounts')
17
+ feed.entries.map { |entry| new(entry, session) }
24
18
  end
25
19
 
26
20
  def web_properties
@@ -1,21 +1,20 @@
1
1
  module Garb
2
2
  module Management
3
3
  class Feed
4
- BASE_URL = "https://www.google.com/analytics/feeds/datasources/ga"
4
+ BASE_URL = 'https://www.googleapis.com/analytics/v3/management'
5
5
 
6
6
  attr_reader :request
7
7
 
8
8
  def initialize(session, path)
9
- @request = DataRequest.new(session, BASE_URL+path)
9
+ @request = Request::Data.new(session, BASE_URL + path)
10
10
  end
11
11
 
12
12
  def parsed_response
13
- @parsed_response ||= Crack::XML.parse(response.body)
13
+ @parsed_response ||= MultiJson.load(response.body)
14
14
  end
15
15
 
16
16
  def entries
17
- # possible to have nil entries, yuck
18
- parsed_response ? [parsed_response['feed']['entry']].flatten.compact : []
17
+ parsed_response && parsed_response['items'] || []
19
18
  end
20
19
 
21
20
  def response
@@ -1,15 +1,17 @@
1
1
  module Garb
2
2
  module Management
3
3
  class Goal
4
+ extend Attributes
5
+ include PathAttribute
4
6
 
5
- attr_accessor :session, :path
6
- attr_accessor :name, :number, :value, :destination, :active
7
+ attr_reader :session
8
+ ga_attribute :name, :number, :value, :active
7
9
 
8
10
  alias :active? :active
9
11
 
10
12
  def self.all(session = Session, path = '/accounts/~all/webproperties/~all/profiles/~all/goals')
11
13
  feed = Feed.new(session, path)
12
- feed.entries.map {|entry| new_from_entry(entry, session)}
14
+ feed.entries.map { |entry| new(entry, session) }
13
15
  end
14
16
 
15
17
  def self.for_account(account)
@@ -24,20 +26,13 @@ module Garb
24
26
  all(profile.session, profile.path + '/goals')
25
27
  end
26
28
 
27
- def self.new_from_entry(entry, session)
28
- goal = new
29
- goal.session = session
30
- goal.path = Garb.parse_link(entry, "self").gsub(Feed::BASE_URL, '')
31
- goal.properties = entry[Garb.to_ga('goal')]
32
- goal
29
+ def initialize(entry, session)
30
+ @entry = entry
31
+ @session = session
33
32
  end
34
33
 
35
- def properties=(properties)
36
- self.name = properties["name"]
37
- self.number = properties["number"].to_i
38
- self.value = properties["value"].to_f
39
- self.active = (properties["active"] == "true")
40
- self.destination = Destination.new(properties[Garb.to_ga('destination')])
34
+ def destination
35
+ @destination ||= Destination.new(@entry['urlDestinationDetails'])
41
36
  end
42
37
  end
43
38
  end
@@ -3,41 +3,32 @@ module Garb
3
3
  class Profile
4
4
 
5
5
  include ProfileReports
6
+ include PathAttribute
7
+ extend Attributes
6
8
 
7
- attr_accessor :session, :path
8
- attr_accessor :id, :table_id, :title, :account_id, :web_property_id
9
+ attr_reader :session
10
+ ga_attribute :id, :name, :account_id, :web_property_id, :website_url
9
11
 
10
12
  def self.all(session = Session, path = '/accounts/~all/webproperties/~all/profiles')
11
13
  feed = Feed.new(session, path)
12
- feed.entries.map {|entry| new_from_entry(entry, session)}
14
+ feed.entries.map { |entry| new(entry, session) }
13
15
  end
14
16
 
15
17
  def self.for_account(account)
16
- all(account.session, account.path+'/webproperties/~all/profiles')
18
+ all(account.session, account.path + '/webproperties/~all/profiles')
17
19
  end
18
20
 
19
21
  def self.for_web_property(web_property)
20
- all(web_property.session, web_property.path+'/profiles')
22
+ all(web_property.session, web_property.path + '/profiles')
21
23
  end
22
24
 
23
- def self.new_from_entry(entry, session)
24
- profile = new
25
- profile.session = session
26
- profile.path = Garb.parse_link(entry, "self").gsub(Feed::BASE_URL, '')
27
- profile.properties = Garb.parse_properties(entry)
28
- profile
29
- end
30
-
31
- def properties=(properties)
32
- self.id = properties['profile_id']
33
- self.table_id = properties['dxp:table_id']
34
- self.title = properties['profile_name']
35
- self.account_id = properties['account_id']
36
- self.web_property_id = properties['web_property_id']
25
+ def initialize(entry, session)
26
+ @entry = entry
27
+ @session = session
37
28
  end
38
29
 
39
30
  def goals
40
- Goal.for_profile(self)
31
+ Goal.for_profile self
41
32
  end
42
33
  end
43
34
  end
@@ -1,26 +1,20 @@
1
1
  module Garb
2
2
  module Management
3
3
  class Segment
4
- attr_accessor :session, :path
5
- attr_accessor :id, :name, :definition
4
+ extend Attributes
5
+ include PathAttribute
6
+
7
+ attr_reader :session
8
+ ga_attribute :name, :definition, :id => 'segmentId'
6
9
 
7
10
  def self.all(session = Session)
8
11
  feed = Feed.new(session, '/segments')
9
- feed.entries.map {|entry| new_from_entry(entry, session)}
10
- end
11
-
12
- def self.new_from_entry(entry, session)
13
- segment = new
14
- segment.session = session
15
- segment.path = Garb.parse_link(entry, "self").gsub(Feed::BASE_URL, '')
16
- segment.properties = entry['dxp:segment']
17
- segment
12
+ feed.entries.map { |entry| new(entry, session) }
18
13
  end
19
14
 
20
- def properties=(properties)
21
- self.id = properties['id']
22
- self.name = properties['name']
23
- self.definition = properties['dxp:definition']
15
+ def initialize(entry, session)
16
+ @entry = entry
17
+ @session = session
24
18
  end
25
19
  end
26
20
  end