opengotham_redis-namespace 0.4.4

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 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