redis-roc 0.5.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,1425 @@
1
+ require 'roc/store/roc_store'
2
+ require 'roc/store/object_initializers'
3
+ module ROC
4
+ module Store
5
+ class TransientStore < ROCStore
6
+ include ObjectInitializers
7
+
8
+ KEYSPACES = {}
9
+ MDSPACES = {}
10
+
11
+ attr_reader :name
12
+
13
+ def initialize(name=nil)
14
+ if name.nil?
15
+ @keyspace = {}
16
+ @mdspace = {}
17
+ else
18
+ @name = name.to_s
19
+ TransientStore::KEYSPACES[@name] ||= {}
20
+ TransientStore::MDSPACES[@name] ||= {}
21
+ end
22
+ end
23
+
24
+ def shared?
25
+ !@name.nil?
26
+ end
27
+
28
+ protected
29
+
30
+ def keyspace
31
+ @keyspace || TransientStore::KEYSPACES[self.name]
32
+ end
33
+
34
+ def mdspace
35
+ @mdspace || TransientStore::MDSPACES[self.name]
36
+ end
37
+
38
+ def expunge_if_expired(key)
39
+ if (md = self.mdspace[key.to_s]) && (ea = md[:expire_at]) && (ea < ::Time.now.to_i)
40
+ self.expunge(key)
41
+ end
42
+ end
43
+
44
+ def expunge(key)
45
+ self.keyspace.delete(key.to_s)
46
+ self.mdspace.delete(key.to_s)
47
+ end
48
+
49
+ def with_type(key, type)
50
+ md = self.mdspace[key.to_s]
51
+ if md.nil? || (md[:type] == type)
52
+ ret = yield
53
+ if self.keyspace[key.to_s]
54
+ self.mdspace[key.to_s] ||= {:type => type}
55
+ end
56
+ ret
57
+ else
58
+ raise TypeError, "#{type} required"
59
+ end
60
+ end
61
+
62
+ public
63
+
64
+ def call(method_name, *args)
65
+ if @multi_mode
66
+ @multi_calls << [method_name, *args]
67
+ 'QUEUED'
68
+ else
69
+ self.send method_name, *args
70
+ end
71
+ end
72
+
73
+ ## start of redis methods
74
+
75
+ # All keys
76
+
77
+ def del(*keys)
78
+ keys.each{|key| expunge_if_expired(key)}
79
+ i = 0
80
+ keys.each do |key|
81
+ if self.exists(key)
82
+ self.expunge(key)
83
+ i += 1
84
+ end
85
+ end
86
+ if keys.size > 1
87
+ true
88
+ else
89
+ i
90
+ end
91
+ end
92
+
93
+ def exists(key)
94
+ expunge_if_expired(key)
95
+ self.keyspace.has_key?(key.to_s)
96
+ end
97
+
98
+ def expire(key, secs)
99
+ self.expireat(key, ::Time.now.to_i + secs.to_i)
100
+ end
101
+
102
+ def expireat(key, epoch)
103
+ if self.exists(key)
104
+ self.mdspace[key.to_s] ||= {}
105
+ self.mdspace[key.to_s][:expire_at] = epoch.to_i
106
+ true
107
+ else
108
+ false
109
+ end
110
+ end
111
+
112
+ def keys(pattern='*')
113
+ if '*' == pattern
114
+ self.keyspace.keys
115
+ else
116
+ raise "patterns not implemented yet"
117
+ end
118
+ end
119
+
120
+ def move(key, db)
121
+ raise NotImplementedError
122
+ end
123
+
124
+ def persist(key)
125
+ if self.exists(key) && (md = self.mdspace[key.to_s]) && md.has_key?(:expire_at)
126
+ md.delete(:expire_at)
127
+ true
128
+ else
129
+ false
130
+ end
131
+ end
132
+
133
+ def randomkey
134
+ ks = self.keys
135
+ ks[Kernel.rand(ks.size)]
136
+ end
137
+
138
+ def rename(key, newkey)
139
+ if key.to_s == newkey.to_s
140
+ raise ArgumentError, "keys are the same"
141
+ elsif self.exists(key)
142
+ self.keyspace[newkey.to_s] = self.keyspace.delete(key.to_s)
143
+ true
144
+ else
145
+ raise ArgumentError, "no such key: #{key}"
146
+ end
147
+ end
148
+
149
+ def renamenx(key, newkey)
150
+ if key.to_s == newkey.to_s
151
+ raise ArgumentError, "keys are the same"
152
+ elsif self.exists(key)
153
+ if self.exists(newkey)
154
+ false
155
+ else
156
+ self.keyspace[newkey.to_s] = self.keyspace.delete(key.to_s)
157
+ true
158
+ end
159
+ else
160
+ raise ArgumentError, "no such key: #{key}"
161
+ end
162
+ end
163
+
164
+ def sort(key, *args)
165
+ opts = parse_sort_args(args)
166
+
167
+ raise ":by not yet supported" if opts.has_key?(:by)
168
+ raise ":get not yet supported" if opts.has_key?(:get)
169
+
170
+ limit = opts[:limit]
171
+ order = (opts[:order] || '').split(' ')
172
+ store = opts[:store]
173
+
174
+ md = self.mdspace[key.to_s]
175
+
176
+ vals = if md.nil?
177
+ []
178
+ elsif 'list' == md[:type]
179
+ self.lrange(key, 0, -1)
180
+ elsif 'set' == md[:type]
181
+ self.smembers(key)
182
+ elsif 'zset' == md[:type]
183
+ self.zrange(key, 0, -1)
184
+ else
185
+ raise TypeError, 'list, set or zset required'
186
+ end
187
+
188
+ sorter = if order.include?('alpha')
189
+ if order.include?('desc')
190
+ lambda{|a, b| b <=> a}
191
+ else
192
+ lambda{|a, b| a <=> b}
193
+ end
194
+ elsif order.include?('desc')
195
+ lambda{|a, b| b.to_f <=> a.to_f}
196
+ else
197
+ lambda{|a, b| a.to_f <=> b.to_f}
198
+ end
199
+
200
+ vals.sort!{|a, b| sorter.call(a, b)}
201
+
202
+ if limit
203
+ vals = vals[*limit]
204
+ end
205
+
206
+ if store
207
+ with_type(store, 'list') do
208
+ self.keyspace[store.to_s] = vals
209
+ end
210
+ end
211
+
212
+ vals
213
+ end
214
+
215
+ def ttl(key)
216
+ val = -1
217
+ if self.exists(key)
218
+ if (md = self.mdspace[key.to_s]) && (ea = md[:expire_at])
219
+ val = ea - ::Time.now.to_i
220
+ end
221
+ end
222
+ val
223
+ end
224
+
225
+ def type(key)
226
+ if md = self.mdspace[key.to_s]
227
+ md[:type].dup
228
+ else
229
+ 'none'
230
+ end
231
+ end
232
+
233
+ # Strings
234
+
235
+ def get(key)
236
+ with_type(key, 'string') do
237
+ expunge_if_expired(key)
238
+ v = self.keyspace[key.to_s]
239
+ v.nil? ? nil : v.dup
240
+ end
241
+ end
242
+
243
+ def set(key, val)
244
+ with_type(key, 'string') do
245
+ expunge_if_expired(key)
246
+ v = if val.is_a?(::String)
247
+ val.dup
248
+ else
249
+ val.to_s
250
+ end
251
+ self.keyspace[key.to_s] = v
252
+ self.persist(key)
253
+ true
254
+ end
255
+ end
256
+
257
+ def getset(key, val)
258
+ current_val = self.get(key)
259
+ self.set(key, val)
260
+ current_val
261
+ end
262
+
263
+ def mget(*keys)
264
+ keys.map{|k| self.get(k)}
265
+ end
266
+
267
+ def mset(*pairs)
268
+ i=0
269
+ while i < pairs.size
270
+ self.set(pairs[i], pairs[i+1])
271
+ i+=2
272
+ end
273
+ true
274
+ end
275
+
276
+ def setnx(key, val)
277
+ if self.exists(key)
278
+ false
279
+ else
280
+ self.set(key, val)
281
+ true
282
+ end
283
+ end
284
+
285
+ def msetnx(*pairs)
286
+ i=0
287
+ any_exist = false
288
+ while i < pairs.size
289
+ if self.exists(pairs[i])
290
+ any_exist = true
291
+ break
292
+ end
293
+ end
294
+ if !any_exist
295
+ i=0
296
+ while i < pairs.size
297
+ self.set(pairs[i], pairs[i+1])
298
+ i+=2
299
+ end
300
+ true
301
+ else
302
+ false
303
+ end
304
+ end
305
+
306
+ def append(key, val)
307
+ if self.exists(key)
308
+ with_type(key, 'string') do
309
+ self.keyspace[key.to_s] << val.to_s
310
+ end
311
+ else
312
+ self.set(key, val)
313
+ end
314
+ self.strlen(key)
315
+ end
316
+
317
+ def getbit(key, index)
318
+ raise ArgumentError, 'setbit takes a non-negative index' unless index > 0
319
+
320
+ bitstring = self.get(key).unpack('B*')[0]
321
+ if index < bitstring.length
322
+ if RUBY_VERSION.match(/^1\.8/)
323
+ bitstring[index].chr.to_i
324
+ else
325
+ bitstring[index].to_i
326
+ end
327
+ else
328
+ 0
329
+ end
330
+ end
331
+
332
+ def setbit(key, index, value)
333
+ raise ArgumentError, 'setbit takes a non-negative index' unless index > 0
334
+ raise ArgumentError, 'setbit takes a 1 or 0 for the value' unless((0 == value) || (1 == value))
335
+
336
+ bitstring = self.get(key).unpack('B*')[0]
337
+ current_val = 0
338
+ if index < bitstring.length
339
+ current_val = if RUBY_VERSION.match(/^1\.8/)
340
+ bitstring[index].chr.to_i
341
+ else
342
+ bitstring[index].to_i
343
+ end
344
+ bitstring[index] = value.to_s
345
+ else
346
+ bitstring << ('0' * (index - bitstring.length))
347
+ bitstring << value.to_s
348
+ end
349
+ self.set(key, [bitstring].pack('B*'))
350
+ current_val
351
+ end
352
+
353
+ def getrange(key, first_index, last_index)
354
+ if self.exists(key)
355
+ with_type(key, 'string') do
356
+ arr = self.keyspace[key.to_s].bytes.to_a[first_index..last_index]
357
+ if arr
358
+ arr.map{|c| c.chr}.join('')
359
+ else
360
+ ''
361
+ end
362
+ end
363
+ else
364
+ ''
365
+ end
366
+ end
367
+
368
+ def setrange(key, start_index, val)
369
+ with_type(key, 'string') do
370
+ expunge_if_expired(key)
371
+ if start_index < 1
372
+ raise "index out of range: #{start_index}"
373
+ end
374
+ length = self.strlen(key)
375
+ padding_length = start_index - length
376
+ v = val.to_s
377
+ if padding_length > 0
378
+ #self.keyspace[key.to_s][length, padding_length + v.length] = ("\u0000" * padding_length) + v
379
+ self.keyspace[key.to_s][length, padding_length + v.length] = ("\000" * padding_length) + v
380
+ else
381
+ self.keyspace[key.to_s][start_index, v.length] = v
382
+ end
383
+ self.strlen(key)
384
+ end
385
+ end
386
+
387
+ def strlen(key)
388
+ val = self.get(key)
389
+ if val.nil?
390
+ 0
391
+ else
392
+ if "".respond_to?(:bytesize)
393
+ val.bytesize
394
+ else
395
+ val.length
396
+ end
397
+ end
398
+ end
399
+
400
+ def incr(key)
401
+ self.incrby(key, 1)
402
+ end
403
+
404
+ def incrby(key, by)
405
+ raise "value (#{by}) is not an integer" unless by.is_a?(::Integer)
406
+ val = self.get(key)
407
+ new_val = val.to_i + by
408
+ self.set(key, new_val.to_s)
409
+ new_val
410
+ end
411
+
412
+ def decr(key)
413
+ self.incrby(key, -1)
414
+ end
415
+
416
+ def decrby(key, by)
417
+ self.incrby(key, -by)
418
+ end
419
+
420
+ # Lists
421
+
422
+ def lrange(key, start_index, stop_index)
423
+ with_type(key, 'list') do
424
+ expunge_if_expired(key)
425
+ val = self.keyspace[key.to_s]
426
+ if val.nil? || (start_index >= val.size) || ( (start_index < 0) && (stop_index < start_index) )
427
+ []
428
+ else
429
+ val[start_index..stop_index] || [] ## never return nil -- happens if start_index is neg and before begining of list
430
+ end
431
+ end
432
+ end
433
+
434
+ def llen(key)
435
+ with_type(key, 'list') do
436
+ expunge_if_expired(key)
437
+ val = self.keyspace[key.to_s]
438
+ if val.nil?
439
+ 0
440
+ else
441
+ val.size
442
+ end
443
+ end
444
+ end
445
+
446
+ def rpush(key, val)
447
+ with_type(key, 'list') do
448
+ if !self.exists(key)
449
+ self.keyspace[key.to_s] = []
450
+ end
451
+ v = if val.is_a?(::String)
452
+ val.dup
453
+ else
454
+ val.to_s
455
+ end
456
+ self.keyspace[key.to_s] << v
457
+ self.keyspace[key.to_s].size
458
+ end
459
+ end
460
+
461
+ def rpushx(key, val)
462
+ if self.exists(key)
463
+ self.rpush(key, val)
464
+ else
465
+ 0
466
+ end
467
+ end
468
+
469
+ def lpush(key, val)
470
+ with_type(key, 'list') do
471
+ if !self.exists(key)
472
+ self.keyspace[key.to_s] = []
473
+ end
474
+ v = if val.is_a?(::String)
475
+ val.dup
476
+ else
477
+ val.to_s
478
+ end
479
+ self.keyspace[key.to_s].unshift(v)
480
+ self.keyspace[key.to_s].size
481
+ end
482
+ end
483
+
484
+ def lpushx(key, val)
485
+ if self.exists(key)
486
+ self.lpush(key, val)
487
+ else
488
+ 0
489
+ end
490
+ end
491
+
492
+ def rpop(key)
493
+ with_type(key, 'list') do
494
+ if !self.exists(key)
495
+ nil
496
+ else
497
+ val = self.keyspace[key.to_s].pop
498
+ if 0 == self.llen(key)
499
+ self.del(key)
500
+ end
501
+ val
502
+ end
503
+ end
504
+ end
505
+
506
+ def lpop(key)
507
+ with_type(key, 'list') do
508
+ if !self.exists(key)
509
+ nil
510
+ else
511
+ val = self.keyspace[key.to_s].shift
512
+ if 0 == self.llen(key)
513
+ self.del(key)
514
+ end
515
+ val
516
+ end
517
+ end
518
+ end
519
+
520
+ def lindex(key, ind)
521
+ with_type(key, 'list') do
522
+ if !self.exists(key)
523
+ nil
524
+ else
525
+ v = self.keyspace[key.to_s][ind]
526
+ v.nil? ? nil : v.dup
527
+ end
528
+ end
529
+ end
530
+
531
+ def lset(key, ind, val)
532
+ with_type(key, 'list') do
533
+ expunge_if_expired(key)
534
+ arr = self.keyspace[key.to_s]
535
+ if arr.nil?
536
+ raise ArgumentError, "No such key: #{key}"
537
+ elsif ((ind < 0) && (ind < -arr.size)) || (ind >= arr.size)
538
+ raise ArgumentError, "index out of range: #{ind}"
539
+ else
540
+ v = if val.is_a?(::String)
541
+ val.dup
542
+ else
543
+ val.to_s
544
+ end
545
+ self.keyspace[key.to_s][ind] = v
546
+ end
547
+ end
548
+ end
549
+
550
+ def lrem(key, count, val)
551
+ with_type(key, 'list') do
552
+ if self.exists(key)
553
+ iterator = self.keyspace[key.to_s]
554
+ limit = iterator.size
555
+ reverse = false
556
+ if count > 0
557
+ limit = count
558
+ elsif count < 0
559
+ limit = count.abs
560
+ iterator = iterator.reverse
561
+ reverse = true
562
+ end
563
+ indexes_to_del = []
564
+ v = val.to_s
565
+ iterator.each_with_index do |test, i|
566
+ if test == v
567
+ if reverse
568
+ indexes_to_del.unshift iterator.size - (i + 1)
569
+ else
570
+ indexes_to_del << i
571
+ end
572
+ end
573
+ if indexes_to_del.size == limit
574
+ break
575
+ end
576
+ end
577
+ correction = 0
578
+ indexes_to_del.each do |i|
579
+ self.keyspace[key.to_s].delete_at(i - correction)
580
+ correction += 1
581
+ end
582
+ indexes_to_del.size
583
+ else
584
+ 0
585
+ end
586
+ end
587
+ end
588
+
589
+ def ltrim(key, start_index, stop_index)
590
+ arr = self.lrange(key, start_index, stop_index)
591
+ if 0 == arr.size
592
+ self.del(key)
593
+ else
594
+ self.keyspace[key.to_s] = arr
595
+ end
596
+ true
597
+ end
598
+
599
+ def rpoplpush(source_key, dest_key)
600
+ if self.exists(source_key)
601
+ val = self.rpop(source_key)
602
+ self.lpush(dest_key, val)
603
+ val
604
+ else
605
+ nil
606
+ end
607
+ end
608
+
609
+ def linsert(key, where, pivot, val)
610
+ if !['before', 'after'].include?(where.to_s.downcase)
611
+ raise ArgumentError "BEFORE or AFTER please"
612
+ else
613
+ if self.exists(key)
614
+ ind = self.keyspace[key.to_s].index(pivot)
615
+ if ind
616
+ if 'after' == where
617
+ ind +=1
618
+ end
619
+ v = if val.is_a?(::String)
620
+ val.dup
621
+ else
622
+ val.to_s
623
+ end
624
+ self.keyspace[key.to_s].insert(ind, v)
625
+ self.keyspace[key.to_s].size
626
+ else
627
+ -1
628
+ end
629
+ else
630
+ 0
631
+ end
632
+ end
633
+ end
634
+
635
+ def blpop
636
+ raise "blocking methods not implemented"
637
+ end
638
+
639
+ def brpop
640
+ raise "blocking methods not implemented"
641
+ end
642
+
643
+ def brpoplpush
644
+ raise "blocking methods not implemented"
645
+ end
646
+
647
+ # Set
648
+
649
+ def sadd(key, val)
650
+ with_type(key, 'set') do
651
+ v = val.to_s
652
+ if !self.exists(key)
653
+ self.keyspace[key.to_s] = {}
654
+ end
655
+ if self.keyspace[key.to_s].has_key?(v)
656
+ false
657
+ else
658
+ self.keyspace[key.to_s][v] = true
659
+ true
660
+ end
661
+ end
662
+ end
663
+
664
+ def scard(key)
665
+ with_type(key, 'set') do
666
+ expunge_if_expired(key)
667
+ val = self.keyspace[key.to_s]
668
+ if val.nil?
669
+ 0
670
+ else
671
+ val.size
672
+ end
673
+ end
674
+ end
675
+
676
+ def smembers(key)
677
+ with_type(key, 'set') do
678
+ expunge_if_expired(key)
679
+ val = self.keyspace[key.to_s]
680
+ if val.nil?
681
+ []
682
+ else
683
+ val.keys
684
+ end
685
+ end
686
+ end
687
+
688
+ def spop(key)
689
+ with_type(key, 'set') do
690
+ expunge_if_expired(key)
691
+ hsh = self.keyspace[key.to_s]
692
+ if hsh.nil?
693
+ nil
694
+ else
695
+ val = hsh.keys.sort{Kernel.rand}[0]
696
+ self.keyspace[key.to_s].delete(val)
697
+ if 0 == self.keyspace[key.to_s].size
698
+ self.del(key)
699
+ end
700
+ val
701
+ end
702
+ end
703
+ end
704
+
705
+ def sismember(key, val)
706
+ with_type(key, 'set') do
707
+ expunge_if_expired(key)
708
+ hsh = self.keyspace[key.to_s]
709
+ if hsh.nil?
710
+ false
711
+ else
712
+ hsh.has_key?(val.to_s)
713
+ end
714
+ end
715
+ end
716
+
717
+ def srem(key, val)
718
+ with_type(key, 'set') do
719
+ expunge_if_expired(key)
720
+ hsh = self.keyspace[key.to_s]
721
+ if hsh.nil?
722
+ false
723
+ else
724
+ !!hsh.delete(val.to_s)
725
+ end
726
+ end
727
+ end
728
+
729
+ def srandmember(key)
730
+ with_type(key, 'set') do
731
+ expunge_if_expired(key)
732
+ hsh = self.keyspace[key.to_s]
733
+ if hsh.nil?
734
+ nil
735
+ else
736
+ hsh.keys.sort{Kernel.rand}[0]
737
+ end
738
+ end
739
+ end
740
+
741
+ def smove(source_key, dest_key, val)
742
+ if self.exists(source_key)
743
+ if self.srem(source_key, val)
744
+ self.sadd(dest_key, val)
745
+ true
746
+ else
747
+ false
748
+ end
749
+ else
750
+ false
751
+ end
752
+ end
753
+
754
+ def sunion(*keys)
755
+ raise ArgumentError, 'sunion needs at least one key' unless keys.size > 0
756
+ union = self.smembers(keys.shift)
757
+ while k = keys.shift
758
+ union = union | self.smembers(k)
759
+ end
760
+ union
761
+ end
762
+
763
+ def sinter(*keys)
764
+ raise ArgumentError, 'sinter needs at least one key' unless keys.size > 0
765
+ inter = self.smembers(keys.shift)
766
+ while k = keys.shift
767
+ inter = inter & self.smembers(k)
768
+ end
769
+ inter
770
+ end
771
+
772
+ def sdiff(*keys)
773
+ raise ArgumentError, 'sinter needs at least one key' unless keys.size > 0
774
+ diff = self.smembers(keys.shift)
775
+ while k = keys.shift
776
+ diff = diff - self.smembers(k)
777
+ end
778
+ diff
779
+ end
780
+
781
+ def sunionstore(key, *other_keys)
782
+ vals = self.sunion(*other_keys)
783
+ vals.each{|v| self.sadd(key, v)}
784
+ vals.size
785
+ end
786
+
787
+ def sinterstore(key, *other_keys)
788
+ vals = self.sinter(*other_keys)
789
+ vals.each{|v| self.sadd(key, v)}
790
+ vals.size
791
+ end
792
+
793
+ def sdiffstore(key, *other_keys)
794
+ vals = self.sdiff(*other_keys)
795
+ vals.each{|v| self.sadd(key, v)}
796
+ vals.size
797
+ end
798
+
799
+ # Sorted Sets
800
+
801
+ def zadd(key, score, val)
802
+ with_type(key, 'zset') do
803
+ s = if score.is_a?(Numeric)
804
+ score
805
+ elsif score.is_a?(::String)
806
+ (score.index('.') ? score.to_f : score.to_i)
807
+ else
808
+ raise ArgumentError, "score is not numeric"
809
+ end
810
+ if !self.exists(key)
811
+ self.keyspace[key.to_s] = {:map => {}, :list => []}
812
+ end
813
+ ret = true
814
+ v = val.to_s
815
+ if self.keyspace[key.to_s][:map].has_key?(v)
816
+ ret = false
817
+ end
818
+ self.keyspace[key.to_s][:map][v] = s
819
+ self.resort(key)
820
+ ret
821
+ end
822
+ end
823
+
824
+ def zcard(key)
825
+ with_type(key, 'zset') do
826
+ expunge_if_expired(key)
827
+ val = self.keyspace[key.to_s]
828
+ if val.nil?
829
+ 0
830
+ else
831
+ val[:list].size
832
+ end
833
+ end
834
+ end
835
+
836
+ def zrange(key, start_index, stop_index, opts=nil)
837
+ opts = parse_zrange_arg(opts)
838
+
839
+ with_type(key, 'zset') do
840
+ expunge_if_expired(key)
841
+ val = self.keyspace[key.to_s]
842
+ if val.nil? || (start_index >= val[:list].size) || ( (start_index < 0) && (stop_index < start_index) )
843
+ []
844
+ else
845
+ ## emulate redis -- a neg start index before beginning meams the beginning
846
+ if (start_index < 0) && (-start_index > val[:list].size)
847
+ start_index = 0
848
+ end
849
+ if opts[:with_scores] || opts[:withscores]
850
+ ret = []
851
+ val[:list][start_index..stop_index].each do |v|
852
+ ret << v
853
+ ret << val[:map][v].to_s
854
+ end
855
+ ret
856
+ else
857
+ val[:list][start_index..stop_index] || [] ## never return nil
858
+ end
859
+ end
860
+ end
861
+ end
862
+
863
+ def zrevrange(key, start_index, stop_index, opts=nil)
864
+ opts = parse_zrange_arg(opts)
865
+
866
+ with_type(key, 'zset') do
867
+ expunge_if_expired(key)
868
+ val = self.keyspace[key.to_s]
869
+ if val.nil? || (start_index >= val[:list].size) || ( (start_index < 0) && (stop_index < start_index) )
870
+ []
871
+ else
872
+ list = val[:list].reverse
873
+ if opts[:with_scores] || opts[:withscores]
874
+ ret = []
875
+ list[start_index..stop_index].each do |v|
876
+ ret << v
877
+ ret << val[:map][v].to_s
878
+ end
879
+ ret
880
+ else
881
+ list[start_index..stop_index]
882
+ end
883
+ end
884
+ end
885
+ end
886
+
887
+ def zrangebyscore(key, min, max, *opts)
888
+ opts = parse_zscore_args(opts)
889
+
890
+ with_type(key, 'zset') do
891
+ expunge_if_expired(key)
892
+ val = self.keyspace[key.to_s]
893
+ if val.nil?
894
+ []
895
+ else
896
+ parse = lambda {|v|
897
+ if v.is_a?(::String)
898
+ if v[0] == '('[0]
899
+ [v[1..v.length-1].to_i, false]
900
+ elsif '+inf' == v
901
+ [1.0/0, true]
902
+ elsif '-inf' == v
903
+ [-1.0/0, true]
904
+ else
905
+ [v.to_i, true]
906
+ end
907
+ else
908
+ [v.to_i, true]
909
+ end
910
+ }
911
+ min_int, min_incl = *parse.call(min)
912
+ max_int, max_incl = *parse.call(max)
913
+
914
+ ret = []
915
+
916
+ pass = lambda { |v, op, incl, test|
917
+ v.send( op + (incl ? '=' : ''), test)
918
+ }
919
+
920
+ val[:list].each do |v|
921
+ if pass.call(val[:map][v], '>', min_incl, min_int) && pass.call(val[:map][v], '<', max_incl, max_int)
922
+ ret << v
923
+ if opts[:with_scores] || opts[:withscores]
924
+ ret << val[:map][v].to_s
925
+ end
926
+ end
927
+ end
928
+ if opts.has_key?(:limit)
929
+ limit = if opts[:with_scores] || opts[:withscores]
930
+ opts[:limit].map{|x| x * 2}
931
+ else
932
+ opts[:limit]
933
+ end
934
+ ret[limit[0], limit[1]]
935
+ else
936
+ ret
937
+ end
938
+ end
939
+ end
940
+ end
941
+
942
+ def zrevrangebyscore(key, max, min, *opts)
943
+ opts = parse_zscore_args(opts)
944
+
945
+ limit = opts.delete(:limit)
946
+ ret = self.zrangebyscore(key, min, max, opts).reverse
947
+ if limit
948
+ ret[limit[0], limit[1]]
949
+ else
950
+ ret
951
+ end
952
+ end
953
+
954
+ def zcount(key, min, max)
955
+ self.zrangebyscore(key, min, max).size
956
+ end
957
+
958
+ def zrank(key, val)
959
+ with_type(key, 'zset') do
960
+ expunge_if_expired(key)
961
+ hsh = self.keyspace[key.to_s]
962
+ if hsh.nil?
963
+ nil
964
+ else
965
+ hsh[:list].index(val.to_s)
966
+ end
967
+ end
968
+ end
969
+
970
+ def zrevrank(key, val)
971
+ r = self.zrank(key, val)
972
+ if r
973
+ self.keyspace[key.to_s][:list].size - (r + 1)
974
+ else
975
+ nil
976
+ end
977
+ end
978
+
979
+ def zscore(key, val)
980
+ with_type(key, 'zset') do
981
+ expunge_if_expired(key)
982
+ hsh = self.keyspace[key.to_s]
983
+ if hsh.nil?
984
+ nil
985
+ else
986
+ v = hsh[:map][val.to_s]
987
+ if v
988
+ v.to_s
989
+ else
990
+ nil
991
+ end
992
+ end
993
+ end
994
+ end
995
+
996
+ def zincrby(key, by, val)
997
+ score = self.zscore(key, val) || '0'
998
+ new_score = (score.index('.') ? score.to_f : score.to_i) + by
999
+ self.zadd(key, new_score, val)
1000
+ new_score.to_s
1001
+ end
1002
+
1003
+ def zrem(key, val)
1004
+ with_type(key, 'zset') do
1005
+ expunge_if_expired(key)
1006
+ hsh = self.keyspace[key.to_s]
1007
+ if hsh.nil?
1008
+ false
1009
+ else
1010
+ if hsh[:map].delete(val.to_s)
1011
+ self.resort(key)
1012
+ true
1013
+ else
1014
+ false
1015
+ end
1016
+ end
1017
+ end
1018
+ end
1019
+
1020
+ def zremrangebyscore(key, min, max)
1021
+ vals = self.zrangebyscore(key, min, max)
1022
+ if vals.size > 0
1023
+ vals.each do |val|
1024
+ self.keyspace[key.to_s][:map].delete(val)
1025
+ end
1026
+ self.resort(key)
1027
+ vals.size
1028
+ else
1029
+ 0
1030
+ end
1031
+ end
1032
+
1033
+ def zremrangebyrank(key, start, stop)
1034
+ vals = self.zrange(key, start, stop)
1035
+ if vals.size > 0
1036
+ vals.each do |val|
1037
+ self.keyspace[key.to_s][:map].delete(val)
1038
+ end
1039
+ self.resort(key)
1040
+ vals.size
1041
+ else
1042
+ 0
1043
+ end
1044
+ end
1045
+
1046
+ ## craziness here is to support both the Ruby Redis gem args style and the raw redis args styl
1047
+ def zunionstore(key, *args)
1048
+ other_keys, opts = parse_zop_args(args)
1049
+
1050
+ raise ArgumentError, 'zunionstore needs at least one key' unless other_keys.size > 0
1051
+ raise ArgumentError, 'mismatch weights count' unless (!opts.has_key?(:weights) || (opts[:weights].size == other_keys.size))
1052
+ with_type(key, 'zset') do
1053
+ sorted_sets = other_keys.map{|k| self.keyspace[k.to_s]}.compact
1054
+ u = sorted_sets.pop
1055
+ if u
1056
+ weight_a = (opts.has_key?(:weights) ? opts[:weights].pop : 1)
1057
+ while ss = sorted_sets.pop
1058
+ weight_b = (opts.has_key?(:weights) ? opts[:weights].pop : 1)
1059
+ u = self.ss_union(u, ss, weight_a, weight_b, (opts.has_key?(:aggregate) ? opts[:aggregate] : 'sum')) ##@@ weights and agg
1060
+ weight_a = 1
1061
+ end
1062
+ else
1063
+ u = {:map => {}, :list => []}
1064
+ end
1065
+ self.keyspace[key.to_s] = u
1066
+ u[:list].size
1067
+ end
1068
+ end
1069
+
1070
+ def zinterstore(key, *args)
1071
+ other_keys, opts = parse_zop_args(args)
1072
+
1073
+ raise ArgumentError, 'zinterstore needs at least one key' unless other_keys.size > 0
1074
+ with_type(key, 'zset') do
1075
+ sorted_sets = other_keys.map{|k| self.keyspace[k.to_s]}.compact
1076
+ i = sorted_sets.pop
1077
+ if i
1078
+ weight_a = (opts.has_key?(:weights) ? opts[:weights].pop : 1)
1079
+ while ss = sorted_sets.pop
1080
+ weight_b = (opts.has_key?(:weights) ? opts[:weights].pop : 1)
1081
+ i = self.ss_intersect(i, ss, weight_a, weight_b, (opts.has_key?(:aggregate) ? opts[:aggregate] : 'sum')) ##@@ weights and agg
1082
+ weight_a = 1
1083
+ end
1084
+ else
1085
+ i = {:map => {}, :list => []}
1086
+ end
1087
+ self.keyspace[key.to_s] = i
1088
+ i[:list].size
1089
+ end
1090
+ end
1091
+
1092
+ # Hashes
1093
+
1094
+ def hget(key, field)
1095
+ with_type(key, 'hash') do
1096
+ expunge_if_expired(key)
1097
+ hsh = self.keyspace[key.to_s]
1098
+ if !hsh.nil? && hsh.has_key?(field.to_s)
1099
+ hsh[field.to_s].dup
1100
+ else
1101
+ nil
1102
+ end
1103
+ end
1104
+ end
1105
+
1106
+ def hexists(key, field)
1107
+ with_type(key, 'hash') do
1108
+ self.exists(key) && self.keyspace[key.to_s].has_key?(field.to_s)
1109
+ end
1110
+ end
1111
+
1112
+ def hset(key, field, val)
1113
+ with_type(key, 'hash') do
1114
+ f = field.to_s
1115
+ v = if val.is_a?(::String)
1116
+ val.dup
1117
+ else
1118
+ val.to_s
1119
+ end
1120
+ if !self.exists(key)
1121
+ self.keyspace[key.to_s] = {}
1122
+ end
1123
+ ret = !self.keyspace[key.to_s].has_key?(f)
1124
+ self.keyspace[key.to_s][f] = v
1125
+ ret
1126
+ end
1127
+ end
1128
+
1129
+ def hgetall(key)
1130
+ with_type(key, 'hash') do
1131
+ hsh = self.keyspace[key.to_s]
1132
+ if hsh
1133
+ hsh.dup
1134
+ else
1135
+ {}
1136
+ end
1137
+ end
1138
+ end
1139
+
1140
+ def hkeys(key)
1141
+ if hsh = self.hgetall(key)
1142
+ hsh.keys
1143
+ else
1144
+ []
1145
+ end
1146
+ end
1147
+
1148
+ def hvals(key)
1149
+ if hsh = self.hgetall(key)
1150
+ hsh.values
1151
+ else
1152
+ []
1153
+ end
1154
+ end
1155
+
1156
+ def hlen(key)
1157
+ if hsh = self.hgetall(key)
1158
+ hsh.size
1159
+ else
1160
+ 0
1161
+ end
1162
+ end
1163
+
1164
+ def hdel(key, field)
1165
+ with_type(key, 'hash') do
1166
+ self.exists(key) && !!self.keyspace[key.to_s].delete(field.to_s)
1167
+ end
1168
+ end
1169
+
1170
+ def hincrby(key, field, by)
1171
+ raise "value (#{by}) is not an integer" unless by.is_a?(::Integer)
1172
+ val = self.hget(key, field)
1173
+ new_val = val.to_i + by
1174
+ self.hset(key, field, new_val)
1175
+ new_val
1176
+ end
1177
+
1178
+ def hmget(key, *fields)
1179
+ fields.map{|f| self.hget(key, f)}
1180
+ end
1181
+
1182
+ def hmset(key, *pairs)
1183
+ i = 0
1184
+ while i < pairs.length
1185
+ self.hset(key, pairs[i], pairs[i+1])
1186
+ i += 2
1187
+ end
1188
+ true
1189
+ end
1190
+
1191
+ def hsetnx(key, field, val)
1192
+ if self.hexists(key, field)
1193
+ false
1194
+ else
1195
+ self.hset(key, field, val)
1196
+ end
1197
+ end
1198
+
1199
+ # Transactions
1200
+
1201
+ def multi
1202
+ if @multi_mode
1203
+ raise "multi calls can't be nested"
1204
+ else
1205
+ @multi_mode = true
1206
+ @multi_calls = []
1207
+ if block_given?
1208
+ begin
1209
+ yield
1210
+ rescue Exception => e
1211
+ self.discard
1212
+ raise e
1213
+ end
1214
+ self.exec
1215
+ end
1216
+ end
1217
+ end
1218
+
1219
+ def exec
1220
+ if @multi_mode
1221
+ ret = []
1222
+ @multi_mode = false
1223
+ @multi_calls.each do |call|
1224
+ ret << (self.call *call)
1225
+ end
1226
+ @multi_calls = []
1227
+ ret
1228
+ else
1229
+ raise "exec without a multi"
1230
+ end
1231
+ end
1232
+
1233
+ def discard
1234
+ if @multi_mode
1235
+ @multi_mode = false
1236
+ @multi_calls = []
1237
+ else
1238
+ raise "discard without a multi"
1239
+ end
1240
+ end
1241
+
1242
+ def in_multi?
1243
+ !!@multi_mode
1244
+ end
1245
+
1246
+ def watch(*keys)
1247
+ if @multi_mode
1248
+ raise "watch inside multi not allowed"
1249
+ end
1250
+ ## nothing, we are non concurrent
1251
+ true
1252
+ end
1253
+
1254
+ def unwatch
1255
+ ## nothing, we are non concurrent
1256
+ true
1257
+ end
1258
+
1259
+ def flushdb
1260
+ if self.shared?
1261
+ TransientStore::KEYSPACES[self.name] = {}
1262
+ TransientStore::MDSPACES[self.name] ={}
1263
+ else
1264
+ @keyspace = {}
1265
+ @mdspace = {}
1266
+ end
1267
+ end
1268
+
1269
+ # non-public helpers for redis methods
1270
+ protected
1271
+
1272
+ def parse_sort_args(args)
1273
+ if args.size == 1 && args[0].is_a?(::Hash)
1274
+ return args[0]
1275
+ end
1276
+ opts = {}
1277
+ while arg = args.shift
1278
+ if 'by' == arg.to_s.downcase
1279
+ opts[:by] = args.shift
1280
+ elsif 'limit' == arg.to_s.downcase
1281
+ opts[:limit] = [args.shift, args.shift]
1282
+ elsif 'get' == arg.to_s.downcase
1283
+ opts[:get] ||= []
1284
+ opts[:get] << args.shift
1285
+ elsif 'store' == arg.to_s.downcase
1286
+ opts[:store] = args.shift
1287
+ end
1288
+ end
1289
+ if args.size > 0
1290
+ opts[:order] = args.join(' ')
1291
+ end
1292
+ opts
1293
+ end
1294
+
1295
+ def parse_zscore_args(opts)
1296
+ if 0 == opts.size
1297
+ {}
1298
+ elsif (opts.size > 1) || !opts[0].is_a?(::Hash)
1299
+ parsed_opts = {}
1300
+ while o = opts.shift
1301
+ if 'withscores' == o.to_s.downcase
1302
+ parsed_opts[:withscores] = true
1303
+ elsif 'withscores' == o.to_s.downcase
1304
+ parsed_opts[:limit] = [opts.shift.to_i, opts.shift.to_i]
1305
+ end
1306
+ end
1307
+ parsed_opts
1308
+ else
1309
+ opts[0]
1310
+ end
1311
+ end
1312
+
1313
+ def parse_zrange_arg(opts)
1314
+ if opts.nil?
1315
+ {}
1316
+ elsif opts.is_a?(::Hash)
1317
+ opts
1318
+ elsif ('withscores' == opts.to_s.downcase)
1319
+ {:withscores => true}
1320
+ else
1321
+ {}
1322
+ end
1323
+ end
1324
+
1325
+ def parse_zop_args(args)
1326
+ other_keys = []
1327
+ opts = {}
1328
+
1329
+ if args[0].is_a?(::Array)
1330
+ other_keys = args.shift
1331
+ elsif args[0].is_a?(::Numeric)
1332
+ num_keys = args.shift
1333
+ 1.upto(num_keys)
1334
+ other_keys << args.shift
1335
+ end
1336
+
1337
+ if (args.size == 1) && args[0].is_a?(::Hash)
1338
+ opts = args[0]
1339
+ else
1340
+ while arg = args.shift
1341
+ if 'weight' == arg.to_s.downcase
1342
+ opts[:weights] = []
1343
+ elsif 'aggregate' == arg.to_s.downcase
1344
+ opts[:aggregate] = nil
1345
+ elsif opts.has_key?(:weights)
1346
+ opts[:weights] << arg.to_i
1347
+ elsif opts.has_key?(:aggregate)
1348
+ opts[:aggregate] = arg.to_s.downcase
1349
+ end
1350
+ end
1351
+ end
1352
+ [other_keys, opts]
1353
+ end
1354
+
1355
+ def resort(key)
1356
+ self.keyspace[key.to_s][:list] = do_sort(self.keyspace[key.to_s][:map])
1357
+ end
1358
+
1359
+ def do_sort(map)
1360
+ map.keys.sort do |a, b|
1361
+ score = (map[a] <=> map[b])
1362
+ if 0 == score
1363
+ a <=> b
1364
+ else
1365
+ score
1366
+ end
1367
+ end
1368
+ end
1369
+
1370
+ def ss_union(a, b, weight_a, weight_b, aggregate)
1371
+ self.do_ss_calc( (a[:list] | b[:list] ), a, b, weight_a, weight_b, aggregate )
1372
+ end
1373
+
1374
+ def ss_intersect(a, b, weight_a, weight_b, aggregate)
1375
+ self.do_ss_calc( (a[:list] & b[:list] ), a, b, weight_a, weight_b, aggregate )
1376
+ end
1377
+
1378
+ def do_ss_calc(set, a, b, weight_a, weight_b, aggregate)
1379
+ r = {:map => {}, :list => []}
1380
+ set.each do |k|
1381
+ a_score = a[:map].has_key?(k) && (a[:map][k] * weight_a)
1382
+ b_score = b[:map].has_key?(k) && (b[:map][k] * weight_b)
1383
+ r[:map][k] = if a_score && b_score
1384
+ case aggregate.to_s.downcase
1385
+ when 'sum'
1386
+ a_score + b_score
1387
+ when 'min'
1388
+ [a_score, b_score].min
1389
+ when 'max'
1390
+ [a_score, b_score].max
1391
+ else
1392
+ raise ArgumentError, "Invalid aggregate: #{aggregate}"
1393
+ end
1394
+ elsif a_score
1395
+ a_score
1396
+ else
1397
+ b_score
1398
+ end
1399
+ end
1400
+ r[:list] = do_sort(r[:map])
1401
+ r
1402
+ end
1403
+
1404
+ ## end of redis methods
1405
+
1406
+ def method_missing(*args)
1407
+ puts "unimplemented: #{args}"
1408
+ end
1409
+
1410
+ public
1411
+
1412
+ def inspect
1413
+ "<#{self.class} @name=#{self.name.inspect}>"
1414
+ end
1415
+
1416
+ def enable_eval
1417
+ require 'roc/store/transient_eval'
1418
+ if !self.class.include?(ROC::Store::TransientEval)
1419
+ self.class.send(:include, ROC::Store::TransientEval)
1420
+ end
1421
+ end
1422
+
1423
+ end
1424
+ end
1425
+ end