redis-objects 0.2.0 → 0.2.1
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/README.rdoc +31 -4
- data/lib/redis/counter.rb +8 -15
- data/lib/redis/list.rb +9 -8
- data/lib/redis/objects.rb +0 -4
- data/lib/redis/serialize.rb +23 -0
- data/lib/redis/set.rb +9 -8
- data/lib/redis/value.rb +8 -10
- data/spec/redis_objects_instance_spec.rb +254 -2
- data/spec/redis_objects_model_spec.rb +38 -10
- data/spec/spec_helper.rb +4 -2
- metadata +3 -3
- data/lib/redis/data_types.rb +0 -84
data/README.rdoc
CHANGED
@@ -47,6 +47,9 @@ Create a new counter. The +counter_name+ is the key stored in Redis.
|
|
47
47
|
@counter.increment
|
48
48
|
@counter.decrement
|
49
49
|
puts @counter.value
|
50
|
+
|
51
|
+
This gem provides a clean way to do atomic blocks as well:
|
52
|
+
|
50
53
|
@counter.increment do |val|
|
51
54
|
raise "Full" if val > MAX_VAL # rewind counter
|
52
55
|
end
|
@@ -72,6 +75,14 @@ Lists work just like Ruby arrays:
|
|
72
75
|
@list.clear
|
73
76
|
# etc
|
74
77
|
|
78
|
+
Complex data types are no problem:
|
79
|
+
|
80
|
+
@list << {:name => "Nate", :city => "San Diego"}
|
81
|
+
@list << {:name => "Peter", :city => "Oceanside"}
|
82
|
+
@list.each do |el|
|
83
|
+
puts "#{el[:name]} lives in #{el[:city]}"
|
84
|
+
end
|
85
|
+
|
75
86
|
=== Sets
|
76
87
|
|
77
88
|
Sets work like the Ruby {Set}[http://ruby-doc.org/core/classes/Set.html] class:
|
@@ -106,6 +117,16 @@ Or store them in Redis:
|
|
106
117
|
@set1.unionstore('unionname', @set2, @set3)
|
107
118
|
members = @set1.redis.get('unionname')
|
108
119
|
|
120
|
+
And use complex data types too:
|
121
|
+
|
122
|
+
@set1 << {:name => "Nate", :city => "San Diego"}
|
123
|
+
@set1 << {:name => "Peter", :city => "Oceanside"}
|
124
|
+
@set2 << {:name => "Nate", :city => "San Diego"}
|
125
|
+
@set2 << {:name => "Jeff", :city => "Del Mar"}
|
126
|
+
|
127
|
+
@set1 & @set2 # Nate
|
128
|
+
@set1 | @set2 # all 3 people
|
129
|
+
|
109
130
|
=== Values
|
110
131
|
|
111
132
|
Simple values are easy as well:
|
@@ -114,10 +135,16 @@ Simple values are easy as well:
|
|
114
135
|
@value.value = 'a'
|
115
136
|
@value.delete
|
116
137
|
|
138
|
+
Of course complex data is no problem:
|
139
|
+
|
140
|
+
@account = Account.create!(params[:account])
|
141
|
+
@newest = Redis::Value.new('newest_account')
|
142
|
+
@newest.value = @account
|
143
|
+
|
117
144
|
== Example 2: Class Usage
|
118
145
|
|
119
|
-
|
120
|
-
ActiveRecord model, DataMapper resource, or other class. Redis::Objects
|
146
|
+
Using Redis::Objects this way makes it trivial to integrate Redis types with an
|
147
|
+
existing ActiveRecord model, DataMapper resource, or other class. Redis::Objects
|
121
148
|
will work with any class that provides an +id+ method that returns a unique
|
122
149
|
value. Redis::Objects will automatically create keys that are unique to
|
123
150
|
each object.
|
@@ -158,7 +185,7 @@ Familiar Ruby array operations Just Work (TM):
|
|
158
185
|
@team.on_base.length # 1
|
159
186
|
@team.on_base.delete('player3')
|
160
187
|
|
161
|
-
Sets work
|
188
|
+
Sets operations work too:
|
162
189
|
|
163
190
|
@team.outfielders << 'outfielder1' << 'outfielder1'
|
164
191
|
@team.outfielders << 'outfielder2'
|
@@ -168,7 +195,7 @@ Sets work like the Ruby {Set}[http://ruby-doc.org/core/classes/Set.html] class:
|
|
168
195
|
end
|
169
196
|
player = @team.outfielders.detect{|of| of == 'outfielder2'}
|
170
197
|
|
171
|
-
|
198
|
+
Counters can be atomically incremented/decremented (but not assigned):
|
172
199
|
|
173
200
|
@team.hits.increment # or incr
|
174
201
|
@team.hits.decrement # or decr
|
data/lib/redis/counter.rb
CHANGED
@@ -22,38 +22,31 @@ class Redis
|
|
22
22
|
# disconnecting all players).
|
23
23
|
def reset(to=options[:start])
|
24
24
|
redis.set(key, to.to_i)
|
25
|
-
@value = to.to_i
|
26
25
|
end
|
27
26
|
|
28
|
-
#
|
27
|
+
# Returns the current value of the counter. Normally just calling the
|
29
28
|
# counter will lazily fetch the value, and only update it if increment
|
30
29
|
# or decrement is called. This forces a network call to redis-server
|
31
30
|
# to get the current value.
|
32
|
-
def
|
33
|
-
|
31
|
+
def value
|
32
|
+
redis.get(key).to_i
|
34
33
|
end
|
34
|
+
alias_method :get, :value
|
35
35
|
|
36
36
|
# Delete a counter. Usage discouraged. Consider +reset+ instead.
|
37
37
|
def delete
|
38
38
|
redis.del(key)
|
39
|
-
@value = nil
|
40
39
|
end
|
41
40
|
alias_method :del, :delete
|
42
41
|
|
43
|
-
# Returns the (possibly cached) value of the counter. Use +get+ to
|
44
|
-
# force a re-get from the Redis server.
|
45
|
-
def value
|
46
|
-
@value ||= get
|
47
|
-
end
|
48
|
-
|
49
42
|
# Increment the counter atomically and return the new value. If passed
|
50
43
|
# a block, that block will be evaluated with the new value of the counter
|
51
44
|
# as an argument. If the block returns nil or throws an exception, the
|
52
45
|
# counter will automatically be decremented to its previous value. This
|
53
46
|
# method is aliased as incr() for brevity.
|
54
47
|
def increment(by=1, &block)
|
55
|
-
|
56
|
-
block_given? ? rewindable_block(:decrement,
|
48
|
+
val = redis.incr(key, by).to_i
|
49
|
+
block_given? ? rewindable_block(:decrement, val, &block) : val
|
57
50
|
end
|
58
51
|
alias_method :incr, :increment
|
59
52
|
|
@@ -63,8 +56,8 @@ class Redis
|
|
63
56
|
# counter will automatically be incremented to its previous value. This
|
64
57
|
# method is aliased as incr() for brevity.
|
65
58
|
def decrement(by=1, &block)
|
66
|
-
|
67
|
-
block_given? ? rewindable_block(:increment,
|
59
|
+
val = redis.decr(key, by).to_i
|
60
|
+
block_given? ? rewindable_block(:increment, val, &block) : val
|
68
61
|
end
|
69
62
|
alias_method :decr, :decrement
|
70
63
|
|
data/lib/redis/list.rb
CHANGED
@@ -6,6 +6,8 @@ class Redis
|
|
6
6
|
class List
|
7
7
|
require 'enumerator'
|
8
8
|
include Enumerable
|
9
|
+
require 'redis/serialize'
|
10
|
+
include Redis::Serialize
|
9
11
|
|
10
12
|
attr_reader :key, :options, :redis
|
11
13
|
def initialize(key, redis=$redis, options={})
|
@@ -22,27 +24,27 @@ class Redis
|
|
22
24
|
|
23
25
|
# Add a member to the end of the list. Redis: RPUSH
|
24
26
|
def push(value)
|
25
|
-
redis.rpush(key, value)
|
27
|
+
redis.rpush(key, to_redis(value))
|
26
28
|
end
|
27
29
|
|
28
30
|
# Remove a member from the end of the list. Redis: RPOP
|
29
31
|
def pop
|
30
|
-
redis.rpop(key)
|
32
|
+
from_redis redis.rpop(key)
|
31
33
|
end
|
32
34
|
|
33
35
|
# Add a member to the start of the list. Redis: LPUSH
|
34
36
|
def unshift(value)
|
35
|
-
redis.lpush(key, value)
|
37
|
+
redis.lpush(key, to_redis(value))
|
36
38
|
end
|
37
39
|
|
38
40
|
# Remove a member from the start of the list. Redis: LPOP
|
39
41
|
def shift
|
40
|
-
redis.lpop(key)
|
42
|
+
from_redis redis.lpop(key)
|
41
43
|
end
|
42
44
|
|
43
45
|
# Return all values in the list. Redis: LRANGE(0,-1)
|
44
46
|
def values
|
45
|
-
range(0, -1)
|
47
|
+
from_redis range(0, -1)
|
46
48
|
end
|
47
49
|
alias_method :get, :values
|
48
50
|
|
@@ -64,7 +66,6 @@ class Redis
|
|
64
66
|
# Redis: LREM
|
65
67
|
def delete(name, count=0)
|
66
68
|
redis.lrem(key, count, name) # weird api
|
67
|
-
get
|
68
69
|
end
|
69
70
|
|
70
71
|
# Iterate through each member of the set. Redis::Objects mixes in Enumerable,
|
@@ -76,13 +77,13 @@ class Redis
|
|
76
77
|
# Return a range of values from +start_index+ to +end_index+. Can also use
|
77
78
|
# the familiar list[start,end] Ruby syntax. Redis: LRANGE
|
78
79
|
def range(start_index, end_index)
|
79
|
-
redis.lrange(key, start_index, end_index)
|
80
|
+
from_redis redis.lrange(key, start_index, end_index)
|
80
81
|
end
|
81
82
|
|
82
83
|
# Return the value at the given index. Can also use familiar list[index] syntax.
|
83
84
|
# Redis: LINDEX
|
84
85
|
def at(index)
|
85
|
-
redis.lindex(key, index)
|
86
|
+
from_redis redis.lindex(key, index)
|
86
87
|
end
|
87
88
|
|
88
89
|
# Return the first element in the list. Redis: LINDEX(0)
|
data/lib/redis/objects.rb
CHANGED
@@ -58,10 +58,6 @@ class Redis
|
|
58
58
|
klass.send :include, InstanceMethods
|
59
59
|
klass.extend ClassMethods
|
60
60
|
|
61
|
-
# Adapted from Redis::Model for marshaling complex data
|
62
|
-
require 'redis/data_types'
|
63
|
-
klass.send :include, Redis::DataTypes
|
64
|
-
|
65
61
|
# Pull in each object type
|
66
62
|
klass.send :include, Redis::Objects::Counters
|
67
63
|
klass.send :include, Redis::Objects::Values
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Redis
|
2
|
+
module Serialize
|
3
|
+
include Marshal
|
4
|
+
|
5
|
+
def to_redis(value)
|
6
|
+
case value
|
7
|
+
when String, Fixnum, Bignum, Float
|
8
|
+
value
|
9
|
+
else
|
10
|
+
dump(value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def from_redis(value)
|
15
|
+
case value
|
16
|
+
when Array
|
17
|
+
value.collect{|v| from_redis(v)}
|
18
|
+
else
|
19
|
+
restore(value) rescue value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/redis/set.rb
CHANGED
@@ -5,6 +5,8 @@ class Redis
|
|
5
5
|
class Set
|
6
6
|
require 'enumerator'
|
7
7
|
include Enumerable
|
8
|
+
require 'redis/serialize'
|
9
|
+
include Redis::Serialize
|
8
10
|
|
9
11
|
attr_reader :key, :options, :redis
|
10
12
|
|
@@ -24,25 +26,24 @@ class Redis
|
|
24
26
|
# Add the specified value to the set only if it does not exist already.
|
25
27
|
# Redis: SADD
|
26
28
|
def add(value)
|
27
|
-
redis.sadd(key, value)
|
29
|
+
redis.sadd(key, to_redis(value))
|
28
30
|
end
|
29
31
|
|
30
32
|
# Return all members in the set. Redis: SMEMBERS
|
31
33
|
def members
|
32
|
-
redis.smembers(key)
|
34
|
+
from_redis redis.smembers(key)
|
33
35
|
end
|
34
36
|
alias_method :get, :members
|
35
37
|
|
36
38
|
# Returns true if the specified value is in the set. Redis: SISMEMBER
|
37
39
|
def member?(value)
|
38
|
-
redis.sismember(key, value)
|
40
|
+
redis.sismember(key, to_redis(value))
|
39
41
|
end
|
40
42
|
alias_method :include?, :member?
|
41
43
|
|
42
44
|
# Delete the value from the set. Redis: SREM
|
43
|
-
def delete(
|
44
|
-
redis.srem(key,
|
45
|
-
get
|
45
|
+
def delete(value)
|
46
|
+
redis.srem(key, value)
|
46
47
|
end
|
47
48
|
|
48
49
|
# Wipe the set entirely. Redis: DEL
|
@@ -68,7 +69,7 @@ class Redis
|
|
68
69
|
#
|
69
70
|
# Redis: SINTER
|
70
71
|
def intersection(*sets)
|
71
|
-
redis.sinter(key, *keys_from_objects(sets))
|
72
|
+
from_redis redis.sinter(key, *keys_from_objects(sets))
|
72
73
|
end
|
73
74
|
alias_method :intersect, :intersection
|
74
75
|
alias_method :inter, :intersection
|
@@ -92,7 +93,7 @@ class Redis
|
|
92
93
|
#
|
93
94
|
# Redis: SUNION
|
94
95
|
def union(*sets)
|
95
|
-
redis.sunion(key, *keys_from_objects(sets))
|
96
|
+
from_redis redis.sunion(key, *keys_from_objects(sets))
|
96
97
|
end
|
97
98
|
alias_method :|, :union
|
98
99
|
alias_method :+, :union
|
data/lib/redis/value.rb
CHANGED
@@ -3,6 +3,9 @@ class Redis
|
|
3
3
|
# Class representing a simple value. You can use standard Ruby operations on it.
|
4
4
|
#
|
5
5
|
class Value
|
6
|
+
require 'redis/serialize'
|
7
|
+
include Redis::Serialize
|
8
|
+
|
6
9
|
attr_reader :key, :options, :redis
|
7
10
|
def initialize(key, redis=$redis, options={})
|
8
11
|
@key = key
|
@@ -11,22 +14,17 @@ class Redis
|
|
11
14
|
@redis.setnx(key, @options[:default]) if @options[:default]
|
12
15
|
end
|
13
16
|
|
14
|
-
def value
|
15
|
-
@value ||= get
|
16
|
-
end
|
17
|
-
|
18
17
|
def value=(val)
|
19
|
-
redis.set(key, val)
|
20
|
-
@value = val
|
18
|
+
redis.set(key, to_redis(val))
|
21
19
|
end
|
22
20
|
|
23
|
-
def
|
24
|
-
|
21
|
+
def value
|
22
|
+
from_redis redis.get(key)
|
25
23
|
end
|
26
|
-
|
24
|
+
alias_method :get, :value
|
25
|
+
|
27
26
|
def delete
|
28
27
|
redis.del(key)
|
29
|
-
@value = nil
|
30
28
|
end
|
31
29
|
alias_method :del, :delete
|
32
30
|
|
@@ -1,18 +1,270 @@
|
|
1
1
|
|
2
2
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
3
3
|
|
4
|
+
require 'redis/counter'
|
5
|
+
require 'redis/list'
|
6
|
+
require 'redis/set'
|
7
|
+
require 'redis/value'
|
8
|
+
|
9
|
+
describe Redis::Value do
|
10
|
+
before :all do
|
11
|
+
@value = Redis::Value.new('spec/value')
|
12
|
+
end
|
13
|
+
|
14
|
+
before :each do
|
15
|
+
@value.delete
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should handle simple values" do
|
19
|
+
@value.should == nil
|
20
|
+
@value.value = 'Trevor Hoffman'
|
21
|
+
@value.should == 'Trevor Hoffman'
|
22
|
+
@value.get.should == 'Trevor Hoffman'
|
23
|
+
@value.del.should be_true
|
24
|
+
@value.should be_nil
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should handle complex marshaled values" do
|
28
|
+
@value.should == nil
|
29
|
+
@value.value = {:json => 'data'}
|
30
|
+
@value.should == {:json => 'data'}
|
31
|
+
@value.get.should == {:json => 'data'}
|
32
|
+
@value.value = [[1,2], {:t3 => 4}]
|
33
|
+
@value.should == [[1,2], {:t3 => 4}]
|
34
|
+
@value.get.should == [[1,2], {:t3 => 4}]
|
35
|
+
@value.del.should be_true
|
36
|
+
@value.should be_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
after :all do
|
40
|
+
@value.delete
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
4
45
|
describe Redis::List do
|
46
|
+
before :all do
|
47
|
+
@list = Redis::List.new('spec/list')
|
48
|
+
end
|
49
|
+
|
50
|
+
before :each do
|
51
|
+
@list.clear
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should handle lists of simple values" do
|
55
|
+
@list.should be_empty
|
56
|
+
@list << 'a'
|
57
|
+
@list.should == ['a']
|
58
|
+
@list.get.should == ['a']
|
59
|
+
@list.unshift 'b'
|
60
|
+
@list.to_s.should == 'b, a'
|
61
|
+
@list.should == ['b','a']
|
62
|
+
@list.get.should == ['b','a']
|
63
|
+
@list.push 'c'
|
64
|
+
@list.should == ['b','a','c']
|
65
|
+
@list.get.should == ['b','a','c']
|
66
|
+
@list.first.should == 'b'
|
67
|
+
@list.last.should == 'c'
|
68
|
+
@list << 'd'
|
69
|
+
@list.should == ['b','a','c','d']
|
70
|
+
@list[1].should == 'a'
|
71
|
+
@list[0].should == 'b'
|
72
|
+
@list[2].should == 'c'
|
73
|
+
@list[3].should == 'd'
|
74
|
+
@list.include?('c').should be_true
|
75
|
+
@list.include?('no').should be_false
|
76
|
+
@list.pop.should == 'd'
|
77
|
+
@list[0].should == @list.at(0)
|
78
|
+
@list[1].should == @list.at(1)
|
79
|
+
@list[2].should == @list.at(2)
|
80
|
+
@list.should == ['b','a','c']
|
81
|
+
@list.get.should == ['b','a','c']
|
82
|
+
@list.shift.should == 'b'
|
83
|
+
@list.should == ['a','c']
|
84
|
+
@list.get.should == ['a','c']
|
85
|
+
@list << 'e' << 'f' << 'e'
|
86
|
+
@list.should == ['a','c','e','f','e']
|
87
|
+
@list.get.should == ['a','c','e','f','e']
|
88
|
+
@list.delete('e').should == 2
|
89
|
+
@list.should == ['a','c','f']
|
90
|
+
@list.get.should == ['a','c','f']
|
91
|
+
@list << 'j'
|
92
|
+
@list.should == ['a','c','f','j']
|
93
|
+
@list[0..2].should == ['a','c','f']
|
94
|
+
@list[1, 3].should == ['c','f','j']
|
95
|
+
@list.length.should == 4
|
96
|
+
@list.size.should == 4
|
97
|
+
@list.should == ['a','c','f','j']
|
98
|
+
@list.get.should == ['a','c','f','j']
|
5
99
|
|
100
|
+
i = -1
|
101
|
+
@list.each do |st|
|
102
|
+
st.should == @list[i += 1]
|
103
|
+
end
|
104
|
+
@list.should == ['a','c','f','j']
|
105
|
+
@list.get.should == ['a','c','f','j']
|
6
106
|
|
107
|
+
@list.each_with_index do |st,i|
|
108
|
+
st.should == @list[i]
|
109
|
+
end
|
110
|
+
@list.should == ['a','c','f','j']
|
111
|
+
@list.get.should == ['a','c','f','j']
|
112
|
+
|
113
|
+
coll = @list.collect{|st| st}
|
114
|
+
coll.should == ['a','c','f','j']
|
115
|
+
@list.should == ['a','c','f','j']
|
116
|
+
@list.get.should == ['a','c','f','j']
|
117
|
+
|
118
|
+
@list << 'a'
|
119
|
+
coll = @list.select{|st| st == 'a'}
|
120
|
+
coll.should == ['a','a']
|
121
|
+
@list.should == ['a','c','f','j','a']
|
122
|
+
@list.get.should == ['a','c','f','j','a']
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should handle lists of complex data types" do
|
126
|
+
@list << {:json => 'data'}
|
127
|
+
@list << {:json2 => 'data2'}
|
128
|
+
@list.first.should == {:json => 'data'}
|
129
|
+
@list.last.should == {:json2 => 'data2'}
|
130
|
+
@list << [1,2,3,[4,5]]
|
131
|
+
@list.last.should == [1,2,3,[4,5]]
|
132
|
+
@list.shift.should == {:json => 'data'}
|
133
|
+
end
|
134
|
+
|
135
|
+
after :all do
|
136
|
+
@list.clear
|
137
|
+
end
|
7
138
|
end
|
8
139
|
|
9
140
|
describe Redis::Set do
|
141
|
+
before :all do
|
142
|
+
@set = Redis::Set.new('spec/set')
|
143
|
+
@set_1 = Redis::Set.new('spec/set_1')
|
144
|
+
@set_2 = Redis::Set.new('spec/set_2')
|
145
|
+
@set_3 = Redis::Set.new('spec/set_3')
|
146
|
+
end
|
10
147
|
|
148
|
+
before :each do
|
149
|
+
@set.clear
|
150
|
+
@set_1.clear
|
151
|
+
@set_2.clear
|
152
|
+
@set_3.clear
|
153
|
+
end
|
11
154
|
|
12
|
-
|
155
|
+
it "should handle sets of simple values" do
|
156
|
+
@set.should be_empty
|
157
|
+
@set << 'a' << 'a' << 'a'
|
158
|
+
@set.should == ['a']
|
159
|
+
@set.get.should == ['a']
|
160
|
+
@set << 'b' << 'b'
|
161
|
+
@set.to_s.should == 'a, b'
|
162
|
+
@set.should == ['a','b']
|
163
|
+
@set.members.should == ['a','b']
|
164
|
+
@set.get.should == ['a','b']
|
165
|
+
@set << 'c'
|
166
|
+
@set.sort.should == ['a','b','c']
|
167
|
+
@set.get.sort.should == ['a','b','c']
|
168
|
+
@set.delete('c')
|
169
|
+
@set.should == ['a','b']
|
170
|
+
@set.get.sort.should == ['a','b']
|
171
|
+
@set.length.should == 2
|
172
|
+
@set.size.should == 2
|
173
|
+
|
174
|
+
i = 0
|
175
|
+
@set.each do |st|
|
176
|
+
i += 1
|
177
|
+
end
|
178
|
+
i.should == @set.length
|
13
179
|
|
14
|
-
|
180
|
+
coll = @set.collect{|st| st}
|
181
|
+
coll.should == ['a','b']
|
182
|
+
@set.should == ['a','b']
|
183
|
+
@set.get.should == ['a','b']
|
15
184
|
|
185
|
+
@set << 'c'
|
186
|
+
@set.member?('c').should be_true
|
187
|
+
@set.include?('c').should be_true
|
188
|
+
@set.member?('no').should be_false
|
189
|
+
coll = @set.select{|st| st == 'c'}
|
190
|
+
coll.should == ['c']
|
191
|
+
@set.sort.should == ['a','b','c']
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should handle set intersections and unions" do
|
195
|
+
@set_1 << 'a' << 'b' << 'c' << 'd' << 'e'
|
196
|
+
@set_2 << 'c' << 'd' << 'e' << 'f' << 'g'
|
197
|
+
@set_3 << 'a' << 'd' << 'g' << 'l' << 'm'
|
198
|
+
@set_1.sort.should == %w(a b c d e)
|
199
|
+
@set_2.sort.should == %w(c d e f g)
|
200
|
+
@set_3.sort.should == %w(a d g l m)
|
201
|
+
(@set_1 & @set_2).sort.should == ['c','d','e']
|
202
|
+
@set_1.intersection(@set_2).sort.should == ['c','d','e']
|
203
|
+
@set_1.intersection(@set_2, @set_3).sort.should == ['d']
|
204
|
+
@set_1.intersect(@set_2).sort.should == ['c','d','e']
|
205
|
+
@set_1.inter(@set_2, @set_3).sort.should == ['d']
|
206
|
+
@set_1.interstore(INTERSTORE_KEY, @set_2).should == 3
|
207
|
+
@set_1.redis.smembers(INTERSTORE_KEY).sort.should == ['c','d','e']
|
208
|
+
@set_1.interstore(INTERSTORE_KEY, @set_2, @set_3).should == 1
|
209
|
+
@set_1.redis.smembers(INTERSTORE_KEY).sort.should == ['d']
|
16
210
|
|
211
|
+
(@set_1 | @set_2).sort.should == ['a','b','c','d','e','f','g']
|
212
|
+
(@set_1 + @set_2).sort.should == ['a','b','c','d','e','f','g']
|
213
|
+
@set_1.union(@set_2).sort.should == ['a','b','c','d','e','f','g']
|
214
|
+
@set_1.union(@set_2, @set_3).sort.should == ['a','b','c','d','e','f','g','l','m']
|
215
|
+
@set_1.unionstore(UNIONSTORE_KEY, @set_2).should == 7
|
216
|
+
@set_1.redis.smembers(UNIONSTORE_KEY).sort.should == ['a','b','c','d','e','f','g']
|
217
|
+
@set_1.unionstore(UNIONSTORE_KEY, @set_2, @set_3).should == 9
|
218
|
+
@set_1.redis.smembers(UNIONSTORE_KEY).sort.should == ['a','b','c','d','e','f','g','l','m']
|
219
|
+
end
|
220
|
+
|
221
|
+
after :all do
|
222
|
+
@set.clear
|
223
|
+
@set_1.clear
|
224
|
+
@set_2.clear
|
225
|
+
@set_3.clear
|
226
|
+
end
|
17
227
|
end
|
18
228
|
|
229
|
+
describe Redis::Counter do
|
230
|
+
before :all do
|
231
|
+
@counter = Redis::Counter.new('spec/counter')
|
232
|
+
@counter2 = Redis::Counter.new('spec/counter')
|
233
|
+
end
|
234
|
+
|
235
|
+
before :each do
|
236
|
+
@counter.reset
|
237
|
+
end
|
238
|
+
|
239
|
+
it "should support increment/decrement of counters" do
|
240
|
+
@counter.key.should == 'spec/counter'
|
241
|
+
@counter.incr(10)
|
242
|
+
@counter.should == 10
|
243
|
+
|
244
|
+
# math proxy ops
|
245
|
+
(@counter == 10).should be_true
|
246
|
+
(@counter <= 10).should be_true
|
247
|
+
(@counter < 11).should be_true
|
248
|
+
(@counter > 9).should be_true
|
249
|
+
(@counter >= 10).should be_true
|
250
|
+
"#{@counter}".should == "10"
|
251
|
+
|
252
|
+
@counter.increment.should == 11
|
253
|
+
@counter.increment.should == 12
|
254
|
+
@counter2.increment.should == 13
|
255
|
+
@counter2.increment(2).should == 15
|
256
|
+
@counter.decrement.should == 14
|
257
|
+
@counter2.decrement.should == 13
|
258
|
+
@counter.decrement.should == 12
|
259
|
+
@counter2.decrement(4).should == 8
|
260
|
+
@counter.should == 8
|
261
|
+
@counter.reset.should be_true
|
262
|
+
@counter.should == 0
|
263
|
+
@counter.reset(15).should be_true
|
264
|
+
@counter.should == 15
|
265
|
+
end
|
266
|
+
|
267
|
+
after :all do
|
268
|
+
@counter.delete
|
269
|
+
end
|
270
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
|
2
2
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
require 'redis/objects'
|
5
|
+
Redis::Objects.redis = $redis
|
6
6
|
|
7
7
|
class Roster
|
8
8
|
include Redis::Objects
|
@@ -76,11 +76,10 @@ describe Redis::Objects do
|
|
76
76
|
@roster2.available_slots.decrement.should == 13
|
77
77
|
@roster.available_slots.decrement.should == 12
|
78
78
|
@roster2.available_slots.decrement(4).should == 8
|
79
|
-
@roster.available_slots.should ==
|
80
|
-
@roster.available_slots.
|
81
|
-
@roster.available_slots.reset.should == 10
|
79
|
+
@roster.available_slots.should == 8
|
80
|
+
@roster.available_slots.reset.should be_true
|
82
81
|
@roster.available_slots.should == 10
|
83
|
-
@roster.available_slots.reset(15).should
|
82
|
+
@roster.available_slots.reset(15).should be_true
|
84
83
|
@roster.available_slots.should == 15
|
85
84
|
@roster.pitchers.increment.should == 1
|
86
85
|
@roster.basic.increment.should == 1
|
@@ -260,7 +259,16 @@ describe Redis::Objects do
|
|
260
259
|
@roster.starting_pitcher = 'Trevor Hoffman'
|
261
260
|
@roster.starting_pitcher.should == 'Trevor Hoffman'
|
262
261
|
@roster.starting_pitcher.get.should == 'Trevor Hoffman'
|
263
|
-
@roster.starting_pitcher.del
|
262
|
+
@roster.starting_pitcher.del.should be_true
|
263
|
+
@roster.starting_pitcher.should be_nil
|
264
|
+
end
|
265
|
+
|
266
|
+
it "should handle complex marshaled values" do
|
267
|
+
@roster.starting_pitcher.should == nil
|
268
|
+
@roster.starting_pitcher = {:json => 'data'}
|
269
|
+
@roster.starting_pitcher.should == {:json => 'data'}
|
270
|
+
@roster.starting_pitcher.get.should == {:json => 'data'}
|
271
|
+
@roster.starting_pitcher.del.should be_true
|
264
272
|
@roster.starting_pitcher.should be_nil
|
265
273
|
end
|
266
274
|
|
@@ -286,19 +294,19 @@ describe Redis::Objects do
|
|
286
294
|
@roster.player_stats[3].should == 'd'
|
287
295
|
@roster.player_stats.include?('c').should be_true
|
288
296
|
@roster.player_stats.include?('no').should be_false
|
289
|
-
@roster.player_stats.pop
|
297
|
+
@roster.player_stats.pop.should == 'd'
|
290
298
|
@roster.player_stats[0].should == @roster.player_stats.at(0)
|
291
299
|
@roster.player_stats[1].should == @roster.player_stats.at(1)
|
292
300
|
@roster.player_stats[2].should == @roster.player_stats.at(2)
|
293
301
|
@roster.player_stats.should == ['b','a','c']
|
294
302
|
@roster.player_stats.get.should == ['b','a','c']
|
295
|
-
@roster.player_stats.shift
|
303
|
+
@roster.player_stats.shift.should == 'b'
|
296
304
|
@roster.player_stats.should == ['a','c']
|
297
305
|
@roster.player_stats.get.should == ['a','c']
|
298
306
|
@roster.player_stats << 'e' << 'f' << 'e'
|
299
307
|
@roster.player_stats.should == ['a','c','e','f','e']
|
300
308
|
@roster.player_stats.get.should == ['a','c','e','f','e']
|
301
|
-
@roster.player_stats.delete('e')
|
309
|
+
@roster.player_stats.delete('e').should == 2
|
302
310
|
@roster.player_stats.should == ['a','c','f']
|
303
311
|
@roster.player_stats.get.should == ['a','c','f']
|
304
312
|
@roster.player_stats << 'j'
|
@@ -401,6 +409,26 @@ describe Redis::Objects do
|
|
401
409
|
@roster_1.redis.smembers(UNIONSTORE_KEY).sort.should == ['a','b','c','d','e','f','g','l','m']
|
402
410
|
end
|
403
411
|
|
412
|
+
it "should handle lists of complex data types" do
|
413
|
+
@roster.player_stats << {:json => 'data'}
|
414
|
+
@roster.player_stats << {:json2 => 'data2'}
|
415
|
+
@roster.player_stats.first.should == {:json => 'data'}
|
416
|
+
@roster.player_stats.last.should == {:json2 => 'data2'}
|
417
|
+
@roster.player_stats << [1,2,3,[4,5]]
|
418
|
+
@roster.player_stats.last.should == [1,2,3,[4,5]]
|
419
|
+
@roster.player_stats.shift.should == {:json => 'data'}
|
420
|
+
end
|
421
|
+
|
422
|
+
it "should handle sets of complex data types" do
|
423
|
+
@roster.outfielders << {:a => 1} << {:b => 2}
|
424
|
+
@roster.outfielders.member?({:b => 2})
|
425
|
+
@roster.outfielders.members.should == [{:b => 2}, {:a => 1}]
|
426
|
+
@roster_1.outfielders << {:a => 1} << {:b => 2}
|
427
|
+
@roster_2.outfielders << {:b => 2} << {:c => 3}
|
428
|
+
(@roster_1.outfielders & @roster_2.outfielders).should == [{:b => 2}]
|
429
|
+
(@roster_1.outfielders | @roster_2.outfielders).should == [{:b=>2}, {:c=>3}, {:a=>1}]
|
430
|
+
end
|
431
|
+
|
404
432
|
it "should provide a lock method that accepts a block" do
|
405
433
|
@roster.resort_lock.key.should == 'roster:1:resort_lock'
|
406
434
|
a = false
|
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
2
2
|
require 'redis'
|
3
|
-
require 'redis/objects'
|
4
3
|
|
5
|
-
|
4
|
+
$redis = Redis.new(:host => ENV['REDIS_HOST'], :port => ENV['REDIS_PORT'])
|
5
|
+
|
6
|
+
UNIONSTORE_KEY = 'test:unionstore'
|
7
|
+
INTERSTORE_KEY = 'test:interstore'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-objects
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nate Wiger
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-11-
|
12
|
+
date: 2009-11-27 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -33,7 +33,6 @@ extra_rdoc_files:
|
|
33
33
|
- README.rdoc
|
34
34
|
files:
|
35
35
|
- lib/redis/counter.rb
|
36
|
-
- lib/redis/data_types.rb
|
37
36
|
- lib/redis/list.rb
|
38
37
|
- lib/redis/lock.rb
|
39
38
|
- lib/redis/objects/core.rb
|
@@ -43,6 +42,7 @@ files:
|
|
43
42
|
- lib/redis/objects/sets.rb
|
44
43
|
- lib/redis/objects/values.rb
|
45
44
|
- lib/redis/objects.rb
|
45
|
+
- lib/redis/serialize.rb
|
46
46
|
- lib/redis/set.rb
|
47
47
|
- lib/redis/value.rb
|
48
48
|
- spec/redis_objects_instance_spec.rb
|
data/lib/redis/data_types.rb
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
class Redis
|
2
|
-
module DataTypes
|
3
|
-
TYPES = %w(String Integer Float EpochTime DateTime Json Yaml IPAddress FilePath Uri Slug)
|
4
|
-
def self.included(klass)
|
5
|
-
TYPES.each do |data_type|
|
6
|
-
if Object.const_defined?(data_type)
|
7
|
-
klass = Object.const_get(data_type)
|
8
|
-
else
|
9
|
-
klass = Object.const_set(data_type, Class.new)
|
10
|
-
end
|
11
|
-
if const_defined?(data_type)
|
12
|
-
klass.extend const_get(data_type)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
module String
|
18
|
-
def to_redis; to_s; end
|
19
|
-
end
|
20
|
-
|
21
|
-
module Integer
|
22
|
-
def from_redis(value); value && value.to_i end
|
23
|
-
end
|
24
|
-
|
25
|
-
module Float
|
26
|
-
def from_redis(value); value && value.to_f end
|
27
|
-
end
|
28
|
-
|
29
|
-
module EpochTime
|
30
|
-
def to_redis(value)
|
31
|
-
value.is_a?(DateTime) ? value.to_time.to_i : value.to_i
|
32
|
-
end
|
33
|
-
|
34
|
-
def from_redis(value) Time.at(value.to_i) end
|
35
|
-
end
|
36
|
-
|
37
|
-
module DateTime
|
38
|
-
def to_redis(value); value.strftime('%FT%T%z') end
|
39
|
-
def from_redis(value); value && ::DateTime.strptime(value, '%FT%T%z') end
|
40
|
-
end
|
41
|
-
|
42
|
-
module Json
|
43
|
-
def to_redis(value); Yajl::Encoder.encode(value) end
|
44
|
-
def from_redis(value); value && Yajl::Parser.parse(value) end
|
45
|
-
end
|
46
|
-
|
47
|
-
module Yaml
|
48
|
-
def to_redis(value); Yaml.dump(value) end
|
49
|
-
def from_redis(value); Yaml.load(value) end
|
50
|
-
end
|
51
|
-
|
52
|
-
module IPAddress
|
53
|
-
def from_redis(value)
|
54
|
-
return nil if value.nil?
|
55
|
-
if value.is_a?(String)
|
56
|
-
IPAddr.new(value.empty? ? '0.0.0.0' : value)
|
57
|
-
else
|
58
|
-
raise "+value+ must be nil or a String"
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
module FilePath
|
64
|
-
require 'pathname'
|
65
|
-
def from_redis(value)
|
66
|
-
value.blank? ? nil : Pathname.new(value)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
module Uri
|
71
|
-
require 'addressable/uri'
|
72
|
-
def from_redis(value)
|
73
|
-
Addressable::URI.parse(value)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
module Slug
|
78
|
-
require 'addressable/uri'
|
79
|
-
def to_redis(value)
|
80
|
-
Addressable::URI.parse(value).display_uri
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|