trackoid 0.1.11 → 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -113,7 +113,6 @@ Imagine you need a web service to track the temperature of all rooms of a nuclea
113
113
  include Mongoid::Tracking
114
114
 
115
115
  track :temperature
116
-
117
116
  end
118
117
 
119
118
 
@@ -124,18 +123,19 @@ Imagine you need a web service to track the temperature of all rooms of a nuclea
124
123
  @room.temperature.set(current_temperature)
125
124
  end
126
125
 
127
- So, you don't need only to increment or decrement a value, yuo can also set an specific value. Now it's easy to know the maximum temperature of the last 30 days for a room:
126
+ So, you are not restricted into incrementing or decrementing a value, you can also store an specific value. Now it's easy to know the maximum temperature of the last 30 days for a room:
128
127
 
129
128
  @room.temperature.last_days(30).max
130
129
 
131
130
 
132
131
  = How does it works?
133
132
 
134
- Trakoid works by embedding date tracking information into models. The date tracking information is limited by a granularity of days for now. As the project evolves and we test performance, my idea is to add finer granularity of hours and perhaps, minutes.
133
+ Trakoid works by embedding date tracking information into the models. The date tracking information is limited by a granularity of days, but you can use aggregates if you absolutely need hour or minutes granularity.
134
+
135
135
 
136
136
  == Scalability and performance
137
137
 
138
- Trackoid is made from the ground up to take advantage of the great scalability features of MongoDB. Trackoid uses "upsert" operations, bypassing Mongoid controllers so that it can be used in a distributed system without data loses. This is perfect for a cloud application.
138
+ Trackoid is made from the ground up to take advantage of the great scalability features of MongoDB. Trackoid uses "upsert" operations, bypassing Mongoid controllers so that it can be used in a distributed system without data loss. This is perfect for a cloud hosted SaaS application!
139
139
 
140
140
  The problem with a distributed system for tracking analytical information is the atomicity of operations. Imagine you must increment visits information from several servers at the same time and how you would do it. With an SQL model, this is somewhat easy because the tradittional approaches for doing this only require INSERT or UPDATE operations that are atomic by nature. But for a Document Oriented Database like MongoDB you need some kind of special operations. MongoDB uses "upsert" commands, which stands for "update or insert". That is, modify this and create if not exists.
141
141
 
@@ -146,6 +146,7 @@ Trackoid issues "upsert" commands directly to the MongoDB driver, with the follo
146
146
 
147
147
  collection.update( {_id:ObjectID}, {$inc: {visits.2010.05.30: 1} }, true )
148
148
 
149
+
149
150
  This way, the collection can receive multiple incremental operations without requiring additional logic for locking or something. The only drawback is that you will not have realtime data in your model. For example:
150
151
 
151
152
  v = @page.visits.today # v is now "5" if there was 5 visits today
@@ -182,10 +183,17 @@ MongoDB stores information in BSON format as a binary representation of a JSON s
182
183
 
183
184
  A year full of statistical data takes only 2.8Kb, if you store integers. If your statistical data includes floats, a year full of information takes 4.3Kb. I said "a year full of data" because Trackoid does not store information for days without data.
184
185
 
185
- For comparison, this README is already 8.5Kb in size.
186
+ For comparison, this README is already 8.5Kb in size...
186
187
 
187
188
 
188
189
  = Revision History
190
+ 0.1.12 - Previously known as "accessors" methods, now they are promoted as
191
+ "Reader" methods and now they live in its own Module.
192
+ (Accessor methods are those like "today", "on", "last_days", etc,
193
+ and now they are called "Readers" to avoid confussion with real
194
+ accessors like "attr_accessors"...)
195
+
196
+ 0.1.11 - Updated Gemspec for a new version of Jeweler
189
197
 
190
198
  0.1.10 - Renamed accessor methods from "all", "last", "first" to
191
199
  "all_values", "first_value" and "last_value" so as not to
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.11
1
+ 0.1.12
@@ -10,10 +10,8 @@ module Mongoid #:nodoc:
10
10
  class_inheritable_accessor :aggregate_fields, :aggregate_klass
