legato 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,3 +3,4 @@
3
3
  Gemfile.lock
4
4
  pkg/*
5
5
  lib/demo.rb
6
+ .rvmrc
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ script: bundle exec rspec spec
data/README.md CHANGED
@@ -143,7 +143,7 @@ Operators on dimensions:
143
143
  * :end_date - The date to end, inclusive
144
144
  * :limit - The maximum number of results to be returned
145
145
  * :offset - The starting index
146
- * :order - metric/dimension to order by
146
+ * :sort - metric/dimension to sort by
147
147
 
148
148
  ## License ##
149
149
 
data/Rakefile CHANGED
@@ -2,6 +2,12 @@ require "bundler/gem_tasks"
2
2
 
3
3
  require 'oauth2'
4
4
 
5
+ # get oauth2 via device code, unimplemented as of now
6
+ # curl -d "client_id={{client_id}}&scope=https://www.googleapis.com/auth/analytics.readonly" https://accounts.google.com/o/oauth2/device/code
7
+ # { "device_code" : "4/8O1xUKWzOdJESG7ednnulZPsbyNt", "user_code" : "3tywhirg", "verification_url" : "http://www.google.com/device", "expires_in" : 1800, "interval" : 5 }
8
+ # curl -d "client_id={{client_id}}&client_secret={{client_secret}}&code=4/8O1xUKWzOdJESG7ednnulZPsbyNt&grant_type=http://oauth.net/grant_type/device/1.0" https://accounts.google.com/o/oauth2/token
9
+ # { "access_token" : "ERspXATXoblahblahblah", "token_type" : "Bearer", "expires_in" : 3600, "refresh_token" : "1/balhaajsdfklasfdjs;df" }
10
+
5
11
  namespace :oauth do
6
12
  def client
7
13
  # This is my test client account for Legato.
data/legato.gemspec CHANGED
@@ -3,6 +3,8 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
  require "legato/version"
4
4
 
5
5
  Gem::Specification.new do |s|
6
+ s.required_ruby_version = '>= 1.9.2'
7
+
6
8
  s.name = "legato"
7
9
  s.version = Legato::VERSION
8
10
  s.authors = ["Tony Pitale"]
@@ -11,7 +13,7 @@ Gem::Specification.new do |s|
11
13
  s.summary = %q{Access the Google Analytics API with Ruby}
12
14
  s.description = %q{Access the Google Analytics Core Reporting and Management APIs with Ruby. Create models for metrics and dimensions. Filter your data to tell you what you need.}
13
15
 
14
- s.rubyforge_project = "legato" # ?
16
+ s.rubyforge_project = "legato"
15
17
 
16
18
  s.files = `git ls-files`.split("\n")
17
19
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -19,11 +21,13 @@ Gem::Specification.new do |s|
19
21
  s.require_paths = ["lib"]
20
22
 
21
23
  # specify any dependencies here; for example:
24
+ s.add_development_dependency "rake"
22
25
  s.add_development_dependency "rspec"
23
26
  s.add_development_dependency "mocha"
24
27
  s.add_development_dependency "bourne"
25
28
  s.add_development_dependency "vcr", "2.0.0.beta2"
26
29
  s.add_development_dependency "fakeweb"
27
30
 
31
+ s.add_runtime_dependency "multi_json"
28
32
  s.add_runtime_dependency "oauth2"
29
33
  end
data/lib/legato.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require "legato/version"
2
2
 
3
- require 'json'
4
3
  require 'oauth2'
4
+ require 'multi_json'
5
5
  require 'cgi'
6
6
  require 'ostruct'
7
7
 
@@ -0,0 +1,5 @@
1
+ module Legato
2
+ class Collection
3
+
4
+ end
5
+ end
data/lib/legato/filter.rb CHANGED
@@ -3,27 +3,29 @@ module Legato
3
3
  attr_accessor :field, :operator, :value, :join_character
4
4
 
5
5
  OPERATORS = {
6
+ # metrics
6
7
  :eql => '==',
7
8
  :not_eql => '!=',
8
9
  :gt => '>',
9
10
  :gte => '>=',
10
11
  :lt => '<',
11
12
  :lte => '<=',
13
+ # dimensions
12
14
  :matches => '==',
13
15
  :does_not_match => '!=',
14
- :contains => '=~',
15
- :does_not_contain => '!~',
16
16
  :substring => '=@',
17
17
  :not_substring => '!@',
18
- :desc => '-',
19
- :descending => '-'
18
+ :contains => '=~', # regex
19
+ :does_not_contain => '!~' # regex
20
+ # :desc => '-',
21
+ # :descending => '-'
20
22
  }
21
23
 
22
- def initialize(field, operator, value, join_character=';')
24
+ def initialize(field, operator, value, join_character)
23
25
  self.field = field
24
26
  self.operator = operator
25
27
  self.value = value
26
- self.join_character = join_character
28
+ self.join_character = join_character # if nil, will be overridden by Query#apply_filter
27
29
  end
28
30
 
29
31
  def google_field
@@ -34,8 +36,12 @@ module Legato
34
36
  OPERATORS[operator]
35
37
  end
36
38
 
39
+ # escape comma and semicolon in value to differentiate
40
+ # from those used as join characters for OR/AND
37
41
  def escaped_value
38
- CGI.escape(value.to_s.gsub(/([,;\\])/) {|c| '\\'+c})
42
+ # backslash is escaped in strings
43
+ # oauth will cgi/uri escape for us
44
+ value.to_s.gsub(/([,;])/) {|c| '\\'+c}
39
45
  end
40
46
 
41
47
  def to_param
@@ -7,7 +7,7 @@ module Legato
7
7
 
8
8
  def all(user, path=default_path)
9
9
  json = user.access_token.get(base_uri + path).body
10
- JSON.parse(json)['items'].map {|item| new(item, user)}
10
+ MultiJson.decode(json)['items'].map {|item| new(item, user)}
11
11
  end
12
12
  end
13
13
  end
@@ -13,12 +13,13 @@ module Legato
13
13
  self.class.default_path + "/" + id.to_s
14
14
  end
15
15
 
16
- attr_accessor :id, :name, :user
16
+ attr_accessor :id, :name, :web_property_id, :user
17
17
 
18
18
  def initialize(attributes, user)
19
19
  self.user = user
20
20
  self.id = attributes['id']
21
21
  self.name = attributes['name']
22
+ self.web_property_id = attributes['webPropertyId']
22
23
  end
23
24
 
24
25
  def self.for_account(account)
data/lib/legato/model.rb CHANGED
@@ -18,11 +18,11 @@ module Legato
18
18
  @filters ||= {}
19
19
  end
20
20
 
21
- def filter(name, block)
21
+ def filter(name, &block)
22
22
  filters[name] = block
23
23
 
24
24
  (class << self; self; end).instance_eval do
25
- define_method(name) {|*args| Query.new(self).apply_filter(*args, block)}
25
+ define_method(name) {|*args| Query.new(self).apply_filter(*args, &block)}
26
26
  end
27
27
  end
28
28
 
data/lib/legato/query.rb CHANGED
@@ -3,18 +3,19 @@ module Legato
3
3
  include Enumerable
4
4
 
5
5
  MONTH = 2592000
6
+ REQUEST_FIELDS = 'columnHeaders/name,rows,totalResults,totalsForAllResults'
6
7
 
7
- def define_filter(name, block)
8
+ def define_filter(name, &block)
8
9
  (class << self; self; end).instance_eval do
9
- define_method(name) {|*args| apply_filter(*args, block)}
10
+ define_method(name) {|*args| apply_filter(*args, &block)}
10
11
  end
11
12
  end
12
13
 
13
14
  def self.define_filter_operators(*methods)
14
15
  methods.each do |method|
15
16
  class_eval <<-CODE
16
- def #{method}(field, value)
17
- Filter.new(field, :#{method}, value) # defaults to joining with AND
17
+ def #{method}(field, value, join_character=nil)
18
+ Filter.new(field, :#{method}, value, join_character)
18
19
  end
19
20
  CODE
20
21
  end
@@ -22,7 +23,7 @@ module Legato
22
23
 
23
24
  attr_reader :parent_klass
24
25
  attr_accessor :profile, :start_date, :end_date
25
- attr_accessor :order, :limit, :offset#, :segment # individual, overwritten
26
+ attr_accessor :sort, :limit, :offset#, :segment # individual, overwritten
26
27
  attr_accessor :filters # appended to, may add :segments later for dynamic segments
27
28
 
28
29
  def initialize(klass)
@@ -33,7 +34,7 @@ module Legato
33
34
  self.end_date = Time.now
34
35
 
35
36
  klass.filters.each do |name, block|
36
- define_filter(name, block)
37
+ define_filter(name, &block)
37
38
  end
38
39
 
39
40
  # may add later for dynamic segments
@@ -42,14 +43,14 @@ module Legato
42
43
  # end
43
44
  end
44
45
 
45
- def apply_filter(*args, block)
46
+ def apply_filter(*args, &block)
46
47
  @profile = extract_profile(args)
47
48
 
48
49
  join_character = Legato.and_join_character # filters are joined by AND
49
50
 
50
51
  # # block returns one filter or an array of filters
51
52
  Array.wrap(instance_exec(*args, &block)).each do |filter|
52
- filter.join_character = join_character
53
+ filter.join_character ||= join_character # only set when not set explicitly
53
54
  self.filters << filter
54
55
 
55
56
  join_character = Legato.or_join_character # arrays are joined by OR
@@ -60,7 +61,7 @@ module Legato
60
61
  def apply_options(options)
61
62
  if options.has_key?(:sort)
62
63
  # warn
63
- options[:order] = options.delete(:sort)
64
+ options[:sort] = options.delete(:sort)
64
65
  end
65
66
 
66
67
  apply_basic_options(options)
@@ -70,7 +71,7 @@ module Legato
70
71
  end
71
72
 
72
73
  def apply_basic_options(options)
73
- [:order, :limit, :offset, :start_date, :end_date].each do |key| #:segment
74
+ [:sort, :limit, :offset, :start_date, :end_date].each do |key| #:segment
74
75
  self.send("#{key}=".to_sym, options[key]) if options.has_key?(key)
75
76
  end
76
77
  end
@@ -108,7 +109,10 @@ module Legato
108
109
  end
109
110
 
110
111
  def load
111
- @collection = request_for_query.collection
112
+ response = request_for_query
113
+ @collection = response.collection
114
+ @total_results = response.total_results
115
+ @totals_for_all_results = response.totals_for_all_results
112
116
  @loaded = true
113
117
  end
114
118
 
@@ -118,12 +122,24 @@ module Legato
118
122
  end
119
123
  alias :to_a :collection
120
124
 
125
+ def total_results
126
+ load unless loaded?
127
+ @total_results
128
+ end
129
+
130
+ def totals_for_all_results
131
+ load unless loaded?
132
+ @totals_for_all_results
133
+ end
134
+
121
135
  def each(&block)
122
136
  collection.each(&block)
123
137
  end
124
138
 
125
139
  # if no filters, we use results to add profile
126
140
  def results(profile=nil, options={})
141
+ options, profile = profile, nil if profile.is_a?(Hash)
142
+
127
143
  self.profile = profile unless profile.nil?
128
144
  apply_options(options)
129
145
  self
@@ -145,8 +161,8 @@ module Legato
145
161
  parent_klass.dimensions
146
162
  end
147
163
 
148
- def order=(arr)
149
- @order = Legato::ListParameter.new(:order, arr)
164
+ def sort=(arr)
165
+ @sort = Legato::ListParameter.new(:sort, arr)
150
166
  end
151
167
 
152
168
  # def segment_id
@@ -165,10 +181,11 @@ module Legato
165
181
  'max-results' => limit,
166
182
  'start-index' => offset,
167
183
  # 'segment' => segment_id,
168
- 'filters' => filters.to_params # defaults to AND filtering
184
+ 'filters' => filters.to_params, # defaults to AND filtering
185
+ 'fields' => REQUEST_FIELDS
169
186
  }
170
187
 
171
- [metrics, dimensions, order].each do |list|
188
+ [metrics, dimensions, sort].each do |list|
172
189
  params.merge!(list.to_params) unless list.nil?
173
190
  end
174
191
 
@@ -11,7 +11,7 @@
11
11
  # end
12
12
  #
13
13
  # def parsed_response
14
- # JSON.parse(raw_response)
14
+ # MultiJson.decode(raw_response)
15
15
  # end
16
16
  #
17
17
  # def raw_response
@@ -6,13 +6,21 @@ module Legato
6
6
  end
7
7
 
8
8
  def data
9
- @data ||= JSON.parse(@raw_response.body)
9
+ @data ||= MultiJson.decode(@raw_response.body)
10
10
  end
11
11
 
12
12
  def collection
13
13
  raw_attributes.map {|attributes| @instance_klass.new(attributes)}
14
14
  end
15
15
 
16
+ def total_results
17
+ data["totalResults"]
18
+ end
19
+
20
+ def totals_for_all_results
21
+ Hash[data["totalsForAllResults"].map{|k,v| [Legato.from_ga_string(k), number_for(v)]}]
22
+ end
23
+
16
24
  private
17
25
  def headers
18
26
  data['columnHeaders']
@@ -23,69 +31,16 @@ module Legato
23
31
  end
24
32
 
25
33
  def rows
26
- data['rows']
34
+ Array.wrap(data['rows']).compact
27
35
  end
28
36
 
29
37
  def raw_attributes
30
38
  rows.map {|row| Hash[fields.zip(row)]}
31
39
  end
32
- end
33
- end
34
40
 
35
- # module Legato
36
- # class Response
37
- # KEYS = ['dxp$metric', 'dxp$dimension']
38
- #
39
- # # we could take the request, and be lazy about loading here, #refactoring
40
- # def initialize(response_body, instance_klass = OpenStruct)
41
- # @data = response_body
42
- # @instance_klass = instance_klass
43
- # end
44
- #
45
- # def results
46
- # if @results.nil?
47
- # @results = ResultSet.new(parse)
48
- # @results.total_results = parse_total_results
49
- # @results.sampled = parse_sampled_flag
50
- # end
51
- #
52
- # @results
53
- # end
54
- #
55
- # def sampled?
56
- # end
57
- #
58
- # private
59
- # def parse
60
- # entries.map do |entry|
61
- # @instance_klass.new(Hash[
62
- # values_for(entry).map {|v| [Garb.from_ga(v['name']), v['value']]}
63
- # ])
64
- # end
65
- # end
66
- #
67
- # def entries
68
- # feed? ? [parsed_data['feed']['entry']].flatten.compact : []
69
- # end
70
- #
71
- # def parse_total_results
72
- # feed? ? parsed_data['feed']['openSearch:totalResults'].to_i : 0
73
- # end
74
- #
75
- # def parse_sampled_flag
76
- # feed? ? (parsed_data['feed']['dxp$containsSampledData'] == 'true') : false
77
- # end
78
- #
79
- # def parsed_data
80
- # @parsed_data ||= JSON.parse(@data)
81
- # end
82
- #
83
- # def feed?
84
- # !parsed_data['feed'].nil?
85
- # end
86
- #
87
- # def values_for(entry)
88
- # KEYS.map {|k| entry[k]}.flatten.compact
89
- # end
90
- # end
91
- # end
41
+ def number_for(str)
42
+ return str.to_f if str.index('.')
43
+ str.to_i
44
+ end
45
+ end
46
+ end
@@ -1,3 +1,3 @@
1
1
  module Legato
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Legato::Filter do
4
4
  context "a Filter instance" do
5
5
  before :each do
6
- @filter = Legato::Filter.new(:exits, :lt, 1000)
6
+ @filter = Legato::Filter.new(:exits, :lt, 1000, nil)
7
7
  end
8
8
 
9
9
  it 'has a field' do
@@ -28,8 +28,8 @@ describe Legato::Filter do
28
28
  @filter.value.should == 1000
29
29
  end
30
30
 
31
- it 'has a default join character' do
32
- @filter.join_character.should == ';'
31
+ it 'has a no default join character' do
32
+ @filter.join_character.should == nil
33
33
  end
34
34
 
35
35
  it 'represents itself as a parameter' do
@@ -45,5 +45,15 @@ describe Legato::Filter do
45
45
  it 'returns to_param if joining with nil' do
46
46
  @filter.join_with(nil).should == @filter.to_param
47
47
  end
48
+
49
+ it 'properly escapes commas' do
50
+ @filter.value = ","
51
+ @filter.escaped_value.should == "\\,"
52
+ end
53
+
54
+ it 'properly escapes semicolons' do
55
+ @filter.value = ";"
56
+ @filter.escaped_value.should == "\\;"
57
+ end
48
58
  end
49
59
  end
@@ -10,10 +10,11 @@ describe Legato::Management::Profile do
10
10
 
11
11
  it 'creates a new profile instance from a hash of attributes' do
12
12
  user = stub
13
- profile = Legato::Management::Profile.new({"id" => 12345, "name" => "Profile 1"}, user)
13
+ profile = Legato::Management::Profile.new({"id" => 12345, "name" => "Profile 1", "webPropertyId" => "WebProperty 7"}, user)
14
14
  profile.user.should == user
15
15
  profile.id.should == 12345
16
16
  profile.name.should == "Profile 1"
17
+ profile.web_property_id.should == "WebProperty 7"
17
18
  end
18
19
 
19
20
  it 'returns an array of all profiles available to a user under an account' do
@@ -38,12 +38,12 @@ describe "Legato::Model" do
38
38
  end
39
39
 
40
40
  it 'creates a class method' do
41
- @model.filter :high, @block
41
+ @model.filter :high, &@block
42
42
  @model.respond_to?(:high).should be_true
43
43
  end
44
44
 
45
45
  it 'stores the filter' do
46
- @model.filter :high, @block
46
+ @model.filter :high, &@block
47
47
  @model.filters[:high].should == @block
48
48
  end
49
49
 
@@ -51,11 +51,11 @@ describe "Legato::Model" do
51
51
  query = stub(:apply_filter => "a query")
52
52
  Legato::Query.stubs(:new).returns(query)
53
53
 
54
- @model.filter :high, @block
54
+ @model.filter :high, &@block
55
55
  @model.high('arg1').should == 'a query'
56
56
 
57
57
  Legato::Query.should have_received(:new).with(@model)
58
- query.should have_received(:apply_filter).with('arg1', @block)
58
+ query.should have_received(:apply_filter).with('arg1')
59
59
  end
60
60
  end
61
61
 
@@ -10,7 +10,7 @@ describe Legato::Query do
10
10
  it "returns a new filter for #{operator} to the set" do
11
11
  Legato::Filter.stubs(:new).returns("a filter")
12
12
  filter = @query.send(operator, :key, 2000)
13
- Legato::Filter.should have_received(:new).with(:key, operator, 2000)
13
+ Legato::Filter.should have_received(:new).with(:key, operator, 2000, nil)
14
14
  end
15
15
  end
16
16
  end
@@ -39,7 +39,7 @@ describe Legato::Query do
39
39
  it "has filter methods that call apply with the given block" do
40
40
  @query.stubs(:apply_filter)
41
41
  @query.high('hi')
42
- @query.should have_received(:apply_filter).with('hi', @block)
42
+ @query.should have_received(:apply_filter).with('hi')
43
43
  end
44
44
 
45
45
  it 'does not load results by default' do
@@ -47,7 +47,7 @@ describe Legato::Query do
47
47
  end
48
48
 
49
49
  it "loads a collection of results" do
50
- response = stub(:collection => [])
50
+ response = stub(:collection => [], :total_results => 0, :totals_for_all_results => {})
51
51
  user = stub(:request => response)
52
52
  @query.stubs(:profile => stub(:user => user))
53
53
 
@@ -56,15 +56,29 @@ describe Legato::Query do
56
56
  @query.loaded?.should be_true
57
57
  @query.profile.user.should have_received(:request).with(@query)
58
58
  response.should have_received(:collection)
59
+ response.should have_received(:total_results)
60
+ response.should have_received(:totals_for_all_results)
59
61
  end
60
62
 
61
63
  it "returns the collection" do
62
- @query.stubs(:request_for_query).returns(stub(:collection => [1,2,3]))
64
+ @query.stubs(:request_for_query).returns(stub(:collection => [1,2,3], :total_results => 3, :totals_for_all_results => {'foo' => 34.2}))
63
65
  @query.load
64
66
  @query.collection.should == [1,2,3]
65
67
  @query.to_a.should == [1,2,3]
66
68
  end
67
69
 
70
+ it "returns the total number of results" do
71
+ @query.stubs(:request_for_query).returns(stub(:collection => [1,2,3], :total_results => 3, :totals_for_all_results => {'foo' => 34.2}))
72
+ @query.load
73
+ @query.total_results.should == 3
74
+ end
75
+
76
+ it "returns the totals for all results" do
77
+ @query.stubs(:request_for_query).returns(stub(:collection => [1,2,3], :total_results => 3, :totals_for_all_results => {'foo' => 34.2}))
78
+ @query.load
79
+ @query.totals_for_all_results.should == {'foo' => 34.2}
80
+ end
81
+
68
82
  it "behaves like an enumerable delegating to the collection" do
69
83
  collection = []
70
84
  collection.stubs(:each)
@@ -100,9 +114,19 @@ describe Legato::Query do
100
114
  @query.should have_received(:apply_options).with({})
101
115
  end
102
116
 
117
+ it 'sets options and returns self' do
118
+ @query.stubs(:profile=)
119
+ @query.stubs(:apply_options)
120
+
121
+ @query.results({:sort => [:city]}).should == @query
122
+
123
+ @query.should have_received(:profile=).never
124
+ @query.should have_received(:apply_options).with({:sort => [:city]})
125
+ end
126
+
103
127
  context 'when applying filters' do
104
128
  before :each do
105
- @filter = Legato::Filter.new(:key, :eql, 1000)
129
+ @filter = Legato::Filter.new(:key, :eql, 1000, nil)
106
130
  @query.stubs(:eql).returns(@filter)
107
131
 
108
132
  @filters = stub(:<<)
@@ -110,17 +134,17 @@ describe Legato::Query do
110
134
  end
111
135
 
112
136
  it 'returns the query' do
113
- @query.apply_filter(@block).should == @query
137
+ @query.apply_filter(&@block).should == @query
114
138
  end
115
139
 
116
140
  it 'executes the block' do
117
- @query.apply_filter(@block)
141
+ @query.apply_filter(&@block)
118
142
  @query.should have_received(:eql).with(:key, 1000)
119
143
  end
120
144
 
121
145
  it 'accepts a profile as the first argument' do
122
146
  profile = Legato::Management::Profile.new({}, stub)
123
- @query.apply_filter(profile, @block)
147
+ @query.apply_filter(profile, &@block)
124
148
  @query.should have_received(:eql)
125
149
  @query.profile.should == profile
126
150
  end
@@ -128,7 +152,7 @@ describe Legato::Query do
128
152
  it 'accepts a profile as the last argument' do
129
153
  profile = Legato::Management::Profile.new({}, stub)
130
154
  block_with_arg = lambda {|count| eql(:key, count)}
131
- @query.apply_filter(100, profile, block_with_arg)
155
+ @query.apply_filter(100, profile, &block_with_arg)
132
156
  @query.should have_received(:eql).with(:key, 100)
133
157
  @query.profile.should == profile
134
158
  end
@@ -136,12 +160,12 @@ describe Legato::Query do
136
160
  it 'does not override the existing profile if none is provide' do
137
161
  @query.profile = Legato::Management::Profile.new({}, stub)
138
162
  block_with_arg = lambda {|count| eql(:key, count)}
139
- @query.apply_filter(100, block_with_arg)
163
+ @query.apply_filter(100, &block_with_arg)
140
164
  @query.profile.should_not == nil
141
165
  end
142
166
 
143
167
  it 'adds to the filter set' do
144
- @query.apply_filter(@block)
168
+ @query.apply_filter(&@block)
145
169
 
146
170
  @filters.should have_received(:<<).with(@filter)
147
171
  end
@@ -150,7 +174,7 @@ describe Legato::Query do
150
174
  block = lambda {|*browsers| browsers.map {|browser| eql(:browser, browser)}}
151
175
  @filter.stubs(:join_character=)
152
176
 
153
- @query.apply_filter('chrome', 'safari', block)
177
+ @query.apply_filter('chrome', 'safari', &block)
154
178
 
155
179
  @filter.should have_received(:join_character=).with(Legato.and_join_character)
156
180
  @filter.should have_received(:join_character=).with(Legato.or_join_character)
@@ -162,26 +186,26 @@ describe Legato::Query do
162
186
  @query.apply_options({}).should == @query
163
187
  end
164
188
 
165
- it "stores the order" do
166
- @query.apply_options({:order => [:page_path]})
167
- @query.order.should == Legato::ListParameter.new(:order, [:page_path])
189
+ it "stores the sort" do
190
+ @query.apply_options({:sort => [:page_path]})
191
+ @query.sort.should == Legato::ListParameter.new(:sort, [:page_path])
168
192
  end
169
193
 
170
- it 'replaces the order' do
171
- @query.order = [:pageviews]
172
- @query.apply_options({:order => [:page_path]})
173
- @query.order.should == Legato::ListParameter.new(:order, [:page_path])
194
+ it 'replaces the sort' do
195
+ @query.sort = [:pageviews]
196
+ @query.apply_options({:sort => [:page_path]})
197
+ @query.sort.should == Legato::ListParameter.new(:sort, [:page_path])
174
198
  end
175
199
 
176
- it "does not replace order if option is omitted" do
177
- @query.order = [:pageviews]
200
+ it "does not replace sort if option is omitted" do
201
+ @query.sort = [:pageviews]
178
202
  @query.apply_options({})
179
- @query.order.should == Legato::ListParameter.new(:order, [:pageviews])
203
+ @query.sort.should == Legato::ListParameter.new(:sort, [:pageviews])
180
204
  end
181
205
 
182
- it "moves :sort option into order" do
206
+ it "moves :sort option into sort" do
183
207
  @query.apply_options({:sort => [:page_path]})
184
- @query.order.should == Legato::ListParameter.new(:order, [:page_path])
208
+ @query.sort.should == Legato::ListParameter.new(:sort, [:page_path])
185
209
  end
186
210
 
187
211
  it "sets the limit" do
@@ -265,6 +289,7 @@ describe Legato::Query do
265
289
  @query.to_params.should == {
266
290
  'ids' => 'ga:1234567890',
267
291
  'start-date' => Legato.format_time(Time.now-Legato::Query::MONTH),
292
+ 'fields' => Legato::Query::REQUEST_FIELDS,
268
293
  'end-date' => Legato.format_time(Time.now)
269
294
  }
270
295
  end
@@ -273,7 +298,11 @@ describe Legato::Query do
273
298
  now = Time.now
274
299
  @query.start_date = now
275
300
  @query.end_date = now
276
- @query.to_params.should == {'start-date' => Legato.format_time(now), 'end-date' => Legato.format_time(now)}
301
+ @query.to_params.should == {
302
+ 'start-date' => Legato.format_time(now),
303
+ 'fields' => Legato::Query::REQUEST_FIELDS,
304
+ 'end-date' => Legato.format_time(now)
305
+ }
277
306
  end
278
307
 
279
308
  it 'includes the limit' do
@@ -311,13 +340,13 @@ describe Legato::Query do
311
340
  @query.to_params['dimensions'].should == 'browser,country'
312
341
  end
313
342
 
314
- it 'includes order' do
315
- order = Legato::ListParameter.new(:order)
316
- order.stubs(:to_params).returns({'order' => 'pageviews'})
317
- order.stubs(:empty?).returns(false)
318
- @query.stubs(:order).returns(order)
343
+ it 'includes sort' do
344
+ sort = Legato::ListParameter.new(:sort)
345
+ sort.stubs(:to_params).returns({'sort' => 'pageviews'})
346
+ sort.stubs(:empty?).returns(false)
347
+ @query.stubs(:sort).returns(sort)
319
348
 
320
- @query.to_params['order'].should == 'pageviews'
349
+ @query.to_params['sort'].should == 'pageviews'
321
350
  end
322
351
  end
323
352
  end
@@ -11,5 +11,18 @@ describe Legato::Response do
11
11
  it 'has a collection of OpenStruct instances' do
12
12
  @response.collection.first.should == OpenStruct.new({:browser=>"Android Browser", :pageviews=>"93"})
13
13
  end
14
+
15
+ it 'has the number of total results' do
16
+ @response.total_results.should == 13
17
+ end
18
+
19
+ it 'has the totals for all results hash' do
20
+ @response.totals_for_all_results.should == {'pageviews' => 3710}
21
+ end
22
+
23
+ it 'handles no rows returned' do
24
+ @response.stubs(:data).returns({'rows' => nil})
25
+ @response.collection.should == []
26
+ end
14
27
  end
15
- end
28
+ end
@@ -1,6 +1,6 @@
1
1
  shared_examples_for "a management finder" do
2
2
  it "returns an array of all #{subject_class_name} available to a user" do
3
- JSON.stubs(:parse).returns({'items' => ['item1', 'item2']})
3
+ MultiJson.stubs(:decode).returns({'items' => ['item1', 'item2']})
4
4
  response = stub(:body => 'some json')
5
5
  access_token = stub(:get => response)
6
6
  user = stub(:access_token => access_token)
@@ -11,7 +11,7 @@ shared_examples_for "a management finder" do
11
11
  user.should have_received(:access_token)
12
12
  access_token.should have_received(:get).with('https://www.googleapis.com/analytics/v3/management'+described_class.default_path)
13
13
  response.should have_received(:body)
14
- JSON.should have_received(:parse).with('some json')
14
+ MultiJson.should have_received(:decode).with('some json')
15
15
  described_class.should have_received(:new).with('item1', user)
16
16
  described_class.should have_received(:new).with('item2', user)
17
17
  end
metadata CHANGED
@@ -1,98 +1,158 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: legato
3
- version: !ruby/object:Gem::Version
4
- version: 0.0.1
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
5
  prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
6
11
  platform: ruby
7
- authors:
12
+ authors:
8
13
  - Tony Pitale
9
14
  autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
- date: 2012-01-14 00:00:00.000000000Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: rspec
16
- requirement: &70178713324740 !ruby/object:Gem::Requirement
17
+
18
+ date: 2012-04-26 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ version_requirements: &id001 !ruby/object:Gem::Requirement
17
23
  none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0'
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ hash: 3
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ prerelease: false
22
32
  type: :development
33
+ requirement: *id001
34
+ name: rake
35
+ - !ruby/object:Gem::Dependency
36
+ version_requirements: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ hash: 3
42
+ segments:
43
+ - 0
44
+ version: "0"
23
45
  prerelease: false
24
- version_requirements: *70178713324740
25
- - !ruby/object:Gem::Dependency
26
- name: mocha
27
- requirement: &70178713323760 !ruby/object:Gem::Requirement
46
+ type: :development
47
+ requirement: *id002
48
+ name: rspec
49
+ - !ruby/object:Gem::Dependency
50
+ version_requirements: &id003 !ruby/object:Gem::Requirement
28
51
  none: false
29
- requirements:
30
- - - ! '>='
31
- - !ruby/object:Gem::Version
32
- version: '0'
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ hash: 3
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ prerelease: false
33
60
  type: :development
61
+ requirement: *id003
62
+ name: mocha
63
+ - !ruby/object:Gem::Dependency
64
+ version_requirements: &id004 !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 3
70
+ segments:
71
+ - 0
72
+ version: "0"
34
73
  prerelease: false
35
- version_requirements: *70178713323760
36
- - !ruby/object:Gem::Dependency
74
+ type: :development
75
+ requirement: *id004
37
76
  name: bourne
38
- requirement: &70178713322240 !ruby/object:Gem::Requirement
77
+ - !ruby/object:Gem::Dependency
78
+ version_requirements: &id005 !ruby/object:Gem::Requirement
39
79
  none: false
40
- requirements:
41
- - - ! '>='
42
- - !ruby/object:Gem::Version
43
- version: '0'
44
- type: :development
80
+ requirements:
81
+ - - "="
82
+ - !ruby/object:Gem::Version
83
+ hash: -161295594
84
+ segments:
85
+ - 2
86
+ - 0
87
+ - 0
88
+ - beta
89
+ - 2
90
+ version: 2.0.0.beta2
45
91
  prerelease: false
46
- version_requirements: *70178713322240
47
- - !ruby/object:Gem::Dependency
92
+ type: :development
93
+ requirement: *id005
48
94
  name: vcr
49
- requirement: &70178713320200 !ruby/object:Gem::Requirement
95
+ - !ruby/object:Gem::Dependency
96
+ version_requirements: &id006 !ruby/object:Gem::Requirement
50
97
  none: false
51
- requirements:
52
- - - =
53
- - !ruby/object:Gem::Version
54
- version: 2.0.0.beta2
55
- type: :development
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ hash: 3
102
+ segments:
103
+ - 0
104
+ version: "0"
56
105
  prerelease: false
57
- version_requirements: *70178713320200
58
- - !ruby/object:Gem::Dependency
106
+ type: :development
107
+ requirement: *id006
59
108
  name: fakeweb
60
- requirement: &70178713319120 !ruby/object:Gem::Requirement
109
+ - !ruby/object:Gem::Dependency
110
+ version_requirements: &id007 !ruby/object:Gem::Requirement
61
111
  none: false
62
- requirements:
63
- - - ! '>='
64
- - !ruby/object:Gem::Version
65
- version: '0'
66
- type: :development
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ hash: 3
116
+ segments:
117
+ - 0
118
+ version: "0"
67
119
  prerelease: false
68
- version_requirements: *70178713319120
69
- - !ruby/object:Gem::Dependency
70
- name: oauth2
71
- requirement: &70178713318020 !ruby/object:Gem::Requirement
72
- none: false
73
- requirements:
74
- - - ! '>='
75
- - !ruby/object:Gem::Version
76
- version: '0'
77
120
  type: :runtime
121
+ requirement: *id007
122
+ name: multi_json
123
+ - !ruby/object:Gem::Dependency
124
+ version_requirements: &id008 !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ hash: 3
130
+ segments:
131
+ - 0
132
+ version: "0"
78
133
  prerelease: false
79
- version_requirements: *70178713318020
80
- description: Access the Google Analytics Core Reporting and Management APIs with Ruby.
81
- Create models for metrics and dimensions. Filter your data to tell you what you
82
- need.
83
- email:
134
+ type: :runtime
135
+ requirement: *id008
136
+ name: oauth2
137
+ description: Access the Google Analytics Core Reporting and Management APIs with Ruby. Create models for metrics and dimensions. Filter your data to tell you what you need.
138
+ email:
84
139
  - tpitale@gmail.com
85
140
  executables: []
141
+
86
142
  extensions: []
143
+
87
144
  extra_rdoc_files: []
88
- files:
145
+
146
+ files:
89
147
  - .gitignore
90
148
  - .rspec
149
+ - .travis.yml
91
150
  - Gemfile
92
151
  - README.md
93
152
  - Rakefile
94
153
  - legato.gemspec
95
154
  - lib/legato.rb
155
+ - lib/legato/collection.rb
96
156
  - lib/legato/core_ext/array.rb
97
157
  - lib/legato/core_ext/string.rb
98
158
  - lib/legato/filter.rb
@@ -130,37 +190,43 @@ files:
130
190
  - spec/spec_helper.rb
131
191
  - spec/support/examples/management_finder.rb
132
192
  - spec/support/macros/oauth.rb
193
+ has_rdoc: true
133
194
  homepage: http://github.com/tpitale/legato
134
195
  licenses: []
196
+
135
197
  post_install_message:
136
198
  rdoc_options: []
137
- require_paths:
199
+
200
+ require_paths:
138
201
  - lib
139
- required_ruby_version: !ruby/object:Gem::Requirement
202
+ required_ruby_version: !ruby/object:Gem::Requirement
140
203
  none: false
141
- requirements:
142
- - - ! '>='
143
- - !ruby/object:Gem::Version
144
- version: '0'
145
- segments:
146
- - 0
147
- hash: 4156627960004650884
148
- required_rubygems_version: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - ">="
206
+ - !ruby/object:Gem::Version
207
+ hash: 55
208
+ segments:
209
+ - 1
210
+ - 9
211
+ - 2
212
+ version: 1.9.2
213
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
214
  none: false
150
- requirements:
151
- - - ! '>='
152
- - !ruby/object:Gem::Version
153
- version: '0'
154
- segments:
215
+ requirements:
216
+ - - ">="
217
+ - !ruby/object:Gem::Version
218
+ hash: 3
219
+ segments:
155
220
  - 0
156
- hash: 4156627960004650884
221
+ version: "0"
157
222
  requirements: []
223
+
158
224
  rubyforge_project: legato
159
- rubygems_version: 1.8.6
225
+ rubygems_version: 1.4.2
160
226
  signing_key:
161
227
  specification_version: 3
162
228
  summary: Access the Google Analytics API with Ruby
163
- test_files:
229
+ test_files:
164
230
  - spec/cassettes/management/accounts.json
165
231
  - spec/cassettes/management/profiles.json
166
232
  - spec/cassettes/management/web_properties.json