familia 0.10.2 → 1.0.0.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.pre-commit-config.yaml +1 -1
  4. data/.rubocop.yml +75 -0
  5. data/.rubocop_todo.yml +63 -0
  6. data/Gemfile +6 -1
  7. data/Gemfile.lock +47 -15
  8. data/README.md +11 -12
  9. data/VERSION.yml +4 -3
  10. data/familia.gemspec +18 -13
  11. data/lib/familia/base.rb +33 -0
  12. data/lib/familia/connection.rb +87 -0
  13. data/lib/familia/core_ext.rb +119 -124
  14. data/lib/familia/errors.rb +33 -0
  15. data/lib/familia/features/api_version.rb +19 -0
  16. data/lib/familia/features/atomic_saves.rb +8 -0
  17. data/lib/familia/features/quantizer.rb +35 -0
  18. data/lib/familia/features/safe_dump.rb +175 -0
  19. data/lib/familia/features.rb +51 -0
  20. data/lib/familia/horreum/class_methods.rb +240 -0
  21. data/lib/familia/horreum/commands.rb +59 -0
  22. data/lib/familia/horreum/relations_management.rb +141 -0
  23. data/lib/familia/horreum/serialization.rb +154 -0
  24. data/lib/familia/horreum/settings.rb +63 -0
  25. data/lib/familia/horreum/utils.rb +43 -0
  26. data/lib/familia/horreum.rb +198 -0
  27. data/lib/familia/logging.rb +249 -0
  28. data/lib/familia/redistype/commands.rb +56 -0
  29. data/lib/familia/redistype/serialization.rb +110 -0
  30. data/lib/familia/redistype.rb +185 -0
  31. data/lib/familia/settings.rb +38 -0
  32. data/lib/familia/types/hashkey.rb +108 -0
  33. data/lib/familia/types/list.rb +155 -0
  34. data/lib/familia/types/sorted_set.rb +234 -0
  35. data/lib/familia/types/string.rb +115 -0
  36. data/lib/familia/types/unsorted_set.rb +123 -0
  37. data/lib/familia/utils.rb +129 -0
  38. data/lib/familia/version.rb +25 -0
  39. data/lib/familia.rb +56 -161
  40. data/lib/redis_middleware.rb +109 -0
  41. data/try/00_familia_try.rb +5 -4
  42. data/try/10_familia_try.rb +21 -17
  43. data/try/20_redis_type_try.rb +67 -0
  44. data/try/{21_redis_object_zset_try.rb → 21_redis_type_zset_try.rb} +2 -2
  45. data/try/{22_redis_object_set_try.rb → 22_redis_type_set_try.rb} +2 -2
  46. data/try/{23_redis_object_list_try.rb → 23_redis_type_list_try.rb} +2 -2
  47. data/try/{24_redis_object_string_try.rb → 24_redis_type_string_try.rb} +6 -6
  48. data/try/{25_redis_object_hash_try.rb → 25_redis_type_hash_try.rb} +3 -3
  49. data/try/26_redis_bool_try.rb +10 -6
  50. data/try/27_redis_horreum_try.rb +40 -0
  51. data/try/30_familia_object_try.rb +21 -20
  52. data/try/35_feature_safedump_try.rb +83 -0
  53. data/try/40_customer_try.rb +140 -0
  54. data/try/41_customer_safedump_try.rb +86 -0
  55. data/try/test_helpers.rb +186 -0
  56. metadata +50 -47
  57. data/lib/familia/helpers.rb +0 -70
  58. data/lib/familia/object.rb +0 -533
  59. data/lib/familia/redisobject.rb +0 -1017
  60. data/lib/familia/test_helpers.rb +0 -40
  61. data/lib/familia/tools.rb +0 -67
  62. data/try/20_redis_object_try.rb +0 -44
