legato 0.0.1 → 0.0.2

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/.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