opengotham_redis-namespace 0.4.4

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.
@@ -0,0 +1,27 @@
1
+ redis-namespace
2
+ ---------------
3
+
4
+ Requires the redis gem.
5
+
6
+ Namespaces all Redis calls.
7
+
8
+ r = Redis::Namespace.new(:ns, :redis => @r)
9
+ r['foo'] = 1000
10
+
11
+ This will perform the equivalent of:
12
+
13
+ redis-cli set ns:foo 1000
14
+
15
+ Useful when you have multiple systems using Redis differently in your app.
16
+
17
+
18
+ Installation
19
+ ============
20
+
21
+ $ gem install redis-namespace
22
+
23
+
24
+ Author
25
+ =====
26
+
27
+ Chris Wanstrath :: chris@ozmm.org
@@ -0,0 +1,32 @@
1
+ task :default => :spec
2
+ task :test => :spec
3
+
4
+ desc "Build a gem"
5
+ task :gem => [ :gemspec, :build ]
6
+
7
+ desc "Run specs"
8
+ task :spec do
9
+ exec "spec spec/redis_spec.rb"
10
+ end
11
+
12
+ begin
13
+ require 'jeweler'
14
+ Jeweler::Tasks.new do |gemspec|
15
+ gemspec.name = "opengotham_redis-namespace"
16
+ gemspec.summary = "Namespaces Redis commands."
17
+ gemspec.email = "mjording@opengotham.com"
18
+ gemspec.homepage = "http://github.com/opengotham/redis-namespace"
19
+ gemspec.authors = ["Matthew Jording"]
20
+ gemspec.version = '0.4.4'
21
+ gemspec.add_dependency 'redis', ">= 2.0.0.rc2"
22
+ gemspec.description = <<description
23
+ Adds a Redis::Namespace class which can be used to namespace calls
24
+ to Redis. This is useful when using a single instance of Redis with
25
+ multiple, different applications.
26
+ description
27
+ end
28
+ Jeweler::GemcutterTasks.new
29
+ rescue LoadError
30
+ warn "Jeweler not available. Install it with:"
31
+ warn "gem install jeweler"
32
+ end
@@ -0,0 +1,243 @@
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
+ # :alternate
29
+ # Add the namespace to every other argument, e.g.
30
+ # MSET key1 value1 key2 value2 =>
31
+ # MSET namespace:key1 value1 namespace:key2 value2
32
+ #
33
+ # The second element in the value array describes how to modify
34
+ # the return value of the Redis call. It can be one of:
35
+ #
36
+ # nil
37
+ # Do nothing.
38
+ # :all
39
+ # Add the namespace to all elements returned, e.g.
40
+ # key1 key2 => namespace:key1 namespace:key2
41
+ COMMANDS = {
42
+ "auth" => [],
43
+ "bgrewriteaof" => [],
44
+ "bgsave" => [],
45
+ "blpop" => [ :exclude_last ],
46
+ "brpop" => [ :exclude_last ],
47
+ "dbsize" => [],
48
+ "decr" => [ :first ],
49
+ "decrby" => [ :first ],
50
+ "del" => [ :all ],
51
+ "exists" => [ :first ],
52
+ "expire" => [ :first ],
53
+ "flushall" => [],
54
+ "flushdb" => [],
55
+ "get" => [ :first ],
56
+ "getset" => [ :first ],
57
+ "hset" => [ :first ],
58
+ "hget" => [ :first ],
59
+ "hdel" => [ :first ],
60
+ "hexists" => [ :first ],
61
+ "hlen" => [ :first ],
62
+ "hkeys" => [ :first ],
63
+ "hvals" => [ :first ],
64
+ "hgetall" => [ :first ],
65
+ "incr" => [ :first ],
66
+ "incrby" => [ :first ],
67
+ "info" => [],
68
+ "keys" => [ :first, :all ],
69
+ "lastsave" => [],
70
+ "lindex" => [ :first ],
71
+ "llen" => [ :first ],
72
+ "lpop" => [ :first ],
73
+ "lpush" => [ :first ],
74
+ "lrange" => [ :first ],
75
+ "lrem" => [ :first ],
76
+ "lset" => [ :first ],
77
+ "ltrim" => [ :first ],
78
+ "mapped_mget" => [ :all, :all ],
79
+ "mget" => [ :all ],
80
+ "monitor" => [ :monitor ],
81
+ "move" => [ :first ],
82
+ "mset" => [ :alternate ],
83
+ "msetnx" => [ :alternate ],
84
+ "quit" => [],
85
+ "randomkey" => [],
86
+ "rename" => [ :all ],
87
+ "renamenx" => [ :all ],
88
+ "rpop" => [ :first ],
89
+ "rpoplpush" => [ :all ],
90
+ "rpush" => [ :first ],
91
+ "sadd" => [ :first ],
92
+ "save" => [],
93
+ "scard" => [ :first ],
94
+ "sdiff" => [ :all ],
95
+ "sdiffstore" => [ :all ],
96
+ "select" => [],
97
+ "set" => [ :first ],
98
+ "setnx" => [ :first ],
99
+ "shutdown" => [],
100
+ "sinter" => [ :all ],
101
+ "sinterstore" => [ :all ],
102
+ "sismember" => [ :first ],
103
+ "slaveof" => [],
104
+ "smembers" => [ :first ],
105
+ "smove" => [ :exclude_last ],
106
+ "sort" => [ :sort ],
107
+ "spop" => [ :first ],
108
+ "srandmember" => [ :first ],
109
+ "srem" => [ :first ],
110
+ "sunion" => [ :all ],
111
+ "sunionstore" => [ :all ],
112
+ "ttl" => [ :first ],
113
+ "type" => [ :first ],
114
+ "zadd" => [ :first ],
115
+ "zcard" => [ :first ],
116
+ "zincrby" => [ :first ],
117
+ "zrange" => [ :first ],
118
+ "zrangebyscore" => [ :first ],
119
+ "zrem" => [ :first ],
120
+ "zremrangebyscore" => [ :first ],
121
+ "zrevrange" => [ :first ],
122
+ "zscore" => [ :first ],
123
+ "[]" => [ :first ],
124
+ "[]=" => [ :first ]
125
+ }
126
+ ALIASES = {
127
+ "flush_db" => "flushdb",
128
+ "flush_all" => "flushall",
129
+ "last_save" => "lastsave",
130
+ "key?" => "exists",
131
+ "delete" => "del",
132
+ "randkey" => "randomkey",
133
+ "list_length" => "llen",
134
+ "push_tail" => "rpush",
135
+ "push_head" => "lpush",
136
+ "pop_tail" => "rpop",
137
+ "pop_head" => "lpop",
138
+ "list_set" => "lset",
139
+ "list_range" => "lrange",
140
+ "list_trim" => "ltrim",
141
+ "list_index" => "lindex",
142
+ "list_rm" => "lrem",
143
+ "set_add" => "sadd",
144
+ "set_delete" => "srem",
145
+ "set_count" => "scard",
146
+ "set_member?" => "sismember",
147
+ "set_members" => "smembers",
148
+ "set_intersect" => "sinter",
149
+ "set_intersect_store" => "sinterstore",
150
+ "set_inter_store" => "sinterstore",
151
+ "set_union" => "sunion",
152
+ "set_union_store" => "sunionstore",
153
+ "set_diff" => "sdiff",
154
+ "set_diff_store" => "sdiffstore",
155
+ "set_move" => "smove",
156
+ "set_unless_exists" => "setnx",
157
+ "rename_unless_exists" => "renamenx",
158
+ "type?" => "type",
159
+ "zset_add" => "zadd",
160
+ "zset_count" => "zcard",
161
+ "zset_range_by_score" => "zrangebyscore",
162
+ "zset_reverse_range" => "zrevrange",
163
+ "zset_range" => "zrange",
164
+ "zset_delete" => "zrem",
165
+ "zset_score" => "zscore",
166
+ "zset_incr_by" => "zincrby",
167
+ "zset_increment_by" => "zincrby"
168
+ }
169
+ # support previous versions of redis gem
170
+ #ALIASES = (defined? Redis::Client) ? Redis::Client::ALIASES : Redis::ALIASES
171
+ #ALIASES = (defined? Redis::Client) ? Redis.instance_methods : Redis::ALIASES
172
+ attr_accessor :namespace
173
+
174
+ def initialize(namespace, options = {})
175
+ @namespace = namespace
176
+ @redis = options[:redis]
177
+ end
178
+
179
+ # Ruby defines a now deprecated type method so we need to override it here
180
+ # since it will never hit method_missing
181
+ def type(key)
182
+ method_missing(:type, key)
183
+ end
184
+
185
+ def method_missing(command, *args, &block)
186
+ (before, after) = COMMANDS[command.to_s] ||
187
+ COMMANDS[ALIASES[command.to_s]]
188
+
189
+ # Add the namespace to any parameters that are keys.
190
+ case before
191
+ when :first
192
+ args[0] = add_namespace(args[0]) if args[0]
193
+ when :all
194
+ args = add_namespace(args)
195
+ when :exclude_first
196
+ first = args.shift
197
+ args = add_namespace(args)
198
+ args.unshift(first) if first
199
+ when :exclude_last
200
+ last = args.pop
201
+ args = add_namespace(args)
202
+ args.push(last) if last
203
+ when :alternate
204
+ args = [ add_namespace(Hash[*args]) ]
205
+ end
206
+
207
+ # Dispatch the command to Redis and store the result.
208
+ result = @redis.send(command, *args, &block)
209
+
210
+ # Remove the namespace from results that are keys.
211
+ result = rem_namespace(result) if after == :all
212
+
213
+ result
214
+ end
215
+
216
+ private
217
+ def add_namespace(key)
218
+ return key unless key && @namespace
219
+
220
+ case key
221
+ when Array
222
+ key.map {|k| add_namespace k}
223
+ when Hash
224
+ Hash[*key.map {|k, v| [ add_namespace(k), v ]}.flatten]
225
+ else
226
+ "#{@namespace}:#{key}"
227
+ end
228
+ end
229
+
230
+ def rem_namespace(key)
231
+ return key unless key && @namespace
232
+
233
+ case key
234
+ when Array
235
+ key.map {|k| rem_namespace k}
236
+ when Hash
237
+ Hash[*key.map {|k, v| [ rem_namespace(k), v ]}.flatten]
238
+ else
239
+ key.to_s.gsub /^#{@namespace}:/, ""
240
+ end
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,144 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'redis/namespace'
3
+ require 'logger'
4
+
5
+ describe "redis" do
6
+ before(:all) do
7
+ # use database 15 for testing so we dont accidentally step on you 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 "should be able to use a namespace" do
26
+ @namespaced['foo'].should == nil
27
+ @namespaced['foo'] = 'chris'
28
+ @namespaced['foo'].should == 'chris'
29
+ @redis['foo'] = 'bob'
30
+ @redis['foo'].should == 'bob'
31
+
32
+ @namespaced.incrby('counter', 2)
33
+ @namespaced['counter'].to_i.should == 2
34
+ @redis['counter'].should == nil
35
+ @namespaced.type('counter').should == 'string'
36
+ end
37
+
38
+ it "should be able to use a namespace with del" do
39
+ @namespaced['foo'] = 1000
40
+ @namespaced['bar'] = 2000
41
+ @namespaced['baz'] = 3000
42
+ @namespaced.del 'foo'
43
+ @namespaced['foo'].should == nil
44
+ @namespaced.del 'bar', 'baz'
45
+ @namespaced['bar'].should == nil
46
+ @namespaced['baz'].should == nil
47
+ end
48
+
49
+ it "should be able to use a namespace with mget" do
50
+ @namespaced['foo'] = 1000
51
+ @namespaced['bar'] = 2000
52
+ @namespaced.mapped_mget('foo', 'bar').should == { 'foo' => '1000', 'bar' => '2000' }
53
+ @namespaced.mapped_mget('foo', 'baz', 'bar').should == {'foo'=>'1000', 'bar'=>'2000'}
54
+ end
55
+
56
+ it "should be able to use a namespace with mset" do
57
+ @namespaced.mset('foo' => '1000', 'bar' => '2000')
58
+ @namespaced.mapped_mget('foo', 'bar').should == { 'foo' => '1000', 'bar' => '2000' }
59
+ @namespaced.mapped_mget('foo', 'baz', 'bar').should == { 'foo' => '1000', 'bar' => '2000'}
60
+ end
61
+
62
+ it "should be able to use a namespace with msetnx" do
63
+ @namespaced.msetnx('foo' => '1000', 'bar' => '2000')
64
+ @namespaced.mapped_mget('foo', 'bar').should == { 'foo' => '1000', 'bar' => '2000' }
65
+ @namespaced.mapped_mget('foo', 'baz', 'bar').should == { 'foo' => '1000', 'bar' => '2000'}
66
+ end
67
+
68
+ it "should be able to use a namespace with hashes" do
69
+ @namespaced.hset('foo', 'key', 'value')
70
+ @namespaced.hset('foo', 'key1', 'value1')
71
+ @namespaced.hget('foo', 'key').should == 'value'
72
+ @namespaced.hgetall('foo').should == {'key' => 'value', 'key1' => 'value1'}
73
+ @namespaced.hlen('foo').should == 2
74
+ @namespaced.hkeys('foo').should == ['key', 'key1']
75
+ end
76
+
77
+ it "should properly intersect three sets" do
78
+ @namespaced.sadd('foo', 1)
79
+ @namespaced.sadd('foo', 2)
80
+ @namespaced.sadd('foo', 3)
81
+ @namespaced.sadd('bar', 2)
82
+ @namespaced.sadd('bar', 3)
83
+ @namespaced.sadd('bar', 4)
84
+ @namespaced.sadd('baz', 3)
85
+ @namespaced.sinter('foo', 'bar', 'baz').should == %w( 3 )
86
+ end
87
+
88
+ it "should properly union two sets" do
89
+ @namespaced.sadd('foo', 1)
90
+ @namespaced.sadd('foo', 2)
91
+ @namespaced.sadd('bar', 2)
92
+ @namespaced.sadd('bar', 3)
93
+ @namespaced.sadd('bar', 4)
94
+ @namespaced.sunion('foo', 'bar').sort.should == %w( 1 2 3 4 )
95
+ end
96
+
97
+ it "should yield the correct list of keys" do
98
+ @namespaced["foo"] = 1
99
+ @namespaced["bar"] = 2
100
+ @namespaced["baz"] = 3
101
+ @namespaced.keys("*").sort.should == %w( bar baz foo )
102
+ end
103
+
104
+ it "can change its namespace" do
105
+ @namespaced['foo'].should == nil
106
+ @namespaced['foo'] = 'chris'
107
+ @namespaced['foo'].should == 'chris'
108
+
109
+ @namespaced.namespace.should == :ns
110
+ @namespaced.namespace = :spec
111
+ @namespaced.namespace.should == :spec
112
+
113
+ @namespaced['foo'].should == nil
114
+ @namespaced['foo'] = 'chris'
115
+ @namespaced['foo'].should == 'chris'
116
+ end
117
+
118
+ it "should support command aliases (delete)" do
119
+ @namespaced.del('foo')
120
+ @redis.should_not have_key('ns:foo')
121
+ end
122
+
123
+ it "should support command aliases (set_add)" do
124
+ @namespaced.sadd('bar', 'quux')
125
+ @namespaced.smembers('bar').should include('quux')
126
+ end
127
+
128
+ it "should support command aliases (push_head)" do
129
+ @namespaced.lpush('bar', 'quux')
130
+ @redis.llen('ns:bar').should == 1
131
+ end
132
+
133
+ it "should support command aliases (zset_add)" do
134
+ @namespaced.zadd('bar', 1, 'quux')
135
+ @redis.zcard('ns:bar').should == 1
136
+ end
137
+
138
+
139
+ Spec::Matchers.define :have_key do |expected|
140
+ match do |redis|
141
+ redis.exists(expected)
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ $TESTING=true
3
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'redis'
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opengotham_redis-namespace
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 4
8
+ - 4
9
+ version: 0.4.4
10
+ platform: ruby
11
+ authors:
12
+ - Matthew Jording
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-05-06 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: redis
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 2
29
+ - 0
30
+ - 0
31
+ - rc2
32
+ version: 2.0.0.rc2
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ description: |
36
+ Adds a Redis::Namespace class which can be used to namespace calls
37
+ to Redis. This is useful when using a single instance of Redis with
38
+ multiple, different applications.
39
+
40
+ email: mjording@opengotham.com
41
+ executables: []
42
+
43
+ extensions: []
44
+
45
+ extra_rdoc_files:
46
+ - LICENSE
47
+ - README.md
48
+ files:
49
+ - LICENSE
50
+ - README.md
51
+ - Rakefile
52
+ - lib/redis/namespace.rb
53
+ - spec/redis_spec.rb
54
+ - spec/spec_helper.rb
55
+ has_rdoc: true
56
+ homepage: http://github.com/opengotham/redis-namespace
57
+ licenses: []
58
+
59
+ post_install_message:
60
+ rdoc_options:
61
+ - --charset=UTF-8
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ segments:
76
+ - 0
77
+ version: "0"
78
+ requirements: []
79
+
80
+ rubyforge_project:
81
+ rubygems_version: 1.3.6
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: Namespaces Redis commands.
85
+ test_files:
86
+ - spec/redis_spec.rb
87
+ - spec/spec_helper.rb