legato 0.0.10 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -68,19 +68,19 @@ Here's what google has to say: http://code.google.com/apis/analytics/docs/gdata/
68
68
 
69
69
  Return entries with exits counts greater than or equal to 2000
70
70
 
71
- filter :high_exits, lambda {gte(:exits, 2000)}
71
+ filter :high_exits, &lambda {gte(:exits, 2000)}
72
72
 
73
73
  Return entries with pageview metric less than or equal to 200
74
74
 
75
- filter :low_pageviews, lambda {lte(:pageviews, 200)}
75
+ filter :low_pageviews, &lambda {lte(:pageviews, 200)}
76
76
 
77
77
  Filters with dimensions
78
78
 
79
- filter :for_browser, lambda {|browser| matches(:broswer, browser)}
79
+ filter :for_browser, &lambda {|browser| matches(:browser, browser)}
80
80
 
81
81
  Filters with OR
82
82
 
83
- filter :browsers, lambda {|*browsers| browsers.map {|browser| matches(:broswer, browser)}}
83
+ filter :browsers, &lambda {|*browsers| browsers.map {|browser| matches(:browser, browser)}}
84
84
 
85
85
 
86
86
  ## Using and Chaining Filters ##
@@ -109,7 +109,7 @@ Be sure to pass the appropriate number of arguments matching the lambda for your
109
109
 
110
110
  For a filter defined like this:
111
111
 
112
- filter :browsers, lambda {|*browsers| browsers.map {|browser| matches(:broswer, browser)}}
112
+ filter :browsers, &lambda {|*browsers| browsers.map {|browser| matches(:browser, browser)}}
113
113
 
114
114
  We can use it like this, passing any number of arguments:
115
115
 
@@ -140,6 +140,45 @@ Operators on dimensions:
140
140
  substring => '=@',
141
141
  not_substring => '!@'
142
142
 