@@ -1,1017 +0,0 @@
1
-
2
- module Familia
3
-
4
- class RedisObject
5
- @registration = {}
6
- @classes = []
7
-
8
- # To be called inside every class that inherits RedisObject
9
- # +meth+ becomes the base for the class and instances methods
10
- # that are created for the given +klass+ (e.g. Obj.list)
11
- def RedisObject.register klass, meth
12
- registration[meth] = klass
13
- end
14
-
15
- def RedisObject.registration
16
- @registration
17
- end
18
-
19
- def RedisObject.classes
20
- @classes
21
- end
22
-
23
- @db, @ttl = nil, nil
24
- class << self
25
- attr_accessor :parent
26
- attr_writer :ttl, :classes, :db, :uri
27
- def ttl v=nil
28
- @ttl = v unless v.nil?
29
- @ttl || (parent ? parent.ttl : nil)
30
- end
31
- def db v=nil
32
- @db = v unless v.nil?
33
- @db || (parent ? parent.db : nil)
34
- end
35
- def uri v=nil
36
- @uri = v unless v.nil?
37
- @uri || (parent ? parent.uri : Familia.uri)
38
- end
39
- def inherited(obj)
40
- obj.db = self.db
41
- obj.ttl = self.ttl
42
- obj.uri = self.uri
43
- obj.parent = self
44
- RedisObject.classes << obj
45
- super(obj)
46
- end
47
- end
48
-
49
- attr_reader :name, :parent
50
- attr_writer :redis
51
-
52
- # RedisObject instances are frozen. `cache` is a hash
53
- # for you to store values retreived from Redis. This is
54
- # not used anywhere by default, but you're encouraged
55
- # to use it in your specific scenarios.
56
- attr_reader :cache
57
-
58
- # +name+: If parent is set, this will be used as the suffix
59
- # for rediskey. Otherwise this becomes the value of the key.
60
- # If this is an Array, the elements will be joined.
61
- #
62
- # Options:
63
- #
64
- # :class => A class that responds to Familia.load_method and
65
- # Familia.dump_method. These will be used when loading and
66
- # saving data from/to redis to unmarshal/marshal the class.
67
- #
68
- # :reference => When true the index of the given value will be
69
- # stored rather than the marshaled value. This assumes that
70
- # the marshaled object is stored at a separate key. When read,
71
- # from_redis looks for that separate key and returns the
72
- # unmarshaled object. :class must be specified. Default: false.
73
- #
74
- # :extend => Extend this instance with the functionality in an
75
- # other module. Literally: "self.extend opts[:extend]".
76
- #
77
- # :parent => The Familia object that this redis object belongs
78
- # to. This can be a class that includes Familia or an instance.
79
- #
80
- # :ttl => the time to live in seconds. When not nil, this will
81
- # set the redis expire for this key whenever #save is called.
82
- # You can also call it explicitly via #update_expiration.
83
- #
84
- # :quantize => append a quantized timestamp to the rediskey.
85
- # Takes one of the following:
86
- # Boolean: include the default stamp (now % 10 minutes)
87
- # Integer: the number of seconds to quantize to (e.g. 1.hour)
88
- # Array: All arguments for qstamp (quantum, pattern, Time.now)
89
- #
90
- # :default => the default value (String-only)
91
- #
92
- # :dump_method => the instance method to call to serialize the
93
- # object before sending it to Redis (default: Familia.dump_method).
94
- #
95
- # :load_method => the class method to call to deserialize the
96
- # object after it's read from Redis (default: Familia.load_method).
97
- #
98
- # :db => the redis database to use (ignored if :redis is used).
99
- #
100
- # :redis => an instance of Redis.
101
- #
102
- # Uses the redis connection of the parent or the value of
103
- # opts[:redis] or Familia.redis (in that order).
104
- def initialize name, opts={}
105
- @name, @opts = name, opts
106
- @name = @name.join(Familia.delim) if Array === @name
107
- #Familia.ld [name, opts, caller[0]].inspect
108
- self.extend @opts[:extend] if Module === @opts[:extend]
109
- @db = @opts.delete(:db)
110
- @parent = @opts.delete(:parent)
111
- @ttl ||= @opts.delete(:ttl)
112
- @redis ||= @opts.delete(:redis)
113
- @cache = {}
114
- init if respond_to? :init
115
- end
116
-
117
- def clear_cache
118
- @cache.clear
119
- end
120
-
121
- def echo meth, trace
122
- redis.echo "[#{self.class}\##{meth}] #{trace} (#{@opts[:class]}\#)"
123
- end
124
-
125
- def redis
126
- return @redis if @redis
127
- parent? ? parent.redis : Familia.redis(db)
128
- end
129
-
130
- # Returns the most likely value for db, checking (in this order):
131
- # * the value from :class if it's a Familia object
132
- # * the value from :parent
133
- # * the value self.class.db
134
- # * assumes the db is 0
135
- #
136
- # After this is called once, this method will always return the
137
- # same value.
138
- def db
139
- # Note it's important that we select this value at the last
140
- # possible moment rather than in initialize b/c the value
141
- # could be modified after that but before this is called.
142
- if @opts[:class] && @opts[:class].ancestors.member?(Familia)
143
- @opts[:class].db
144
- elsif parent?
145
- parent.db
146
- else
147
- self.class.db || @db || 0
148
- end
149
- end
150
-
151
- def ttl
152
- @ttl ||
153
- (parent.ttl if parent?) ||
154
- (@opts[:class].ttl if class?) ||
155
- (self.class.ttl if self.class.respond_to?(:ttl))
156
- end
157
-
158
- # returns a redis key based on the parent
159
- # object so it will include the proper index.
160
- def rediskey
161
- if parent?
162
- # We need to check if the parent has a specific suffix
163
- # for the case where we have specified one other than :object.
164
- suffix = parent.kind_of?(Familia) && parent.class.suffix != :object ? parent.class.suffix : name
165
- k = parent.rediskey(name, nil)
166
- else
167
- k = [name].flatten.compact.join(Familia.delim)
168
- end
169
- if @opts[:quantize]
170
- args = case @opts[:quantize]
171
- when Numeric
172
- [@opts[:quantize]] # :quantize => 1.minute
173
- when Array
174
- @opts[:quantize] # :quantize => [1.day, '%m%D']
175
- else
176
- [] # :quantize => true
177
- end
178
- k = [k, qstamp(*args)].join(Familia.delim)
179
- end
180
- k
181
- end
182
-
183
- def class?
184
- !@opts[:class].to_s.empty? && @opts[:class].kind_of?(Familia)
185
- end
186
-
187
- def parent?
188
- Class === parent || Module === parent || parent.kind_of?(Familia)
189
- end
190
-
191
- def qstamp quantum=nil, pattern=nil, now=Familia.now
192
- quantum ||= ttl || 10.minutes
193
- pattern ||= '%H%M'
194
- rounded = now - (now % quantum)
195
- Time.at(rounded).utc.strftime(pattern)
196
- end
197
-
198
- def update_expiration(ttl=nil)
199
- ttl ||= self.ttl
200
- return if ttl.to_i.zero? # nil will be zero
201
- Familia.ld "#{rediskey} to #{ttl}"
202
- expire ttl.to_i
203
- end
204
-
205
- def move db
206
- redis.move rediskey, db
207
- end
208
-
209
- def rename newkey
210
- redis.rename rediskey, newkey
211
- end
212
-
213
- def renamenx newkey
214
- redis.renamenx rediskey, newkey
215
- end
216
-
217
- def type
218
- redis.type rediskey
219
- end
220
-
221
- def delete
222
- redis.del rediskey
223
- end
224
- alias_method :clear, :delete
225
- alias_method :del, :delete
226
-
227
- #def destroy!
228
- # clear
229
- # # TODO: delete redis objects for this instance
230
- #end
231
-
232
- def exists?
233
- redis.exists(rediskey) && !size.zero?
234
- end
235
-
236
- def realttl
237
- redis.ttl rediskey
238
- end
239
-
240
- def expire sec
241
- redis.expire rediskey, sec.to_i
242
- end
243
-
244
- def expireat unixtime
245
- redis.expireat rediskey, unixtime
246
- end
247
-
248
- def persist
249
- redis.persist rediskey
250
- end
251
-
252
- def dump_method
253
- @opts[:dump_method] || Familia.dump_method
254
- end
255
-
256
- def load_method
257
- @opts[:load_method] || Familia.load_method
258
- end
259
-
260
- def to_redis v
261
- return v unless @opts[:class]
262
- ret = case @opts[:class]
263
- when ::Symbol, ::String, ::Integer, ::Float, Gibbler::Digest
264
- v
265
- when ::NilClass
266
- ""
267
- else
268
- if ::String === v
269
- v
270
-
271
- elsif @opts[:reference] == true
272
- unless v.respond_to? :index
273
- raise Familia::Problem, "#{v.class} does not have an index method"
274
- end
275
- unless v.kind_of?(Familia)
276
- raise Familia::Problem, "#{v.class} is not Familia (#{name})"
277
- end
278
- v.index
279
-
280
- elsif v.respond_to? dump_method
281
- v.send dump_method
282
-
283
- else
284
- raise Familia::Problem, "No such method: #{v.class}.#{dump_method}"
285
- end
286
- end
287
- if ret.nil?
288
- Familia.ld "[#{self.class}\#to_redis] nil returned for #{@opts[:class]}\##{name}"
289
- end
290
- ret
291
- end
292
-
293
- def multi_from_redis *values
294
- Familia.ld "multi_from_redis: (#{@opts}) #{values}"
295
- return [] if values.empty?
296
- return values.flatten unless @opts[:class]
297
- ret = case @opts[:class]
298
- when ::String
299
- v.to_s
300
- when ::Symbol
301
- v.to_s.to_sym
302
- when ::Integer, ::Float
303
- @opts[:class].induced_from v
304
- else
305
- objs = values
306
-
307
- if @opts[:reference] == true
308
- objs = @opts[:class].rawmultiget *values
309
- end
310
- objs.compact!
311
- if @opts[:class].respond_to? load_method
312
- objs.collect! { |obj|
313
- begin
314
- v = @opts[:class].send load_method, obj
315
- if v.nil?
316
- Familia.ld "[#{self.class}\#multi_from_redis] nil returned for #{@opts[:class]}\##{name}"
317
- end
318
- v
319
- rescue => ex
320
- Familia.info v
321
- Familia.info "Parse error for #{rediskey} (#{load_method}): #{ex.message}"
322
- Familia.info ex.backtrace
323
- nil
324
- end
325
- }
326
- else
327
- raise Familia::Problem, "No such method: #{@opts[:class]}##{load_method}"
328
- end
329
- objs.compact # don't use compact! b/c the return value appears in ret
330
- end
331
- ret
332
- end
333
-
334
- def from_redis v
335
- return @opts[:default] if v.nil?
336
- return v unless @opts[:class]
337
- ret = multi_from_redis v
338
- ret.first unless ret.nil? # return the object or nil
339
- end
340
-
341
- end
342
-
343
-
344
- class List < RedisObject
345
-
346
- def size
347
- redis.llen rediskey
348
- end
349
- alias_method :length, :size
350
-
351
- def empty?
352
- size == 0
353
- end
354
-
355
- def push *values
356
- echo :push, caller[0] if Familia.debug
357
- values.flatten.compact.each { |v| redis.rpush rediskey, to_redis(v) }
358
- redis.ltrim rediskey, -@opts[:maxlength], -1 if @opts[:maxlength]
359
- update_expiration
360
- self
361
- end
362
-
363
- def << v
364
- push v
365
- end
366
- alias_method :add, :<<
367
-
368
- def unshift *values
369
- values.flatten.compact.each { |v| redis.lpush rediskey, to_redis(v) }
370
- # TODO: test maxlength
371
- redis.ltrim rediskey, 0, @opts[:maxlength] - 1 if @opts[:maxlength]
372
- update_expiration
373
- self
374
- end
375
-
376
- def pop
377
- from_redis redis.rpop(rediskey)
378
- end
379
-
380
- def shift
381
- from_redis redis.lpop(rediskey)
382
- end
383
-
384
- def [] idx, count=nil
385
- if idx.is_a? Range
386
- range idx.first, idx.last
387
- elsif count
388
- case count <=> 0
389
- when 1 then range(idx, idx + count - 1)
390
- when 0 then []
391
- when -1 then nil
392
- end
393
- else
394
- at idx
395
- end
396
- end
397
- alias_method :slice, :[]
398
-
399
- def delete v, count=0
400
- redis.lrem rediskey, count, to_redis(v)
401
- end
402
- alias_method :remove, :delete
403
- alias_method :rem, :delete
404
- alias_method :del, :delete
405
-
406
- def range sidx=0, eidx=-1
407
- el = rangeraw sidx, eidx
408
- multi_from_redis *el
409
- end
410
-
411
- def rangeraw sidx=0, eidx=-1
412
- redis.lrange(rediskey, sidx, eidx)
413
- end
414
-
415
- def members count=-1
416
- echo :members, caller[0] if Familia.debug
417
- count -= 1 if count > 0
418
- range 0, count
419
- end
420
- alias_method :all, :members
421
- alias_method :to_a, :members
422
-
423
- def membersraw count=-1
424
- count -= 1 if count > 0
425
- rangeraw 0, count
426
- end
427
-
428
- #def revmembers count=1 #TODO
429
- # range -count, 0
430
- #end
431
-
432
- def each &blk
433
- range.each &blk
434
- end
435
-
436
- def each_with_index &blk
437
- range.each_with_index &blk
438
- end
439
-
440
- def eachraw &blk
441
- rangeraw.each &blk
442
- end
443
-
444
- def eachraw_with_index &blk
445
- rangeraw.each_with_index &blk
446
- end
447
-
448
- def collect &blk
449
- range.collect &blk
450
- end
451
-
452
- def select &blk
453
- range.select &blk
454
- end
455
-
456
- def collectraw &blk
457
- rangeraw.collect &blk
458
- end
459
-
460
- def selectraw &blk
461
- rangeraw.select &blk
462
- end
463
-
464
- def at idx
465
- from_redis redis.lindex(rediskey, idx)
466
- end
467
-
468
- def first
469
- at 0
470
- end
471
-
472
- def last
473
- at -1
474
- end
475
-
476
- # TODO: def replace
477
- ## Make the value stored at KEY identical to the given list
478
- #define_method :"#{name}_sync" do |*latest|
479
- # latest = latest.flatten.compact
480
- # # Do nothing if we're given an empty Array.
481
- # # Otherwise this would clear all current values
482
- # if latest.empty?
483
- # false
484
- # else
485
- # # Convert to a list of index values if we got the actual objects
486
- # latest = latest.collect { |obj| obj.index } if klass === latest.first
487
- # current = send("#{name_plural}raw")
488
- # added = latest-current
489
- # removed = current-latest
490
- # #Familia.info "#{self.index}: adding: #{added}"
491
- # added.each { |v| self.send("add_#{name_singular}", v) }
492
- # #Familia.info "#{self.index}: removing: #{removed}"
493
- # removed.each { |v| self.send("remove_#{name_singular}", v) }
494
- # true
495
- # end
496
- #end
497
-
498
- Familia::RedisObject.register self, :list
499
- end
500
-
501
- class Set < RedisObject
502
-
503
- def size
504
- redis.scard rediskey
505
- end
506
- alias_method :length, :size
507
-
508
- def empty?
509
- size == 0
510
- end
511
-
512
- def add *values
513
- values.flatten.compact.each { |v| redis.sadd? rediskey, to_redis(v) }
514
- update_expiration
515
- self
516
- end
517
-
518
- def << v
519
- add v
520
- end
521
-
522
- def members
523
- echo :members, caller[0] if Familia.debug
524
- el = membersraw
525
- multi_from_redis *el
526
- end
527
- alias_method :all, :members
528
- alias_method :to_a, :members
529
-
530
- def membersraw
531
- redis.smembers(rediskey)
532
- end
533
-
534
- def each &blk
535
- members.each &blk
536
- end
537
-
538
- def each_with_index &blk
539
- members.each_with_index &blk
540
- end
541
-
542
- def collect &blk
543
- members.collect &blk
544
- end
545
-
546
- def select &blk
547
- members.select &blk
548
- end
549
-
550
- def eachraw &blk
551
- membersraw.each &blk
552
- end
553
-
554
- def eachraw_with_index &blk
555
- membersraw.each_with_index &blk
556
- end
557
-
558
- def collectraw &blk
559
- membersraw.collect &blk
560
- end
561
-
562
- def selectraw &blk
563
- membersraw.select &blk
564
- end
565
-
566
- def member? v
567
- redis.sismember rediskey, to_redis(v)
568
- end
569
- alias_method :include?, :member?
570
-
571
- def delete v
572
- redis.srem rediskey, to_redis(v)
573
- end
574
- alias_method :remove, :delete
575
- alias_method :rem, :delete
576
- alias_method :del, :delete
577
-
578
- def intersection *setkeys
579
- # TODO
580
- end
581
-
582
- def pop
583
- redis.spop rediskey
584
- end
585
-
586
- def move dstkey, v
587
- redis.smove rediskey, dstkey, v
588
- end
589
-
590
- def random
591
- from_redis randomraw
592
- end
593
-
594
- def randomraw
595
- redis.srandmember(rediskey)
596
- end
597
-
598
- ## Make the value stored at KEY identical to the given list
599
- #define_method :"#{name}_sync" do |*latest|
600
- # latest = latest.flatten.compact
601
- # # Do nothing if we're given an empty Array.
602
- # # Otherwise this would clear all current values
603
- # if latest.empty?
604
- # false
605
- # else
606
- # # Convert to a list of index values if we got the actual objects
607
- # latest = latest.collect { |obj| obj.index } if klass === latest.first
608
- # current = send("#{name_plural}raw")
609
- # added = latest-current
610
- # removed = current-latest
611
- # #Familia.info "#{self.index}: adding: #{added}"
612
- # added.each { |v| self.send("add_#{name_singular}", v) }
613
- # #Familia.info "#{self.index}: removing: #{removed}"
614
- # removed.each { |v| self.send("remove_#{name_singular}", v) }
615
- # true
616
- # end
617
- #end
618
-
619
- Familia::RedisObject.register self, :set
620
- end
621
-
622
- class SortedSet < RedisObject
623
-
624
- def size
625
- redis.zcard rediskey
626
- end
627
- alias_method :length, :size
628
-
629
- def empty?
630
- size == 0
631
- end
632
-
633
- # NOTE: The argument order is the reverse of #add
634
- # e.g. obj.metrics[VALUE] = SCORE
635
- def []= v, score
636
- add score, v
637
- end
638
-
639
- # NOTE: The argument order is the reverse of #[]=
640
- def add score, v
641
- ret = redis.zadd rediskey, score, to_redis(v)
642
- update_expiration
643
- ret
644
- end
645
-
646
- def score v
647
- ret = redis.zscore rediskey, to_redis(v)
648
- ret.nil? ? nil : ret.to_f
649
- end
650
- alias_method :[], :score
651
-
652
- def member? v
653
- !rank(v).nil?
654
- end
655
- alias_method :include?, :member?
656
-
657
- # rank of member +v+ when ordered lowest to highest (starts at 0)
658
- def rank v
659
- ret = redis.zrank rediskey, to_redis(v)
660
- ret.nil? ? nil : ret.to_i
661
- end
662
-
663
- # rank of member +v+ when ordered highest to lowest (starts at 0)
664
- def revrank v
665
- ret = redis.zrevrank rediskey, to_redis(v)
666
- ret.nil? ? nil : ret.to_i
667
- end
668
-
669
- def members count=-1, opts={}
670
- count -= 1 if count > 0
671
- el = membersraw count, opts
672
- multi_from_redis *el
673
- end
674
- alias_method :to_a, :members
675
- alias_method :all, :members
676
-
677
- def membersraw count=-1, opts={}
678
- count -= 1 if count > 0
679
- rangeraw 0, count, opts
680
- end
681
-
682
- def revmembers count=-1, opts={}
683
- count -= 1 if count > 0
684
- el = revmembersraw count, opts
685
- multi_from_redis *el
686
- end
687
-
688
- def revmembersraw count=-1, opts={}
689
- count -= 1 if count > 0
690
- revrangeraw 0, count, opts
691
- end
692
-
693
- def each &blk
694
- members.each &blk
695
- end
696
-
697
- def each_with_index &blk
698
- members.each_with_index &blk
699
- end
700
-
701
- def collect &blk
702
- members.collect &blk
703
- end
704
-
705
- def select &blk
706
- members.select &blk
707
- end
708
-
709
- def eachraw &blk
710
- membersraw.each &blk
711
- end
712
-
713
- def eachraw_with_index &blk
714
- membersraw.each_with_index &blk
715
- end
716
-
717
- def collectraw &blk
718
- membersraw.collect &blk
719
- end
720
-
721
- def selectraw &blk
722
- membersraw.select &blk
723
- end
724
-
725
- def range sidx, eidx, opts={}
726
- echo :range, caller[0] if Familia.debug
727
- el = rangeraw(sidx, eidx, opts)
728
- multi_from_redis *el
729
- end
730
-
731
- def rangeraw sidx, eidx, opts={}
732
- # NOTE: :withscores (no underscore) is the correct naming for the
733
- # redis-4.x gem. We pass :withscores through explicitly b/c
734
- # redis.zrange et al only accept that one optional argument.
735
- # Passing `opts`` through leads to an ArgumentError:
736
- #
737
- # sorted_sets.rb:374:in `zrevrange': wrong number of arguments (given 4, expected 3) (ArgumentError)
738
- #
739
- redis.zrange(rediskey, sidx, eidx, **opts)
740
- end
741
-
742
- def revrange sidx, eidx, opts={}
743
- echo :revrange, caller[0] if Familia.debug
744
- el = revrangeraw(sidx, eidx, opts)
745
- multi_from_redis *el
746
- end
747
-
748
- def revrangeraw sidx, eidx, opts={}
749
- redis.zrevrange(rediskey, sidx, eidx, **opts)
750
- end
751
-
752
- # e.g. obj.metrics.rangebyscore (now-12.hours), now, :limit => [0, 10]
753
- def rangebyscore sscore, escore, opts={}
754
- echo :rangebyscore, caller[0] if Familia.debug
755
- el = rangebyscoreraw(sscore, escore, opts)
756
- multi_from_redis *el
757
- end
758
-
759
- def rangebyscoreraw sscore, escore, opts={}
760
- echo :rangebyscoreraw, caller[0] if Familia.debug
761
- redis.zrangebyscore(rediskey, sscore, escore, **opts)
762
- end
763
-
764
- def remrangebyrank srank, erank
765
- redis.zremrangebyrank rediskey, srank, erank
766
- end
767
-
768
- def remrangebyscore sscore, escore
769
- redis.zremrangebyscore rediskey, sscore, escore
770
- end
771
-
772
- def increment v, by=1
773
- redis.zincrby(rediskey, by, v).to_i
774
- end
775
- alias_method :incr, :increment
776
- alias_method :incrby, :increment
777
-
778
- def decrement v, by=1
779
- increment v, -by
780
- end
781
- alias_method :decr, :decrement
782
- alias_method :decrby, :decrement
783
-
784
- def delete v
785
- redis.zrem rediskey, to_redis(v)
786
- end
787
- alias_method :remove, :delete
788
- alias_method :rem, :delete
789
- alias_method :del, :delete
790
-
791
- def at idx
792
- range(idx, idx).first
793
- end
794
-
795
- # Return the first element in the list. Redis: ZRANGE(0)
796
- def first
797
- at(0)
798
- end
799
-
800
- # Return the last element in the list. Redis: ZRANGE(-1)
801
- def last
802
- at(-1)
803
- end
804
-
805
- Familia::RedisObject.register self, :zset
806
- end
807
-
808
- class HashKey < RedisObject
809
-
810
- def size
811
- redis.hlen rediskey
812
- end
813
- alias_method :length, :size
814
-
815
- def empty?
816
- size == 0
817
- end
818
-
819
- def []= n, v
820
- ret = redis.hset rediskey, n, to_redis(v)
821
- update_expiration
822
- ret
823
- rescue TypeError => e
824
- echo :hset, caller[0] if Familia.debug
825
- klass = v.class
826
- msg = "Cannot store #{n} => #{v.inspect} (#{klass}) in #{rediskey}"
827
- raise e.class, msg
828
- end
829
- alias_method :put, :[]=
830
- alias_method :store, :[]=
831
-
832
- def [] n
833
- from_redis redis.hget(rediskey, n)
834
- end
835
- alias_method :get, :[]
836
-
837
- def fetch n, default=nil
838
- ret = self[n]
839
- if ret.nil?
840
- raise IndexError.new("No such index for: #{n}") if default.nil?
841
- default
842
- else
843
- ret
844
- end
845
- end
846
-
847
- def keys
848
- redis.hkeys rediskey
849
- end
850
-
851
- def values
852
- el = redis.hvals(rediskey)
853
- multi_from_redis *el
854
- end
855
-
856
- def all
857
- # TODO: from_redis
858
- redis.hgetall rediskey
859
- end
860
- alias_method :to_hash, :all
861
- alias_method :clone, :all
862
-
863
- def has_key? n
864
- redis.hexists rediskey, n
865
- end
866
- alias_method :include?, :has_key?
867
- alias_method :member?, :has_key?
868
-
869
- def delete n
870
- redis.hdel rediskey, n
871
- end
872
- alias_method :remove, :delete
873
- alias_method :rem, :delete
874
- alias_method :del, :delete
875
-
876
- def increment n, by=1
877
- redis.hincrby(rediskey, n, by).to_i
878
- end
879
- alias_method :incr, :increment
880
- alias_method :incrby, :increment
881
-
882
- def decrement n, by=1
883
- increment n, -by
884
- end
885
- alias_method :decr, :decrement
886
- alias_method :decrby, :decrement
887
-
888
- def update h={}
889
- raise ArgumentError, "Argument to bulk_set must be a hash" unless Hash === h
890
- data = h.inject([]){ |ret,pair| ret << [pair[0], to_redis(pair[1])] }.flatten
891
- ret = redis.hmset(rediskey, *data)
892
- update_expiration
893
- ret
894
- end
895
- alias_method :merge!, :update
896
-
897
- def values_at *names
898
- el = redis.hmget(rediskey, *names.flatten.compact)
899
- multi_from_redis *el
900
- end
901
-
902
- Familia::RedisObject.register self, :hash
903
- end
904
-
905
- class String < RedisObject
906
-
907
- def init
908
- end
909
-
910
- def size
911
- to_s.size
912
- end
913
- alias_method :length, :size
914
-
915
- def empty?
916
- size == 0
917
- end
918
-
919
- def value
920
- echo :value, caller[0..5] if Familia.debug
921
- redis.setnx rediskey, @opts[:default] if @opts[:default]
922
- from_redis redis.get(rediskey)
923
- end
924
- alias_method :content, :value
925
- alias_method :get, :value
926
-
927
- def to_s
928
- value.to_s # value can return nil which to_s should not
929
- end
930
-
931
- def to_i
932
- value.to_i
933
- end
934
-
935
- def value= v
936
- ret = redis.set rediskey, to_redis(v)
937
- update_expiration
938
- ret
939
- end
940
- alias_method :replace, :value=
941
- alias_method :set, :value=
942
-
943
- def setnx v
944
- ret = redis.setnx rediskey, to_redis(v)
945
- update_expiration
946
- ret
947
- end
948
-
949
- def increment
950
- ret = redis.incr rediskey
951
- update_expiration
952
- ret
953
- end
954
- alias_method :incr, :increment
955
-
956
- def incrementby int
957
- ret = redis.incrby rediskey, int.to_i
958
- update_expiration
959
- ret
960
- end
961
- alias_method :incrby, :incrementby
962
-
963
- def decrement
964
- ret = redis.decr rediskey
965
- update_expiration
966
- ret
967
- end
968
- alias_method :decr, :decrement
969
-
970
- def decrementby int
971
- ret = redis.decrby rediskey, int.to_i
972
- update_expiration
973
- ret
974
- end
975
- alias_method :decrby, :decrementby
976
-
977
- def append v
978
- ret = redis.append rediskey, v
979
- update_expiration
980
- ret
981
- end
982
- alias_method :<<, :append
983
-
984
- def getbit offset
985
- redis.getbit rediskey, offset
986
- end
987
-
988
- def setbit offset, v
989
- ret = redis.setbit rediskey, offset, v
990
- update_expiration
991
- ret
992
- end
993
-
994
- def getrange spoint, epoint
995
- redis.getrange rediskey, spoint, epoint
996
- end
997
-
998
- def setrange offset, v
999
- ret = redis.setrange rediskey, offset, v
1000
- update_expiration
1001
- ret
1002
- end
1003
-
1004
- def getset v
1005
- ret = redis.getset rediskey, v
1006
- update_expiration
1007
- ret
1008
- end
1009
-
1010
- def nil?
1011
- value.nil?
1012
- end
1013
-
1014
- Familia::RedisObject.register self, :string
1015
- end
1016
-
1017
- end