11
11
  self.aggregate_fields = {}
12
12
  self.aggregate_klass = nil
13
- delegate :aggregate_fields,
14
- :aggregate_klass,
15
- :aggregated?,
16
- :to => "self.class"
13
+ delegate :aggregate_fields, :aggregate_klass, :aggregated?,
14
+ :to => "self.class"
17
15
  end
18
16
  end
19
17
 
@@ -134,7 +132,7 @@ module Mongoid #:nodoc:
134
132
  end
135
133
 
136
134
  def create_aggregation_accessors(name)
137
- # Aggregation accessors in the model acts like a named scope
135
+ # Aggregation accessors in the model acts like a named scopes
138
136
  define_method(name) do |*args|
139
137
  TrackerAggregates.new(self, name, args)
140
138
  end
@@ -147,51 +145,8 @@ module Mongoid #:nodoc:
147
145
  raise NoMethodError
148
146
  end
149
147
  end
150
-
151
148
  end
152
149
 
153
150
  end
154
-
155
-
156
- # class Aggregate
157
- # include Mongoid::Document
158
- # end
159
- #
160
- # track :visits
161
- # aggregate :browsers do
162
- # ["google"]
163
- # end
164
- # aggregate :referers do
165
- # ["domain.com"]
166
- # end
167
- #
168
- #
169
- # self.visits(agg).inc
170
- # self.visits.today
171
- # self.visits.browsers.today
172
- #
173
- # users
174
- # { _id: 32334333, name:"pepe", visits_data:{} }
175
- #
176
- # users_aggregates
177
- # { _id: 11221223, data_for: 32334333, ns: "browsers", key: nil, visits_data:{} }
178
- # { _id: 11223223, data_for: 32334333, ns: "browsers", key: "google", visits_data:{} }
179
- # { _id: 11224432, data_for: 32334333, ns: "browsers", key: "firefox", visits_data:{} }
180
- #
181
- #
182
- # class UsersAggregate
183
- # include Mongoid::Document
184
- # include Mongoid::Tracking
185
- #
186
- # belongs_to :users
187
- # field :ns
188
- # field :key
189
- #
190
- # track :visits
191
- # track :uniques
192
- # end
193
- #
194
- #
195
-
196
151
  end
197
152
  end
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Tracking
4
+ # Reader methods (previously known as "accessors")
5
+ module Readers
6
+
7
+ # Access methods
8
+ def today
9
+ data_for(Date.today)
10
+ end
11
+
12
+ def yesterday
13
+ data_for(Date.today - 1)
14
+ end
15
+
16
+ def first_value
17
+ data_for(first_date)
18
+ end
19
+
20
+ def last_value
21
+ data_for(last_date)
22
+ end
23
+
24
+ def last_days(how_much = 7)
25
+ return [today] unless how_much > 0
26
+ date, values = Date.today, []
27
+ (date - how_much.abs + 1).step(date) {|d| values << data_for(d) }
28
+ values
29
+ end
30
+
31
+ def on(date)
32
+ return date.collect {|d| data_for(d)} if date.is_a?(Range)
33
+ data_for(date)
34
+ end
35
+
36
+ def all_values
37
+ on(first_date..last_date) if first_date
38
+ end
39
+
40
+ # Utility methods
41
+ def first_date
42
+ # We are guaranteed _m and _d to exists unless @data is a malformed
43
+ # hash, so we need to do this nasty "return nil", sorry...
44
+ # TODO: I'm open to change this to a cleaner algorithm :-)
45
+ return nil unless _y = @data.keys.min
46
+ return nil unless _m = @data[_y].keys.min
47
+ return nil unless _d = @data[_y][_m].keys.min
48
+ Date.new(_y.to_i, _m.to_i, _d.to_i)
49
+ end
50
+
51
+ def last_date
52
+ # We are guaranteed _m and _d to exists unless @data is a malformed
53
+ # hash, so we need to do this nasty "return nil", sorry...
54
+ # TODO: I'm open to change this to a cleaner algorithm :-)
55
+ return nil unless _y = @data.keys.max
56
+ return nil unless _m = @data[_y].keys.max
57
+ return nil unless _d = @data[_y][_m].keys.max
58
+ Date.new(_y.to_i, _m.to_i, _d.to_i)
59
+ end
60
+
61
+ end
62
+ end
63
+ end
@@ -3,7 +3,9 @@ module Mongoid #:nodoc:
3
3
  module Tracking
4
4
  # This internal class handles all interaction for a track field.
5
5
  class Tracker
6
-
6
+
7
+ include Readers
8
+
7
9
  def initialize(owner, field, aggregate_data)
8
10
  @owner, @for = owner, field
9
11
  @for_data = @owner.internal_track_name(@for)
@@ -92,62 +94,6 @@ module Mongoid #:nodoc:
92
94
  end
93
95
  end
94
96
 
95
-
96
- # Access methods
97
- def today
98
- data_for(Date.today)
99
- end
100
-
101
- def yesterday
102
- data_for(Date.today - 1)
103
- end
104
-
105
- def first_value
106
- data_for(first_date)
107
- end
108
-
109
- def last_value
110
- data_for(last_date)
111
- end
112
-
113
- def last_days(how_much = 7)
114
- return [today] unless how_much > 0
115
- date, values = Date.today, []
116
- (date - how_much.abs + 1).step(date) {|d| values << data_for(d) }
117
- values
118
- end
119
-
120
- def on(date)
121
- return date.collect {|d| data_for(d)} if date.is_a?(Range)
122
- data_for(date)
123
- end
124
-
125
- def all_values
126
- on(first_date..last_date) if first_date
127
- end
128
-
129
- # Utility methods
130
- def first_date
131
- # We are guaranteed _m and _d to exists unless @data is a malformed
132
- # hash, so we need to do this nasty "return nil", sorry...
133
- # TODO: I'm open to change this to a cleaner algorithm :-)
134
- return nil unless _y = @data.keys.min
135
- return nil unless _m = @data[_y].keys.min
136
- return nil unless _d = @data[_y][_m].keys.min
137
- Date.new(_y.to_i, _m.to_i, _d.to_i)
138
- end
139
-
140
- def last_date
141
- # We are guaranteed _m and _d to exists unless @data is a malformed
142
- # hash, so we need to do this nasty "return nil", sorry...
143
- # TODO: I'm open to change this to a cleaner algorithm :-)
144
- return nil unless _y = @data.keys.max
145
- return nil unless _m = @data[_y].keys.max
146
- return nil unless _d = @data[_y][_m].keys.max
147
- Date.new(_y.to_i, _m.to_i, _d.to_i)
148
- end
149
-
150
- # Private methods
151
97
  private
152
98
  def data_for(date)
153
99
  return nil if date.nil?
@@ -184,7 +130,7 @@ module Mongoid #:nodoc:
184
130
  end
185
131
  end
186
132
 
187
- # WARNING: This is +only+ for debugging pourposes (rspec y tal)
133
+ # WARNING: This is +only+ for debugging purposes (rspec y tal)
188
134
  def _original_hash
189
135
  @data
190
136
  end
@@ -20,11 +20,9 @@ module Mongoid #:nodoc:
20
20
  @criteria.send(name)
21
21
  end
22
22
 
23
- # Define all accessors here. Basically we are delegating to the Track
23
+ # Define all readers here. Basically we are delegating to the Track
24
24
  # object for every object in the criteria
