redis-objects-legacy 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1097 @@
1
+
2
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
+
4
+ require 'redis/objects'
5
+
6
+ class Roster
7
+ include Redis::Objects
8
+ counter :available_slots, :start => 10
9
+ counter :pitchers, :limit => :max_pitchers
10
+ counter :basic
11
+ hash_key :contact_information, :marshal_keys=>{'updated_at'=>true}
12
+ lock :resort, :timeout => 2
13
+ value :starting_pitcher, :marshal => true
14
+ list :player_stats, :marshal => true
15
+ set :outfielders, :marshal => true
16
+ sorted_set :rank
17
+ lock :per_field
18
+
19
+ # global class counters
20
+ counter :total_players_online, :global => true
21
+ set :all_players_online, :global => true
22
+ value :last_player, :global => true
23
+ lock :nasty_global_mutex, :global => true # remember it appends "_lock"
24
+ sorted_set :global_player_leaderboard, :global => true
25
+ hash_key :global_player_online_status, :global => true
26
+
27
+ # custom keys
28
+ counter :player_totals, :key => 'players/#{username}/total'
29
+ list :all_player_stats, :key => 'players:all_stats', :global => true
30
+ set :total_wins, :key => 'players:#{id}:all_stats'
31
+ value :my_rank, :key => 'players:my_rank:#{username}'
32
+
33
+ # now support class interpolation as well. not sure why not previously
34
+ def self.jimmyhat; 350; end
35
+ value :weird_key, :key => 'players:weird_key:#{jimmyhat}', :global => true
36
+
37
+ #callable as key
38
+ counter :daily, :global => true, :key => Proc.new { |roster| "#{roster.name}:#{Time.now.strftime('%Y-%m-%dT%H')}:daily" }
39
+
40
+ # set default expiration
41
+ value :value_with_expiration, :expiration => 10
42
+ value :value_with_expireat, :expireat => Time.now + 10.seconds
43
+ set :set_with_expiration, :expiration => 10
44
+ set :set_with_expireat, :expireat => Time.now + 10.seconds
45
+ list :list_with_expiration, :expiration => 10
46
+ list :list_with_expireat, :expireat => Time.now + 10.seconds
47
+ hash_key :hash_with_expiration, :expiration => 10
48
+ hash_key :hash_with_expireat, :expireat => Time.now + 10.seconds
49
+ counter :counter_with_expiration, :expiration => 10
50
+ counter :counter_with_expireat, :expireat => Time.now + 10.seconds
51
+ sorted_set :sorted_set_with_expiration,:expiration => 10
52
+ sorted_set :sorted_set_with_expireat, :expireat => Time.now + 10.seconds
53
+
54
+ value :value_with_expireat_with_lambda, :expireat => lambda { Time.now + 10.seconds }
55
+
56
+ def initialize(id=1) @id = id end
57
+ def id; @id; end
58
+ def username; "user#{id}"; end
59
+ def max_pitchers; 3; end
60
+ end
61
+
62
+ class VanillaRoster < Roster
63
+ # No explicit Redis::Objects
64
+ end
65
+
66
+ class CustomRoster < Roster
67
+ include Redis::Objects
68
+
69
+ counter :basic # Override
70
+ counter :special # New
71
+ end
72
+
73
+ class MethodRoster
74
+ def increment(attribute, by=1)
75
+ 42
76
+ end
77
+
78
+ def initialize(id=1) @id = id end
79
+ def id; @id; end
80
+ end
81
+
82
+ class CustomMethodRoster < MethodRoster
83
+ include Redis::Objects
84
+
85
+ attr_accessor :counter
86
+ counter :basic
87
+ end
88
+
89
+ class UidRoster < Roster
90
+ attr_accessor :uid
91
+ def initialize(uid=123) @uid = uid end
92
+ end
93
+
94
+ class CustomIdFieldRoster < UidRoster
95
+ redis_id_field :uid
96
+ include Redis::Objects
97
+ counter :basic
98
+ end
99
+
100
+ describe Redis::Objects do
101
+ before do
102
+ @roster = Roster.new
103
+ @roster2 = Roster.new
104
+
105
+ @roster_1 = Roster.new(1)
106
+ @roster_2 = Roster.new(2)
107
+ @roster_3 = Roster.new(3)
108
+
109
+ @vanilla_roster = VanillaRoster.new
110
+ @custom_roster = CustomRoster.new
111
+
112
+ @roster.available_slots.reset
113
+ @roster.pitchers.reset
114
+ @roster.basic.reset
115
+ @roster.resort_lock.clear
116
+ @roster.starting_pitcher.delete
117
+ @roster.player_stats.clear
118
+ @roster.outfielders.clear
119
+ @roster.contact_information.clear
120
+ @roster_1.outfielders.clear
121
+ @roster_2.outfielders.clear
122
+ @roster_3.outfielders.clear
123
+ @roster.redis.del(UNIONSTORE_KEY)
124
+ @roster.redis.del(INTERSTORE_KEY)
125
+ @roster.redis.del(DIFFSTORE_KEY)
126
+
127
+ Roster.total_players_online.reset
128
+ Roster.all_player_stats.clear
129
+ Roster.all_players_online.clear
130
+ Roster.last_player.delete
131
+ Roster.weird_key.clear
132
+
133
+ @roster.player_totals.clear
134
+ @roster.all_player_stats.clear
135
+ @roster.total_wins.clear
136
+ @roster.my_rank.clear
137
+
138
+ @roster.daily.clear
139
+
140
+ @custom_roster.basic.reset
141
+ @custom_roster.special.reset
142
+ end
143
+
144
+ it "should provide a connection method" do
145
+ Roster.redis.should == Redis::Objects.redis
146
+ # Roster.redis.should.be.kind_of(Redis)
147
+ end
148
+
149
+ it "should support interpolation of key names" do
150
+ @roster.player_totals.incr
151
+ @roster.redis.get('players/user1/total').should == '1'
152
+ @roster.redis.get('players/#{username}/total').should.be.nil
153
+ @roster.all_player_stats << 'a'
154
+ @roster.redis.lindex('players:all_stats', 0).should == 'a'
155
+ @roster.total_wins << 'a'
156
+ # test for interpolation of key names
157
+ @roster.redis.smembers('players:#{id}:all_stats').should == []
158
+ @roster.redis.smembers('players:1:all_stats').should == ['a']
159
+ @roster.my_rank = 'a'
160
+ @roster.redis.get('players:my_rank:user1').should == 'a'
161
+ Roster.weird_key = 'tuka'
162
+ Roster.redis.get("players:weird_key:#{Roster.jimmyhat}").should == 'tuka'
163
+
164
+ k = "Roster:#{Time.now.strftime('%Y-%m-%dT%H')}:daily"
165
+ @roster.daily.incr
166
+ @roster.redis.get(k).should == '1'
167
+ end
168
+
169
+ it "should be able to directly assign value of hash" do
170
+ @roster.contact_information['John_Name'] = 'John Doe'
171
+ @roster.contact_information = { 'John_Phone' => '12345678', 'John_Address' => '321 LANE' }
172
+ @roster.contact_information['John_Phone'].should == '12345678'
173
+ @roster.contact_information['John_Address'].should == '321 LANE'
174
+ @roster.contact_information['John_Name'].should.be.nil
175
+ end
176
+
177
+ it "should be able to get/set contact info" do
178
+ @roster.contact_information['John_Phone'] = '123415352'
179
+ @roster.contact_information['John_Address'] = '123 LANE'
180
+ @roster.contact_information['John_Phone'].should == '123415352'
181
+ @roster.contact_information['John_Address'].should == '123 LANE'
182
+ @roster.contact_information['asdasd'].should.be.nil
183
+ @roster.contact_information.size.should == 2
184
+ end
185
+
186
+ it 'should be able to expire keys and then persist them' do
187
+ # on a hash_key
188
+ @roster.contact_information['Jenny_Phone'] = '8675309'
189
+ @roster.contact_information.expire 30
190
+ @roster.contact_information.ttl.should > -1
191
+ @roster.contact_information.ttl.should <= 30
192
+ @roster.contact_information.persist
193
+ @roster.contact_information.ttl.should == -1
194
+ @roster.contact_information['Jenny_Phone'].should == '8675309'
195
+
196
+ # on a value
197
+ @roster.my_rank = 42
198
+ @roster.my_rank.expire 30
199
+ @roster.my_rank.ttl.should > -1
200
+ @roster.my_rank.ttl.should <= 30
201
+ @roster.my_rank.persist
202
+ @roster.my_rank.ttl.should == -1
203
+ @roster.my_rank.to_i.should == 42
204
+ end
205
+
206
+ it "should be marshalling hash keys" do
207
+ @roster.contact_information['updated_at'] = Time.now
208
+ @roster.contact_information['updated_at'].class.should == Time
209
+ end
210
+
211
+ it "should create counter accessors" do
212
+ [:available_slots, :pitchers, :basic].each do |m|
213
+ @roster.respond_to?(m).should == true
214
+ end
215
+ end
216
+
217
+ it "should handle obtaining / clearing locks" do
218
+ # class-level calls for Admin reasons
219
+ # this is a really weird API and I wonder if anyone actually uses it
220
+ id = 88
221
+ roster = Roster.new(id)
222
+ roster.per_field_lock.exists?.should == false
223
+ Roster.obtain_lock(:per_field, id) do
224
+ roster.per_field_lock.exists?.should == true
225
+ end
226
+ roster.per_field_lock.exists?.should == false
227
+
228
+ Roster.clear_lock(:per_field, id)
229
+ roster.per_field_lock.exists?.should == false
230
+ end
231
+
232
+ it "should support increment/decrement of counters" do
233
+ @roster.available_slots.key.should == 'roster:1:available_slots'
234
+ @roster.available_slots.should == 10
235
+
236
+ # math proxy ops
237
+ (@roster.available_slots == 10).should.be.true
238
+ (@roster.available_slots <= 10).should.be.true
239
+ (@roster.available_slots < 11).should.be.true
240
+ (@roster.available_slots > 9).should.be.true
241
+ (@roster.available_slots >= 10).should.be.true
242
+ "#{@roster.available_slots}".should == "10"
243
+
244
+ @roster.available_slots.increment.should == 11
245
+ @roster.available_slots.increment.should == 12
246
+ @roster2.available_slots.increment.should == 13
247
+ @roster2.available_slots.increment(2).should == 15
248
+ @roster.available_slots.decrement.should == 14
249
+ @roster2.available_slots.decrement.should == 13
250
+ @roster.available_slots.decrement.should == 12
251
+ @roster2.available_slots.decrement(4).should == 8
252
+ @roster.available_slots.should == 8
253
+ @roster.available_slots.reset.should.be.true
254
+ @roster.available_slots.should == 10
255
+ @roster.available_slots.reset(15).should.be.true
256
+ @roster.available_slots.should == 15
257
+ @roster.pitchers.increment.should == 1
258
+ @roster.basic.increment.should == 1
259
+ @roster2.basic.decrement.should == 0
260
+ @roster.basic.get.should == 0
261
+ end
262
+
263
+ it "should support class-level increment/decrement of counters" do
264
+ Roster.get_counter(:available_slots, @roster.id).should == 10
265
+ Roster.increment_counter(:available_slots, @roster.id).should == 11
266
+ Roster.increment_counter(:available_slots, @roster.id, 3).should == 14
267
+ Roster.decrement_counter(:available_slots, @roster.id, 2).should == 12
268
+ Roster.decrement_counter(:available_slots, @roster.id).should == 11
269
+ Roster.reset_counter(:available_slots, @roster.id).should == true
270
+ Roster.get_counter(:available_slots, @roster.id).should == 10
271
+ Roster.getset_counter(:available_slots, @roster.id, 555).should == 10
272
+ Roster.get_counter(:available_slots, @roster.id).should == 555
273
+ end
274
+
275
+ it "should support class-level increment/decrement of global counters" do
276
+ Roster.total_players_online.should == 0
277
+ Roster.total_players_online.increment.should == 1
278
+ Roster.total_players_online.decrement.should == 0
279
+ Roster.total_players_online.increment(3).should == 3
280
+ Roster.total_players_online.decrement(2).should == 1
281
+ Roster.total_players_online.reset.should.be.true
282
+ Roster.total_players_online.should == 0
283
+
284
+ Roster.get_counter(:total_players_online).should == 0
285
+ Roster.increment_counter(:total_players_online).should == 1
286
+ Roster.increment_counter(:total_players_online, nil, 3).should == 4
287
+ Roster.decrement_counter(:total_players_online, nil, 2).should == 2
288
+ Roster.decrement_counter(:total_players_online).should == 1
289
+ Roster.reset_counter(:total_players_online).should == true
290
+ Roster.get_counter(:total_players_online).should == 0
291
+ Roster.getset_counter(:total_players_online, nil, 111).should == 0
292
+ Roster.get_counter(:total_players_online).should == 111
293
+ end
294
+
295
+ it "should support class-level manipulation of global objects" do
296
+ Roster.nasty_global_mutex_lock.exists?.should == false
297
+ Roster.nasty_global_mutex_lock do
298
+ Roster.nasty_global_mutex_lock.exists?.should == true
299
+ end
300
+ Roster.nasty_global_mutex_lock.exists?.should == false
301
+
302
+ Roster.global_player_leaderboard.exists?.should == false
303
+ Roster.global_player_leaderboard.add('nate', 22)
304
+ Roster.global_player_leaderboard.add('jim', 11)
305
+ Roster.global_player_leaderboard.rank('nate').should == 1 # 0-based
306
+
307
+ Roster.global_player_online_status.exists?.should == false
308
+ Roster.global_player_online_status['nate'] = 'online'
309
+ Roster.global_player_online_status['jeff'] = 'offline'
310
+ Roster.global_player_online_status['nate'].should == 'online'
311
+ Roster.global_player_online_status['jeff'].should == 'offline'
312
+ Roster.global_player_online_status['bobby'].should.be.nil
313
+ end
314
+
315
+ it "should take an atomic block for increment/decrement" do
316
+ a = false
317
+ @roster.available_slots.should == 10
318
+ @roster.available_slots.decr do |cnt|
319
+ if cnt >= 0
320
+ a = true
321
+ end
322
+ end
323
+ @roster.available_slots.should == 9
324
+ a.should.be.true
325
+
326
+ @roster.available_slots.should == 9
327
+ @roster.available_slots.decr do |cnt|
328
+ @roster.available_slots.should == 8
329
+ false
330
+ end
331
+ @roster.available_slots.should == 8
332
+
333
+ @roster.available_slots.should == 8
334
+ @roster.available_slots.decr do |cnt|
335
+ @roster.available_slots.should == 7
336
+ nil # should rewind
337
+ end
338
+ @roster.available_slots.should == 8
339
+
340
+ @roster.available_slots.should == 8
341
+ @roster.available_slots.decr(4) do |cnt|
342
+ @roster.available_slots.should == 4
343
+ nil # should rewind
344
+ end
345
+ @roster.available_slots.should == 8
346
+
347
+ @roster.available_slots.should == 8
348
+ @roster.available_slots.incr do |cnt|
349
+ if 1 == 2 # should rewind
350
+ true
351
+ end
352
+ end
353
+ @roster.available_slots.should == 8
354
+
355
+ @roster.available_slots.should == 8
356
+ @roster.available_slots.incr(5) do |cnt|
357
+ if 1 == 2 # should rewind
358
+ true
359
+ end
360
+ end
361
+ @roster.available_slots.should == 8
362
+
363
+ @roster.available_slots.should == 8
364
+ @roster.available_slots.incr do |cnt|
365
+ @roster.available_slots.should == 9
366
+ []
367
+ end
368
+ @roster.available_slots.should == 9
369
+
370
+ @roster.available_slots.should == 9
371
+ begin
372
+ @roster.available_slots.decr do |cnt|
373
+ @roster.available_slots.should == 8
374
+ raise 'oops'
375
+ end
376
+ rescue
377
+ end
378
+ @roster.available_slots.should == 9
379
+
380
+ # check return value from the block
381
+ value =
382
+ @roster.available_slots.decr do |cnt|
383
+ @roster.available_slots.should == 8
384
+ 42
385
+ end
386
+ value.should == 42
387
+ @roster.available_slots.should == 8
388
+ end
389
+
390
+ it "should take an atomic block for increment/decrement class methods" do
391
+ a = false
392
+ Roster.get_counter(:available_slots, @roster.id).should == 10
393
+ Roster.decrement_counter(:available_slots, @roster.id) do |cnt|
394
+ if cnt >= 0
395
+ a = true
396
+ end
397
+ end
398
+ Roster.get_counter(:available_slots, @roster.id).should == 9
399
+ a.should.be.true
400
+
401
+ Roster.get_counter(:available_slots, @roster.id).should == 9
402
+ Roster.decrement_counter(:available_slots, @roster.id) do |cnt|
403
+ Roster.get_counter(:available_slots, @roster.id).should == 8
404
+ false
405
+ end
406
+ Roster.get_counter(:available_slots, @roster.id).should == 8
407
+
408
+ Roster.get_counter(:available_slots, @roster.id).should == 8
409
+ Roster.decrement_counter(:available_slots, @roster.id) do |cnt|
410
+ Roster.get_counter(:available_slots, @roster.id).should == 7
411
+ nil # should rewind
412
+ end
413
+ Roster.get_counter(:available_slots, @roster.id).should == 8
414
+
415
+ Roster.get_counter(:available_slots, @roster.id).should == 8
416
+ Roster.decrement_counter(:available_slots, @roster.id, 4) do |cnt|
417
+ Roster.get_counter(:available_slots, @roster.id).should == 4
418
+ nil # should rewind
419
+ end
420
+ Roster.get_counter(:available_slots, @roster.id).should == 8
421
+
422
+ Roster.get_counter(:available_slots, @roster.id).should == 8
423
+ Roster.increment_counter(:available_slots, @roster.id) do |cnt|
424
+ if 1 == 2 # should rewind
425
+ true
426
+ end
427
+ end
428
+ Roster.get_counter(:available_slots, @roster.id).should == 8
429
+
430
+ Roster.get_counter(:available_slots, @roster.id).should == 8
431
+ Roster.increment_counter(:available_slots, @roster.id, 4) do |cnt|
432
+ if 1 == 2 # should rewind
433
+ true
434
+ end
435
+ end
436
+ Roster.get_counter(:available_slots, @roster.id).should == 8
437
+
438
+ Roster.get_counter(:available_slots, @roster.id).should == 8
439
+ Roster.increment_counter(:available_slots, @roster.id) do |cnt|
440
+ Roster.get_counter(:available_slots, @roster.id).should == 9
441
+ []
442
+ end
443
+ Roster.get_counter(:available_slots, @roster.id).should == 9
444
+
445
+ Roster.get_counter(:available_slots, @roster.id).should == 9
446
+ begin
447
+ Roster.decrement_counter(:available_slots, @roster.id) do |cnt|
448
+ Roster.get_counter(:available_slots, @roster.id).should == 8
449
+ raise 'oops'
450
+ end
451
+ rescue
452
+ end
453
+ Roster.get_counter(:available_slots, @roster.id).should == 9
454
+
455
+ # check return value from the block
456
+ value =
457
+ Roster.decrement_counter(:available_slots, @roster.id) do |cnt|
458
+ Roster.get_counter(:available_slots, @roster.id).should == 8
459
+ 42
460
+ end
461
+ value.should == 42
462
+ Roster.get_counter(:available_slots, @roster.id).should == 8
463
+ end
464
+
465
+ it "should properly throw errors on bad counters" do
466
+ error = nil
467
+ begin
468
+ Roster.increment_counter(:badness, 2)
469
+ rescue => error
470
+ end
471
+ error.should.be.kind_of(NoMethodError)
472
+
473
+ error = nil
474
+ begin
475
+ Roster.increment_counter(:available_slots, nil)
476
+ rescue => error
477
+ end
478
+ error.should.be.kind_of(Redis::Objects::MissingID)
479
+
480
+ error = nil
481
+ begin
482
+ Roster.obtain_lock(:badness, 2){}
483
+ rescue => error
484
+ end
485
+ error.should.be.kind_of(Redis::Objects::UndefinedLock)
486
+
487
+ error = nil
488
+ begin
489
+ @roster.available_slots = 42
490
+ rescue => error
491
+ end
492
+ error.should.be.kind_of(NoMethodError)
493
+
494
+ error = nil
495
+ begin
496
+ @roster.available_slots += 69
497
+ rescue => error
498
+ end
499
+ error.should.be.kind_of(NoMethodError)
500
+
501
+ error = nil
502
+ begin
503
+ @roster.available_slots -= 15
504
+ rescue => error
505
+ end
506
+ error.should.be.kind_of(NoMethodError)
507
+ end
508
+
509
+ it "should support obtain_lock as a class method" do
510
+ error = nil
511
+ begin
512
+ Roster.obtain_lock(:resort, 2) do
513
+ Roster.redis.get("roster:2:resort_lock").should.not.be.nil
514
+ end
515
+ rescue => error
516
+ end
517
+
518
+ error.should.be.nil
519
+ Roster.redis.get("roster:2:resort_lock").should.be.nil
520
+ end
521
+
522
+ it "should handle simple values" do
523
+ @roster.starting_pitcher.should == nil
524
+ @roster.starting_pitcher = 'Trevor Hoffman'
525
+ @roster.starting_pitcher.should == 'Trevor Hoffman'
526
+ @roster.starting_pitcher.get.should == 'Trevor Hoffman'
527
+ @roster.starting_pitcher = 'Tom Selleck'
528
+ @roster.starting_pitcher.should == 'Tom Selleck'
529
+ @roster.starting_pitcher.del.should == 1
530
+ @roster.starting_pitcher.should.be.nil
531
+ end
532
+
533
+ it "should handle complex marshaled values" do
534
+ @roster.starting_pitcher.should == nil
535
+ @roster.starting_pitcher = {:json => 'data'}
536
+ @roster.starting_pitcher.should == {:json => 'data'}
537
+ @roster.starting_pitcher.get.should == {:json => 'data'}
538
+ @roster.starting_pitcher.del.should == 1
539
+ @roster.starting_pitcher.should.be.nil
540
+ end
541
+
542
+ it "should be able to directly assign value of list" do
543
+ @roster.player_stats << 'c'
544
+ @roster.player_stats = ['a', 'b']
545
+ @roster.player_stats.get.should == ['a', 'b']
546
+ end
547
+
548
+ it "should handle lists of simple values" do
549
+ @roster.player_stats.should.be.empty
550
+ @roster.player_stats << 'a'
551
+ @roster.player_stats.should == ['a']
552
+ @roster.player_stats.get.should == ['a']
553
+ @roster.player_stats.unshift 'b'
554
+ @roster.player_stats.to_s.should == 'b, a'
555
+ @roster.player_stats.should == ['b','a']
556
+ @roster.player_stats.get.should == ['b','a']
557
+ @roster.player_stats.push 'c'
558
+ @roster.player_stats.should == ['b','a','c']
559
+ @roster.player_stats.get.should == ['b','a','c']
560
+ @roster.player_stats.first.should == 'b'
561
+ @roster.player_stats.last.should == 'c'
562
+ @roster.player_stats << 'd'
563
+ @roster.player_stats.should == ['b','a','c','d']
564
+ @roster.player_stats[1].should == 'a'
565
+ @roster.player_stats[0].should == 'b'
566
+ @roster.player_stats[2].should == 'c'
567
+ @roster.player_stats[3].should == 'd'
568
+ @roster.player_stats.include?('c').should.be.true
569
+ @roster.player_stats.include?('no').should.be.false
570
+ @roster.player_stats.pop.should == 'd'
571
+ @roster.player_stats[0].should == @roster.player_stats.at(0)
572
+ @roster.player_stats[1].should == @roster.player_stats.at(1)
573
+ @roster.player_stats[2].should == @roster.player_stats.at(2)
574
+ @roster.player_stats.should == ['b','a','c']
575
+ @roster.player_stats.get.should == ['b','a','c']
576
+ @roster.player_stats.shift.should == 'b'
577
+ @roster.player_stats.should == ['a','c']
578
+ @roster.player_stats.get.should == ['a','c']
579
+ @roster.player_stats << 'e' << 'f' << 'e'
580
+ @roster.player_stats.should == ['a','c','e','f','e']
581
+ @roster.player_stats.get.should == ['a','c','e','f','e']
582
+ @roster.player_stats.delete('e').should == 2
583
+ @roster.player_stats.should == ['a','c','f']
584
+ @roster.player_stats.get.should == ['a','c','f']
585
+ @roster.player_stats << 'j'
586
+ @roster.player_stats.should == ['a','c','f','j']
587
+ @roster.player_stats[0..2].should == ['a','c','f']
588
+ @roster.player_stats[1, 3].should == ['c','f','j']
589
+ @roster.player_stats.length.should == 4
590
+ @roster.player_stats.size.should == 4
591
+ @roster.player_stats.should == ['a','c','f','j']
592
+ @roster.player_stats.get.should == ['a','c','f','j']
593
+ @roster.player_stats.push *['h','i']
594
+ @roster.player_stats.should == ['a','c','f','j','h','i']
595
+ @roster.player_stats.get.should == ['a','c','f','j','h','i']
596
+
597
+ i = -1
598
+ @roster.player_stats.each do |st|
599
+ st.should == @roster.player_stats[i += 1]
600
+ end
601
+ @roster.player_stats.should == ['a','c','f','j','h','i']
602
+ @roster.player_stats.get.should == ['a','c','f','j','h','i']
603
+
604
+ @roster.player_stats.each_with_index do |st,i|
605
+ st.should == @roster.player_stats[i]
606
+ end
607
+ @roster.player_stats.should == ['a','c','f','j','h','i']
608
+ @roster.player_stats.get.should == ['a','c','f','j','h','i']
609
+
610
+ coll = @roster.player_stats.collect{|st| st}
611
+ coll.should == ['a','c','f','j','h','i']
612
+ @roster.player_stats.should == ['a','c','f','j','h','i']
613
+ @roster.player_stats.get.should == ['a','c','f','j','h','i']
614
+
615
+ @roster.player_stats << 'a'
616
+ coll = @roster.player_stats.select{|st| st == 'a'}
617
+ coll.should == ['a','a']
618
+ @roster.player_stats.should == ['a','c','f','j','h','i','a']
619
+ @roster.player_stats.get.should == ['a','c','f','j','h','i','a']
620
+ end
621
+
622
+ it "should be able to directly assign values of set" do
623
+ @roster.outfielders << 'c'
624
+ @roster.outfielders = ['a', 'b']
625
+ @roster.outfielders.member?('a').should.be.true
626
+ @roster.outfielders.member?('b').should.be.true
627
+ @roster.outfielders.member?('c').should.be.false
628
+ end
629
+
630
+ it "should handle sets of simple values" do
631
+ @roster.outfielders.should.be.empty
632
+ @roster.outfielders << 'a' << 'a' << 'a'
633
+ @roster.outfielders.should == ['a']
634
+ @roster.outfielders.get.should == ['a']
635
+ @roster.outfielders << 'b' << 'b'
636
+ @roster.outfielders.sort.should == ['a','b']
637
+ @roster.outfielders.members.sort.should == ['a','b']
638
+ @roster.outfielders.get.sort.should == ['a','b']
639
+ @roster.outfielders << 'c'
640
+ @roster.outfielders.sort.should == ['a','b','c']
641
+ @roster.outfielders.get.sort.should == ['a','b','c']
642
+ @roster.outfielders.delete('c')
643
+ @roster.outfielders.sort.should == ['a','b']
644
+ @roster.outfielders.get.sort.should == ['a','b']
645
+ @roster.outfielders.length.should == 2
646
+ @roster.outfielders.size.should == 2
647
+
648
+ i = 0
649
+ @roster.outfielders.each do |st|
650
+ i += 1
651
+ end
652
+ i.should == @roster.outfielders.length
653
+
654
+ coll = @roster.outfielders.collect{|st| st}
655
+ coll.sort.should == ['a','b']
656
+ @roster.outfielders.sort.should == ['a','b']
657
+ @roster.outfielders.get.sort.should == ['a','b']
658
+
659
+ @roster.outfielders << 'c'
660
+ @roster.outfielders.member?('c').should.be.true
661
+ @roster.outfielders.include?('c').should.be.true
662
+ @roster.outfielders.member?('no').should.be.false
663
+ coll = @roster.outfielders.select{|st| st == 'c'}
664
+ coll.should == ['c']
665
+ @roster.outfielders.sort.should == ['a','b','c']
666
+ end
667
+
668
+ it "should handle set intersections and unions" do
669
+ @roster_1.outfielders << 'a' << 'b' << 'c' << 'd' << 'e'
670
+ @roster_2.outfielders << 'c' << 'd' << 'e' << 'f' << 'g'
671
+ @roster_3.outfielders << 'a' << 'd' << 'g' << 'l' << 'm'
672
+ @roster_1.outfielders.sort.should == %w(a b c d e)
673
+ @roster_2.outfielders.sort.should == %w(c d e f g)
674
+ @roster_3.outfielders.sort.should == %w(a d g l m)
675
+ (@roster_1.outfielders & @roster_2.outfielders).sort.should == ['c','d','e']
676
+ @roster_1.outfielders.intersection(@roster_2.outfielders).sort.should == ['c','d','e']
677
+ @roster_1.outfielders.intersection(@roster_2.outfielders, @roster_3.outfielders).sort.should == ['d']
678
+ @roster_1.outfielders.intersect(@roster_2.outfielders).sort.should == ['c','d','e']
679
+ @roster_1.outfielders.inter(@roster_2.outfielders, @roster_3.outfielders).sort.should == ['d']
680
+
681
+ @roster_1.outfielders.interstore(INTERSTORE_KEY, @roster_2.outfielders).should == 3
682
+ @roster_1.redis.smembers(INTERSTORE_KEY).sort.map{|v| Marshal.restore(v)}.should == ['c','d','e']
683
+
684
+ @roster_1.outfielders.interstore(INTERSTORE_KEY, @roster_2.outfielders, @roster_3.outfielders).should == 1
685
+ @roster_1.redis.smembers(INTERSTORE_KEY).sort.map{|v| Marshal.restore(v)}.should == ['d']
686
+
687
+ (@roster_1.outfielders | @roster_2.outfielders).sort.should == ['a','b','c','d','e','f','g']
688
+ (@roster_1.outfielders + @roster_2.outfielders).sort.should == ['a','b','c','d','e','f','g']
689
+ @roster_1.outfielders.union(@roster_2.outfielders).sort.should == ['a','b','c','d','e','f','g']
690
+ @roster_1.outfielders.union(@roster_2.outfielders, @roster_3.outfielders).sort.should == ['a','b','c','d','e','f','g','l','m']
691
+
692
+ @roster_1.outfielders.unionstore(UNIONSTORE_KEY, @roster_2.outfielders).should == 7
693
+ @roster_1.redis.smembers(UNIONSTORE_KEY).map{|v| Marshal.restore(v)}.sort.should == ['a','b','c','d','e','f','g']
694
+
695
+ @roster_1.outfielders.unionstore(UNIONSTORE_KEY, @roster_2.outfielders, @roster_3.outfielders).should == 9
696
+ @roster_1.redis.smembers(UNIONSTORE_KEY).map{|v| Marshal.restore(v)}.sort.should == ['a','b','c','d','e','f','g','l','m']
697
+ end
698
+
699
+ it "should handle class-level global lists of simple values" do
700
+ Roster.all_player_stats.should.be.empty
701
+ Roster.all_player_stats << 'a'
702
+ Roster.all_player_stats.should == ['a']
703
+ Roster.all_player_stats.get.should == ['a']
704
+ Roster.all_player_stats.unshift 'b'
705
+ Roster.all_player_stats.to_s.should == 'b, a'
706
+ Roster.all_player_stats.should == ['b','a']
707
+ Roster.all_player_stats.get.should == ['b','a']
708
+ Roster.all_player_stats.push 'c'
709
+ Roster.all_player_stats.should == ['b','a','c']
710
+ Roster.all_player_stats.get.should == ['b','a','c']
711
+ Roster.all_player_stats.first.should == 'b'
712
+ Roster.all_player_stats.last.should == 'c'
713
+ Roster.all_player_stats << 'd'
714
+ Roster.all_player_stats.should == ['b','a','c','d']
715
+ Roster.all_player_stats[1].should == 'a'
716
+ Roster.all_player_stats[0].should == 'b'
717
+ Roster.all_player_stats[2].should == 'c'
718
+ Roster.all_player_stats[3].should == 'd'
719
+ Roster.all_player_stats.include?('c').should.be.true
720
+ Roster.all_player_stats.include?('no').should.be.false
721
+ Roster.all_player_stats.pop.should == 'd'
722
+ Roster.all_player_stats[0].should == Roster.all_player_stats.at(0)
723
+ Roster.all_player_stats[1].should == Roster.all_player_stats.at(1)
724
+ Roster.all_player_stats[2].should == Roster.all_player_stats.at(2)
725
+ Roster.all_player_stats.should == ['b','a','c']
726
+ Roster.all_player_stats.get.should == ['b','a','c']
727
+ Roster.all_player_stats.shift.should == 'b'
728
+ Roster.all_player_stats.should == ['a','c']
729
+ Roster.all_player_stats.get.should == ['a','c']
730
+ Roster.all_player_stats << 'e' << 'f' << 'e'
731
+ Roster.all_player_stats.should == ['a','c','e','f','e']
732
+ Roster.all_player_stats.get.should == ['a','c','e','f','e']
733
+ Roster.all_player_stats.delete('e').should == 2
734
+ Roster.all_player_stats.should == ['a','c','f']
735
+ Roster.all_player_stats.get.should == ['a','c','f']
736
+ Roster.all_player_stats << 'j'
737
+ Roster.all_player_stats.should == ['a','c','f','j']
738
+ Roster.all_player_stats[0..2].should == ['a','c','f']
739
+ Roster.all_player_stats[1, 3].should == ['c','f','j']
740
+ Roster.all_player_stats.length.should == 4
741
+ Roster.all_player_stats.size.should == 4
742
+ Roster.all_player_stats.should == ['a','c','f','j']
743
+ Roster.all_player_stats.get.should == ['a','c','f','j']
744
+
745
+ i = -1
746
+ Roster.all_player_stats.each do |st|
747
+ st.should == Roster.all_player_stats[i += 1]
748
+ end
749
+ Roster.all_player_stats.should == ['a','c','f','j']
750
+ Roster.all_player_stats.get.should == ['a','c','f','j']
751
+
752
+ Roster.all_player_stats.each_with_index do |st,i|
753
+ st.should == Roster.all_player_stats[i]
754
+ end
755
+ Roster.all_player_stats.should == ['a','c','f','j']
756
+ Roster.all_player_stats.get.should == ['a','c','f','j']
757
+
758
+ coll = Roster.all_player_stats.collect{|st| st}
759
+ coll.should == ['a','c','f','j']
760
+ Roster.all_player_stats.should == ['a','c','f','j']
761
+ Roster.all_player_stats.get.should == ['a','c','f','j']
762
+
763
+ Roster.all_player_stats << 'a'
764
+ coll = Roster.all_player_stats.select{|st| st == 'a'}
765
+ coll.should == ['a','a']
766
+ Roster.all_player_stats.should == ['a','c','f','j','a']
767
+ Roster.all_player_stats.get.should == ['a','c','f','j','a']
768
+ end
769
+
770
+ it "should handle class-level global sets of simple values" do
771
+ Roster.all_players_online.should.be.empty
772
+ Roster.all_players_online << 'a' << 'a' << 'a'
773
+ Roster.all_players_online.should == ['a']
774
+ Roster.all_players_online.get.should == ['a']
775
+ Roster.all_players_online << 'b' << 'b'
776
+ Roster.all_players_online.sort.should == ['a','b']
777
+ Roster.all_players_online.members.sort.should == ['a','b']
778
+ Roster.all_players_online.get.sort.should == ['a','b']
779
+ Roster.all_players_online << 'c'
780
+ Roster.all_players_online.sort.should == ['a','b','c']
781
+ Roster.all_players_online.get.sort.should == ['a','b','c']
782
+ Roster.all_players_online.delete('c')
783
+ Roster.all_players_online.sort.should == ['a','b']
784
+ Roster.all_players_online.get.sort.should == ['a','b']
785
+ Roster.all_players_online.length.should == 2
786
+ Roster.all_players_online.size.should == 2
787
+
788
+ i = 0
789
+ Roster.all_players_online.each do |st|
790
+ i += 1
791
+ end
792
+ i.should == Roster.all_players_online.length
793
+
794
+ coll = Roster.all_players_online.collect{|st| st}
795
+ coll.sort.should == ['a','b']
796
+ Roster.all_players_online.sort.should == ['a','b']
797
+ Roster.all_players_online.get.sort.should == ['a','b']
798
+
799
+ Roster.all_players_online << 'c'
800
+ Roster.all_players_online.member?('c').should.be.true
801
+ Roster.all_players_online.include?('c').should.be.true
802
+ Roster.all_players_online.member?('no').should.be.false
803
+ coll = Roster.all_players_online.select{|st| st == 'c'}
804
+ coll.should == ['c']
805
+ Roster.all_players_online.sort.should == ['a','b','c']
806
+ end
807
+
808
+ it "should handle class-level global values" do
809
+ Roster.last_player.should == nil
810
+ Roster.last_player = 'Trevor Hoffman'
811
+ Roster.last_player.should == 'Trevor Hoffman'
812
+ Roster.last_player.get.should == 'Trevor Hoffman'
813
+ Roster.last_player = 'Tom Selleck'
814
+ Roster.last_player.should == 'Tom Selleck'
815
+ Roster.last_player.del.should == 1
816
+ Roster.last_player.should.be.nil
817
+ end
818
+
819
+ it "should easily enable @object.class.global_objects" do
820
+ @roster.class.all_players_online.should.be.empty
821
+ @roster.class.all_players_online << 'a' << 'a' << 'a'
822
+ @roster.class.all_players_online.should == ['a']
823
+ @roster2.class.all_players_online.should == ['a']
824
+
825
+ @roster.all_players_online.should == ['a']
826
+ @roster2.all_players_online.should == ['a']
827
+
828
+ @roster.class.all_player_stats.should.be.empty
829
+ @roster.class.all_player_stats << 'a'
830
+ @roster.class.all_player_stats.should == ['a']
831
+ @roster.class.all_player_stats.get.should == ['a']
832
+ @roster.class.all_player_stats.unshift 'b'
833
+ @roster.class.all_player_stats.to_s.should == 'b, a'
834
+ @roster.class.all_player_stats.should == ['b','a']
835
+ @roster2.class.all_player_stats.should == ['b','a']
836
+
837
+ @roster.all_player_stats.should == ['b','a']
838
+ @roster2.all_player_stats.should == ['b','a']
839
+ @roster2.all_player_stats << 'b'
840
+ @roster.all_player_stats.should == ['b','a','b']
841
+
842
+ @roster.last_player.should == nil
843
+ @roster.class.last_player = 'Trevor Hoffman'
844
+ @roster.last_player.should == 'Trevor Hoffman'
845
+ @roster.last_player.get.should == 'Trevor Hoffman'
846
+ @roster2.last_player.get.should == 'Trevor Hoffman'
847
+ @roster2.last_player = 'Tom Selleck'
848
+ @roster.last_player.should == 'Tom Selleck'
849
+ @roster.last_player.del.should == 1
850
+ @roster.last_player.should.be.nil
851
+ @roster2.last_player.should.be.nil
852
+ end
853
+
854
+ it "should handle lists of complex data types" do
855
+ @roster.player_stats << {:json => 'data'}
856
+ @roster.player_stats << {:json2 => 'data2'}
857
+ @roster.player_stats.first.should == {:json => 'data'}
858
+ @roster.player_stats.last.should == {:json2 => 'data2'}
859
+ @roster.player_stats << [1,2,3,[4,5]]
860
+ @roster.player_stats.last.should == [1,2,3,[4,5]]
861
+ @roster.player_stats.shift.should == {:json => 'data'}
862
+ end
863
+
864
+ it "should handle sets of complex data types" do
865
+ @roster.outfielders << {:a => 1}
866
+ @roster.outfielders.members.should == [{:a => 1}]
867
+ @roster.outfielders << {:b => 2}
868
+ @roster.outfielders.member?({:b => 2})
869
+ @roster_1.outfielders << {:a => 1} << {:b => 2}
870
+ @roster_2.outfielders << {:b => 2} << {:c => 3}
871
+ (@roster_1.outfielders & @roster_2.outfielders).should == [{:b => 2}]
872
+ #(@roster_1.outfielders | @roster_2.outfielders).members.should ==
873
+ end
874
+
875
+ it "should provide a lock method that accepts a block" do
876
+ @roster.resort_lock.key.should == 'roster:1:resort_lock'
877
+ a = false
878
+ @roster.resort_lock.lock do
879
+ a = true
880
+ end
881
+ a.should.be.true
882
+ end
883
+
884
+ it "should raise an exception if the timeout is exceeded" do
885
+ @roster.redis.set(@roster.resort_lock.key, 1)
886
+ error = nil
887
+ begin
888
+ @roster.resort_lock.lock {}
889
+ rescue => error
890
+ end
891
+ error.should.not.be.nil
892
+ error.should.be.kind_of(Redis::Lock::LockTimeout)
893
+ end
894
+
895
+ it "should pick up objects from superclass automatically" do
896
+ @vanilla_roster.available_slots.should.be.kind_of(Redis::Counter)
897
+ @vanilla_roster.pitchers.should.be.kind_of(Redis::Counter)
898
+ @vanilla_roster.basic.should.be.kind_of(Redis::Counter)
899
+ @vanilla_roster.resort_lock.should.be.kind_of(Redis::Lock)
900
+ @vanilla_roster.starting_pitcher.should.be.kind_of(Redis::Value)
901
+ @vanilla_roster.player_stats.should.be.kind_of(Redis::List)
902
+ @vanilla_roster.outfielders.should.be.kind_of(Redis::Set)
903
+ @vanilla_roster.rank.should.be.kind_of(Redis::SortedSet)
904
+
905
+ # custom keys
906
+ @vanilla_roster.player_totals.should.be.kind_of(Redis::Counter)
907
+ @vanilla_roster.all_player_stats.should.be.kind_of(Redis::List)
908
+ @vanilla_roster.total_wins.should.be.kind_of(Redis::Set)
909
+ @vanilla_roster.my_rank.should.be.kind_of(Redis::Value)
910
+ @vanilla_roster.weird_key.should.be.kind_of(Redis::Value)
911
+
912
+ # globals via class
913
+ @vanilla_roster.total_players_online.should.be.kind_of(Redis::Counter)
914
+ @vanilla_roster.all_player_stats.should.be.kind_of(Redis::List)
915
+ @vanilla_roster.all_players_online.should.be.kind_of(Redis::Set)
916
+ @vanilla_roster.last_player.should.be.kind_of(Redis::Value)
917
+
918
+ VanillaRoster.total_players_online.should.be.kind_of(Redis::Counter)
919
+ VanillaRoster.all_player_stats.should.be.kind_of(Redis::List)
920
+ VanillaRoster.all_players_online.should.be.kind_of(Redis::Set)
921
+ VanillaRoster.last_player.should.be.kind_of(Redis::Value)
922
+ end
923
+
924
+ it "should allow subclass overrides of the same redis object" do
925
+ @roster.basic.should == 0
926
+ @custom_roster.basic.increment.should == 1
927
+ @roster2.basic.should == 0
928
+ CustomRoster.new.basic.should == 1
929
+ @custom_roster.basic.decrement.should == 0
930
+ end
931
+
932
+ it "should handle new subclass objects" do
933
+ @custom_roster.special.increment.should == 1
934
+ end
935
+
936
+ it "should allow passing of increment/decrement to super class" do
937
+ @custom_method_roster = CustomMethodRoster.new
938
+ @custom_method_roster.counter.should.be.nil
939
+
940
+ @custom_method_roster.increment(:counter).should == 42
941
+
942
+ @custom_method_roster.increment(:basic).should == 1
943
+ @custom_method_roster.basic.increment.should == 2
944
+ @custom_method_roster.decrement(:basic).should == 1
945
+ @custom_method_roster.basic.decrement.should == 0
946
+ @custom_method_roster.basic.reset.should.be.true
947
+ @custom_method_roster.basic.should == 0
948
+ @custom_method_roster.basic.should.be.kind_of(Redis::Counter)
949
+ end
950
+
951
+ it "should respond to #to_json" do
952
+ @roster = Roster.new
953
+ @roster.player_totals.increment
954
+ json = JSON.parse(@roster.player_totals.to_json)
955
+ json['value'].should == 1
956
+ end
957
+
958
+ it "should persist object with custom id field name" do
959
+ @custom_id_field_roster = CustomIdFieldRoster.new()
960
+ @custom_id_field_roster.uid.should == 123 # sanity
961
+ @custom_id_field_roster.increment(:basic).should == 1
962
+ @custom_id_field_roster.basic.increment.should == 2
963
+ @custom_id_field_roster.basic.reset
964
+ @custom_id_field_roster.basic.should == 0
965
+ end
966
+
967
+ it "should pick up class methods from superclass automatically" do
968
+ CounterRoster = Class.new(Roster)
969
+ CounterRoster.counter :extended_counter
970
+ extended_roster = CounterRoster.new
971
+ extended_roster.basic.should.be.kind_of(Redis::Counter)
972
+ extended_roster.extended_counter.should.be.kind_of(Redis::Counter)
973
+ @roster.respond_to?(:extended_counter).should == false
974
+
975
+ HashKeyRoster = Class.new(Roster)
976
+ HashKeyRoster.hash_key :extended_hash_key
977
+ extended_roster = HashKeyRoster.new
978
+ extended_roster.contact_information.should.be.kind_of(Redis::HashKey)
979
+ extended_roster.extended_hash_key.should.be.kind_of(Redis::HashKey)
980
+ @roster.respond_to?(:extended_hash_key).should == false
981
+
982
+ LockRoster = Class.new(Roster)
983
+ LockRoster.lock :extended
984
+ extended_roster = LockRoster.new
985
+ extended_roster.resort_lock.should.be.kind_of(Redis::Lock)
986
+ extended_roster.extended_lock.should.be.kind_of(Redis::Lock)
987
+ @roster.respond_to?(:extended_lock).should == false
988
+
989
+ ValueRoster = Class.new(Roster)
990
+ ValueRoster.value :extended_value
991
+ extended_roster = ValueRoster.new
992
+ extended_roster.starting_pitcher.should.be.kind_of(Redis::Value)
993
+ extended_roster.extended_value.should.be.kind_of(Redis::Value)
994
+ @roster.respond_to?(:extended_value).should == false
995
+
996
+ ListRoster = Class.new(Roster)
997
+ ListRoster.list :extended_list
998
+ extended_roster = ListRoster.new
999
+ extended_roster.player_stats.should.be.kind_of(Redis::List)
1000
+ extended_roster.extended_list.should.be.kind_of(Redis::List)
1001
+ @roster.respond_to?(:extended_list).should == false
1002
+
1003
+ SetRoster = Class.new(Roster)
1004
+ SetRoster.set :extended_set
1005
+ extended_roster = SetRoster.new
1006
+ extended_roster.outfielders.should.be.kind_of(Redis::Set)
1007
+ extended_roster.extended_set.should.be.kind_of(Redis::Set)
1008
+ @roster.respond_to?(:extended_set).should == false
1009
+
1010
+ SortedSetRoster = Class.new(Roster)
1011
+ SortedSetRoster.sorted_set :extended_sorted_set
1012
+ extended_roster = SortedSetRoster.new
1013
+ extended_roster.rank.should.be.kind_of(Redis::SortedSet)
1014
+ extended_roster.extended_sorted_set.should.be.kind_of(Redis::SortedSet)
1015
+ @roster.respond_to?(:extended_sorted_set).should == false
1016
+ end
1017
+
1018
+ it "should set time to live in seconds when expiration option assigned" do
1019
+ @roster.value_with_expiration.value = 'val'
1020
+ @roster.value_with_expiration.ttl.should > 0
1021
+ @roster.value_with_expiration.ttl.should <= 10
1022
+
1023
+ @roster.set_with_expiration << 'val'
1024
+ @roster.set_with_expiration.ttl.should > 0
1025
+ @roster.set_with_expiration.ttl.should <= 10
1026
+
1027
+ @roster.list_with_expiration << 'val'
1028
+ @roster.list_with_expiration.ttl.should > 0
1029
+ @roster.list_with_expiration.ttl.should <= 10
1030
+
1031
+ @roster.hash_with_expiration[:foo] = :bar
1032
+ @roster.hash_with_expiration.ttl.should > 0
1033
+ @roster.hash_with_expiration.ttl.should <= 10
1034
+
1035
+ @roster.counter_with_expiration.increment
1036
+ @roster.counter_with_expiration.ttl.should > 0
1037
+ @roster.counter_with_expiration.ttl.should <= 10
1038
+
1039
+ @roster.sorted_set_with_expiration[:foo] = 1
1040
+ @roster.sorted_set_with_expiration.ttl.should > 0
1041
+ @roster.sorted_set_with_expiration.ttl.should <= 10
1042
+ end
1043
+
1044
+ it "should set expiration when expireat option assigned" do
1045
+ @roster.value_with_expireat.value = 'val'
1046
+ @roster.value_with_expireat.ttl.should > 0
1047
+ @roster.value_with_expireat.ttl.should <= 10
1048
+
1049
+ @roster.set_with_expireat << 'val'
1050
+ @roster.set_with_expireat.ttl.should > 0
1051
+ @roster.set_with_expireat.ttl.should <= 10
1052
+
1053
+ @roster.list_with_expireat << 'val'
1054
+ @roster.list_with_expireat.ttl.should > 0
1055
+ @roster.list_with_expireat.ttl.should <= 10
1056
+
1057
+ @roster.hash_with_expireat[:foo] = :bar
1058
+ @roster.hash_with_expireat.ttl.should > 0
1059
+ @roster.hash_with_expireat.ttl.should <= 10
1060
+
1061
+ @roster.sorted_set_with_expireat[:foo] = 1
1062
+ @roster.sorted_set_with_expireat.ttl.should > 0
1063
+ @roster.sorted_set_with_expireat.ttl.should <= 10
1064
+ end
1065
+
1066
+ it "should set expiration when expireat option assigned with lambda" do
1067
+ travel(1.minute) do
1068
+ # non-proc expireat is not affected by time travel
1069
+ @roster.value_with_expireat.value = 'val'
1070
+ @roster.value_with_expireat.ttl.should > 0
1071
+ @roster.value_with_expireat.ttl.should <= 10
1072
+
1073
+ @roster.value_with_expireat_with_lambda.value = 'val'
1074
+ @roster.value_with_expireat_with_lambda.ttl.should > 60
1075
+ @roster.value_with_expireat_with_lambda.ttl.should <= 70
1076
+ end
1077
+ end
1078
+
1079
+ it "should allow deleting the entire object" do
1080
+ (@roster.redis.keys & @roster.redis_instance_keys).count.should > 0
1081
+ @roster.redis_delete_objects.should > 0
1082
+ (@roster.redis.keys & @roster.redis_instance_keys).count.should == 0
1083
+ end
1084
+
1085
+ it "should be able to return all instance keys" do
1086
+ @roster.redis_instance_keys.include?('roster:1:player_stats').should == true
1087
+ @roster.redis_instance_keys.include?('players:my_rank:user1').should == true
1088
+ @roster.redis_instance_keys.include?('roster:1:player_stats').should == true
1089
+ @roster.redis_instance_keys.include?('players:all_stats').should == false
1090
+ end
1091
+
1092
+ it "should handle per-class connection handles" do
1093
+ redis = Roster.redis
1094
+ Roster.redis = redis
1095
+ Roster.redis.should == redis
1096
+ end
1097
+ end