redis-objects-legacy 1.6.0

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.
@@ -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