redis-objects 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|