trackoid_mongoid4 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.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +24 -0
- data/.rspec +3 -0
- data/.travis.yml +14 -0
- data/Gemfile +11 -0
- data/LICENSE +20 -0
- data/README.md +318 -0
- data/Rakefile +7 -0
- data/config/mongoid.yml +6 -0
- data/lib/mongoid/tracking/aggregates.rb +162 -0
- data/lib/mongoid/tracking/core_ext/range.rb +53 -0
- data/lib/mongoid/tracking/core_ext/time.rb +52 -0
- data/lib/mongoid/tracking/core_ext.rb +3 -0
- data/lib/mongoid/tracking/errors.rb +40 -0
- data/lib/mongoid/tracking/reader_extender.rb +92 -0
- data/lib/mongoid/tracking/readers.rb +85 -0
- data/lib/mongoid/tracking/tracker.rb +243 -0
- data/lib/mongoid/tracking/tracker_aggregates.rb +42 -0
- data/lib/mongoid/tracking.rb +112 -0
- data/lib/trackoid_mongoid4/version.rb +5 -0
- data/lib/trackoid_mongoid4.rb +12 -0
- data/spec/aggregates_spec.rb +490 -0
- data/spec/embedded_spec.rb +96 -0
- data/spec/ext/range_spec.rb +114 -0
- data/spec/ext/time_spec.rb +142 -0
- data/spec/reader_extender_spec.rb +57 -0
- data/spec/readers_spec.rb +93 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/timezone_spec.rb +303 -0
- data/spec/trackoid_mongoid4_spec.rb +257 -0
- data/trackoid_mongoid4.gemspec +24 -0
- metadata +143 -0
@@ -0,0 +1,490 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
class TestModel
|
4
|
+
include Mongoid::Document
|
5
|
+
include Mongoid::Tracking
|
6
|
+
|
7
|
+
field :name # Dummy field
|
8
|
+
|
9
|
+
# Note that references to "track" and "aggregate" in this test are mixed
|
10
|
+
# for testing purposes. Trackoid does not make any difference in the
|
11
|
+
# declaration order of tracking fields and aggregate tokens.
|
12
|
+
track :visits
|
13
|
+
aggregate :browsers do |b| b.split.first.downcase if b; end
|
14
|
+
|
15
|
+
track :uniques
|
16
|
+
aggregate :referers do |r| r.split.last.downcase if r; end
|
17
|
+
end
|
18
|
+
|
19
|
+
class SecondTestModel
|
20
|
+
include Mongoid::Document
|
21
|
+
include Mongoid::Tracking
|
22
|
+
|
23
|
+
field :name # Dummy field
|
24
|
+
track :something
|
25
|
+
|
26
|
+
aggregate :aggregate_one do 1 end
|
27
|
+
aggregate :aggregate_two do "p" end
|
28
|
+
aggregate :aggregate_three do BSON::ObjectId.new end
|
29
|
+
aggregate :aggregate_four do Time.now end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Namespaced models to test avoid name collisions
|
33
|
+
# Collitions may happen when declaring internal aggregate classes for a model
|
34
|
+
# which has the same name as other models in another namespace
|
35
|
+
module MyCompany
|
36
|
+
class TestPerson
|
37
|
+
include Mongoid::Document
|
38
|
+
include Mongoid::Tracking
|
39
|
+
|
40
|
+
field :my_name
|
41
|
+
|
42
|
+
track :logins
|
43
|
+
aggregate :initials do |n| n.to_s[0]; end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module YourCompany
|
48
|
+
class TestPerson
|
49
|
+
include Mongoid::Document
|
50
|
+
include Mongoid::Tracking
|
51
|
+
|
52
|
+
field :your_name
|
53
|
+
|
54
|
+
track :logins
|
55
|
+
aggregate :initials do |n| n.to_s[0]; end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
describe Mongoid::Tracking::Aggregates do
|
61
|
+
|
62
|
+
before(:all) do
|
63
|
+
@mock = TestModel.new(:name => "TestInstance")
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should define a class model named after the original model" do
|
67
|
+
expect(defined?(TestModelAggregates)).not_to eq(nil)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should define a class model named after the original second model" do
|
71
|
+
expect(defined?(SecondTestModelAggregates)).not_to eq(nil)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should create a has_many relationship in the original model" do
|
75
|
+
# Note that due to ActiveSupport "class_inheritable_accessor" this method
|
76
|
+
# is available both as class method and instance method.
|
77
|
+
expect(@mock.class.method_defined?(:browsers_accessor)).to eq true
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should have the aggregates klass in a class/instance var" do
|
81
|
+
# Note that due to ActiveSupport "class_inheritable_accessor" this method
|
82
|
+
# is available both as class method and instance method.
|
83
|
+
@mock.class.aggregate_klass == TestModelAggregates
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should create a hash in the class with all aggregate fields" do
|
87
|
+
# Note that due to ActiveSupport "class_inheritable_accessor" this method
|
88
|
+
# is available both as class method and instance method.
|
89
|
+
expect(@mock.class.aggregate_fields.keys.to_set).to eq [ :browsers, :referers ].to_set
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should create an array in the class with all aggregate fields even when monkey patching" do
|
93
|
+
class TestModel
|
94
|
+
aggregate :quarters do |q|
|
95
|
+
"Q1";
|
96
|
+
end
|
97
|
+
end
|
98
|
+
expect(@mock.class.aggregate_fields.keys.to_set).to eq [ :browsers, :referers, :quarters ].to_set
|
99
|
+
end
|
100
|
+
|
101
|
+
it "the aggregated class should have the same tracking fields as the parent class" do
|
102
|
+
expect(TestModelAggregates.tracked_fields).to eq TestModel.tracked_fields
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should raise error if we try to add an aggregation token twice" do
|
106
|
+
expect {
|
107
|
+
class TestModel
|
108
|
+
aggregate :referers do
|
109
|
+
"(none)"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
}.to raise_error Mongoid::Tracking::Errors::AggregationAlreadyDefined
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should raise error if we try to use 'hours' as aggregate" do
|
116
|
+
expect {
|
117
|
+
class TestModel
|
118
|
+
aggregate :hours do
|
119
|
+
"(none)"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
}.to raise_error Mongoid::Tracking::Errors::AggregationNameDeprecated
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should have Mongoid accessors defined" do
|
126
|
+
tm = TestModel.create(:name => "Dummy")
|
127
|
+
expect(tm.send(tm.class.send(:internal_accessor_name, "browsers")).class).to eq Mongoid::Relations::Targets::Enumerable
|
128
|
+
expect(tm.send(tm.class.send(:internal_accessor_name, "referers")).class).to eq Mongoid::Relations::Targets::Enumerable
|
129
|
+
expect(tm.send(tm.class.send(:internal_accessor_name, "quarters")).class).to eq Mongoid::Relations::Targets::Enumerable
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should indicate this is an aggregated traking object with aggregated?" do
|
133
|
+
expect(@mock.aggregated?).to eq true
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should indicate this is an aggregated class with aggregated?" do
|
137
|
+
expect(@mock.class.aggregated?).to eq true
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should raise error if already defined class with the same aggregated klass name" do
|
141
|
+
expect {
|
142
|
+
class MockTestAggregates
|
143
|
+
def dummy; true; end
|
144
|
+
end
|
145
|
+
class MockTest
|
146
|
+
include Mongoid::Document
|
147
|
+
include Mongoid::Tracking
|
148
|
+
track :something
|
149
|
+
aggregate :other_something do
|
150
|
+
"other"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
}.to raise_error Mongoid::Tracking::Errors::ClassAlreadyDefined
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should NOT raise error if the already defined class is our aggregated model" do
|
157
|
+
expect {
|
158
|
+
class MockTest2
|
159
|
+
include Mongoid::Document
|
160
|
+
include Mongoid::Tracking
|
161
|
+
track :something
|
162
|
+
end
|
163
|
+
class MockTest2
|
164
|
+
include Mongoid::Document
|
165
|
+
include Mongoid::Tracking
|
166
|
+
track :something_else
|
167
|
+
aggregate :other_something do
|
168
|
+
"other"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
}.not_to raise_error #Mongoid::Tracking::Errors::ClassAlreadyDefined
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should raise error although the already defined class includes tracking" do
|
175
|
+
expect {
|
176
|
+
class MockTest3Aggregates
|
177
|
+
include Mongoid::Document
|
178
|
+
include Mongoid::Tracking
|
179
|
+
track :something
|
180
|
+
end
|
181
|
+
class MockTest3
|
182
|
+
include Mongoid::Document
|
183
|
+
include Mongoid::Tracking
|
184
|
+
track :something_else
|
185
|
+
aggregate :other_something do
|
186
|
+
"other"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
}.to raise_error Mongoid::Tracking::Errors::ClassAlreadyDefined
|
190
|
+
end
|
191
|
+
|
192
|
+
describe "testing different object class for aggregation key" do
|
193
|
+
let(:second_test_model) do
|
194
|
+
SecondTestModel.create(name: "test")
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should correctly save all aggregation keys as strings (inc)" do
|
198
|
+
second_test_model.something("test").inc
|
199
|
+
expect(second_test_model.something.aggregate_one.first.key.is_a?(String)).to eq true
|
200
|
+
expect(second_test_model.something.aggregate_two.first.key.is_a?(String)).to eq true
|
201
|
+
expect(second_test_model.something.aggregate_three.first.key.is_a?(String)).to eq true
|
202
|
+
expect(second_test_model.something.aggregate_four.first.key.is_a?(String)).to eq true
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should correctly save all aggregation keys as strings (set)" do
|
206
|
+
second_test_model.something("test").set(5)
|
207
|
+
expect(second_test_model.something.aggregate_one.first.key.is_a?(String)).to eq true
|
208
|
+
expect(second_test_model.something.aggregate_two.first.key.is_a?(String)).to eq true
|
209
|
+
expect(second_test_model.something.aggregate_three.first.key.is_a?(String)).to eq true
|
210
|
+
expect(second_test_model.something.aggregate_four.first.key.is_a?(String)).to eq true
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
describe "when tracking a model with aggregation data" do
|
215
|
+
let(:test_model) do
|
216
|
+
TestModel.create(:name => "test")
|
217
|
+
end
|
218
|
+
|
219
|
+
it "calling an aggregation scope should return the appropiate class" do
|
220
|
+
expect(test_model.browsers.class).to eq Mongoid::Tracking::TrackerAggregates
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should increment visits for all aggregated instances" do
|
224
|
+
test_model.visits("Mozilla Firefox").inc
|
225
|
+
expect(test_model.browsers.count).to eq 1
|
226
|
+
expect(test_model.referers.count).to eq 1
|
227
|
+
expect(test_model.quarters.count).to eq 1
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should increment visits for specific aggregation keys" do
|
231
|
+
test_model.visits("Mozilla Firefox").inc
|
232
|
+
expect(test_model.browsers("mozilla").size).to eq 1
|
233
|
+
expect(test_model.referers("firefox").size).to eq 1
|
234
|
+
expect(test_model.quarters("Q1").size).to eq 1
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should NOT increment visits for different aggregation keys" do
|
238
|
+
expect(test_model.browsers("internet explorer").size).to eq 0
|
239
|
+
expect(test_model.referers("yahoo slurp").size).to eq 0
|
240
|
+
expect(test_model.quarters("Q2").size).to eq 0
|
241
|
+
end
|
242
|
+
|
243
|
+
it "should have 1 visits today" do
|
244
|
+
test_model.visits("Mozilla Firefox").inc
|
245
|
+
expect(test_model.visits.browsers.today).to eq [["mozilla", 1]]
|
246
|
+
expect(test_model.visits.referers.today).to eq [["firefox", 1]]
|
247
|
+
end
|
248
|
+
|
249
|
+
it "should have 0 visits yesterday" do
|
250
|
+
test_model.visits("Mozilla Firefox").inc
|
251
|
+
expect(test_model.visits.browsers.yesterday).to eq [["mozilla", 0]]
|
252
|
+
expect(test_model.visits.referers.yesterday).to eq [["firefox", 0]]
|
253
|
+
end
|
254
|
+
|
255
|
+
it "should have 1 visits last 7 days" do
|
256
|
+
test_model.visits("Mozilla Firefox").inc
|
257
|
+
expect(test_model.visits.browsers.last_days(7)).to eq [["mozilla", [0, 0, 0, 0, 0, 0, 1]]]
|
258
|
+
expect(test_model.visits.referers.last_days(7)).to eq [["firefox", [0, 0, 0, 0, 0, 0, 1]]]
|
259
|
+
end
|
260
|
+
|
261
|
+
it "should work also for arbitrary days" do
|
262
|
+
test_model.visits("Mozilla Firefox").inc
|
263
|
+
expect(test_model.visits.browsers.last_days(15)).to eq [["mozilla", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]]]
|
264
|
+
expect(test_model.visits.referers.last_days(15)).to eq [["firefox", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]]]
|
265
|
+
end
|
266
|
+
|
267
|
+
it "should work adding 1 visit with different aggregation data" do
|
268
|
+
test_model.visits("Mozilla Firefox").inc
|
269
|
+
test_model.visits("Google Chrome").inc
|
270
|
+
expect(test_model.visits.browsers.today).to match [["mozilla", 1], ["google", 1]]
|
271
|
+
expect(test_model.visits.referers.today).to match [["firefox", 1], ["chrome", 1]]
|
272
|
+
|
273
|
+
# Just for testing array manipulations
|
274
|
+
expect(test_model.visits.browsers.today.inject(0) {|total, c| c.last + total }).to eq 2
|
275
|
+
end
|
276
|
+
|
277
|
+
it "should return only values when specifying the aggregation key" do
|
278
|
+
test_model.visits("Mozilla Firefox").inc
|
279
|
+
expect(test_model.visits.browsers("mozilla").today).to eq 1
|
280
|
+
end
|
281
|
+
|
282
|
+
it "should work also with set" do
|
283
|
+
test_model.visits("Mozilla Firefox").inc
|
284
|
+
test_model.visits("Google Chrome").set(5)
|
285
|
+
expect(test_model.visits.browsers.today).to match [["mozilla", 1], ["google", 5]]
|
286
|
+
expect(test_model.visits.referers.today).to match [["firefox", 1], ["chrome", 5]]
|
287
|
+
expect(test_model.visits.today).to eq 5
|
288
|
+
end
|
289
|
+
|
290
|
+
it "should work without aggregation information" do
|
291
|
+
test_model.visits("Mozilla Firefox").set(1)
|
292
|
+
test_model.visits("Google Chrome").set(6)
|
293
|
+
test_model.visits.inc
|
294
|
+
|
295
|
+
|
296
|
+
expect(test_model.visits.browsers.today).to match [["mozilla", 1], ["google", 6]]
|
297
|
+
expect(test_model.visits.referers.today).to match [["firefox", 1], ["chrome", 6]]
|
298
|
+
|
299
|
+
# A more throughout test would check totals...
|
300
|
+
visits_today = test_model.visits.today
|
301
|
+
visits_today_with_browser = test_model.visits.browsers.today.inject(0) {|total, c| c.last + total }
|
302
|
+
expect(visits_today).to eq visits_today_with_browser
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
describe "When using reset method for aggregates" do
|
307
|
+
let(:test_model) do
|
308
|
+
TestModel.create(:name => "test")
|
309
|
+
end
|
310
|
+
|
311
|
+
before(:each) do
|
312
|
+
test_model.visits("Mozilla Firefox").set(1, "2010-07-11")
|
313
|
+
test_model.visits("Google Chrome").set(2, "2010-07-11")
|
314
|
+
test_model.visits("Internet Explorer").set(3, "2010-07-11")
|
315
|
+
|
316
|
+
test_model.visits("Mozilla Firefox").set(4, "2010-07-14")
|
317
|
+
test_model.visits("Google Chrome").set(5, "2010-07-14")
|
318
|
+
test_model.visits("Internet Explorer").set(6, "2010-07-14")
|
319
|
+
|
320
|
+
test_model.uniques("Mozilla Firefox").set(1, "2010-07-11")
|
321
|
+
test_model.uniques("Google Chrome").set(2, "2010-07-11")
|
322
|
+
test_model.uniques("Internet Explorer").set(3, "2010-07-11")
|
323
|
+
|
324
|
+
test_model.uniques("Mozilla Firefox").set(4, "2010-07-14")
|
325
|
+
test_model.uniques("Google Chrome").set(5, "2010-07-14")
|
326
|
+
test_model.uniques("Internet Explorer").set(6, "2010-07-14")
|
327
|
+
end
|
328
|
+
|
329
|
+
it "should have the correct values when using a value" do
|
330
|
+
test_model.visits.reset(99, "2010-07-14")
|
331
|
+
|
332
|
+
expect(test_model.visits.on("2010-07-14")).to eq 99
|
333
|
+
expect(test_model.visits.browsers.all_values).to match [
|
334
|
+
["mozilla", [1, 0, 0, 99]],
|
335
|
+
["google", [2, 0, 0, 99]],
|
336
|
+
["internet", [3, 0, 0, 99]]
|
337
|
+
]
|
338
|
+
expect(test_model.visits.referers.all_values).to match [
|
339
|
+
["firefox", [1, 0, 0, 99]],
|
340
|
+
["chrome", [2, 0, 0, 99]],
|
341
|
+
["explorer", [3, 0, 0, 99]]
|
342
|
+
]
|
343
|
+
end
|
344
|
+
|
345
|
+
it "should delete the values when using nil" do
|
346
|
+
test_model.visits.reset(nil, "2010-07-14")
|
347
|
+
expect(test_model.visits.on("2010-07-14")).to eq 0
|
348
|
+
expect(test_model.visits.browsers.all_values).to match [
|
349
|
+
["mozilla", [1]],
|
350
|
+
["google", [2]],
|
351
|
+
["internet", [3]]
|
352
|
+
]
|
353
|
+
expect(test_model.visits.referers.all_values).to match [
|
354
|
+
["firefox", [1]],
|
355
|
+
["chrome", [2]],
|
356
|
+
["explorer", [3]]
|
357
|
+
]
|
358
|
+
end
|
359
|
+
|
360
|
+
it "erase method sould also work" do
|
361
|
+
test_model.visits.erase("2010-07-14")
|
362
|
+
|
363
|
+
expect(test_model.visits.on("2010-07-14")).to eq 0
|
364
|
+
expect(test_model.visits.browsers.all_values).to match [
|
365
|
+
["mozilla", [1]],
|
366
|
+
["google", [2]],
|
367
|
+
["internet", [3]]
|
368
|
+
]
|
369
|
+
end
|
370
|
+
|
371
|
+
it "should reset the correct tracking fields" do
|
372
|
+
test_model.visits.reset(99, "2010-07-14")
|
373
|
+
|
374
|
+
expect(test_model.uniques.on("2010-07-14")).to eq 6
|
375
|
+
expect(test_model.uniques.browsers.all_values).to match [
|
376
|
+
["mozilla", [1, 0, 0, 4]],
|
377
|
+
["google", [2, 0, 0, 5]],
|
378
|
+
["internet", [3, 0, 0, 6]]
|
379
|
+
]
|
380
|
+
expect(test_model.uniques.referers.all_values).to match [
|
381
|
+
["firefox", [1, 0, 0, 4]],
|
382
|
+
["chrome", [2, 0, 0, 5]],
|
383
|
+
["explorer", [3, 0, 0, 6]]
|
384
|
+
]
|
385
|
+
end
|
386
|
+
|
387
|
+
it "should erase the correct tracking fields" do
|
388
|
+
test_model.visits.erase("2010-07-14")
|
389
|
+
|
390
|
+
expect(test_model.uniques.on("2010-07-14")).to eq 6
|
391
|
+
expect(test_model.uniques.browsers.all_values).to match [
|
392
|
+
["mozilla", [1, 0, 0, 4]],
|
393
|
+
["google", [2, 0, 0, 5]],
|
394
|
+
["internet", [3, 0, 0, 6]]
|
395
|
+
]
|
396
|
+
expect(test_model.uniques.referers.all_values).to match [
|
397
|
+
["firefox", [1, 0, 0, 4]],
|
398
|
+
["chrome", [2, 0, 0, 5]],
|
399
|
+
["explorer", [3, 0, 0, 6]]
|
400
|
+
]
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
describe "Testing all accessors" do
|
405
|
+
let(:test_model) { TestModel.create(name: "test") }
|
406
|
+
|
407
|
+
before do
|
408
|
+
# For 'first' values
|
409
|
+
test_model.visits("Mozilla Firefox").set(1, "2010-07-11")
|
410
|
+
test_model.visits("Google Chrome").set(2, "2010-07-12")
|
411
|
+
test_model.visits("Internet Explorer").set(3, "2010-07-13")
|
412
|
+
|
413
|
+
# For 'last' values
|
414
|
+
test_model.visits("Mozilla Firefox").set(4, "2010-07-14")
|
415
|
+
test_model.visits("Google Chrome").set(5, "2010-07-15")
|
416
|
+
test_model.visits("Internet Explorer").set(6, "2010-07-16")
|
417
|
+
end
|
418
|
+
|
419
|
+
it "should return the correct values for .all_values" do
|
420
|
+
expect(test_model.visits.all_values).to eq [1, 2, 3, 4, 5, 6]
|
421
|
+
end
|
422
|
+
|
423
|
+
it "should return the all values for every aggregate" do
|
424
|
+
expect(test_model.visits.browsers.all_values).to match [
|
425
|
+
["mozilla", [1, 0, 0, 4]],
|
426
|
+
["google", [2, 0, 0, 5]],
|
427
|
+
["internet", [3, 0, 0, 6]]
|
428
|
+
]
|
429
|
+
end
|
430
|
+
|
431
|
+
it "should return the correct first_date for every aggregate" do
|
432
|
+
expect(test_model.visits.browsers.first_date).to match [
|
433
|
+
["mozilla", Time.parse("2010-07-11")],
|
434
|
+
["google", Time.parse("2010-07-12")],
|
435
|
+
["internet", Time.parse("2010-07-13")]
|
436
|
+
]
|
437
|
+
end
|
438
|
+
|
439
|
+
it "should return the correct last_date for every aggregate" do
|
440
|
+
expect(test_model.visits.browsers.last_date).to match [
|
441
|
+
["mozilla", Time.parse("2010-07-14")],
|
442
|
+
["google", Time.parse("2010-07-15")],
|
443
|
+
["internet", Time.parse("2010-07-16")]
|
444
|
+
]
|
445
|
+
end
|
446
|
+
|
447
|
+
it "should return the first value for aggregates" do
|
448
|
+
expect(test_model.visits.browsers.first_value).to match [
|
449
|
+
["mozilla", 1],
|
450
|
+
["google", 2],
|
451
|
+
["internet", 3]
|
452
|
+
]
|
453
|
+
end
|
454
|
+
|
455
|
+
it "should return the last value for aggregates" do
|
456
|
+
expect(test_model.visits.browsers.last_value).to match [
|
457
|
+
["mozilla", 4],
|
458
|
+
["google", 5],
|
459
|
+
["internet", 6]
|
460
|
+
]
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
describe "When using models with same name on different namespaces" do
|
465
|
+
let(:test_person1) do
|
466
|
+
MyCompany::TestPerson.create(my_name: "twoixter")
|
467
|
+
end
|
468
|
+
|
469
|
+
let(:test_person2) do
|
470
|
+
YourCompany::TestPerson.create(your_name: "test")
|
471
|
+
end
|
472
|
+
|
473
|
+
before do
|
474
|
+
test_person1.logins("ASCII").set(1, "2012-07-07")
|
475
|
+
test_person1.logins("EBCDIC").set(1, "2012-07-07")
|
476
|
+
|
477
|
+
test_person2.logins("UTF8").set(1, "2012-07-07")
|
478
|
+
test_person2.logins("LATIN1").set(1, "2012-07-07")
|
479
|
+
end
|
480
|
+
|
481
|
+
it "should be different objects" do
|
482
|
+
expect(test_person1.my_name).not_to eq test_person2.your_name
|
483
|
+
end
|
484
|
+
|
485
|
+
it "should yield different aggregates" do
|
486
|
+
expect(test_person1.logins.initials.on("2012-07-07")).to match [["A", 1], ["E", 1]]
|
487
|
+
expect(test_person2.logins.initials.on("2012-07-07")).to match [["U", 1], ["L", 1]]
|
488
|
+
end
|
489
|
+
end
|
490
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
#
|
2
|
+
# This is a simple spec to check if tracking works within embedded documents
|
3
|
+
# If this passes, the rest of the funcionality it's assumed to work (TZ, etc)
|
4
|
+
#
|
5
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
6
|
+
|
7
|
+
class TestEmbedOne
|
8
|
+
include Mongoid::Document
|
9
|
+
field :name # Dummy field
|
10
|
+
embeds_one :embedded_test
|
11
|
+
end
|
12
|
+
|
13
|
+
class TestEmbedMany
|
14
|
+
include Mongoid::Document
|
15
|
+
field :name # Dummy field
|
16
|
+
embeds_many :embedded_test
|
17
|
+
end
|
18
|
+
|
19
|
+
class EmbeddedTest
|
20
|
+
include Mongoid::Document
|
21
|
+
include Mongoid::Tracking
|
22
|
+
field :name # Dummy field
|
23
|
+
track :visits
|
24
|
+
embedded_in :test_embed_one
|
25
|
+
embedded_in :test_embed_many
|
26
|
+
end
|
27
|
+
|
28
|
+
# And Now For Something Completely Different...
|
29
|
+
|
30
|
+
class TestEmbedOuter
|
31
|
+
include Mongoid::Document
|
32
|
+
field :name # Dummy field
|
33
|
+
embeds_one :test_embed_middle
|
34
|
+
end
|
35
|
+
|
36
|
+
class TestEmbedMiddle
|
37
|
+
include Mongoid::Document
|
38
|
+
field :name # Dummy field
|
39
|
+
embedded_in :test_embed_outer
|
40
|
+
embeds_one :test_embed_final
|
41
|
+
end
|
42
|
+
|
43
|
+
class TestEmbedFinal
|
44
|
+
include Mongoid::Document
|
45
|
+
include Mongoid::Tracking
|
46
|
+
field :name # Dummy field
|
47
|
+
track :visits
|
48
|
+
embedded_in :test_embed_middle
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
describe Mongoid::Tracking do
|
53
|
+
|
54
|
+
# Useful to see all MongoDB operations in place while RSpec is working
|
55
|
+
before(:all) do
|
56
|
+
# Moped.logger.level = Logger::DEBUG
|
57
|
+
end
|
58
|
+
|
59
|
+
context "within a document which embeds one or more models with tracking" do
|
60
|
+
let(:today) { Time.now }
|
61
|
+
let(:mock_one) { TestEmbedOne.create(:name => "Parent", :embedded_test => {:name => "Child"}) }
|
62
|
+
let(:mock_many) { TestEmbedMany.create(:name => "Parent", :embedded_test => [{:name => "Child1"}, {:name => "Child2"} ]) }
|
63
|
+
|
64
|
+
it "should have the tracking field working and not bleed to the parent" do
|
65
|
+
expect(mock_one.respond_to?(:visits)).to eq(false)
|
66
|
+
expect(mock_one.embedded_test.respond_to?(:visits)).to eq(true)
|
67
|
+
expect(mock_many.respond_to?(:visits)).to eq(false)
|
68
|
+
expect(mock_many.embedded_test.first.respond_to?(:visits)).to eq(true)
|
69
|
+
expect(mock_many.embedded_test.last.respond_to?(:visits)).to eq(true)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "the tracking data should work fine" do
|
73
|
+
mock_one.embedded_test.visits.inc(today)
|
74
|
+
expect(mock_one.embedded_test.visits.on(today)).to eq 1
|
75
|
+
|
76
|
+
mock_many.embedded_test.first.visits.inc(today)
|
77
|
+
expect(mock_many.embedded_test.first.visits.on(today)).to eq 1
|
78
|
+
expect(mock_many.embedded_test.last.visits.on(today)).to eq 0
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "within a three level embedded model" do
|
83
|
+
let(:today) { Time.now }
|
84
|
+
let(:outer) { TestEmbedOuter.create(:name => "Outer", :test_embed_middle => {:name => "Middle", :test_embed_final => { :name => "Final" }}) }
|
85
|
+
|
86
|
+
it "should just work..." do
|
87
|
+
expect(outer.respond_to?(:visits)).to eq(false)
|
88
|
+
expect(outer.test_embed_middle.respond_to?(:visits)).to eq(false)
|
89
|
+
expect(outer.test_embed_middle.test_embed_final.respond_to?(:visits)).to eq(true)
|
90
|
+
|
91
|
+
outer.test_embed_middle.test_embed_final.visits.inc(today)
|
92
|
+
expect(outer.test_embed_middle.test_embed_final.visits.on(today)).to eq 1
|
93
|
+
expect(outer.test_embed_middle.test_embed_final.visits.on(today - 1.day)).to eq 0
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Range do
|
4
|
+
describe "using the diff method" do
|
5
|
+
before do
|
6
|
+
ENV['TZ'] = 'America/Los_Angeles'
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should work for normal ranges" do
|
10
|
+
expect((0..2).diff).to eq 3
|
11
|
+
expect((0...2).diff).to eq 2
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should work for Time ranges (DAYS)" do
|
15
|
+
now = Time.now
|
16
|
+
inc_range = now..(now + 1*Range::DAYS)
|
17
|
+
exc_range = now...(now + 1*Range::DAYS)
|
18
|
+
|
19
|
+
expect(inc_range.diff).to eq 2
|
20
|
+
expect(exc_range.diff).to eq 1
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should work for Time ranges (HOURS)" do
|
24
|
+
now = Time.now
|
25
|
+
inc_range = now..(now + 10*Range::HOURS)
|
26
|
+
exc_range = now...(now + 10*Range::HOURS)
|
27
|
+
|
28
|
+
expect(inc_range.diff(Range::HOURS)).to eq 11
|
29
|
+
expect(exc_range.diff(Range::HOURS)).to eq 10
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should also work when using helper methods" do
|
33
|
+
now = Time.now
|
34
|
+
inc_range = now..(now + 10*Range::HOURS)
|
35
|
+
exc_range = now...(now + 10*Range::HOURS)
|
36
|
+
|
37
|
+
expect(inc_range.hour_diff).to eq 11
|
38
|
+
expect(exc_range.hour_diff).to eq 10
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should behave like normal ranges for 1 element" do
|
42
|
+
now = Time.now
|
43
|
+
inc_range = now..now
|
44
|
+
exc_range = now...now
|
45
|
+
|
46
|
+
expect(inc_range.diff).to eq 1
|
47
|
+
expect(exc_range.diff).to eq 0
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should keep Time UTC and DST properties" do
|
51
|
+
date1 = Time.local(2011, 4, 1, 0, 0)
|
52
|
+
date2 = Time.utc(2011, 4, 1, 0, 0)
|
53
|
+
|
54
|
+
range1 = date1..date1
|
55
|
+
range2 = date2..date2
|
56
|
+
|
57
|
+
range1.first.should_not be_utc
|
58
|
+
range1.first.should be_dst
|
59
|
+
range2.first.should be_utc
|
60
|
+
range2.first.should_not be_dst
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "using the map method" do
|
65
|
+
it "should work for normal ranges (using enumerator)" do
|
66
|
+
inc_result = (0..2).map.to_a
|
67
|
+
inc_result.should == [0, 1, 2]
|
68
|
+
|
69
|
+
exc_result = (0...2).map.to_a
|
70
|
+
exc_result.should == [0, 1]
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should work for normal ranges (using block)" do
|
74
|
+
inc_result = (0..2).map {|e| e}
|
75
|
+
inc_result.should == [0, 1, 2]
|
76
|
+
|
77
|
+
exc_result = (0...2).map {|e| e}
|
78
|
+
exc_result.should == [0, 1]
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should work for Time ranges" do
|
82
|
+
date = Time.utc(2011, 4, 1, 0, 0)
|
83
|
+
inc_range = date..(date + 5*Range::DAYS)
|
84
|
+
inc_result = inc_range.map {|d| d.to_i_timestamp}
|
85
|
+
inc_result.should == [15065, 15066, 15067, 15068, 15069, 15070]
|
86
|
+
|
87
|
+
exc_range = date...(date + 5*Range::DAYS)
|
88
|
+
exc_result = exc_range.map {|d| d.to_i_timestamp}
|
89
|
+
exc_result.should == [15065, 15066, 15067, 15068, 15069]
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should work for empty excluding Time ranges" do
|
93
|
+
date = Time.utc(2011, 4, 1, 0, 0)
|
94
|
+
exc_range = date...date
|
95
|
+
exc_result = exc_range.map {|d| d.to_i_timestamp}
|
96
|
+
exc_result.should == []
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should return an array if no block given" do
|
100
|
+
date = Time.utc(2011, 4, 1, 0, 0)
|
101
|
+
result = (date..(date + 5*Range::DAYS)).map
|
102
|
+
result.count.should == 6
|
103
|
+
|
104
|
+
# Result is now an array
|
105
|
+
result.map(&:to_i_timestamp).should == [15065, 15066, 15067, 15068, 15069, 15070]
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should also work using helper methods" do
|
109
|
+
date = Time.utc(2011, 4, 1, 0, 0)
|
110
|
+
result = (date..(date + 5*Range::HOURS)).hour_map
|
111
|
+
result.count.should == 6
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|