redis-namespace-with-multi 1.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/LICENSE +20 -0
- data/README.md +38 -0
- data/Rakefile +7 -0
- data/lib/redis/namespace.rb +316 -0
- data/lib/redis-namespace.rb +1 -0
- data/spec/redis_spec.rb +361 -0
- data/spec/spec_helper.rb +18 -0
- metadata +95 -0
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,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'
|
data/spec/redis_spec.rb
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -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: []
|