trackoid 0.1.2 → 0.1.3

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.2
1
+ 0.1.3
@@ -129,6 +129,10 @@ module Mongoid #:nodoc:
129
129
  TrackerAggregates.new(self, name, args)
130
130
  end
131
131
 
132
+ define_method("#{name}_with_track") do |track_field, *args|
133
+ TrackerAggregates.new(self, name, args, track_field)
134
+ end
135
+
132
136
  define_method("#{name}=") do
133
137
  raise NoMethodError
134
138
  end
@@ -6,16 +6,29 @@ module Mongoid #:nodoc:
6
6
 
7
7
  def initialize(owner, field, aggregate_data)
8
8
  @owner, @for = owner, field
9
- @data = @owner.read_attribute(@for)
9
+ @for_data = @owner.internal_track_name(@for)
10
+ @data = @owner.read_attribute(@for_data)
10
11
 
11
12
  # The following is needed if the "field" Mongoid definition for our
12
13
  # internal tracking field does not include option ":default => {}"
13
14
  if @data.nil?
14
- @owner.write_attribute(@for, {})
15
- @data = @owner.read_attribute(@for)
15
+ @owner.write_attribute(@for_data, {})
16
+ @data = @owner.read_attribute(@for_data)
16
17
  end
17
18
 
18
- @aggregate_data = aggregate_data
19
+ @aggregate_data = aggregate_data.first if aggregate_data.first
20
+ end
21
+
22
+ # Delegate all missing methods to the aggregate accessors. This enables
23
+ # us to call an aggregation token after the tracking field.
24
+ #
25
+ # Example:
26
+ #
27
+ # <tt>@object.visits.browsers ...</tt>
28
+ #
29
+ def method_missing(name, *args, &block)
30
+ super unless @owner.aggregate_fields.member?(name)
31
+ @owner.send("#{name}_with_track".to_sym, @for, *args, &block)
19
32
  end
20
33
 
21
34
  # Update methods
@@ -30,10 +43,10 @@ module Mongoid #:nodoc:
30
43
  return unless @owner.aggregated?
31
44
 
32
45
  @owner.aggregate_fields.each do |(k,v)|
33
- token = v.call(@aggregate_data)
46
+ next unless token = v.call(@aggregate_data)
47
+
34
48
  fk = @owner.class.name.to_s.foreign_key.to_sym
35
- selector = { fk => @owner.id, :ns => k, :key => token }
36
-
49
+ selector = { fk => @owner.id, :ns => k, :key => token.to_s }
37
50
  @owner.aggregate_klass.collection.update( selector,
38
51
  { (how_much > 0 ? "$inc" : "$dec") => update_hash(how_much.abs, date) },
39
52
  :upsert => true)
@@ -50,6 +63,7 @@ module Mongoid #:nodoc:
50
63
 
51
64
  def set(how_much, date = DateTime.now)
52
65
  raise Errors::ModelNotSaved, "Can't update a new record" if @owner.new_record?
66
+
53
67
  update_data(how_much, date)