143
+ ## Dynamic Segment
144
+
145
+ Your query can have a dynamic segment, which works with filter expressions. It
146
+ works like an [advanced
147
+ segment](https://support.google.com/analytics/answer/1033017?hl=en), except you
148
+ don't have to create it beforehand, you can just specify it at query time.
149
+
150
+ Some of the numbers you'll get will be different from using a filter, since
151
+ [the subset of visits matched happens before dimensions and metrics are
152
+ calculated](http://ga-dev-tools.appspot.com/explorer/) (hover on the `segment`
153
+ parameter to see).
154
+
155
+ Some metrics and dimensions are not allowed for segments, see the [API
156
+ documentation](https://developers.google.com/analytics/devguides/reporting/core/v3/reference#segment)
157
+ for more details.
158
+
159
+ ### Defining, using and chaining filters
160
+
161
+ Return entries with exits counts greater than or equal to 2000
162
+
163
+ segment :high_exits do
164
+ gte(:exits, 2000)
165
+ end
166
+
167
+ Return entries with pageview metric less than or equal to 200
168
+
169
+ segment :low_pageviews do
170
+ lte(:pageviews, 200)
171
+ end
172
+
173
+ You can chain them
174
+
175
+ Exit.high_exits.low_pageviews(profile)
176
+
177
+ and call them directly on the profile
178
+
179
+ profile.exit.high_exits.low_pageviews
180
+
181
+
143
182
  ## Accounts, WebProperties, Profiles, and Goals ##
144
183
 
145
184
  > Legato::Management::Account.all(user)
data/lib/legato/model.rb CHANGED
@@ -26,6 +26,18 @@ module Legato
26
26
  end
27
27
  end
28
28
 
29
+ def segments
30
+ @segments ||= {}
31
+ end
32
+
33
+ def segment(name, &block)
34
+ segments[name] = block
35
+
36
+ (class << self; self; end).instance_eval do
37
+ define_method(name) {|*args| Query.new(self).apply_segment_filter(*args, &block)}
38
+ end
39
+ end
40
+
29
41
  def set_instance_klass(klass)
30
42
  @instance_klass = klass
31
43
  end
data/lib/legato/query.rb CHANGED
@@ -11,6 +11,12 @@ module Legato
11
11
  end
12
12
  end
13
13
 
14
+ def define_segment_filter(name, &block)
15
+ (class << self; self; end).instance_eval do
16
+ define_method(name) {|*args| apply_segment_filter(*args, &block)}
17
+ end
18
+ end
19
+
14
20
  def self.define_filter_operators(*methods)
15
21
  methods.each do |method|
16
22
  class_eval <<-CODE
@@ -24,12 +30,13 @@ module Legato
24
30
  attr_reader :parent_klass
25
31
  attr_accessor :profile, :start_date, :end_date
26
32
  attr_accessor :sort, :limit, :offset, :quota_user #, :segment # individual, overwritten
27
- attr_accessor :filters # appended to, may add :segments later for dynamic segments
33
+ attr_accessor :filters, :segment_filters # combined, can be appended to
28
34
 
29
35
  def initialize(klass)
30
36
  @loaded = false
31
37
  @parent_klass = klass
32
38
  self.filters = FilterSet.new
39
+ self.segment_filters = FilterSet.new
33
40
  self.start_date = Time.now - MONTH
34
41
  self.end_date = Time.now
35
42
 
@@ -37,10 +44,9 @@ module Legato
37
44
  define_filter(name, &block)
38
45
  end
39
46
 
40
- # may add later for dynamic segments
41
- # klass.segment_definitions.each do |name, segment|
42
- # self.class.define_segment(name, segment)
43
- # end
47
+ klass.segments.each do |name, block|
48
+ define_segment_filter(name, &block)
49
+ end
44
50
  end
45
51
 
46
52
  def instance_klass
@@ -48,6 +54,14 @@ module Legato
48
54
  end
49
55
 
50
56
  def apply_filter(*args, &block)
57
+ apply_filter_expression(self.filters, *args, &block)
58
+ end
59
+
60
+ def apply_segment_filter(*args, &block)
61
+ apply_filter_expression(self.segment_filters, *args, &block)
62
+ end
63
+
64
+ def apply_filter_expression(filter_set, *args, &block)
51
65
  @profile = extract_profile(args)
52
66
 
53
67
  join_character = Legato.and_join_character # filters are joined by AND
@@ -55,7 +69,7 @@ module Legato
55
69
  # # block returns one filter or an array of filters
56
70
  Array.wrap(instance_exec(*args, &block)).each do |filter|
57
71
  filter.join_character ||= join_character # only set when not set explicitly
58
- self.filters << filter
72
+ filter_set << filter
59
73
 
60
74
  join_character = Legato.or_join_character # arrays are joined by OR
61
75
  end
@@ -169,6 +183,10 @@ module Legato
169
183
  @sort = Legato::ListParameter.new(:sort, arr)
170
184
  end
171
185
 
186
+ def segment
187
+ "dynamic::#{segment_filters.to_params}" if segment_filters.any?
188
+ end
189
+
172
190
  # def segment_id
173
191
  # segment.nil? ? nil : "gaid::#{segment}"
174
192
  # end
@@ -184,7 +202,7 @@ module Legato
184
202
  'end-date' => Legato.format_time(end_date),
185
203
  'max-results' => limit,
186
204
  'start-index' => offset,
187
- # 'segment' => segment_id,
205
+ 'segment' => segment,
188
206
  'filters' => filters.to_params, # defaults to AND filtering
189
207
  'fields' => REQUEST_FIELDS,
190
208
  'quotaUser' => quota_user
@@ -1,3 +1,3 @@
1
1
  module Legato
2
- VERSION = "0.0.10"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -69,6 +69,33 @@ describe "Legato::Model" do
69
69
  end
70
70
  end
71
71
 
72
+ context "with segments" do
73
+ before :each do
74
+ @block = lambda {}
75
+ end
76
+
77
+ it 'creates a class method' do
78
+ @model.segment :high, &@block
79
+ @model.respond_to?(:high).should be_true
80
+ end
81
+
82
+ it 'stores the segment' do
83
+ @model.segment :high, &@block
84
+ @model.segments[:high].should == @block
85
+ end
86
+
87
+ it 'returns a Query instance for a segment' do
88
+ query = stub(:apply_segment_filter => "a query")
89
+ Legato::Query.stubs(:new).returns(query)
90
+
91
+ @model.segment :high, &@block
92
+ @model.high('arg1').should == 'a query'
93
+
94
+ Legato::Query.should have_received(:new).with(@model)
95
+ query.should have_received(:apply_segment_filter).with('arg1')
96
+ end
97
+ end
98
+
72
99
  # xit 'has an instance klass'
73
100
  # xit 'sets an instance klass'
74
101
 
@@ -20,6 +20,7 @@ describe Legato::Query do
20
20
  @klass = Class.new
21
21
  @block = lambda {eql(:key, 1000)}
22
22
  @klass.stubs(:filters).returns({:high => @block})
23
+ @klass.stubs(:segments).returns([])
23
24
 
24
25
  @query = Legato::Query.new(@klass)
25
26
  end
@@ -354,6 +355,13 @@ describe Legato::Query do
354
355
  @query.to_params['filters'].should == 'filter set parameters'
355
356
  end
356
357
 
358
+ it 'includes the dynamic segment' do
359
+ segment_filters = stub(:to_params => 'segment parameter', :any? => true)
360
+ @query.stubs(:segment_filters).returns(segment_filters)
361
+
362
+ @query.to_params['segment'].should == 'dynamic::segment parameter'
363
+ end
364
+
357
365
  it 'includes metrics' do
358
366
  metrics = Legato::ListParameter.new(:metrics)
359
367
  metrics.stubs(:to_params).returns({'metrics' => 'pageviews,exits'})
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legato
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.10
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-07 00:00:00.000000000 Z
12
+ date: 2013-07-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake