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 +13 -5
- data/VERSION +1 -1
- data/lib/trackoid/aggregates.rb +3 -48
- data/lib/trackoid/readers.rb +63 -0
- data/lib/trackoid/tracker.rb +4 -58
- data/lib/trackoid/tracker_aggregates.rb +2 -4
- data/lib/trackoid.rb +1 -0
- data/spec/readers_spec.rb +77 -0
- data/spec/spec.opts +1 -1
- data/spec/trackoid_spec.rb +2 -57
- data/trackoid.gemspec +5 -2
- metadata +6 -3
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
|
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
|
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
|
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.
|
1
|
+
0.1.12
|
data/lib/trackoid/aggregates.rb
CHANGED
@@ -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
|
-
:
|
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
|
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
|
data/lib/trackoid/tracker.rb
CHANGED
@@ -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
|
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
|
23
|
+
# Define all readers here. Basically we are delegating to the Track
|
24
24
|
# object for every object in the criteria
|
25
|
-
|
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
@@ -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
|
2
|
+
--format documentation
|
data/spec/trackoid_spec.rb
CHANGED
@@ -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
|
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
|
-
|
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.
|
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-
|
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
|
-
-
|
9
|
-
version: 0.1.
|
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-
|
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
|