54
68
  @owner.collection.update( @owner._selector,
55
69
  { "$set" => update_hash(how_much, date) },
@@ -113,7 +127,7 @@ module Mongoid #:nodoc:
113
127
 
114
128
  def update_hash(num, date)
115
129
  {
116
- "#{@for}.#{date_literal(date)}" => num
130
+ "#{@for_data}.#{date_literal(date)}" => num
117
131
  }
118
132
  end
119
133
 
@@ -4,23 +4,57 @@ module Mongoid #:nodoc:
4
4
  # This internal class handles all interaction of an aggregation token.
5
5
  class TrackerAggregates
6
6
 
7
- def initialize(owner, token, key_selector)
7
+ def initialize(owner, token, key_selector, track_field = nil)
8
8
  @owner, @token = owner, token
9
9
  @key = key_selector
10
+ @track_field = track_field
10
11
 
11
12
  @accessor = @owner.class.send(:internal_accessor_name, @token)
12
13
  @selector = {:ns => @token}
13
14
  @selector.merge!(:key => @key.first) if @key.first
15
+ @criteria = @owner.send(@accessor).where(@selector)
14
16
  end
15
17
 
18
+ # REFACTOR THIS
19
+ # WE ARE DOING SOMETHING LIKE:
20
+ #
21
+ # => browsers("something").visits
22
+ #
23
+ # WHILE A BEST APPROACH IS LIKE
24
+ #
25
+ # => visits.browsers("something")
26
+
27
+ # visits (Tracker) -> browsers (TrackerAggregates) -> count (to Criteria)
28
+ # -> today (data)
29
+
30
+ # Delegate all missing methods to the underlying Mongoid Criteria
16
31
  def method_missing(name, *args, &block)
17
- @criteria ||= @owner.send(@accessor).where(@selector)
18
-
32
+ @criteria.send(name)
33
+
19
34
  # Delegate all missing methods to the underlying Mongoid Criteria
20
- return @criteria.send(name) unless @owner.tracked_fields.member?(name)
35
+ # return @criteria.send(name) unless @owner.tracked_fields.member?(name)
36
+ # super
21
37
 
22
38
  # Otherwise, it's a track method, so process it
23
- @criteria.collect {|x| x.send(name, *args, &block) }
39
+ # @criteria.collect {|x| x.send(name, *args, &block) }
40
+ end
41
+
42
+ def today
43
+ @criteria.collect {|c|
44
+ [c.key, c.send(@track_field).today] if @track_field
45
+ }
46
+ end
47
+
48
+ def yesterday
49
+ @criteria.collect {|c|
50
+ [c.key, c.send(@track_field).yesterday] if @track_field
51
+ }
52
+ end
53
+
54
+ def last_days(how_much = 7)
55
+ @criteria.collect {|c|
56
+ [c.key, c.send(@track_field).last_days(how_much)] if @track_field
57
+ }
24
58
  end
25
59
 
26
60
  end
@@ -25,9 +25,9 @@ module Mongoid #:nodoc:
25
25
  # This is necessary so that Mongoid does not "dirty" the field
26
26
  # potentially overwriting the original data.
27
27
  def track(name)
28
- set_tracking_field(name)
29
- create_tracking_accessors(name)
30
- update_aggregates(name) if aggregated?
28
+ set_tracking_field(name.to_sym)
29
+ create_tracking_accessors(name.to_sym)
30
+ update_aggregates(name.to_sym) if aggregated?
31
31
  end
32
32
 
33
33
  protected
@@ -42,7 +42,7 @@ module Mongoid #:nodoc:
42
42
  field internal_track_name(name), :type => Hash # , :default => {}
43
43
  # Should we make an index for this field?
44
44
  index internal_track_name(name)
45
- tracked_fields << name.to_sym
45
+ tracked_fields << name
46
46
  end
47
47
 
48
48
  # Creates the tracking field accessor and also disables the original
@@ -50,8 +50,8 @@ module Mongoid #:nodoc:
50
50
  # Mongoid fields ensures they doesn't get dirty, so Mongoid does not
51
51
  # overwrite old data.
52
52
  def create_tracking_accessors(name)
53
- define_method("#{name}") do |*aggr|
54
- Tracker.new(self, "#{name}_data".to_sym, aggr)
53
+ define_method(name) do |*aggr|
54
+ Tracker.new(self, name, aggr)
55
55
  end
56
56
 
57
57
  # Should we just "undef" this methods?
@@ -10,10 +10,10 @@ class TestModel
10
10
  # for testing pourposes. Trackoid does not make any difference in the
11
11
  # declaration order of tracking fields and aggregate tokens.
12
12
  track :visits
13
- aggregate :browsers do; "Mozilla".downcase; end
13
+ aggregate :browsers do |b| b.split.first.downcase if b; end
14
14
 
15
15
  track :uniques
16
- aggregate :referers do; "GoogleBot".downcase; end
16
+ aggregate :referers do |r| r.split.last.downcase if r; end
17
17
  end
18
18
 
19
19
  class SecondTestModel
@@ -62,8 +62,8 @@ describe Mongoid::Tracking::Aggregates do
62
62
 
63
63
  it "should create an array in the class with all aggregate fields even when monkey patching" do
64
64
  class TestModel
65
- aggregate :quarters do
66
- "Q1"
65
+ aggregate :quarters do |q|
66
+ "Q1";
67
67
  end
68
68
  end
69
69
  @mock.class.aggregate_fields.keys.to_set.should == [ :browsers, :referers, :quarters ].to_set
@@ -167,7 +167,7 @@ describe Mongoid::Tracking::Aggregates do
167
167
  end
168
168
 
169
169
  it "should increment visits for all aggregated instances" do
170
- @mock.visits("Aggregate data").inc
170
+ @mock.visits("Mozilla Firefox").inc
171
171
  @mock.browsers.count.should == 1
172
172
  @mock.referers.count.should == 1
173
173
  @mock.quarters.count.should == 1
@@ -175,7 +175,7 @@ describe Mongoid::Tracking::Aggregates do
175
175
 
176
176
  it "should increment visits for specific aggregation keys" do
177
177
  @mock.browsers("mozilla").size.should == 1
178
- @mock.referers("googlebot").size.should == 1
178
+ @mock.referers("firefox").size.should == 1
179
179
  @mock.quarters("Q1").size.should == 1
180
180
  end
181
181
 
@@ -185,12 +185,47 @@ describe Mongoid::Tracking::Aggregates do
185
185
  @mock.quarters("Q2").size.should == 0
186
186
  end
187
187
 
188
- it "should have 1 in visits" do
189
- tt = @mock.browsers.visits.collect {|v| v.last_days(7)}
190
- tt.should == [[0, 0, 0, 0, 0, 0, 1]]
188
+ it "should have 1 visits today" do
189
+ @mock.visits.browsers.today.should == [["mozilla", 1]]
190
+ @mock.visits.referers.today.should == [["firefox", 1]]
191
+ end
192
+
193
+ it "should have 0 visits yesterday" do
194
+ @mock.visits.browsers.today.should == [["mozilla", 1]]
195
+ @mock.visits.referers.today.should == [["firefox", 1]]
196
+ end
197
+
198
+ it "should have 1 visits last 7 days" do
199
+ @mock.visits.browsers.last_days(7).should == [["mozilla", [0, 0, 0, 0, 0, 0, 1]]]
200
+ @mock.visits.referers.last_days(7).should == [["firefox", [0, 0, 0, 0, 0, 0, 1]]]
201
+ end
202
+
203
+ it "should work adding 1 visit with different aggregation data" do
204
+ @mock.visits("Google Chrome").inc
205
+ @mock.visits.browsers.today.should == [["mozilla", 1], ["google", 1]]
206
+ @mock.visits.referers.today.should == [["firefox", 1], ["chrome", 1]]
207
+
208
+ # Just for testing array manipulations
209
+ @mock.visits.browsers.today.inject(0) {|total, c| total + c.last }.should == 2
210
+ end
211
+
212
+ it "let's chek what happens when sorting the best browser..." do
213
+ @mock.visits("Google Chrome").inc
214
+ @mock.visits.browsers.today.should == [["mozilla", 1], ["google", 2]]
215
+ @mock.visits.browsers.today.max {|a,b| a.second <=> b.second }.should == ["google", 2]
216
+ end
217
+
218
+ it "should work without aggregation information" do
219
+ @mock.visits.inc
220
+ @mock.visits.browsers.today.should == [["mozilla", 1], ["google", 2]]
221
+ @mock.visits.referers.today.should == [["firefox", 1], ["chrome", 2]]
222
+
223
+ # A more throughout test would check totals...
224
+ visits_today = @mock.visits.today
225
+ visits_today_with_browser = @mock.visits.browsers.today.inject(0) {|total, c| total + c.last }
226
+ visits_today.should == visits_today_with_browser + 1
191
227
  end
192
228
 
193
-
194
229
  end
195
230
 
196
231
 
data/trackoid.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{trackoid}
8
- s.version = "0.1.2"
8
+ s.version = "0.1.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jose Miguel Perez"]
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trackoid
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 29
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 2
10
- version: 0.1.2
9
+ - 3
10
+ version: 0.1.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jose Miguel Perez