redis-objects-legacy 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,65 @@
1
+ require File.dirname(__FILE__) + '/base_object'
2
+ require 'zlib'
3
+
4
+ class Redis
5
+ #
6
+ # Class representing a simple value. You can use standard Ruby operations on it.
7
+ #
8
+ class Value < BaseObject
9
+ def value=(val)
10
+ allow_expiration do
11
+ if val.nil?
12
+ delete
13
+ else
14
+ redis.set key, marshal(val)
15
+ end
16
+ end
17
+ end
18
+ alias_method :set, :value=
19
+
20
+ def value
21
+ value = unmarshal(redis.get(key))
22
+ if value.nil? && !@options[:default].nil?
23
+ @options[:default]
24
+ else
25
+ value
26
+ end
27
+ end
28
+ alias_method :get, :value
29
+
30
+ def marshal(value, *args)
31
+ if !value.nil? && options[:compress]
32
+ compress(super)
33
+ else
34
+ super
35
+ end
36
+ end
37
+
38
+ def unmarshal(value, *args)
39
+ if !value.nil? && options[:compress]
40
+ super(decompress(value), *args)
41
+ else
42
+ super
43
+ end
44
+ end
45
+
46
+ def decompress(value)
47
+ Zlib::Inflate.inflate(value)
48
+ end
49
+
50
+ def compress(value)
51
+ Zlib::Deflate.deflate(value)
52
+ end
53
+
54
+ def inspect
55
+ "#<Redis::Value #{value.inspect}>"
56
+ end
57
+
58
+ def ==(other); value == other end
59
+ def nil?; value.nil? end
60
+
61
+ def method_missing(*args)
62
+ self.value.send *args
63
+ end
64
+ end
65
+ end
@@ -0,0 +1 @@
1
+ require 'redis/objects'
@@ -0,0 +1,46 @@
1
+
2
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
+
4
+ # tests whether autoload functionality works correctly; had issues previously
5
+
6
+ require 'redis/objects'
7
+ # $redis used automatically
8
+
9
+ describe 'Redis::Objects' do
10
+ it "should autoload everything" do
11
+ defined?(::Redis::Counter).should == "constant"
12
+ x = Redis::Counter.new('x')
13
+ x.class.name.should == "Redis::Counter"
14
+ x.redis.should == REDIS_HANDLE
15
+
16
+ defined?(::Redis::HashKey).should == "constant"
17
+ x = Redis::HashKey.new('x')
18
+ x.class.name.should == "Redis::HashKey"
19
+ x.redis.should == REDIS_HANDLE
20
+
21
+ defined?(::Redis::List).should == "constant"
22
+ x = Redis::List.new('x')
23
+ x.class.name.should == "Redis::List"
24
+ x.redis.should == REDIS_HANDLE
25
+
26
+ defined?(::Redis::Lock).should == "constant"
27
+ x = Redis::Lock.new('x')
28
+ x.class.name.should == "Redis::Lock"
29
+ x.redis.should == REDIS_HANDLE
30
+
31
+ defined?(::Redis::Set).should == "constant"
32
+ x = Redis::Set.new('x')
33
+ x.class.name.should == "Redis::Set"
34
+ x.redis.should == REDIS_HANDLE
35
+
36
+ defined?(::Redis::SortedSet).should == "constant"
37
+ x = Redis::SortedSet.new('x')
38
+ x.class.name.should == "Redis::SortedSet"
39
+ x.redis.should == REDIS_HANDLE
40
+
41
+ defined?(::Redis::Value).should == "constant"
42
+ x = Redis::Value.new('x')
43
+ x.class.name.should == "Redis::Value"
44
+ x.redis.should == REDIS_HANDLE
45
+ end
46
+ end
@@ -0,0 +1,24 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ require 'redis/objects'
4
+ Redis::Objects.redis = REDIS_HANDLE
5
+
6
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../../redis-namespace/lib')
7
+ begin
8
+ require 'redis/namespace'
9
+
10
+ describe 'Redis::Namespace compat' do
11
+ it "tests the compatibility of Hash and ::Hash conflicts" do
12
+ ns = Redis::Namespace.new("resque", :redis => REDIS_HANDLE)
13
+ ns.instance_eval { rem_namespace({"resque:x" => nil}) }.should == {"x"=>nil}
14
+ class Foo
15
+ include Redis::Objects
16
+ end
17
+ ns.instance_eval { rem_namespace({"resque:x" => nil}) }.should == {"x"=>nil}
18
+ end
19
+ end
20
+
21
+ rescue LoadError
22
+ # Redis::Namespace not installed
23
+ puts "Skipping Redis::Namespace tests as redis-namespace is not installed"
24
+ end
@@ -0,0 +1,162 @@
1
+
2
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
+
4
+ require 'redis/objects'
5
+ # $redis used automatically
6
+
7
+ begin
8
+ require 'active_record'
9
+ ActiveRecord::Base.establish_connection(
10
+ :adapter => 'sqlite3',
11
+ :database => File.expand_path(File.dirname(__FILE__) + '/redis_objects_test.sqlite3')
12
+ )
13
+
14
+ # monkey patch to use migrations both in Rails 4.x and 5.x
15
+ class ActiveRecord::Migration
16
+ class << self
17
+ def [](version)
18
+ self
19
+ end
20
+ end
21
+ end unless ActiveRecord::Migration.respond_to?(:[])
22
+
23
+ class CreateBlogs < ActiveRecord::Migration[4.2]
24
+ def self.up
25
+ create_table :blogs do |t|
26
+ t.string :name
27
+ # t.integer :num_posts, :default => 0
28
+ t.timestamps null: true
29
+ end
30
+ end
31
+
32
+ def self.down
33
+ drop_table :blogs
34
+ end
35
+ end
36
+
37
+ class Blog < ActiveRecord::Base
38
+ include Redis::Objects
39
+ has_many :posts
40
+ counter :num_posts
41
+ end
42
+
43
+ class CreatePosts < ActiveRecord::Migration[4.2]
44
+ def self.up
45
+ create_table :posts do |t|
46
+ t.string :title
47
+ t.string :description, :length => 200
48
+ t.integer :total
49
+ t.integer :blog_id
50
+ t.timestamps null: true
51
+ end
52
+ end
53
+
54
+ def self.down
55
+ drop_table :posts
56
+ end
57
+ end
58
+
59
+ class Post < ActiveRecord::Base
60
+ include Redis::Objects
61
+ counter :total
62
+ counter :num_comments
63
+ # Unfortunately, counter counter_cache appears to be broken
64
+ # belongs_to :blog, :counter_cache => :num_posts
65
+ belongs_to :blog
66
+ has_many :comments
67
+ end
68
+
69
+ class CreateComments < ActiveRecord::Migration[4.2]
70
+ def self.up
71
+ create_table :comments do |t|
72
+ t.string :body
73
+ t.integer :post_id
74
+ t.timestamps null: true
75
+ end
76
+ end
77
+
78
+ def self.down
79
+ drop_table :comments
80
+ end
81
+ end
82
+
83
+ class Comment < ActiveRecord::Base
84
+ include Redis::Objects
85
+ belongs_to :post
86
+ # Unfortunately, counter counter_cache appears to be broken
87
+ # belongs_to :post, :counter_cache => :num_comments
88
+ end
89
+
90
+ describe ActiveRecord do
91
+ before do
92
+ CreateComments.up
93
+ CreatePosts.up
94
+ CreateBlogs.up
95
+ end
96
+ after do
97
+ CreateComments.down
98
+ CreatePosts.down
99
+ CreateBlogs.down
100
+ end
101
+
102
+ it "exercises ActiveRecord in more detail" do
103
+ @ar = Post.new
104
+ should.raise(Redis::Objects::NilObjectId){ @ar.total }
105
+ @ar.save!
106
+ @ar.destroy
107
+
108
+ # @ar.total.reset
109
+ @ar2 = Post.new
110
+ @ar2.save!
111
+ @ar2.total.reset
112
+ @ar2.total.increment.should == 1
113
+ @ar2.id.should == 2
114
+ @ar2.increment(:total).should == 2
115
+ @ar2[:total].should == nil # DB column
116
+ @ar2.redis.get(@ar2.redis_field_key('total')).to_i.should == 2
117
+ @ar2[:total] = 3 # DB column
118
+ @ar2.total.decrement.should == 1
119
+ @ar2.total.reset
120
+ @ar2.total.should == 0
121
+ @ar2.total.reset(55)
122
+ @ar2.total.should == 55
123
+ @ar2.total.getset(12).should == 55
124
+ @ar2.total.should == 12
125
+ @ar2.destroy
126
+ end
127
+
128
+ it "uses the redis objects counter cache when present" do
129
+ blog = Blog.create
130
+ post = Post.create :blog => blog
131
+ blog.num_posts.incr
132
+ blog.num_posts.should == 1
133
+ Post.counter_defined?(:num_comments).should == true
134
+ post.num_comments.should == 0
135
+
136
+ comment = Comment.create :post => post
137
+ post.num_comments.incr
138
+ post.comments.count.should == 1
139
+ post.id.should == 1
140
+ comment.destroy
141
+ blog.num_posts.delete
142
+ blog.destroy
143
+ end
144
+
145
+ it "falls back to ActiveRecord if redis counter is not defined" do
146
+ blog = Blog.create
147
+ blog.id.should == 1
148
+ blog.num_posts.should == 0
149
+ post = Post.create :blog => blog
150
+ blog.num_posts.incr
151
+ blog.num_posts.should == 1
152
+ blog2 = Blog.create
153
+ Post.create :blog => blog2
154
+ Post.create :blog => blog2
155
+ blog.reload.num_posts.should == 1
156
+ blog2.num_posts.incr
157
+ blog2.num_posts.incr
158
+ blog2.reload.num_posts.should == 2
159
+ blog.num_posts.should == 1
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,276 @@
1
+ #
2
+ # Connection tests - a bit ugly but important
3
+ #
4
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
5
+
6
+ require 'redis/objects'
7
+ require 'connection_pool'
8
+
9
+ BAD_REDIS = "totally bad bogus redis handle"
10
+
11
+ # Grab a global handle
12
+ describe 'Connection tests' do
13
+ it "should support overriding object handles with a vanilla redis connection" do
14
+ class CustomConnectionObject
15
+ include Redis::Objects
16
+
17
+ def id
18
+ return 1
19
+ end
20
+
21
+ redis_handle = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT, :db => 31)
22
+ value :redis_value, :redis => redis_handle, :key => 'rval'
23
+ value :default_redis_value, :key => 'rval'
24
+ end
25
+
26
+ obj = CustomConnectionObject.new
27
+
28
+ obj.default_redis_value.value.should == nil
29
+ obj.redis_value.value.should == nil
30
+
31
+ obj.default_redis_value.value = 'foo'
32
+ obj.default_redis_value.value.should == 'foo'
33
+ obj.redis_value.value.should == nil
34
+
35
+ obj.default_redis_value.clear
36
+ obj.redis_value.value = 'foo'
37
+ obj.redis_value.value.should == 'foo'
38
+ obj.default_redis_value.value.should == nil
39
+
40
+ obj.redis_value.clear
41
+ obj.default_redis_value.clear
42
+ end
43
+
44
+ it "should support mget" do
45
+ class CustomConnectionObject
46
+ include Redis::Objects
47
+
48
+ def id
49
+ return 1
50
+ end
51
+
52
+ redis_handle = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT, :db => 31)
53
+ value :redis_value, :key => 'rval'
54
+ end
55
+
56
+ obj = CustomConnectionObject.new
57
+
58
+ obj.redis_value.value = 'foo'
59
+
60
+ obj.class.mget(:redis_value, []).should == []
61
+ obj.class.mget(:redis_value, [obj]).should == ['foo']
62
+
63
+ obj.redis_value.clear
64
+ end
65
+
66
+ it "should support overriding object handles with a connection_pool" do
67
+ class CustomConnectionObject
68
+ include Redis::Objects
69
+
70
+ def id
71
+ return 1
72
+ end
73
+
74
+ redis_handle = ConnectionPool.new { Redis.new(:host => REDIS_HOST, :port => REDIS_PORT, :db => 31) }
75
+ value :redis_value, :redis => redis_handle, :key => 'rval'
76
+ value :default_redis_value, :key => 'rval'
77
+ end
78
+
79
+ obj = CustomConnectionObject.new
80
+
81
+ obj.default_redis_value.value.should == nil
82
+ obj.redis_value.value.should == nil
83
+
84
+ obj.default_redis_value.value = 'foo'
85
+ obj.default_redis_value.value.should == 'foo'
86
+ obj.redis_value.value.should == nil
87
+
88
+ obj.default_redis_value.clear
89
+ obj.redis_value.value = 'foo'
90
+ obj.redis_value.value.should == 'foo'
91
+ obj.default_redis_value.value.should == nil
92
+
93
+ obj.redis_value.clear
94
+ obj.default_redis_value.clear
95
+ end
96
+
97
+ it "should support local handles with a vanilla redis connection" do
98
+ Redis.current = nil # reset from other tests
99
+ Redis::Objects.redis = nil
100
+ @redis_handle = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
101
+
102
+ # Redis.current is lazily auto-populated to touch 6379
103
+ # This why we choose the weird 9212 port to avoid
104
+ Redis.current.inspect.should == Redis.new.inspect
105
+ Redis::Objects.redis.inspect.should == Redis.new.inspect
106
+
107
+ v = Redis::Value.new('conn/value', @redis_handle)
108
+ v.clear
109
+ v.value = 'yay'
110
+ v.value.should == 'yay'
111
+
112
+ h = Redis::HashKey.new('conn/hash', @redis_handle)
113
+ h.clear
114
+ h['k'] = 'v'
115
+
116
+ l = Redis::List.new('conn/list', @redis_handle)
117
+ l.clear
118
+ l << 3
119
+ l << 4
120
+ l << 5
121
+
122
+ s = Redis::Set.new('conn/set', @redis_handle)
123
+ s.clear
124
+ s << 5
125
+ s << 5
126
+ s << 6
127
+ s << 7
128
+
129
+ z = Redis::SortedSet.new('conn/zset', @redis_handle)
130
+ z.clear
131
+ z['a'] = 8
132
+ z['b'] = 7
133
+ z['c'] = 9
134
+ z['d'] = 6
135
+
136
+ c = Redis::Counter.new('conn/counter', @redis_handle)
137
+ c.reset
138
+ c.incr(3)
139
+ c.decr(1)
140
+ end
141
+
142
+ it "should support local handles with a connection_pool" do
143
+ Redis.current = nil # reset from other tests
144
+ Redis::Objects.redis = nil
145
+ @redis_handle = ConnectionPool.new { Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) }
146
+
147
+ # Redis.current is lazily auto-populated to touch 6379
148
+ # This why we choose the weird 9212 port to avoid
149
+ Redis.current.inspect.should == Redis.new.inspect
150
+ Redis::Objects.redis.inspect.should == Redis.new.inspect
151
+
152
+ v = Redis::Value.new('conn/value', @redis_handle)
153
+ v.clear
154
+ v.value = 'yay'
155
+ v.value.should == 'yay'
156
+
157
+ h = Redis::HashKey.new('conn/hash', @redis_handle)
158
+ h.clear
159
+ h['k'] = 'v'
160
+
161
+ l = Redis::List.new('conn/list', @redis_handle)
162
+ l.clear
163
+ l << 3
164
+ l << 4
165
+ l << 5
166
+
167
+ s = Redis::Set.new('conn/set', @redis_handle)
168
+ s.clear
169
+ s << 5
170
+ s << 5
171
+ s << 6
172
+ s << 7
173
+
174
+ z = Redis::SortedSet.new('conn/zset', @redis_handle)
175
+ z.clear
176
+ z['a'] = 8
177
+ z['b'] = 7
178
+ z['c'] = 9
179
+ z['d'] = 6
180
+
181
+ c = Redis::Counter.new('conn/counter', @redis_handle)
182
+ c.reset
183
+ c.incr(3)
184
+ c.decr(1)
185
+ end
186
+
187
+ it "should support Redis.current" do
188
+ Redis.current = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
189
+
190
+ Redis::Value.new('conn/value').should == 'yay'
191
+ Redis::HashKey.new('conn/hash').keys.should == ['k']
192
+ Redis::List.new('conn/list').sort.should == ['3', '4', '5']
193
+ Redis::Set.new('conn/set').sort.should == ['5', '6', '7']
194
+ Redis::SortedSet.new('conn/zset').should == ['d', 'b', 'a', 'c']
195
+ Redis::Counter.new('conn/counter').should == 2
196
+ end
197
+
198
+ it "should support Redis::Objects.redis= with a connection_pool" do
199
+ @redis_handle = ConnectionPool.new { Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) }
200
+
201
+ # Redis.current is lazily auto-populated to touch 6379
202
+ # This why we choose the weird 9212 port to avoid
203
+ Redis.current = BAD_REDIS
204
+ Redis::Objects.redis.should == BAD_REDIS
205
+
206
+ # This set of tests sucks, it fucks up the per-data-type handles
207
+ # because Redis.current is then set to a BS value, and the lazy
208
+ # init code in redis-rb will keep that value until we clear it.
209
+ # This ends up fucking any sequential tests.
210
+ raises_exception{ Redis::Value.new('conn/value').should.be.nil }
211
+ raises_exception{ Redis::HashKey.new('conn/hash').keys.should == [] }
212
+ raises_exception{ Redis::List.new('conn/list').sort.should == [] }
213
+ raises_exception{ Redis::Set.new('conn/set').sort.should == [] }
214
+ raises_exception{ Redis::SortedSet.new('conn/zset').should == [] }
215
+ raises_exception{ Redis::Counter.new('conn/counter').get.should == 0 }
216
+
217
+ Redis::Objects.redis = @redis_handle
218
+ Redis::Value.new('fart').redis.is_a?(Redis::Objects::ConnectionPoolProxy).should == true
219
+
220
+ # These should now get the correct handle
221
+ Redis::Value.new('conn/value').should == 'yay'
222
+ Redis::HashKey.new('conn/hash').keys.should == ['k']
223
+ Redis::List.new('conn/list').sort.should == ['3', '4', '5']
224
+ Redis::Set.new('conn/set').sort.should == ['5', '6', '7']
225
+ Redis::SortedSet.new('conn/zset').should == ['d', 'b', 'a', 'c']
226
+ Redis::Counter.new('conn/counter').should == 2
227
+
228
+ end
229
+
230
+ it "should support Redis::Objects.redis= with a vanilla redis connection" do
231
+ # reset redis
232
+ Redis::Objects.redis = nil
233
+ @redis_handle = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
234
+
235
+ # Redis.current is lazily auto-populated to touch 6379
236
+ # This why we choose the weird 9212 port to avoid
237
+ Redis.current = BAD_REDIS
238
+ Redis::Objects.redis.should == BAD_REDIS
239
+
240
+ # This set of tests sucks, it fucks up the per-data-type handles
241
+ # because Redis.current is then set to a BS value, and the lazy
242
+ # init code in redis-rb will keep that value until we clear it.
243
+ # This ends up fucking any sequential tests.
244
+ raises_exception{ Redis::Value.new('conn/value').should.be.nil }
245
+ raises_exception{ Redis::HashKey.new('conn/hash').keys.should == [] }
246
+ raises_exception{ Redis::List.new('conn/list').sort.should == [] }
247
+ raises_exception{ Redis::Set.new('conn/set').sort.should == [] }
248
+ raises_exception{ Redis::SortedSet.new('conn/zset').should == [] }
249
+ raises_exception{ Redis::Counter.new('conn/counter').get.should == 0 }
250
+
251
+ Redis::Objects.redis = @redis_handle
252
+ Redis::Value.new('fart').redis.should == @redis_handle
253
+
254
+ # These should now get the correct handle
255
+ Redis::Value.new('conn/value').should == 'yay'
256
+ Redis::HashKey.new('conn/hash').keys.should == ['k']
257
+ Redis::List.new('conn/list').sort.should == ['3', '4', '5']
258
+ Redis::Set.new('conn/set').sort.should == ['5', '6', '7']
259
+ Redis::SortedSet.new('conn/zset').should == ['d', 'b', 'a', 'c']
260
+ Redis::Counter.new('conn/counter').should == 2
261
+
262
+ # Fix for future tests
263
+ Redis.current = @redis_handle
264
+ end
265
+
266
+ it "should support pipelined changes" do
267
+ list = Redis::List.new('pipelined/list')
268
+ key = Redis::HashKey.new('pipelined/hash')
269
+ Redis::Objects.redis.pipelined do
270
+ key['foo'] = 'bar'
271
+ list.push 1, 2
272
+ end
273
+ key.all.should == { 'foo' => 'bar' }
274
+ list.values.should == %w[1 2]
275
+ end
276
+ end