redis-objects 0.2.1 → 0.2.4
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.
- data/ChangeLog +23 -0
- data/README.rdoc +190 -97
- data/lib/redis/counter.rb +10 -12
- data/lib/redis/helpers/core_commands.rb +54 -0
- data/lib/redis/helpers/serialize.rb +25 -0
- data/lib/redis/list.rb +8 -10
- data/lib/redis/lock.rb +42 -5
- data/lib/redis/objects/counters.rb +41 -23
- data/lib/redis/objects/lists.rb +19 -9
- data/lib/redis/objects/locks.rb +21 -11
- data/lib/redis/objects/sets.rb +20 -9
- data/lib/redis/objects/sorted_sets.rb +45 -0
- data/lib/redis/objects/values.rb +29 -12
- data/lib/redis/objects.rb +10 -8
- data/lib/redis/set.rb +33 -11
- data/lib/redis/sorted_set.rb +275 -0
- data/lib/redis/value.rb +10 -12
- data/spec/redis_objects_instance_spec.rb +339 -32
- data/spec/redis_objects_model_spec.rb +203 -2
- data/spec/spec_helper.rb +1 -0
- metadata +28 -13
- data/lib/redis/objects/core.rb +0 -0
- data/lib/redis/serialize.rb +0 -23
|
@@ -4,9 +4,10 @@ require 'redis/counter'
|
|
|
4
4
|
class Redis
|
|
5
5
|
module Objects
|
|
6
6
|
class UndefinedCounter < StandardError; end #:nodoc:
|
|
7
|
+
class MissingID < StandardError; end #:nodoc:
|
|
8
|
+
|
|
7
9
|
module Counters
|
|
8
10
|
def self.included(klass)
|
|
9
|
-
klass.instance_variable_set('@counters', {})
|
|
10
11
|
klass.instance_variable_set('@initialized_counters', {})
|
|
11
12
|
klass.send :include, InstanceMethods
|
|
12
13
|
klass.extend ClassMethods
|
|
@@ -14,64 +15,81 @@ class Redis
|
|
|
14
15
|
|
|
15
16
|
# Class methods that appear in your class when you include Redis::Objects.
|
|
16
17
|
module ClassMethods
|
|
17
|
-
attr_reader :
|
|
18
|
+
attr_reader :initialized_counters
|
|
18
19
|
|
|
19
20
|
# Define a new counter. It will function like a regular instance
|
|
20
21
|
# method, so it can be used alongside ActiveRecord, DataMapper, etc.
|
|
21
22
|
def counter(name, options={})
|
|
22
23
|
options[:start] ||= 0
|
|
23
24
|
options[:type] ||= options[:start] == 0 ? :increment : :decrement
|
|
24
|
-
@
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
@redis_objects[name] = options.merge(:type => :counter)
|
|
26
|
+
if options[:global]
|
|
27
|
+
instance_eval <<-EndMethods
|
|
28
|
+
def #{name}
|
|
29
|
+
@#{name} ||= Redis::Counter.new(field_key(:#{name}, ''), redis, @redis_objects[:#{name}])
|
|
30
|
+
end
|
|
31
|
+
EndMethods
|
|
32
|
+
class_eval <<-EndMethods
|
|
33
|
+
def #{name}
|
|
34
|
+
self.class.#{name}
|
|
35
|
+
end
|
|
36
|
+
EndMethods
|
|
37
|
+
else
|
|
38
|
+
class_eval <<-EndMethods
|
|
39
|
+
def #{name}
|
|
40
|
+
@#{name} ||= Redis::Counter.new(field_key(:#{name}), redis, self.class.redis_objects[:#{name}])
|
|
41
|
+
end
|
|
42
|
+
EndMethods
|
|
43
|
+
end
|
|
30
44
|
end
|
|
31
45
|
|
|
32
46
|
# Get the current value of the counter. It is more efficient
|
|
33
47
|
# to use the instance method if possible.
|
|
34
|
-
def get_counter(name, id)
|
|
35
|
-
verify_counter_defined!(name)
|
|
48
|
+
def get_counter(name, id=nil)
|
|
49
|
+
verify_counter_defined!(name, id)
|
|
36
50
|
initialize_counter!(name, id)
|
|
37
51
|
redis.get(field_key(name, id)).to_i
|
|
38
52
|
end
|
|
39
53
|
|
|
40
54
|
# Increment a counter with the specified name and id. Accepts a block
|
|
41
55
|
# like the instance method. See Redis::Objects::Counter for details.
|
|
42
|
-
def increment_counter(name, id, by=1, &block)
|
|
43
|
-
verify_counter_defined!(name)
|
|
56
|
+
def increment_counter(name, id=nil, by=1, &block)
|
|
57
|
+
verify_counter_defined!(name, id)
|
|
44
58
|
initialize_counter!(name, id)
|
|
45
|
-
value = redis.
|
|
59
|
+
value = redis.incrby(field_key(name, id), by).to_i
|
|
46
60
|
block_given? ? rewindable_block(:decrement_counter, name, id, value, &block) : value
|
|
47
61
|
end
|
|
48
62
|
|
|
49
63
|
# Decrement a counter with the specified name and id. Accepts a block
|
|
50
64
|
# like the instance method. See Redis::Objects::Counter for details.
|
|
51
|
-
def decrement_counter(name, id, by=1, &block)
|
|
52
|
-
verify_counter_defined!(name)
|
|
65
|
+
def decrement_counter(name, id=nil, by=1, &block)
|
|
66
|
+
verify_counter_defined!(name, id)
|
|
53
67
|
initialize_counter!(name, id)
|
|
54
|
-
value = redis.
|
|
68
|
+
value = redis.decrby(field_key(name, id), by).to_i
|
|
55
69
|
block_given? ? rewindable_block(:increment_counter, name, id, value, &block) : value
|
|
56
70
|
end
|
|
57
71
|
|
|
58
72
|
# Reset a counter to its starting value.
|
|
59
|
-
def reset_counter(name, id, to=nil)
|
|
60
|
-
verify_counter_defined!(name)
|
|
61
|
-
to = @
|
|
62
|
-
redis.set(field_key(name, id), to)
|
|
73
|
+
def reset_counter(name, id=nil, to=nil)
|
|
74
|
+
verify_counter_defined!(name, id)
|
|
75
|
+
to = @redis_objects[name][:start] if to.nil?
|
|
76
|
+
redis.set(field_key(name, id), to.to_i)
|
|
77
|
+
true
|
|
63
78
|
end
|
|
64
79
|
|
|
65
80
|
private
|
|
66
81
|
|
|
67
|
-
def verify_counter_defined!(name) #:nodoc:
|
|
68
|
-
raise Redis::Objects::UndefinedCounter, "Undefined counter :#{name} for class #{self.name}" unless @
|
|
82
|
+
def verify_counter_defined!(name, id) #:nodoc:
|
|
83
|
+
raise Redis::Objects::UndefinedCounter, "Undefined counter :#{name} for class #{self.name}" unless @redis_objects.has_key?(name)
|
|
84
|
+
if id.nil? and !@redis_objects[name][:global]
|
|
85
|
+
raise Redis::Objects::MissingID, "Missing ID for non-global counter #{self.name}##{name}"
|
|
86
|
+
end
|
|
69
87
|
end
|
|
70
88
|
|
|
71
89
|
def initialize_counter!(name, id) #:nodoc:
|
|
72
90
|
key = field_key(name, id)
|
|
73
91
|
unless @initialized_counters[key]
|
|
74
|
-
redis.setnx(key, @
|
|
92
|
+
redis.setnx(key, @redis_objects[name][:start])
|
|
75
93
|
end
|
|
76
94
|
@initialized_counters[key] = true
|
|
77
95
|
end
|
data/lib/redis/objects/lists.rb
CHANGED
|
@@ -5,24 +5,34 @@ class Redis
|
|
|
5
5
|
module Objects
|
|
6
6
|
module Lists
|
|
7
7
|
def self.included(klass)
|
|
8
|
-
klass.instance_variable_set('@lists', {})
|
|
9
8
|
klass.send :include, InstanceMethods
|
|
10
9
|
klass.extend ClassMethods
|
|
11
10
|
end
|
|
12
11
|
|
|
13
12
|
# Class methods that appear in your class when you include Redis::Objects.
|
|
14
13
|
module ClassMethods
|
|
15
|
-
attr_reader :lists
|
|
16
|
-
|
|
17
14
|
# Define a new list. It will function like a regular instance
|
|
18
15
|
# method, so it can be used alongside ActiveRecord, DataMapper, etc.
|
|
19
16
|
def list(name, options={})
|
|
20
|
-
@
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
@redis_objects[name] = options.merge(:type => :list)
|
|
18
|
+
if options[:global]
|
|
19
|
+
instance_eval <<-EndMethods
|
|
20
|
+
def #{name}
|
|
21
|
+
@#{name} ||= Redis::List.new(field_key(:#{name}, ''), redis, @redis_objects[:#{name}])
|
|
22
|
+
end
|
|
23
|
+
EndMethods
|
|
24
|
+
class_eval <<-EndMethods
|
|
25
|
+
def #{name}
|
|
26
|
+
self.class.#{name}
|
|
27
|
+
end
|
|
28
|
+
EndMethods
|
|
29
|
+
else
|
|
30
|
+
class_eval <<-EndMethods
|
|
31
|
+
def #{name}
|
|
32
|
+
@#{name} ||= Redis::List.new(field_key(:#{name}), redis, self.class.redis_objects[:#{name}])
|
|
33
|
+
end
|
|
34
|
+
EndMethods
|
|
35
|
+
end
|
|
26
36
|
end
|
|
27
37
|
end
|
|
28
38
|
|
data/lib/redis/objects/locks.rb
CHANGED
|
@@ -6,25 +6,35 @@ class Redis
|
|
|
6
6
|
class UndefinedLock < StandardError; end #:nodoc:
|
|
7
7
|
module Locks
|
|
8
8
|
def self.included(klass)
|
|
9
|
-
klass.instance_variable_set('@locks', {})
|
|
10
9
|
klass.send :include, InstanceMethods
|
|
11
10
|
klass.extend ClassMethods
|
|
12
11
|
end
|
|
13
12
|
|
|
14
13
|
# Class methods that appear in your class when you include Redis::Objects.
|
|
15
14
|
module ClassMethods
|
|
16
|
-
attr_reader :locks
|
|
17
|
-
|
|
18
15
|
# Define a new lock. It will function like a model attribute,
|
|
19
16
|
# so it can be used alongside ActiveRecord/DataMapper, etc.
|
|
20
17
|
def lock(name, options={})
|
|
21
18
|
options[:timeout] ||= 5 # seconds
|
|
22
|
-
@
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
@redis_objects[name] = options.merge(:type => :lock)
|
|
20
|
+
if options[:global]
|
|
21
|
+
instance_eval <<-EndMethods
|
|
22
|
+
def #{name}_lock(&block)
|
|
23
|
+
@#{name} ||= Redis::Lock.new(field_key(:#{name}_lock, ''), redis, @redis_objects[:#{name}])
|
|
24
|
+
end
|
|
25
|
+
EndMethods
|
|
26
|
+
class_eval <<-EndMethods
|
|
27
|
+
def #{name}_lock(&block)
|
|
28
|
+
self.class.#{name}(block)
|
|
29
|
+
end
|
|
30
|
+
EndMethods
|
|
31
|
+
else
|
|
32
|
+
class_eval <<-EndMethods
|
|
33
|
+
def #{name}_lock(&block)
|
|
34
|
+
@#{name} ||= Redis::Lock.new(field_key(:#{name}_lock), redis, self.class.redis_objects[:#{name}])
|
|
35
|
+
end
|
|
36
|
+
EndMethods
|
|
37
|
+
end
|
|
28
38
|
end
|
|
29
39
|
|
|
30
40
|
# Obtain a lock, and execute the block synchronously. Any other code
|
|
@@ -34,7 +44,7 @@ class Redis
|
|
|
34
44
|
verify_lock_defined!(name)
|
|
35
45
|
raise ArgumentError, "Missing block to #{self.name}.obtain_lock" unless block_given?
|
|
36
46
|
lock_name = field_key("#{name}_lock", id)
|
|
37
|
-
Redis::Lock.new(
|
|
47
|
+
Redis::Lock.new(lock_name, redis, self.redis_objects[name]).lock(&block)
|
|
38
48
|
end
|
|
39
49
|
|
|
40
50
|
# Clear the lock. Use with care - usually only in an Admin page to clear
|
|
@@ -48,7 +58,7 @@ class Redis
|
|
|
48
58
|
private
|
|
49
59
|
|
|
50
60
|
def verify_lock_defined!(name)
|
|
51
|
-
raise Redis::Objects::UndefinedLock, "Undefined lock :#{name} for class #{self.name}" unless @
|
|
61
|
+
raise Redis::Objects::UndefinedLock, "Undefined lock :#{name} for class #{self.name}" unless @redis_objects.has_key?(name)
|
|
52
62
|
end
|
|
53
63
|
end
|
|
54
64
|
end
|
data/lib/redis/objects/sets.rb
CHANGED
|
@@ -5,24 +5,35 @@ class Redis
|
|
|
5
5
|
module Objects
|
|
6
6
|
module Sets
|
|
7
7
|
def self.included(klass)
|
|
8
|
-
klass.instance_variable_set('@sets', {})
|
|
9
8
|
klass.send :include, InstanceMethods
|
|
10
9
|
klass.extend ClassMethods
|
|
11
10
|
end
|
|
12
11
|
|
|
13
12
|
# Class methods that appear in your class when you include Redis::Objects.
|
|
14
13
|
module ClassMethods
|
|
15
|
-
attr_reader :sets
|
|
16
|
-
|
|
17
14
|
# Define a new list. It will function like a regular instance
|
|
18
15
|
# method, so it can be used alongside ActiveRecord, DataMapper, etc.
|
|
19
16
|
def set(name, options={})
|
|
20
|
-
@
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
@redis_objects[name] = options.merge(:type => :set)
|
|
18
|
+
if options[:global]
|
|
19
|
+
instance_eval <<-EndMethods
|
|
20
|
+
def #{name}
|
|
21
|
+
@#{name} ||= Redis::Set.new(field_key(:#{name}, ''), redis, @redis_objects[:#{name}])
|
|
22
|
+
end
|
|
23
|
+
EndMethods
|
|
24
|
+
class_eval <<-EndMethods
|
|
25
|
+
def #{name}
|
|
26
|
+
self.class.#{name}
|
|
27
|
+
end
|
|
28
|
+
EndMethods
|
|
29
|
+
else
|
|
30
|
+
class_eval <<-EndMethods
|
|
31
|
+
def #{name}
|
|
32
|
+
@#{name} ||= Redis::Set.new(field_key(:#{name}), redis, self.class.redis_objects[:#{name}])
|
|
33
|
+
end
|
|
34
|
+
EndMethods
|
|
35
|
+
end
|
|
36
|
+
|
|
26
37
|
end
|
|
27
38
|
end
|
|
28
39
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# This is the class loader, for use as "include Redis::Objects::Sets"
|
|
2
|
+
# For the object itself, see "Redis::Set"
|
|
3
|
+
require 'redis/sorted_set'
|
|
4
|
+
class Redis
|
|
5
|
+
module Objects
|
|
6
|
+
module SortedSets
|
|
7
|
+
def self.included(klass)
|
|
8
|
+
klass.send :include, InstanceMethods
|
|
9
|
+
klass.extend ClassMethods
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Class methods that appear in your class when you include Redis::Objects.
|
|
13
|
+
module ClassMethods
|
|
14
|
+
# Define a new list. It will function like a regular instance
|
|
15
|
+
# method, so it can be used alongside ActiveRecord, DataMapper, etc.
|
|
16
|
+
def sorted_set(name, options={})
|
|
17
|
+
@redis_objects[name] = options.merge(:type => :sorted_set)
|
|
18
|
+
if options[:global]
|
|
19
|
+
instance_eval <<-EndMethods
|
|
20
|
+
def #{name}
|
|
21
|
+
@#{name} ||= Redis::SortedSet.new(field_key(:#{name}, ''), redis, @redis_objects[:#{name}])
|
|
22
|
+
end
|
|
23
|
+
EndMethods
|
|
24
|
+
class_eval <<-EndMethods
|
|
25
|
+
def #{name}
|
|
26
|
+
self.class.#{name}
|
|
27
|
+
end
|
|
28
|
+
EndMethods
|
|
29
|
+
else
|
|
30
|
+
class_eval <<-EndMethods
|
|
31
|
+
def #{name}
|
|
32
|
+
@#{name} ||= Redis::SortedSet.new(field_key(:#{name}), redis, self.class.redis_objects[:#{name}])
|
|
33
|
+
end
|
|
34
|
+
EndMethods
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Instance methods that appear in your class when you include Redis::Objects.
|
|
41
|
+
module InstanceMethods
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/redis/objects/values.rb
CHANGED
|
@@ -5,27 +5,44 @@ class Redis
|
|
|
5
5
|
module Objects
|
|
6
6
|
module Values
|
|
7
7
|
def self.included(klass)
|
|
8
|
-
klass.instance_variable_set('@values', {})
|
|
9
8
|
klass.send :include, InstanceMethods
|
|
10
9
|
klass.extend ClassMethods
|
|
11
10
|
end
|
|
12
11
|
|
|
13
12
|
# Class methods that appear in your class when you include Redis::Objects.
|
|
14
13
|
module ClassMethods
|
|
15
|
-
attr_reader :values
|
|
16
|
-
|
|
17
14
|
# Define a new simple value. It will function like a regular instance
|
|
18
15
|
# method, so it can be used alongside ActiveRecord, DataMapper, etc.
|
|
19
16
|
def value(name, options={})
|
|
20
|
-
@
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
#{name}
|
|
27
|
-
|
|
28
|
-
|
|
17
|
+
@redis_objects[name] = options.merge(:type => :value)
|
|
18
|
+
if options[:global]
|
|
19
|
+
instance_eval <<-EndMethods
|
|
20
|
+
def #{name}
|
|
21
|
+
@#{name} ||= Redis::Value.new(field_key(:#{name}, ''), redis, @redis_objects[:#{name}])
|
|
22
|
+
end
|
|
23
|
+
def #{name}=(value)
|
|
24
|
+
#{name}.value = value
|
|
25
|
+
end
|
|
26
|
+
EndMethods
|
|
27
|
+
class_eval <<-EndMethods
|
|
28
|
+
def #{name}
|
|
29
|
+
self.class.#{name}
|
|
30
|
+
end
|
|
31
|
+
def #{name}=(value)
|
|
32
|
+
self.class.#{name} = value
|
|
33
|
+
end
|
|
34
|
+
EndMethods
|
|
35
|
+
else
|
|
36
|
+
class_eval <<-EndMethods
|
|
37
|
+
def #{name}
|
|
38
|
+
@#{name} ||= Redis::Value.new(field_key(:#{name}), redis, self.class.redis_objects[:#{name}])
|
|
39
|
+
end
|
|
40
|
+
def #{name}=(value)
|
|
41
|
+
#{name}.value = value
|
|
42
|
+
end
|
|
43
|
+
EndMethods
|
|
44
|
+
end
|
|
45
|
+
|
|
29
46
|
end
|
|
30
47
|
end
|
|
31
48
|
|
data/lib/redis/objects.rb
CHANGED
|
@@ -6,7 +6,7 @@ class Redis
|
|
|
6
6
|
# Redis::Objects enables high-performance atomic operations in your app
|
|
7
7
|
# by leveraging the atomic features of the Redis server. To use Redis::Objects,
|
|
8
8
|
# first include it in any class you want. (This example uses an ActiveRecord
|
|
9
|
-
# subclass, but that is *not* required.) Then, use +counter
|
|
9
|
+
# subclass, but that is *not* required.) Then, use +counter+, +lock+, +set+, etc
|
|
10
10
|
# to define your primitives:
|
|
11
11
|
#
|
|
12
12
|
# class Game < ActiveRecord::Base
|
|
@@ -14,8 +14,8 @@ class Redis
|
|
|
14
14
|
#
|
|
15
15
|
# counter :joined_players
|
|
16
16
|
# counter :active_players
|
|
17
|
-
# set :player_ids
|
|
18
17
|
# lock :archive_game
|
|
18
|
+
# set :player_ids
|
|
19
19
|
# end
|
|
20
20
|
#
|
|
21
21
|
# The, you can use these counters both for bookeeping and as atomic actions:
|
|
@@ -39,10 +39,11 @@ class Redis
|
|
|
39
39
|
dir = File.expand_path(__FILE__.sub(/\.rb$/,''))
|
|
40
40
|
|
|
41
41
|
autoload :Counters, File.join(dir, 'counters')
|
|
42
|
-
autoload :Values, File.join(dir, 'values')
|
|
43
42
|
autoload :Lists, File.join(dir, 'lists')
|
|
44
|
-
autoload :Sets, File.join(dir, 'sets')
|
|
45
43
|
autoload :Locks, File.join(dir, 'locks')
|
|
44
|
+
autoload :Sets, File.join(dir, 'sets')
|
|
45
|
+
autoload :SortedSets, File.join(dir, 'sorted_sets')
|
|
46
|
+
autoload :Values, File.join(dir, 'values')
|
|
46
47
|
|
|
47
48
|
class NotConnected < StandardError; end
|
|
48
49
|
|
|
@@ -55,22 +56,23 @@ class Redis
|
|
|
55
56
|
def included(klass)
|
|
56
57
|
# Core (this file)
|
|
57
58
|
klass.instance_variable_set('@redis', @redis)
|
|
59
|
+
klass.instance_variable_set('@redis_objects', {})
|
|
58
60
|
klass.send :include, InstanceMethods
|
|
59
61
|
klass.extend ClassMethods
|
|
60
62
|
|
|
61
63
|
# Pull in each object type
|
|
62
64
|
klass.send :include, Redis::Objects::Counters
|
|
63
|
-
klass.send :include, Redis::Objects::Values
|
|
64
65
|
klass.send :include, Redis::Objects::Lists
|
|
65
|
-
klass.send :include, Redis::Objects::Sets
|
|
66
66
|
klass.send :include, Redis::Objects::Locks
|
|
67
|
+
klass.send :include, Redis::Objects::Sets
|
|
68
|
+
klass.send :include, Redis::Objects::SortedSets
|
|
69
|
+
klass.send :include, Redis::Objects::Values
|
|
67
70
|
end
|
|
68
71
|
end
|
|
69
72
|
|
|
70
73
|
# Class methods that appear in your class when you include Redis::Objects.
|
|
71
74
|
module ClassMethods
|
|
72
|
-
attr_accessor :redis
|
|
73
|
-
|
|
75
|
+
attr_accessor :redis, :redis_objects
|
|
74
76
|
|
|
75
77
|
# Set the Redis prefix to use. Defaults to model_name
|
|
76
78
|
def prefix=(prefix) @prefix = prefix end
|
data/lib/redis/set.rb
CHANGED
|
@@ -5,16 +5,18 @@ class Redis
|
|
|
5
5
|
class Set
|
|
6
6
|
require 'enumerator'
|
|
7
7
|
include Enumerable
|
|
8
|
-
require 'redis/
|
|
9
|
-
include Redis::
|
|
8
|
+
require 'redis/helpers/core_commands'
|
|
9
|
+
include Redis::Helpers::CoreCommands
|
|
10
|
+
require 'redis/helpers/serialize'
|
|
11
|
+
include Redis::Helpers::Serialize
|
|
10
12
|
|
|
11
13
|
attr_reader :key, :options, :redis
|
|
12
14
|
|
|
13
15
|
# Create a new Set.
|
|
14
|
-
def initialize(key,
|
|
16
|
+
def initialize(key, *args)
|
|
15
17
|
@key = key
|
|
16
|
-
@
|
|
17
|
-
@
|
|
18
|
+
@options = args.last.is_a?(Hash) ? args.pop : {}
|
|
19
|
+
@redis = args.first || $redis
|
|
18
20
|
end
|
|
19
21
|
|
|
20
22
|
# Works like add. Can chain together: list << 'a' << 'b'
|
|
@@ -46,11 +48,6 @@ class Redis
|
|
|
46
48
|
redis.srem(key, value)
|
|
47
49
|
end
|
|
48
50
|
|
|
49
|
-
# Wipe the set entirely. Redis: DEL
|
|
50
|
-
def clear
|
|
51
|
-
redis.del(key)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
51
|
# Iterate through each member of the set. Redis::Objects mixes in Enumerable,
|
|
55
52
|
# so you can also use familiar methods like +collect+, +detect+, and so forth.
|
|
56
53
|
def each(&block)
|
|
@@ -99,11 +96,36 @@ class Redis
|
|
|
99
96
|
alias_method :+, :union
|
|
100
97
|
|
|
101
98
|
# Calculate the union and store it in Redis as +name+. Returns the number
|
|
102
|
-
# of elements in the stored union. Redis:
|
|
99
|
+
# of elements in the stored union. Redis: SUNIONSTORE
|
|
103
100
|
def unionstore(name, *sets)
|
|
104
101
|
redis.sunionstore(name, key, *keys_from_objects(sets))
|
|
105
102
|
end
|
|
106
103
|
|
|
104
|
+
# Return the difference vs another set. Can pass it either another set
|
|
105
|
+
# object or set name. Also available as ^ or - which is a bit cleaner:
|
|
106
|
+
#
|
|
107
|
+
# members_difference = set1 ^ set2
|
|
108
|
+
# members_difference = set1 - set2
|
|
109
|
+
#
|
|
110
|
+
# If you want to specify multiple sets, you must use +difference+:
|
|
111
|
+
#
|
|
112
|
+
# members_difference = set1.difference(set2, set3, set4)
|
|
113
|
+
# members_difference = set1.diff(set2, set3, set4)
|
|
114
|
+
#
|
|
115
|
+
# Redis: SDIFF
|
|
116
|
+
def difference(*sets)
|
|
117
|
+
from_redis redis.sdiff(key, *keys_from_objects(sets))
|
|
118
|
+
end
|
|
119
|
+
alias_method :diff, :difference
|
|
120
|
+
alias_method :^, :difference
|
|
121
|
+
alias_method :-, :difference
|
|
122
|
+
|
|
123
|
+
# Calculate the diff and store it in Redis as +name+. Returns the number
|
|
124
|
+
# of elements in the stored union. Redis: SDIFFSTORE
|
|
125
|
+
def diffstore(name, *sets)
|
|
126
|
+
redis.sdiffstore(name, key, *keys_from_objects(sets))
|
|
127
|
+
end
|
|
128
|
+
|
|
107
129
|
# The number of members in the set. Aliased as size. Redis: SCARD
|
|
108
130
|
def length
|
|
109
131
|
redis.scard(key)
|