25
- [ :today, :yesterday, :last_days, :all_values, :first_value, :last_value,
26
- :first_date, :last_date
27
- ].each {|name|
25
+ Readers.instance_methods.each {|name|
28
26
  define_method(name) do |*args|
29
27
  return nil unless @track_field
30
28
  if @key
data/lib/trackoid.rb CHANGED
@@ -3,6 +3,7 @@ require 'rubygems'
3
3
  gem "mongoid", ">= 1.9.0"
4
4
 
5
5
  require 'trackoid/errors'
6
+ require 'trackoid/readers'
6
7
  require 'trackoid/tracker'
7
8
  require 'trackoid/aggregates'
8
9
  require 'trackoid/tracker_aggregates'
@@ -0,0 +1,77 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ class Test
4
+ include Mongoid::Document
5
+ include Mongoid::Tracking
6
+
7
+ field :name # Dummy field
8
+ track :visits
9
+ end
10
+
11
+ describe "Testing Readers with a model" do
12
+ before do
13
+ Test.delete_all
14
+ Test.create(:name => "test")
15
+ @object_id = Test.first.id
16
+ @mock = Test.find(@object_id)
17
+ end
18
+
19
+ it "should return the correct values for a range using 'on'" do
20
+ @mock.visits.set(1, "2010-07-11")
21
+ @mock.visits.set(2, "2010-07-12")
22
+ @mock.visits.set(3, "2010-07-13")
23
+
24
+ range = Date.parse("2010-07-11")..Date.parse("2010-07-13")
25
+ @mock.visits.on(range).should == [1, 2, 3]
26
+ end
27
+
28
+
29
+ it "should return the correct values for .all_values" do
30
+ @mock.visits.set(1, "2010-07-11")
31
+ @mock.visits.set(2, "2010-07-12")
32
+ @mock.visits.set(3, "2010-07-13")
33
+
34
+ @mock.visits.all_values.should == [1, 2, 3]
35
+ end
36
+
37
+ it "should return the correct values for .all_values (Take II)" do
38
+ @mock.visits.set(5, "2010-07-01")
39
+ @mock.visits.set(10, "2010-07-30")
40
+
41
+ @mock.visits.all_values.should == [5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10]
42
+ @mock.visits.last_value.should == 10
43
+ @mock.visits.first_value.should == 5
44
+ end
45
+
46
+ end
47
+
48
+
49
+ describe "Testing Readers with an empty model" do
50
+ before do
51
+ Test.delete_all
52
+ Test.create(:name => "test")
53
+ @object_id = Test.first.id
54
+ @mock = Test.first
55
+ end
56
+
57
+ it "should return nil for .first_date" do
58
+ @mock.visits.first_date.should be_nil
59
+ end
60
+
61
+ it "should return nil for .last_date" do
62
+ @mock.visits.last_date.should be_nil
63
+ end
64
+
65
+ it "should return nil for .first_value" do
66
+ @mock.visits.first_value.should be_nil
67
+ end
68
+
69
+ it "should return nil for .last_value" do
70
+ @mock.visits.last_value.should be_nil
71
+ end
72
+
73
+ it "should return nil for .all_values" do
74
+ @mock.visits.all_values.should be_nil
75
+ end
76
+ end
77
+
data/spec/spec.opts CHANGED
@@ -1,2 +1,2 @@
1
1
  --color
2
- --format nested
2
+ --format documentation
@@ -135,7 +135,7 @@ describe Mongoid::Tracking do
135
135
  @mock.visits.last_days(2).sum.should == 4
136
136
  end
137
137
 
138
- it "should correctly handle the last 7 days" do
138
+ it "should correctly handle the 7 days" do
139
139
  @mock.visits.last_days.should == [0, 0, 0, 0, 0, 2, 2]
140
140
  end
141
141
 
@@ -161,62 +161,7 @@ describe Mongoid::Tracking do
161
161
  end
162
162
  end
163
163
 
164
- describe "Testing accessors with an empty model" do
165
- before do
166
- Test.delete_all
167
- Test.create(:name => "test")
168
- @object_id = Test.first.id
169
- @mock = Test.first
170
- end
171
-
172
- it "should return nil for .first_date" do
173
- @mock.visits.first_date.should be_nil
174
- end
175
-
176
- it "should return nil for .last_date" do
177
- @mock.visits.last_date.should be_nil
178
- end
179
-
180
- it "should return nil for .first_date" do
181
- @mock.visits.first_value.should be_nil
182
- end
183
-
184
- it "should return nil for .last_value" do
185
- @mock.visits.last_value.should be_nil
186
- end
187
-
188
- it "should return nil for .all_values" do
189
- @mock.visits.all_values.should be_nil
190
- end
191
- end
192
-
193
- describe "Testing new range accessors with an empty model" do
194
- before do
195
- Test.delete_all
196
- Test.create(:name => "test")
197
- @object_id = Test.first.id
198
- @mock = Test.first
199
- end
200
-
201
- it "should return the correct values for .all_values" do
202
- @mock.visits.set(1, "2010-07-11")
203
- @mock.visits.set(2, "2010-07-12")
204
- @mock.visits.set(3, "2010-07-13")
205
-
206
- @mock.visits.all_values.should == [1, 2, 3]
207
- end
208
-
209
- it "should return the correct values for .all_values (Take II)" do
210
- @mock.visits.set(5, "2010-07-01")
211
- @mock.visits.set(10, "2010-07-30")
212
-
213
- @mock.visits.all_values.should == [5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10]
214
- @mock.visits.last_value.should == 10
215
- @mock.visits.first_value.should == 5
216
- end
217
- end
218
-
219
- context "testing accessor operations without reloading models" do
164
+ context "testing reader operations without reloading models" do
220
165
  before(:all) do
221
166
  Test.delete_all
222
167
  Test.create(:name => "test")
data/trackoid.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{trackoid}
8
- s.version = "0.1.11"
8
+ s.version = "0.1.12"
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"]
12
- s.date = %q{2011-03-11}
12
+ s.date = %q{2011-03-24}
13
13
  s.description = %q{Trackoid uses an embeddable approach to track analytics data using the poweful features of MongoDB for scalability}
14
14
  s.email = %q{josemiguel@perezruiz.com}
15
15
  s.extra_rdoc_files = [
@@ -25,10 +25,12 @@ Gem::Specification.new do |s|
25
25
  "lib/trackoid.rb",
26
26
  "lib/trackoid/aggregates.rb",
27
27
  "lib/trackoid/errors.rb",
28
+ "lib/trackoid/readers.rb",
28
29
  "lib/trackoid/tracker.rb",
29
30
  "lib/trackoid/tracker_aggregates.rb",
30
31
  "lib/trackoid/tracking.rb",
31
32
  "spec/aggregates_spec.rb",
33
+ "spec/readers_spec.rb",
32
34
  "spec/spec.opts",
33
35
  "spec/spec_helper.rb",
34
36
  "spec/trackoid_spec.rb",
@@ -40,6 +42,7 @@ Gem::Specification.new do |s|
40
42
  s.summary = %q{Trackoid is an easy scalable analytics tracker using MongoDB and Mongoid}
41
43
  s.test_files = [
42
44
  "spec/aggregates_spec.rb",
45
+ "spec/readers_spec.rb",
43
46
  "spec/spec_helper.rb",
44
47
  "spec/trackoid_spec.rb"
45
48
  ]
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 11
9
- version: 0.1.11
8
+ - 12
9
+ version: 0.1.12
10
10
  platform: ruby
11
11
  authors:
12
12
  - Jose Miguel Perez
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-03-11 00:00:00 +01:00
17
+ date: 2011-03-24 00:00:00 +01:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -50,10 +50,12 @@ files:
50
50
  - lib/trackoid.rb
51
51
  - lib/trackoid/aggregates.rb
52
52
  - lib/trackoid/errors.rb
53
+ - lib/trackoid/readers.rb
53
54
  - lib/trackoid/tracker.rb
54
55
  - lib/trackoid/tracker_aggregates.rb
55
56
  - lib/trackoid/tracking.rb
56
57
  - spec/aggregates_spec.rb
58
+ - spec/readers_spec.rb
57
59
  - spec/spec.opts
58
60
  - spec/spec_helper.rb
59
61
  - spec/trackoid_spec.rb
@@ -92,5 +94,6 @@ specification_version: 3
92
94
  summary: Trackoid is an easy scalable analytics tracker using MongoDB and Mongoid
93
95
  test_files:
94
96
  - spec/aggregates_spec.rb
97
+ - spec/readers_spec.rb
95
98
  - spec/spec_helper.rb
96
99
  - spec/trackoid_spec.rb