redis-namespace-with-multi 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Chris Wanstrath
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ redis-namespace
2
+ ---------------
3
+
4
+ Requires the redis gem.
5
+
6
+ Namespaces all Redis calls.
7
+
8
+ ``` ruby
9
+ r = Redis::Namespace.new(:ns, :redis => @r)
10
+ r['foo'] = 1000
11
+ ```
12
+
13
+ This will perform the equivalent of:
14
+
15
+ redis-cli set ns:foo 1000
16
+
17
+ Useful when you have multiple systems using Redis differently in your app.
18
+
19
+
20
+ Installation
21
+ ============
22
+
23
+ $ gem install redis-namespace
24
+
25
+
26
+
27
+ Testing
28
+ =======
29
+
30
+ $ bundle install
31
+ $ rake
32
+
33
+
34
+ Author
35
+ =====
36
+
37
+ Chris Wanstrath :: chris@ozmm.org
38
+ Terence Lee :: hone02@gmail.com
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ task :default => :spec
2
+ task :test => :spec
3
+
4
+ desc "Run specs"
5
+ task :spec do
6
+ exec "rspec spec/redis_spec.rb"
7
+ end
@@ -0,0 +1,316 @@
1
+ require 'redis'
2
+
3
+ class Redis
4
+ class Namespace
5
+ # The following table defines how input parameters and result
6
+ # values should be modified for the namespace.
7
+ #
8
+ # COMMANDS is a hash. Each key is the name of a command and each
9
+ # value is a two element array.
10
+ #
11
+ # The first element in the value array describes how to modify the
12
+ # arguments passed. It can be one of:
13
+ #
14
+ # nil
15
+ # Do nothing.
16
+ # :first
17
+ # Add the namespace to the first argument passed, e.g.
18
+ # GET key => GET namespace:key
19
+ # :all
20
+ # Add the namespace to all arguments passed, e.g.
21
+ # MGET key1 key2 => MGET namespace:key1 namespace:key2
22
+ # :exclude_first
23
+ # Add the namespace to all arguments but the first, e.g.
24
+ # :exclude_last
25
+ # Add the namespace to all arguments but the last, e.g.
26
+ # BLPOP key1 key2 timeout =>
27
+ # BLPOP namespace:key1 namespace:key2 timeout
28
+ # :exclude_options
29
+ # Add the namespace to all arguments, except the last argument,
30
+ # if the last argument is a hash of options.
31
+ # ZUNIONSTORE key1 2 key2 key3 WEIGHTS 2 1 =>
32
+ # ZUNIONSTORE namespace:key1 2 namespace:key2 namespace:key3 WEIGHTS 2 1
33
+ # :alternate
34
+ # Add the namespace to every other argument, e.g.
35
+ # MSET key1 value1 key2 value2 =>
36
+ # MSET namespace:key1 value1 namespace:key2 value2
37
+ #
38
+ # The second element in the value array describes how to modify
39
+ # the return value of the Redis call. It can be one of:
40
+ #
41
+ # nil
42
+ # Do nothing.
43
+ # :all
44
+ # Add the namespace to all elements returned, e.g.
45
+ # key1 key2 => namespace:key1 namespace:key2
46
+ COMMANDS = {
47
+ "append" => [:first],
48
+ "auth" => [],
49
+ "bgrewriteaof" => [],
50
+ "bgsave" => [],
51
+ "bitcount" => [ :first ],
52
+ "bitop" => [ :exclude_first ],
53
+ "blpop" => [ :exclude_last, :first ],
54
+ "brpop" => [ :exclude_last ],
55
+ "brpoplpush" => [ :exclude_last ],
56
+ "config" => [],
57
+ "dbsize" => [],
58
+ "debug" => [ :exclude_first ],
59
+ "decr" => [ :first ],
60
+ "decrby" => [ :first ],
61
+ "del" => [ :all ],
62
+ "discard" => [],
63
+ "dump" => [ :first ],
64
+ "exists" => [ :first ],
65
+ "expire" => [ :first ],
66
+ "expireat" => [ :first ],
67
+ "flushall" => [],
68
+ "flushdb" => [],
69
+ "get" => [ :first ],
70
+ "getbit" => [ :first ],
71
+ "getrange" => [ :first ],
72
+ "getset" => [ :first ],
73
+ "hset" => [ :first ],
74
+ "hsetnx" => [ :first ],
75
+ "hget" => [ :first ],
76
+ "hincrby" => [ :first ],
77
+ "hincrbyfloat" => [ :first ],
78
+ "hmget" => [ :first ],
79
+ "hmset" => [ :first ],
80
+ "hdel" => [ :first ],
81
+ "hexists" => [ :first ],
82
+ "hlen" => [ :first ],
83
+ "hkeys" => [ :first ],
84
+ "hvals" => [ :first ],
85
+ "hgetall" => [ :first ],
86
+ "incr" => [ :first ],
87
+ "incrby" => [ :first ],
88
+ "incrbyfloat" => [ :first ],
89
+ "info" => [],
90
+ "keys" => [ :first, :all ],
91
+ "lastsave" => [],
92
+ "lindex" => [ :first ],
93
+ "linsert" => [ :first ],
94
+ "llen" => [ :first ],
95
+ "lpop" => [ :first ],
96
+ "lpush" => [ :first ],
97
+ "lpushx" => [ :first ],
98
+ "lrange" => [ :first ],
99
+ "lrem" => [ :first ],
100
+ "lset" => [ :first ],
101
+ "ltrim" => [ :first ],
102
+ "mapped_hmset" => [ :first ],
103
+ "mapped_hmget" => [ :first ],
104
+ "mapped_mget" => [ :all, :all ],
105
+ "mget" => [ :all ],
106
+ "monitor" => [ :monitor ],
107
+ "move" => [ :first ],
108
+ "mset" => [ :alternate ],
109
+ "msetnx" => [ :alternate ],
110
+ "object" => [ :exclude_first ],
111
+ "persist" => [ :first ],
112
+ "pexpire" => [ :first ],
113
+ "pexpireat" => [ :first ],
114
+ "ping" => [],
115
+ "psetex" => [ :first ],
116
+ "psubscribe" => [ :all ],
117
+ "pttl" => [ :first ],
118
+ "publish" => [ :first ],
119
+ "punsubscribe" => [ :all ],
120
+ "quit" => [],
121
+ "randomkey" => [],
122
+ "rename" => [ :all ],
123
+ "renamenx" => [ :all ],
124
+ "restore" => [ :first ],
125
+ "rpop" => [ :first ],
126
+ "rpoplpush" => [ :all ],
127
+ "rpush" => [ :first ],
128
+ "rpushx" => [ :first ],
129
+ "sadd" => [ :first ],
130
+ "save" => [],
131
+ "scard" => [ :first ],
132
+ "sdiff" => [ :all ],
133
+ "sdiffstore" => [ :all ],
134
+ "select" => [],
135
+ "set" => [ :first ],
136
+ "setbit" => [ :first ],
137
+ "setex" => [ :first ],
138
+ "setnx" => [ :first ],
139
+ "setrange" => [ :first ],
140
+ "shutdown" => [],
141
+ "sinter" => [ :all ],
142
+ "sinterstore" => [ :all ],
143
+ "sismember" => [ :first ],
144
+ "slaveof" => [],
145
+ "smembers" => [ :first ],
146
+ "smove" => [ :exclude_last ],
147
+ "sort" => [ :sort ],
148
+ "spop" => [ :first ],
149
+ "srandmember" => [ :first ],
150
+ "srem" => [ :first ],
151
+ "subscribe" => [ :all ],
152
+ "sunion" => [ :all ],
153
+ "sunionstore" => [ :all ],
154
+ "ttl" => [ :first ],
155
+ "type" => [ :first ],
156
+ "unsubscribe" => [ :all ],
157
+ "watch" => [ :all ],
158
+ "zadd" => [ :first ],
159
+ "zcard" => [ :first ],
160
+ "zcount" => [ :first ],
161
+ "zincrby" => [ :first ],
162
+ "zinterstore" => [ :exclude_options ],
163
+ "zrange" => [ :first ],
164
+ "zrangebyscore" => [ :first ],
165
+ "zrank" => [ :first ],
166
+ "zrem" => [ :first ],
167
+ "zremrangebyrank" => [ :first ],
168
+ "zremrangebyscore" => [ :first ],
169
+ "zrevrange" => [ :first ],
170
+ "zrevrangebyscore" => [ :first ],
171
+ "zrevrank" => [ :first ],
172
+ "zscore" => [ :first ],
173
+ "zunionstore" => [ :exclude_options ],
174
+ "[]" => [ :first ],
175
+ "[]=" => [ :first ]
176
+ }
177
+
178
+ # support previous versions of redis gem
179
+ ALIASES = case
180
+ when defined? Redis::Client::ALIASES then Redis::Client::ALIASES
181
+ when defined? Redis::ALIASES then Redis::ALIASES
182
+ else {}
183
+ end
184
+
185
+ attr_accessor :namespace
186
+ attr_reader :redis
187
+
188
+ def initialize(namespace, options = {})
189
+ @namespace = namespace
190
+ @redis = options[:redis] || Redis.current
191
+ end
192
+
193
+ # Ruby defines a now deprecated type method so we need to override it here
194
+ # since it will never hit method_missing
195
+ def type(key)
196
+ method_missing(:type, key)
197
+ end
198
+
199
+ alias_method :self_respond_to?, :respond_to?
200
+
201
+ def respond_to?(command, include_private=false)
202
+ if self_respond_to?(command, include_private)
203
+ true
204
+ else
205
+ @redis.respond_to?(command, include_private)
206
+ end
207
+ end
208
+
209
+ def keys(query = nil)
210
+ query.nil? ? super("*") : super
211
+ end
212
+
213
+ def multi(&block)
214
+ namespaced_block(:multi, &block)
215
+ end
216
+
217
+ def pipelined(&block)
218
+ namespaced_block(:pipelined, &block)
219
+ end
220
+
221
+ def method_missing(command, *args, &block)
222
+ handling = COMMANDS[command.to_s] ||
223
+ COMMANDS[ALIASES[command.to_s]]
224
+
225
+ # redis-namespace does not know how to handle this command.
226
+ # Passing it to @redis as is.
227
+ if handling.nil?
228
+ return @redis.send(command, *args, &block)
229
+ end
230
+
231
+ (before, after) = handling
232
+
233
+ # Add the namespace to any parameters that are keys.
234
+ case before
235
+ when :first
236
+ args[0] = add_namespace(args[0]) if args[0]
237
+ when :all
238
+ args = add_namespace(args)
239
+ when :exclude_first
240
+ first = args.shift
241
+ args = add_namespace(args)
242
+ args.unshift(first) if first
243
+ when :exclude_last
244
+ last = args.pop
245
+ args = add_namespace(args)
246
+ args.push(last) if last
247
+ when :exclude_options
248
+ if args.last.is_a?(Hash)
249
+ last = args.pop
250
+ args = add_namespace(args)
251
+ args.push(last)
252
+ else
253
+ args = add_namespace(args)
254
+ end
255
+ when :alternate
256
+ args.each_with_index { |a, i| args[i] = add_namespace(a) if i.even? }
257
+ when :sort
258
+ args[0] = add_namespace(args[0]) if args[0]
259
+ [:by, :get, :store].each do |key|
260
+ args[1][key] = add_namespace(args[1][key]) if args[1][key]
261
+ end if args[1].is_a?(Hash)
262
+ end
263
+
264
+ # Dispatch the command to Redis and store the result.
265
+ result = @redis.send(command, *args, &block)
266
+
267
+ # Remove the namespace from results that are keys.
268
+ case after
269
+ when :all
270
+ result = rem_namespace(result)
271
+ when :first
272
+ result[0] = rem_namespace(result[0]) if result
273
+ end
274
+
275
+ result
276
+ end
277
+
278
+ private
279
+
280
+ def namespaced_block(command, &block)
281
+ original = @redis
282
+ result = redis.send(command) do |r|
283
+ @redis = r
284
+ yield self
285
+ end
286
+ @redis = original
287
+ result
288
+ end
289
+
290
+ def add_namespace(key)
291
+ return key unless key && @namespace
292
+
293
+ case key
294
+ when Array
295
+ key.map {|k| add_namespace k}
296
+ when Hash
297
+ Hash[*key.map {|k, v| [ add_namespace(k), v ]}.flatten]
298
+ else
299
+ "#{@namespace}:#{key}"
300
+ end
301
+ end
302
+
303
+ def rem_namespace(key)
304
+ return key unless key && @namespace
305
+
306
+ case key
307
+ when Array
308
+ key.map {|k| rem_namespace k}
309
+ when Hash
310
+ Hash[*key.map {|k, v| [ rem_namespace(k), v ]}.flatten]
311
+ else
312
+ key.to_s.gsub /^#{@namespace}:/, ""
313
+ end
314
+ end
315
+ end
316
+ end
@@ -0,0 +1 @@
1
+ require 'redis/namespace'
@@ -0,0 +1,361 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "redis" do
4
+ @redis_version = Gem::Version.new(Redis.current.info["redis_version"])
5
+
6
+ before(:all) do
7
+ # use database 15 for testing so we dont accidentally step on your real data
8
+ @redis = Redis.new :db => 15
9
+ end
10
+
11
+ before(:each) do
12
+ @namespaced = Redis::Namespace.new(:ns, :redis => @redis)
13
+ @namespaced.flushdb
14
+ @redis['foo'] = 'bar'
15
+ end
16
+
17
+ after(:each) do
18
+ @redis.flushdb
19
+ end
20
+
21
+ after(:all) do
22
+ @redis.quit
23
+ end
24
+
25
+ it "proxies `client` to the client" do
26
+ @namespaced.client.should == @redis.client
27
+ end
28
+
29
+ it "should be able to use a namespace" do
30
+ @namespaced['foo'].should == nil
31
+ @namespaced['foo'] = 'chris'
32
+ @namespaced['foo'].should == 'chris'
33
+ @redis['foo'] = 'bob'
34
+ @redis['foo'].should == 'bob'
35
+
36
+ @namespaced.incrby('counter', 2)
37
+ @namespaced['counter'].to_i.should == 2
38
+ @redis['counter'].should == nil
39
+ @namespaced.type('counter').should == 'string'
40
+ end
41
+
42
+ it "should be able to use a namespace with bpop" do
43
+ @namespaced.rpush "foo", "string"
44
+ @namespaced.rpush "foo", "ns:string"
45
+ @namespaced.blpop("foo", 1).should == ["foo", "string"]
46
+ @namespaced.blpop("foo", 1).should == ["foo", "ns:string"]
47
+ @namespaced.blpop("foo", 1).should == nil
48
+ end
49
+
50
+ it "should be able to use a namespace with del" do
51
+ @namespaced['foo'] = 1000
52
+ @namespaced['bar'] = 2000
53
+ @namespaced['baz'] = 3000
54
+ @namespaced.del 'foo'
55
+ @namespaced['foo'].should == nil
56
+ @namespaced.del 'bar', 'baz'
57
+ @namespaced['bar'].should == nil
58
+ @namespaced['baz'].should == nil
59
+ end
60
+
61
+ it 'should be able to use a namespace with append' do
62
+ @namespaced['foo'] = 'bar'
63
+ @namespaced.append('foo','n').should == 4
64
+ @namespaced['foo'].should == 'barn'
65
+ @redis['foo'].should == 'bar'
66
+ end
67
+
68
+ it 'should be able to use a namespace with brpoplpush' do
69
+ @namespaced.lpush('foo','bar')
70
+ @namespaced.brpoplpush('foo','bar',0).should == 'bar'
71
+ @namespaced.lrange('foo',0,-1).should == []
72
+ @namespaced.lrange('bar',0,-1).should == ['bar']
73
+ end
74
+
75
+ it 'should be able to use a namespace with getbit' do
76
+ @namespaced.set('foo','bar')
77
+ @namespaced.getbit('foo',1).should == 1
78
+ end
79
+
80
+ it 'should be able to use a namespace with getrange' do
81
+ @namespaced.set('foo','bar')
82
+ @namespaced.getrange('foo',0,-1).should == 'bar'
83
+ end
84
+
85
+ it 'should be able to use a namespace with linsert' do
86
+ @namespaced.rpush('foo','bar')
87
+ @namespaced.rpush('foo','barn')
88
+ @namespaced.rpush('foo','bart')
89
+ @namespaced.linsert('foo','BEFORE','barn','barf').should == 4
90
+ @namespaced.lrange('foo',0,-1).should == ['bar','barf','barn','bart']
91
+ end
92
+
93
+ it 'should be able to use a namespace with lpushx' do
94
+ @namespaced.lpushx('foo','bar').should == 0
95
+ @namespaced.lpush('foo','boo')
96
+ @namespaced.lpushx('foo','bar').should == 2
97
+ @namespaced.lrange('foo',0,-1).should == ['bar','boo']
98
+ end
99
+
100
+ it 'should be able to use a namespace with rpushx' do
101
+ @namespaced.rpushx('foo','bar').should == 0
102
+ @namespaced.lpush('foo','boo')
103
+ @namespaced.rpushx('foo','bar').should == 2
104
+ @namespaced.lrange('foo',0,-1).should == ['boo','bar']
105
+ end
106
+
107
+ it 'should be able to use a namespace with setbit' do
108
+ @namespaced.setbit('virgin_key', 1, 1)
109
+ @namespaced.exists('virgin_key').should be_true
110
+ @namespaced.get('virgin_key').should == @namespaced.getrange('virgin_key',0,-1)
111
+ end
112
+
113
+ it 'should be able to use a namespace with setrange' do
114
+ @namespaced.setrange('foo', 0, 'bar')
115
+ @namespaced['foo'].should == 'bar'
116
+
117
+ @namespaced.setrange('bar', 2, 'foo')
118
+ @namespaced['bar'].should == "\000\000foo"
119
+ end
120
+
121
+ it "should be able to use a namespace with mget" do
122
+ @namespaced['foo'] = 1000
123
+ @namespaced['bar'] = 2000
124
+ @namespaced.mapped_mget('foo', 'bar').should == { 'foo' => '1000', 'bar' => '2000' }
125
+ @namespaced.mapped_mget('foo', 'baz', 'bar').should == {'foo'=>'1000', 'bar'=>'2000', 'baz' => nil}
126
+ end
127
+
128
+ it "should be able to use a namespace with mset" do
129
+ @namespaced.mset('foo', '1000', 'bar', '2000')
130
+ @namespaced.mapped_mget('foo', 'bar').should == { 'foo' => '1000', 'bar' => '2000' }
131
+ @namespaced.mapped_mget('foo', 'baz', 'bar').should == { 'foo' => '1000', 'bar' => '2000', 'baz' => nil}
132
+ end
133
+
134
+ it "should be able to use a namespace with msetnx" do
135
+ @namespaced.msetnx('foo', '1000', 'bar', '2000')
136
+ @namespaced.mapped_mget('foo', 'bar').should == { 'foo' => '1000', 'bar' => '2000' }
137
+ @namespaced.mapped_mget('foo', 'baz', 'bar').should == { 'foo' => '1000', 'bar' => '2000', 'baz' => nil}
138
+ end
139
+
140
+ it "should be able to use a namespace with hashes" do
141
+ @namespaced.hset('foo', 'key', 'value')
142
+ @namespaced.hset('foo', 'key1', 'value1')
143
+ @namespaced.hget('foo', 'key').should == 'value'
144
+ @namespaced.hgetall('foo').should == {'key' => 'value', 'key1' => 'value1'}
145
+ @namespaced.hlen('foo').should == 2
146
+ @namespaced.hkeys('foo').should == ['key', 'key1']
147
+ @namespaced.hmset('bar', 'key', 'value', 'key1', 'value1')
148
+ @namespaced.hmget('bar', 'key', 'key1')
149
+ @namespaced.hmset('bar', 'a_number', 1)
150
+ @namespaced.hmget('bar', 'a_number').should == ['1']
151
+ @namespaced.hincrby('bar', 'a_number', 3)
152
+ @namespaced.hmget('bar', 'a_number').should == ['4']
153
+ @namespaced.hgetall('bar').should == {'key' => 'value', 'key1' => 'value1', 'a_number' => '4'}
154
+
155
+ @namespaced.hsetnx('foonx','nx',10).should be_true
156
+ @namespaced.hsetnx('foonx','nx',12).should be_false
157
+ @namespaced.hget('foonx','nx').should == "10"
158
+ @namespaced.hkeys('foonx').should == %w{ nx }
159
+ @namespaced.hvals('foonx').should == %w{ 10 }
160
+ @namespaced.mapped_hmset('baz', {'key' => 'value', 'key1' => 'value1', 'a_number' => 4})
161
+ @namespaced.mapped_hmget('baz', 'key', 'key1', 'a_number').should == {'key' => 'value', 'key1' => 'value1', 'a_number' => '4'}
162
+ @namespaced.hgetall('baz').should == {'key' => 'value', 'key1' => 'value1', 'a_number' => '4'}
163
+ end
164
+
165
+ it "should properly intersect three sets" do
166
+ @namespaced.sadd('foo', 1)
167
+ @namespaced.sadd('foo', 2)
168
+ @namespaced.sadd('foo', 3)
169
+ @namespaced.sadd('bar', 2)
170
+ @namespaced.sadd('bar', 3)
171
+ @namespaced.sadd('bar', 4)
172
+ @namespaced.sadd('baz', 3)
173
+ @namespaced.sinter('foo', 'bar', 'baz').should == %w( 3 )
174
+ end
175
+
176
+ it "should properly union two sets" do
177
+ @namespaced.sadd('foo', 1)
178
+ @namespaced.sadd('foo', 2)
179
+ @namespaced.sadd('bar', 2)
180
+ @namespaced.sadd('bar', 3)
181
+ @namespaced.sadd('bar', 4)
182
+ @namespaced.sunion('foo', 'bar').sort.should == %w( 1 2 3 4 )
183
+ end
184
+
185
+ it "should properly union two sorted sets with options" do
186
+ @namespaced.zadd('sort1', 1, 1)
187
+ @namespaced.zadd('sort1', 2, 2)
188
+ @namespaced.zadd('sort2', 2, 2)
189
+ @namespaced.zadd('sort2', 3, 3)
190
+ @namespaced.zadd('sort2', 4, 4)
191
+ @namespaced.zunionstore('union', ['sort1', 'sort2'], :weights => [2, 1])
192
+ @namespaced.zrevrange('union', 0, -1).should == %w( 2 4 3 1 )
193
+ end
194
+
195
+ it "should properly union two sorted sets without options" do
196
+ @namespaced.zadd('sort1', 1, 1)
197
+ @namespaced.zadd('sort1', 2, 2)
198
+ @namespaced.zadd('sort2', 2, 2)
199
+ @namespaced.zadd('sort2', 3, 3)
200
+ @namespaced.zadd('sort2', 4, 4)
201
+ @namespaced.zunionstore('union', ['sort1', 'sort2'])
202
+ @namespaced.zrevrange('union', 0, -1).should == %w( 4 2 3 1 )
203
+ end
204
+
205
+ it "should add namespace to sort" do
206
+ @namespaced.sadd('foo', 1)
207
+ @namespaced.sadd('foo', 2)
208
+ @namespaced.set('weight_1', 2)
209
+ @namespaced.set('weight_2', 1)
210
+ @namespaced.set('value_1', 'a')
211
+ @namespaced.set('value_2', 'b')
212
+
213
+ @namespaced.sort('foo').should == %w( 1 2 )
214
+ @namespaced.sort('foo', :limit => [0, 1]).should == %w( 1 )
215
+ @namespaced.sort('foo', :order => 'desc').should == %w( 2 1 )
216
+ @namespaced.sort('foo', :by => 'weight_*').should == %w( 2 1 )
217
+ @namespaced.sort('foo', :get => 'value_*').should == %w( a b )
218
+
219
+ @namespaced.sort('foo', :store => 'result')
220
+ @namespaced.lrange('result', 0, -1).should == %w( 1 2 )
221
+ end
222
+
223
+ it "should yield the correct list of keys" do
224
+ @namespaced["foo"] = 1
225
+ @namespaced["bar"] = 2
226
+ @namespaced["baz"] = 3
227
+ @namespaced.keys("*").sort.should == %w( bar baz foo )
228
+ @namespaced.keys.sort.should == %w( bar baz foo )
229
+ end
230
+
231
+ it "should add namepsace to multi blocks" do
232
+ @namespaced.mapped_hmset "a_hash", {"foo" => "bar"}
233
+ @namespaced.multi do |r|
234
+ r.del "a_hash"
235
+ r.mapped_hmset "a_hash", {"yin" => "yang"}
236
+ end
237
+ @namespaced.hgetall("a_hash").should == {"yin" => "yang"}
238
+ end
239
+
240
+ it "should add namespace to pipelined blocks" do
241
+ @namespaced.mapped_hmset "a_hash", {"foo" => "bar"}
242
+ @namespaced.pipelined do |r|
243
+ r.del "a_hash"
244
+ r.mapped_hmset "a_hash", {"yin" => "yang"}
245
+ end
246
+ @namespaced.hgetall("a_hash").should == {"yin" => "yang"}
247
+ end
248
+
249
+ it "should returned response array from pipelined block" do
250
+ @namespaced.mset "foo", "bar", "yin", "yang"
251
+ result = @namespaced.pipelined do |r|
252
+ r["foo"]
253
+ r["yin"]
254
+ end
255
+ result.should == ["bar", "yang"]
256
+ end
257
+
258
+ it "can change its namespace" do
259
+ @namespaced['foo'].should == nil
260
+ @namespaced['foo'] = 'chris'
261
+ @namespaced['foo'].should == 'chris'
262
+
263
+ @namespaced.namespace.should == :ns
264
+ @namespaced.namespace = :spec
265
+ @namespaced.namespace.should == :spec
266
+
267
+ @namespaced['foo'].should == nil
268
+ @namespaced['foo'] = 'chris'
269
+ @namespaced['foo'].should == 'chris'
270
+ end
271
+
272
+ it "should respond to :namespace=" do
273
+ @namespaced.respond_to?(:namespace=).should == true
274
+ end
275
+
276
+ if @redis_version >= Gem::Version.new("2.6.0")
277
+ describe "redis 2.6 commands" do
278
+ it "should namespace bitcount" do
279
+ pending "awaiting implementaton of command in redis gem"
280
+ end
281
+
282
+ it "should namespace bitop" do
283
+ pending "awaiting implementaton of command in redis gem"
284
+ end
285
+
286
+ it "should namespace dump" do
287
+ pending "awaiting implementaton of command in redis gem"
288
+ end
289
+
290
+ it "should namespace hincrbyfloat" do
291
+ @namespaced.hset('mykey', 'field', 10.50)
292
+ @namespaced.hincrbyfloat('mykey', 'field', 0.1).should == 10.6
293
+ end
294
+
295
+ it "should namespace incrbyfloat" do
296
+ @namespaced.set('mykey', 10.50)
297
+ @namespaced.incrbyfloat('mykey', 0.1).should == 10.6
298
+ end
299
+
300
+ it "should namespace object" do
301
+ @namespaced.set('foo', 1000)
302
+ @namespaced.object('encoding', 'foo').should == 'int'
303
+ end
304
+
305
+ it "should namespace persist" do
306
+ @namespaced.set('mykey', 'Hello')
307
+ @namespaced.expire('mykey', 60)
308
+ @namespaced.persist('mykey').should == true
309
+ @namespaced.ttl('mykey').should == -1
310
+ end
311
+
312
+ it "should namespace pexpire" do
313
+ @namespaced.set('mykey', 'Hello')
314
+ @namespaced.pexpire('mykey', 60000).should == true
315
+ end
316
+
317
+ it "should namespace pexpireat" do
318
+ @namespaced.set('mykey', 'Hello')
319
+ @namespaced.pexpire('mykey', 1555555555005).should == true
320
+ end
321
+
322
+ it "should namespace psetex" do
323
+ @namespaced.psetex('mykey', 10000, 'Hello').should == 'OK'
324
+ @namespaced.get('mykey').should == 'Hello'
325
+ end
326
+
327
+ it "should namespace pttl" do
328
+ @namespaced.set('mykey', 'Hello')
329
+ @namespaced.expire('mykey', 1)
330
+ @namespaced.pttl('mykey').should == 1000
331
+ end
332
+
333
+ it "should namespace restore" do
334
+ pending "awaiting implementaton of command in redis gem"
335
+ end
336
+ end
337
+ end
338
+
339
+ # Only test aliasing functionality for Redis clients that support aliases.
340
+ unless Redis::Namespace::ALIASES.empty?
341
+ it "should support command aliases (delete)" do
342
+ @namespaced.delete('foo')
343
+ @redis.should_not have_key('ns:foo')
344
+ end
345
+
346
+ it "should support command aliases (set_add)" do
347
+ @namespaced.set_add('bar', 'quux')
348
+ @namespaced.smembers('bar').should include('quux')
349
+ end
350
+
351
+ it "should support command aliases (push_head)" do
352
+ @namespaced.push_head('bar', 'quux')
353
+ @redis.llen('ns:bar').should == 1
354
+ end
355
+
356
+ it "should support command aliases (zset_add)" do
357
+ @namespaced.zset_add('bar', 1, 'quux')
358
+ @redis.zcard('ns:bar').should == 1
359
+ end
360
+ end
361
+ end
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup(:default, :test)
4
+ Bundler.require(:default, :test)
5
+
6
+ require 'rspec'
7
+ require 'redis'
8
+ require 'logger'
9
+
10
+ $TESTING=true
11
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
12
+ require 'redis/namespace'
13
+
14
+ RSpec::Matchers.define :have_key do |expected|
15
+ match do |redis|
16
+ redis.exists(expected)
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis-namespace-with-multi
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Chris Wanstrath
9
+ - Terence Lee
10
+ - Carl Zulauf
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2012-08-24 00:00:00.000000000Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: redis
18
+ requirement: &70195823091700 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 3.0.0
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *70195823091700
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: &70195823091300 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ! '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: *70195823091300
38
+ - !ruby/object:Gem::Dependency
39
+ name: rspec
40
+ requirement: &70195823090840 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *70195823090840
49
+ description: ! 'Adds a Redis::Namespace class which can be used to namespace calls
50
+
51
+ to Redis. This is useful when using a single instance of Redis with
52
+
53
+ multiple, different applications.
54
+
55
+
56
+ This fork adds support for the much loved multi and pipelined commands.
57
+
58
+ '
59
+ email: carl@linkleaf.com
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - README.md
65
+ - Rakefile
66
+ - LICENSE
67
+ - lib/redis/namespace.rb
68
+ - lib/redis-namespace.rb
69
+ - spec/redis_spec.rb
70
+ - spec/spec_helper.rb
71
+ homepage: http://github.com/carlzulauf/redis-namespace
72
+ licenses: []
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 1.8.15
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: Namespaces Redis commands. Fork with working pipelined/multi.
95
+ test_files: []