redis-namespace-1.5.2/0000755000004100000410000000000012545202540014606 5ustar www-datawww-dataredis-namespace-1.5.2/Rakefile0000644000004100000410000000041612545202540016254 0ustar www-datawww-datarequire 'rubygems' require "rspec/core/rake_task" require "bundler/gem_tasks" RSpec::Core::RakeTask.new(:spec) do |spec| spec.pattern = 'spec/*_spec.rb' spec.rspec_opts = ['--backtrace'] spec.ruby_opts = ['-w'] end task :default => :spec task :test => :spec redis-namespace-1.5.2/spec/0000755000004100000410000000000012545202540015540 5ustar www-datawww-dataredis-namespace-1.5.2/spec/deprecation_spec.rb0000644000004100000410000000551512545202540021402 0ustar www-datawww-datarequire File.dirname(__FILE__) + '/spec_helper' require 'stringio' describe Redis::Namespace do # Blind passthrough of unhandled commands will be removed # in 2.0; the following tests ensure that we support them # until that point, & that we can programatically disable # them in the meantime. context 'deprecated 1.x behaviour' do let(:redis) { double(Redis) } let(:namespaced) do Redis::Namespace.new(:ns, options.merge(:redis => redis)) end let(:options) { Hash.new } subject { namespaced } its(:deprecations?) { should be false } its(:warning?) { should be true } context('with REDIS_NAMESPACE_DEPRECATIONS') do around(:each) {|e| with_env('REDIS_NAMESPACE_DEPRECATIONS'=>'1', &e) } its(:deprecations?) { should be true } end context('with REDIS_NAMESPACE_QUIET') do around(:each) {|e| with_env('REDIS_NAMESPACE_QUIET'=>'1', &e) } its(:warning?) { should be false } end before(:each) do allow(redis).to receive(:unhandled) do |*args| "unhandled(#{args.inspect})" end end # This behaviour will hold true after the 2.x migration context('with deprecations enabled') do let(:options) { {:deprecations => true} } its(:deprecations?) { should be true } context('with an unhandled command') do it { should_not respond_to :unhandled } it('raises a NoMethodError') do expect do namespaced.unhandled('foo') end.to raise_exception NoMethodError end end end # This behaviour will no longer be available after the 2.x migration context('with deprecations disabled') do let(:options) { {:deprecations => false} } its(:deprecations?) { should be false } context('with an an unhandled command') do it { should respond_to :unhandled } it 'blindly passes through' do expect(redis).to receive(:unhandled) capture_stderr do response = namespaced.unhandled('foo') expect(response).to eq 'unhandled(["foo"])' end end it 'warns with helpful output' do capture_stderr(stderr = StringIO.new) do namespaced.unhandled('bar') end warning = stderr.tap(&:rewind).read expect(warning).to_not be_empty expect(warning).to include %q(Passing 'unhandled' command to redis as is) expect(warning).to include __FILE__ end context('and warnings disabled') do let(:options) { super().merge(:warning => false)} it 'does not warn' do capture_stderr(stderr = StringIO.new) do namespaced.unhandled('bar') end warning = stderr.tap(&:rewind).read expect(warning).to be_empty end end end end end end redis-namespace-1.5.2/spec/redis_spec.rb0000644000004100000410000007042612545202540020216 0ustar www-datawww-data# encoding: utf-8 require File.dirname(__FILE__) + '/spec_helper' describe "redis" do @redis_version = Gem::Version.new(Redis.current.info["redis_version"]) before(:all) do # use database 15 for testing so we dont accidentally step on your real data @redis = Redis.new :db => 15 end before(:each) do @namespaced = Redis::Namespace.new(:ns, :redis => @redis) @namespaced.flushdb @redis['foo'] = 'bar' end after(:each) do @redis.flushdb end after(:all) do @redis.quit end it "proxies `client` to the client" do @namespaced.client.should eq(@redis.client) end it "should be able to use a namespace" do @namespaced['foo'].should eq(nil) @namespaced['foo'] = 'chris' @namespaced['foo'].should eq('chris') @redis['foo'] = 'bob' @redis['foo'].should eq('bob') @namespaced.incrby('counter', 2) @namespaced['counter'].to_i.should eq(2) @redis['counter'].should eq(nil) @namespaced.type('counter').should eq('string') end context 'when sending capital commands (issue 68)' do it 'should be able to use a namespace' do @namespaced.send('SET', 'fubar', 'quux') @redis.get('fubar').should be_nil @namespaced.get('fubar').should eq 'quux' end end it "should be able to use a namespace with bpop" do @namespaced.rpush "foo", "string" @namespaced.rpush "foo", "ns:string" @namespaced.rpush "foo", "string_no_timeout" @namespaced.blpop("foo", 1).should eq(["foo", "string"]) @namespaced.blpop("foo", 1).should eq(["foo", "ns:string"]) @namespaced.blpop("foo").should eq(["foo", "string_no_timeout"]) @namespaced.blpop("foo", 1).should eq(nil) end it "should be able to use a namespace with del" do @namespaced['foo'] = 1000 @namespaced['bar'] = 2000 @namespaced['baz'] = 3000 @namespaced.del 'foo' @namespaced['foo'].should eq(nil) @namespaced.del 'bar', 'baz' @namespaced['bar'].should eq(nil) @namespaced['baz'].should eq(nil) end it 'should be able to use a namespace with append' do @namespaced['foo'] = 'bar' @namespaced.append('foo','n').should eq(4) @namespaced['foo'].should eq('barn') @redis['foo'].should eq('bar') end it 'should be able to use a namespace with brpoplpush' do @namespaced.lpush('foo','bar') @namespaced.brpoplpush('foo','bar',0).should eq('bar') @namespaced.lrange('foo',0,-1).should eq([]) @namespaced.lrange('bar',0,-1).should eq(['bar']) end it 'should be able to use a namespace with getbit' do @namespaced.set('foo','bar') @namespaced.getbit('foo',1).should eq(1) end it 'should be able to use a namespace with getrange' do @namespaced.set('foo','bar') @namespaced.getrange('foo',0,-1).should eq('bar') end it 'should be able to use a namespace with linsert' do @namespaced.rpush('foo','bar') @namespaced.rpush('foo','barn') @namespaced.rpush('foo','bart') @namespaced.linsert('foo','BEFORE','barn','barf').should eq(4) @namespaced.lrange('foo',0,-1).should eq(['bar','barf','barn','bart']) end it 'should be able to use a namespace with lpushx' do @namespaced.lpushx('foo','bar').should eq(0) @namespaced.lpush('foo','boo') @namespaced.lpushx('foo','bar').should eq(2) @namespaced.lrange('foo',0,-1).should eq(['bar','boo']) end it 'should be able to use a namespace with rpushx' do @namespaced.rpushx('foo','bar').should eq(0) @namespaced.lpush('foo','boo') @namespaced.rpushx('foo','bar').should eq(2) @namespaced.lrange('foo',0,-1).should eq(['boo','bar']) end it 'should be able to use a namespace with setbit' do @namespaced.setbit('virgin_key', 1, 1) @namespaced.exists('virgin_key').should be_true @namespaced.get('virgin_key').should eq(@namespaced.getrange('virgin_key',0,-1)) end it 'should be able to use a namespace with setrange' do @namespaced.setrange('foo', 0, 'bar') @namespaced['foo'].should eq('bar') @namespaced.setrange('bar', 2, 'foo') @namespaced['bar'].should eq("\000\000foo") end it "should be able to use a namespace with mget" do @namespaced['foo'] = 1000 @namespaced['bar'] = 2000 @namespaced.mapped_mget('foo', 'bar').should eq({ 'foo' => '1000', 'bar' => '2000' }) @namespaced.mapped_mget('foo', 'baz', 'bar').should eq({'foo'=>'1000', 'bar'=>'2000', 'baz' => nil}) end it "should be able to use a namespace with mset" do @namespaced.mset('foo', '1000', 'bar', '2000') @namespaced.mapped_mget('foo', 'bar').should eq({ 'foo' => '1000', 'bar' => '2000' }) @namespaced.mapped_mget('foo', 'baz', 'bar').should eq({ 'foo' => '1000', 'bar' => '2000', 'baz' => nil}) @namespaced.mapped_mset('foo' => '3000', 'bar' => '5000') @namespaced.mapped_mget('foo', 'bar').should eq({ 'foo' => '3000', 'bar' => '5000' }) @namespaced.mapped_mget('foo', 'baz', 'bar').should eq({ 'foo' => '3000', 'bar' => '5000', 'baz' => nil}) end it "should be able to use a namespace with msetnx" do @namespaced.msetnx('foo', '1000', 'bar', '2000') @namespaced.mapped_mget('foo', 'bar').should eq({ 'foo' => '1000', 'bar' => '2000' }) @namespaced.mapped_mget('foo', 'baz', 'bar').should eq({ 'foo' => '1000', 'bar' => '2000', 'baz' => nil}) end it "should be able to use a namespace with mapped_msetnx" do @namespaced.set('foo','1') @namespaced.mapped_msetnx('foo'=>'1000', 'bar'=>'2000').should be_false @namespaced.mapped_mget('foo', 'bar').should eq({ 'foo' => '1', 'bar' => nil }) @namespaced.mapped_msetnx('bar'=>'2000', 'baz'=>'1000').should be_true @namespaced.mapped_mget('foo', 'bar').should eq({ 'foo' => '1', 'bar' => '2000' }) end it "should be able to use a namespace with hashes" do @namespaced.hset('foo', 'key', 'value') @namespaced.hset('foo', 'key1', 'value1') @namespaced.hget('foo', 'key').should eq('value') @namespaced.hgetall('foo').should eq({'key' => 'value', 'key1' => 'value1'}) @namespaced.hlen('foo').should eq(2) @namespaced.hkeys('foo').should eq(['key', 'key1']) @namespaced.hmset('bar', 'key', 'value', 'key1', 'value1') @namespaced.hmget('bar', 'key', 'key1') @namespaced.hmset('bar', 'a_number', 1) @namespaced.hmget('bar', 'a_number').should eq(['1']) @namespaced.hincrby('bar', 'a_number', 3) @namespaced.hmget('bar', 'a_number').should eq(['4']) @namespaced.hgetall('bar').should eq({'key' => 'value', 'key1' => 'value1', 'a_number' => '4'}) @namespaced.hsetnx('foonx','nx',10).should be_true @namespaced.hsetnx('foonx','nx',12).should be_false @namespaced.hget('foonx','nx').should eq("10") @namespaced.hkeys('foonx').should eq(%w{ nx }) @namespaced.hvals('foonx').should eq(%w{ 10 }) @namespaced.mapped_hmset('baz', {'key' => 'value', 'key1' => 'value1', 'a_number' => 4}) @namespaced.mapped_hmget('baz', 'key', 'key1', 'a_number').should eq({'key' => 'value', 'key1' => 'value1', 'a_number' => '4'}) @namespaced.hgetall('baz').should eq({'key' => 'value', 'key1' => 'value1', 'a_number' => '4'}) end it "should properly intersect three sets" do @namespaced.sadd('foo', 1) @namespaced.sadd('foo', 2) @namespaced.sadd('foo', 3) @namespaced.sadd('bar', 2) @namespaced.sadd('bar', 3) @namespaced.sadd('bar', 4) @namespaced.sadd('baz', 3) @namespaced.sinter('foo', 'bar', 'baz').should eq(%w( 3 )) end it "should properly union two sets" do @namespaced.sadd('foo', 1) @namespaced.sadd('foo', 2) @namespaced.sadd('bar', 2) @namespaced.sadd('bar', 3) @namespaced.sadd('bar', 4) @namespaced.sunion('foo', 'bar').sort.should eq(%w( 1 2 3 4 )) end it "should properly union two sorted sets with options" do @namespaced.zadd('sort1', 1, 1) @namespaced.zadd('sort1', 2, 2) @namespaced.zadd('sort2', 2, 2) @namespaced.zadd('sort2', 3, 3) @namespaced.zadd('sort2', 4, 4) @namespaced.zunionstore('union', ['sort1', 'sort2'], :weights => [2, 1]) @namespaced.zrevrange('union', 0, -1).should eq(%w( 2 4 3 1 )) end it "should properly union two sorted sets without options" do @namespaced.zadd('sort1', 1, 1) @namespaced.zadd('sort1', 2, 2) @namespaced.zadd('sort2', 2, 2) @namespaced.zadd('sort2', 3, 3) @namespaced.zadd('sort2', 4, 4) @namespaced.zunionstore('union', ['sort1', 'sort2']) @namespaced.zrevrange('union', 0, -1).should eq(%w( 4 2 3 1 )) end it "should properly intersect two sorted sets without options" do @namespaced.zadd('food', 1, 'orange') @namespaced.zadd('food', 2, 'banana') @namespaced.zadd('food', 3, 'eggplant') @namespaced.zadd('color', 2, 'orange') @namespaced.zadd('color', 3, 'yellow') @namespaced.zadd('color', 4, 'eggplant') @namespaced.zinterstore('inter', ['food', 'color']) inter_values = @namespaced.zrevrange('inter', 0, -1, :with_scores => true) inter_values.should =~ [['orange', 3.0], ['eggplant', 7.0]] end it "should properly intersect two sorted sets with options" do @namespaced.zadd('food', 1, 'orange') @namespaced.zadd('food', 2, 'banana') @namespaced.zadd('food', 3, 'eggplant') @namespaced.zadd('color', 2, 'orange') @namespaced.zadd('color', 3, 'yellow') @namespaced.zadd('color', 4, 'eggplant') @namespaced.zinterstore('inter', ['food', 'color'], :aggregate => "min") inter_values = @namespaced.zrevrange('inter', 0, -1, :with_scores => true) inter_values.should =~ [['orange', 1.0], ['eggplant', 3.0]] end it "should add namespace to sort" do @namespaced.sadd('foo', 1) @namespaced.sadd('foo', 2) @namespaced.set('weight_1', 2) @namespaced.set('weight_2', 1) @namespaced.set('value_1', 'a') @namespaced.set('value_2', 'b') @namespaced.sort('foo').should eq(%w( 1 2 )) @namespaced.sort('foo', :limit => [0, 1]).should eq(%w( 1 )) @namespaced.sort('foo', :order => 'desc').should eq(%w( 2 1 )) @namespaced.sort('foo', :by => 'weight_*').should eq(%w( 2 1 )) @namespaced.sort('foo', :get => 'value_*').should eq(%w( a b )) @namespaced.sort('foo', :get => '#').should eq(%w( 1 2 )) @namespaced.sort('foo', :get => ['#', 'value_*']).should eq([["1", "a"], ["2", "b"]]) @namespaced.sort('foo', :store => 'result') @namespaced.lrange('result', 0, -1).should eq(%w( 1 2 )) end it "should yield the correct list of keys" do @namespaced["foo"] = 1 @namespaced["bar"] = 2 @namespaced["baz"] = 3 @namespaced.keys("*").sort.should eq(%w( bar baz foo )) @namespaced.keys.sort.should eq(%w( bar baz foo )) end it "should add namepsace to multi blocks" do @namespaced.mapped_hmset "foo", {"key" => "value"} @namespaced.multi do |r| r.del "foo" r.mapped_hmset "foo", {"key1" => "value1"} end @namespaced.hgetall("foo").should eq({"key1" => "value1"}) end it "should pass through multi commands without block" do @namespaced.mapped_hmset "foo", {"key" => "value"} @namespaced.multi @namespaced.del "foo" @namespaced.mapped_hmset "foo", {"key1" => "value1"} @namespaced.exec @namespaced.hgetall("foo").should eq({"key1" => "value1"}) end it 'should return futures without attempting to remove namespaces' do @namespaced.multi do @future = @namespaced.keys('*') end @future.class.should be(Redis::Future) end it "should add namespace to pipelined blocks" do @namespaced.mapped_hmset "foo", {"key" => "value"} @namespaced.pipelined do |r| r.del "foo" r.mapped_hmset "foo", {"key1" => "value1"} end @namespaced.hgetall("foo").should eq({"key1" => "value1"}) end it "should returned response array from pipelined block" do @namespaced.mset "foo", "bar", "key", "value" result = @namespaced.pipelined do |r| r["foo"] r["key"] end result.should eq(["bar", "value"]) end it "should add namespace to strlen" do @namespaced.set("mykey", "123456") @namespaced.strlen("mykey").should eq(6) end it "should not add namespace to echo" do @namespaced.echo(123).should eq("123") end it 'should not add namespace to disconnect!' do expect(@redis).to receive(:disconnect!).with().and_call_original expect(@namespaced.disconnect!).to be nil end it "can change its namespace" do @namespaced['foo'].should eq(nil) @namespaced['foo'] = 'chris' @namespaced['foo'].should eq('chris') @namespaced.namespace.should eq(:ns) @namespaced.namespace = :spec @namespaced.namespace.should eq(:spec) @namespaced['foo'].should eq(nil) @namespaced['foo'] = 'chris' @namespaced['foo'].should eq('chris') end it "can accept a temporary namespace" do @namespaced.namespace.should eq(:ns) @namespaced['foo'].should eq(nil) @namespaced.namespace(:spec) do |temp_ns| temp_ns.namespace.should eq(:spec) temp_ns['foo'].should eq(nil) temp_ns['foo'] = 'jake' temp_ns['foo'].should eq('jake') end @namespaced.namespace.should eq(:ns) @namespaced['foo'].should eq(nil) end it "should respond to :namespace=" do @namespaced.respond_to?(:namespace=).should eq(true) end it "should respond to :warning=" do @namespaced.respond_to?(:warning=).should == true end it "should raise an exception when an unknown command is passed" do expect { @namespaced.unknown('foo') }.to raise_exception NoMethodError end # Redis 2.6 RC reports its version as 2.5. if @redis_version >= Gem::Version.new("2.5.0") describe "redis 2.6 commands" do it "should namespace bitcount" do @redis.set('ns:foo', 'foobar') expect(@namespaced.bitcount('foo')).to eq 26 expect(@namespaced.bitcount('foo', 0, 0)).to eq 4 expect(@namespaced.bitcount('foo', 1, 1)).to eq 6 expect(@namespaced.bitcount('foo', 3, 5)).to eq 10 end it "should namespace bitop" do try_encoding('UTF-8') do @redis.set("ns:foo", "a") @redis.set("ns:bar", "b") @namespaced.bitop(:and, "foo&bar", "foo", "bar") @namespaced.bitop(:or, "foo|bar", "foo", "bar") @namespaced.bitop(:xor, "foo^bar", "foo", "bar") @namespaced.bitop(:not, "~foo", "foo") expect(@redis.get("ns:foo&bar")).to eq "\x60" expect(@redis.get("ns:foo|bar")).to eq "\x63" expect(@redis.get("ns:foo^bar")).to eq "\x03" expect(@redis.get("ns:~foo")).to eq "\x9E" end end it "should namespace dump and restore" do @redis.set("ns:foo", "a") v = @namespaced.dump("foo") @redis.del("ns:foo") expect(@namespaced.restore("foo", 1000, v)).to be_true expect(@redis.get("ns:foo")).to eq 'a' expect(@redis.ttl("ns:foo")).to satisfy {|v| (0..1).include?(v) } @redis.rpush("ns:bar", %w(b c d)) w = @namespaced.dump("bar") @redis.del("ns:bar") expect(@namespaced.restore("bar", 1000, w)).to be_true expect(@redis.lrange('ns:bar', 0, -1)).to eq %w(b c d) expect(@redis.ttl("ns:foo")).to satisfy {|v| (0..1).include?(v) } end it "should namespace hincrbyfloat" do @namespaced.hset('mykey', 'field', 10.50) @namespaced.hincrbyfloat('mykey', 'field', 0.1).should eq(10.6) end it "should namespace incrbyfloat" do @namespaced.set('mykey', 10.50) @namespaced.incrbyfloat('mykey', 0.1).should eq(10.6) end it "should namespace object" do @namespaced.set('foo', 1000) @namespaced.object('encoding', 'foo').should eq('int') end it "should namespace persist" do @namespaced.set('mykey', 'Hello') @namespaced.expire('mykey', 60) @namespaced.persist('mykey').should eq(true) @namespaced.ttl('mykey').should eq(-1) end it "should namespace pexpire" do @namespaced.set('mykey', 'Hello') @namespaced.pexpire('mykey', 60000).should eq(true) end it "should namespace pexpireat" do @namespaced.set('mykey', 'Hello') @namespaced.pexpire('mykey', 1555555555005).should eq(true) end it "should namespace psetex" do @namespaced.psetex('mykey', 10000, 'Hello').should eq('OK') @namespaced.get('mykey').should eq('Hello') end it "should namespace pttl" do @namespaced.set('mykey', 'Hello') @namespaced.expire('mykey', 1) @namespaced.pttl('mykey').should >= 0 end it "should namespace eval keys passed in as array args" do @namespaced. eval("return {KEYS[1], KEYS[2]}", %w[k1 k2], %w[arg1 arg2]). should eq(%w[ns:k1 ns:k2]) end it "should namespace eval keys passed in as hash args" do @namespaced. eval("return {KEYS[1], KEYS[2]}", :keys => %w[k1 k2], :argv => %w[arg1 arg2]). should eq(%w[ns:k1 ns:k2]) end context '#evalsha' do let!(:sha) do @namespaced.script(:load, "return {KEYS[1], KEYS[2]}") end it "should namespace evalsha keys passed in as array args" do @namespaced. evalsha(sha, %w[k1 k2], %w[arg1 arg2]). should eq(%w[ns:k1 ns:k2]) end it "should namespace evalsha keys passed in as hash args" do @namespaced. evalsha(sha, :keys => %w[k1 k2], :argv => %w[arg1 arg2]). should eq(%w[ns:k1 ns:k2]) end end context "in a nested namespace" do let(:nested_namespace) { Redis::Namespace.new(:nest, :redis => @namespaced) } let(:sha) { nested_namespace.script(:load, "return {KEYS[1], KEYS[2]}") } it "should namespace eval keys passed in as hash args" do nested_namespace. eval("return {KEYS[1], KEYS[2]}", :keys => %w[k1 k2], :argv => %w[arg1 arg2]). should eq(%w[ns:nest:k1 ns:nest:k2]) end it "should namespace evalsha keys passed in as hash args" do nested_namespace.evalsha(sha, :keys => %w[k1 k2], :argv => %w[arg1 arg2]). should eq(%w[ns:nest:k1 ns:nest:k2]) end end end end # Redis 2.8 RC reports its version as 2.7. if @redis_version >= Gem::Version.new("2.7.105") describe "redis 2.8 commands" do context 'keyspace scan methods' do let(:keys) do %w(alpha ns:beta gamma ns:delta ns:epsilon ns:zeta:one ns:zeta:two ns:theta) end let(:namespaced_keys) do keys.map{|k| k.dup.sub!(/\Ans:/,'') }.compact.sort end before(:each) do keys.each do |key| @redis.set(key, key) end end let(:matching_namespaced_keys) do namespaced_keys.select{|k| k[/\Azeta:/] }.compact.sort end context '#scan' do context 'when :match supplied' do it 'should retrieve the proper keys' do _, result = @namespaced.scan(0, :match => 'zeta:*', :count => 1000) result.should =~ matching_namespaced_keys end end context 'without :match supplied' do it 'should retrieve the proper keys' do _, result = @namespaced.scan(0, :count => 1000) result.should =~ namespaced_keys end end end if Redis.current.respond_to?(:scan) context '#scan_each' do context 'when :match supplied' do context 'when given a block' do it 'should yield unnamespaced' do results = [] @namespaced.scan_each(:match => 'zeta:*', :count => 1000) {|k| results << k } results.should =~ matching_namespaced_keys end end context 'without a block' do it 'should return an Enumerator that un-namespaces' do enum = @namespaced.scan_each(:match => 'zeta:*', :count => 1000) enum.to_a.should =~ matching_namespaced_keys end end end context 'without :match supplied' do context 'when given a block' do it 'should yield unnamespaced' do results = [] @namespaced.scan_each(:count => 1000){ |k| results << k } results.should =~ namespaced_keys end end context 'without a block' do it 'should return an Enumerator that un-namespaces' do enum = @namespaced.scan_each(:count => 1000) enum.to_a.should =~ namespaced_keys end end end end if Redis.current.respond_to?(:scan_each) end context 'hash scan methods' do before(:each) do @redis.mapped_hmset('hsh', {'zeta:wrong:one' => 'WRONG', 'wrong:two' => 'WRONG'}) @redis.mapped_hmset('ns:hsh', hash) end let(:hash) do {'zeta:one' => 'OK', 'zeta:two' => 'OK', 'three' => 'OKAY'} end let(:hash_matching_subset) do # select is not consistent from 1.8.7 -> 1.9.2 :( hash.reject {|k,v| !k[/\Azeta:/] } end context '#hscan' do context 'when supplied :match' do it 'should retrieve the proper keys' do _, results = @namespaced.hscan('hsh', 0, :match => 'zeta:*') results.should =~ hash_matching_subset.to_a end end context 'without :match supplied' do it 'should retrieve all hash keys' do _, results = @namespaced.hscan('hsh', 0) results.should =~ @redis.hgetall('ns:hsh').to_a end end end if Redis.current.respond_to?(:hscan) context '#hscan_each' do context 'when :match supplied' do context 'when given a block' do it 'should yield the correct hash keys unchanged' do results = [] @namespaced.hscan_each('hsh', :match => 'zeta:*', :count => 1000) { |kv| results << kv} results.should =~ hash_matching_subset.to_a end end context 'without a block' do it 'should return an Enumerator that yields the correct hash keys unchanged' do enum = @namespaced.hscan_each('hsh', :match => 'zeta:*', :count => 1000) enum.to_a.should =~ hash_matching_subset.to_a end end end context 'without :match supplied' do context 'when given a block' do it 'should yield all hash keys unchanged' do results = [] @namespaced.hscan_each('hsh', :count => 1000){ |k| results << k } results.should =~ hash.to_a end end context 'without a block' do it 'should return an Enumerator that yields all keys unchanged' do enum = @namespaced.hscan_each('hsh', :count => 1000) enum.to_a.should =~ hash.to_a end end end end if Redis.current.respond_to?(:hscan_each) end context 'set scan methods' do before(:each) do set.each { |elem| @namespaced.sadd('set', elem) } @redis.sadd('set', 'WRONG') end let(:set) do %w(zeta:one zeta:two three) end let(:matching_subset) do set.select { |e| e[/\Azeta:/] } end context '#sscan' do context 'when supplied :match' do it 'should retrieve the matching set members from the proper set' do _, results = @namespaced.sscan('set', 0, :match => 'zeta:*', :count => 1000) results.should =~ matching_subset end end context 'without :match supplied' do it 'should retrieve all set members from the proper set' do _, results = @namespaced.sscan('set', 0, :count => 1000) results.should =~ set end end end if Redis.current.respond_to?(:sscan) context '#sscan_each' do context 'when :match supplied' do context 'when given a block' do it 'should yield the correct hset elements unchanged' do results = [] @namespaced.sscan_each('set', :match => 'zeta:*', :count => 1000) { |kv| results << kv} results.should =~ matching_subset end end context 'without a block' do it 'should return an Enumerator that yields the correct set elements unchanged' do enum = @namespaced.sscan_each('set', :match => 'zeta:*', :count => 1000) enum.to_a.should =~ matching_subset end end end context 'without :match supplied' do context 'when given a block' do it 'should yield all set elements unchanged' do results = [] @namespaced.sscan_each('set', :count => 1000){ |k| results << k } results.should =~ set end end context 'without a block' do it 'should return an Enumerator that yields all set elements unchanged' do enum = @namespaced.sscan_each('set', :count => 1000) enum.to_a.should =~ set end end end end if Redis.current.respond_to?(:sscan_each) end context 'zset scan methods' do before(:each) do hash.each {|member, score| @namespaced.zadd('zset', score, member)} @redis.zadd('zset', 123.45, 'WRONG') end let(:hash) do {'zeta:one' => 1, 'zeta:two' => 2, 'three' => 3} end let(:hash_matching_subset) do # select is not consistent from 1.8.7 -> 1.9.2 :( hash.reject {|k,v| !k[/\Azeta:/] } end context '#zscan' do context 'when supplied :match' do it 'should retrieve the matching set elements and their scores' do results = [] @namespaced.zscan_each('zset', :match => 'zeta:*', :count => 1000) { |ms| results << ms } results.should =~ hash_matching_subset.to_a end end context 'without :match supplied' do it 'should retrieve all set elements and their scores' do results = [] @namespaced.zscan_each('zset', :count => 1000) { |ms| results << ms } results.should =~ hash.to_a end end end if Redis.current.respond_to?(:zscan) context '#zscan_each' do context 'when :match supplied' do context 'when given a block' do it 'should yield the correct set elements and scores unchanged' do results = [] @namespaced.zscan_each('zset', :match => 'zeta:*', :count => 1000) { |ms| results << ms} results.should =~ hash_matching_subset.to_a end end context 'without a block' do it 'should return an Enumerator that yields the correct set elements and scoresunchanged' do enum = @namespaced.zscan_each('zset', :match => 'zeta:*', :count => 1000) enum.to_a.should =~ hash_matching_subset.to_a end end end context 'without :match supplied' do context 'when given a block' do it 'should yield all set elements and scores unchanged' do results = [] @namespaced.zscan_each('zset', :count => 1000){ |ms| results << ms } results.should =~ hash.to_a end end context 'without a block' do it 'should return an Enumerator that yields all set elements and scores unchanged' do enum = @namespaced.zscan_each('zset', :count => 1000) enum.to_a.should =~ hash.to_a end end end end if Redis.current.respond_to?(:zscan_each) end end end if @redis_version >= Gem::Version.new("2.8.9") it 'should namespace pfadd' do 5.times { |n| @namespaced.pfadd("pf", n) } @redis.pfcount("ns:pf").should == 5 end it 'should namespace pfcount' do 5.times { |n| @redis.pfadd("ns:pf", n) } @namespaced.pfcount("pf").should == 5 end it 'should namespace pfmerge' do 5.times do |n| @redis.pfadd("ns:pfa", n) @redis.pfadd("ns:pfb", n+5) end @namespaced.pfmerge("pfc", "pfa", "pfb") @redis.pfcount("ns:pfc").should == 10 end end end redis-namespace-1.5.2/spec/spec_helper.rb0000644000004100000410000000261712545202540020364 0ustar www-datawww-datarequire 'rubygems' require 'bundler' Bundler.setup(:default, :test) Bundler.require(:default, :test) require 'rspec' require 'redis' require 'logger' $TESTING=true $:.unshift File.join(File.dirname(__FILE__), '..', 'lib') require 'redis/namespace' module Helper def capture_stderr(io = nil) require 'stringio' io ||= StringIO.new begin original, $stderr = $stderr, io yield rescue Redis::CommandError # ignore Redis::CommandError for test and # return captured messages $stderr.string.chomp ensure $stderr = original end end def with_env(env = {}) backup_env = ENV.to_hash.dup ENV.update(env) yield ensure ENV.replace(backup_env) end def silent verbose, $VERBOSE = $VERBOSE, false begin yield ensure $VERBOSE = verbose end end def with_external_encoding(encoding) original_encoding = Encoding.default_external begin silent { Encoding.default_external = Encoding.find(encoding) } yield ensure silent { Encoding.default_external = original_encoding } end end def try_encoding(encoding, &block) if defined?(Encoding) with_external_encoding(encoding, &block) else yield end end end RSpec.configure do |c| c.include Helper end RSpec::Matchers.define :have_key do |expected| match do |redis| redis.exists(expected) end end redis-namespace-1.5.2/lib/0000755000004100000410000000000012545202540015354 5ustar www-datawww-dataredis-namespace-1.5.2/lib/redis-namespace.rb0000644000004100000410000000003212545202540020734 0ustar www-datawww-datarequire 'redis/namespace' redis-namespace-1.5.2/lib/redis/0000755000004100000410000000000012545202540016462 5ustar www-datawww-dataredis-namespace-1.5.2/lib/redis/namespace.rb0000644000004100000410000003633112545202540020751 0ustar www-datawww-datarequire 'redis' require 'redis/namespace/version' class Redis class Namespace # The following table defines how input parameters and result # values should be modified for the namespace. # # COMMANDS is a hash. Each key is the name of a command and each # value is a two element array. # # The first element in the value array describes how to modify the # arguments passed. It can be one of: # # nil # Do nothing. # :first # Add the namespace to the first argument passed, e.g. # GET key => GET namespace:key # :all # Add the namespace to all arguments passed, e.g. # MGET key1 key2 => MGET namespace:key1 namespace:key2 # :exclude_first # Add the namespace to all arguments but the first, e.g. # :exclude_last # Add the namespace to all arguments but the last, e.g. # BLPOP key1 key2 timeout => # BLPOP namespace:key1 namespace:key2 timeout # :exclude_options # Add the namespace to all arguments, except the last argument, # if the last argument is a hash of options. # ZUNIONSTORE key1 2 key2 key3 WEIGHTS 2 1 => # ZUNIONSTORE namespace:key1 2 namespace:key2 namespace:key3 WEIGHTS 2 1 # :alternate # Add the namespace to every other argument, e.g. # MSET key1 value1 key2 value2 => # MSET namespace:key1 value1 namespace:key2 value2 # :sort # Add namespace to first argument if it is non-nil # Add namespace to second arg's :by and :store if second arg is a Hash # Add namespace to each element in second arg's :get if second arg is # a Hash; forces second arg's :get to be an Array if present. # :eval_style # Add namespace to each element in keys argument (via options hash or multi-args) # :scan_style # Add namespace to :match option, or supplies "#{namespace}:*" if not present. # # The second element in the value array describes how to modify # the return value of the Redis call. It can be one of: # # nil # Do nothing. # :all # Add the namespace to all elements returned, e.g. # key1 key2 => namespace:key1 namespace:key2 COMMANDS = { "append" => [:first], "auth" => [], "bgrewriteaof" => [], "bgsave" => [], "bitcount" => [ :first ], "bitop" => [ :exclude_first ], "blpop" => [ :exclude_last, :first ], "brpop" => [ :exclude_last, :first ], "brpoplpush" => [ :exclude_last ], "config" => [], "dbsize" => [], "debug" => [ :exclude_first ], "decr" => [ :first ], "decrby" => [ :first ], "del" => [ :all ], "discard" => [], "disconnect!" => [], "dump" => [ :first ], "echo" => [], "exists" => [ :first ], "expire" => [ :first ], "expireat" => [ :first ], "eval" => [ :eval_style ], "evalsha" => [ :eval_style ], "exec" => [], "flushall" => [], "flushdb" => [], "get" => [ :first ], "getbit" => [ :first ], "getrange" => [ :first ], "getset" => [ :first ], "hset" => [ :first ], "hsetnx" => [ :first ], "hget" => [ :first ], "hincrby" => [ :first ], "hincrbyfloat" => [ :first ], "hmget" => [ :first ], "hmset" => [ :first ], "hdel" => [ :first ], "hexists" => [ :first ], "hlen" => [ :first ], "hkeys" => [ :first ], "hscan" => [ :first ], "hscan_each" => [ :first ], "hvals" => [ :first ], "hgetall" => [ :first ], "incr" => [ :first ], "incrby" => [ :first ], "incrbyfloat" => [ :first ], "info" => [], "keys" => [ :first, :all ], "lastsave" => [], "lindex" => [ :first ], "linsert" => [ :first ], "llen" => [ :first ], "lpop" => [ :first ], "lpush" => [ :first ], "lpushx" => [ :first ], "lrange" => [ :first ], "lrem" => [ :first ], "lset" => [ :first ], "ltrim" => [ :first ], "mapped_hmset" => [ :first ], "mapped_hmget" => [ :first ], "mapped_mget" => [ :all, :all ], "mapped_mset" => [ :all ], "mapped_msetnx" => [ :all ], "mget" => [ :all ], "monitor" => [ :monitor ], "move" => [ :first ], "multi" => [], "mset" => [ :alternate ], "msetnx" => [ :alternate ], "object" => [ :exclude_first ], "persist" => [ :first ], "pexpire" => [ :first ], "pexpireat" => [ :first ], "pfadd" => [ :first ], "pfcount" => [ :all ], "pfmerge" => [ :all ], "ping" => [], "psetex" => [ :first ], "psubscribe" => [ :all ], "pttl" => [ :first ], "publish" => [ :first ], "punsubscribe" => [ :all ], "quit" => [], "randomkey" => [], "rename" => [ :all ], "renamenx" => [ :all ], "restore" => [ :first ], "rpop" => [ :first ], "rpoplpush" => [ :all ], "rpush" => [ :first ], "rpushx" => [ :first ], "sadd" => [ :first ], "save" => [], "scard" => [ :first ], "scan" => [ :scan_style, :second ], "scan_each" => [ :scan_style, :all ], "script" => [], "sdiff" => [ :all ], "sdiffstore" => [ :all ], "select" => [], "set" => [ :first ], "setbit" => [ :first ], "setex" => [ :first ], "setnx" => [ :first ], "setrange" => [ :first ], "shutdown" => [], "sinter" => [ :all ], "sinterstore" => [ :all ], "sismember" => [ :first ], "slaveof" => [], "smembers" => [ :first ], "smove" => [ :exclude_last ], "sort" => [ :sort ], "spop" => [ :first ], "srandmember" => [ :first ], "srem" => [ :first ], "sscan" => [ :first ], "sscan_each" => [ :first ], "strlen" => [ :first ], "subscribe" => [ :all ], "sunion" => [ :all ], "sunionstore" => [ :all ], "ttl" => [ :first ], "type" => [ :first ], "unsubscribe" => [ :all ], "unwatch" => [ :all ], "watch" => [ :all ], "zadd" => [ :first ], "zcard" => [ :first ], "zcount" => [ :first ], "zincrby" => [ :first ], "zinterstore" => [ :exclude_options ], "zrange" => [ :first ], "zrangebyscore" => [ :first ], "zrank" => [ :first ], "zrem" => [ :first ], "zremrangebyrank" => [ :first ], "zremrangebyscore" => [ :first ], "zrevrange" => [ :first ], "zrevrangebyscore" => [ :first ], "zrevrank" => [ :first ], "zscan" => [ :first ], "zscan_each" => [ :first ], "zscore" => [ :first ], "zunionstore" => [ :exclude_options ], "[]" => [ :first ], "[]=" => [ :first ] } # Support 1.8.7 by providing a namespaced reference to Enumerable::Enumerator Enumerator = Enumerable::Enumerator unless defined?(::Enumerator) attr_writer :namespace attr_reader :redis attr_accessor :warning def initialize(namespace, options = {}) @namespace = namespace @redis = options[:redis] || Redis.current @warning = !!options.fetch(:warning) do !ENV['REDIS_NAMESPACE_QUIET'] end @deprecations = !!options.fetch(:deprecations) do ENV['REDIS_NAMESPACE_DEPRECATIONS'] end end def deprecations? @deprecations end def warning? @warning end def client @redis.client end # Ruby defines a now deprecated type method so we need to override it here # since it will never hit method_missing def type(key) call_with_namespace(:type, key) end alias_method :self_respond_to?, :respond_to? # emulate Ruby 1.9+ and keep respond_to_missing? logic together. def respond_to?(command, include_private=false) super or respond_to_missing?(command, include_private) end def keys(query = nil) call_with_namespace(:keys, query || '*') end def multi(&block) if block_given? namespaced_block(:multi, &block) else call_with_namespace(:multi) end end def pipelined(&block) namespaced_block(:pipelined, &block) end def namespace(desired_namespace = nil) if desired_namespace yield Redis::Namespace.new(desired_namespace, :redis => @redis) end @namespace end def exec call_with_namespace(:exec) end def eval(*args) call_with_namespace(:eval, *args) end def method_missing(command, *args, &block) normalized_command = command.to_s.downcase if COMMANDS.include?(normalized_command) call_with_namespace(command, *args, &block) elsif @redis.respond_to?(normalized_command) && !deprecations? # blind passthrough is deprecated and will be removed in 2.0 # redis-namespace does not know how to handle this command. # Passing it to @redis as is, where redis-namespace shows # a warning message if @warning is set. if warning? call_site = caller.reject { |l| l.start_with?(__FILE__) }.first warn("Passing '#{command}' command to redis as is; blind " + "passthrough has been deprecated and will be removed in " + "redis-namespace 2.0 (at #{call_site})") end @redis.send(command, *args, &block) else super end end def respond_to_missing?(command, include_all=false) return true if COMMANDS.include?(command.to_s.downcase) # blind passthrough is deprecated and will be removed in 2.0 if @redis.respond_to?(command, include_all) && !deprecations? return true end defined?(super) && super end def call_with_namespace(command, *args, &block) handling = COMMANDS[command.to_s.downcase] if handling.nil? fail("Redis::Namespace does not know how to handle '#{command}'.") end (before, after) = handling # Add the namespace to any parameters that are keys. case before when :first args[0] = add_namespace(args[0]) if args[0] when :all args = add_namespace(args) when :exclude_first first = args.shift args = add_namespace(args) args.unshift(first) if first when :exclude_last last = args.pop unless args.length == 1 args = add_namespace(args) args.push(last) if last when :exclude_options if args.last.is_a?(Hash) last = args.pop args = add_namespace(args) args.push(last) else args = add_namespace(args) end when :alternate args.each_with_index { |a, i| args[i] = add_namespace(a) if i.even? } when :sort args[0] = add_namespace(args[0]) if args[0] if args[1].is_a?(Hash) [:by, :store].each do |key| args[1][key] = add_namespace(args[1][key]) if args[1][key] end args[1][:get] = Array(args[1][:get]) args[1][:get].each_index do |i| args[1][:get][i] = add_namespace(args[1][:get][i]) unless args[1][:get][i] == "#" end end when :eval_style # redis.eval() and evalsha() can either take the form: # # redis.eval(script, [key1, key2], [argv1, argv2]) # # Or: # # redis.eval(script, :keys => ['k1', 'k2'], :argv => ['arg1', 'arg2']) # # This is a tricky + annoying special case, where we only want the `keys` # argument to be namespaced. if args.last.is_a?(Hash) args.last[:keys] = add_namespace(args.last[:keys]) else args[1] = add_namespace(args[1]) end when :scan_style options = (args.last.kind_of?(Hash) ? args.pop : {}) options[:match] = add_namespace(options.fetch(:match, '*')) args << options if block original_block = block block = proc { |key| original_block.call rem_namespace(key) } end end # Dispatch the command to Redis and store the result. result = @redis.send(command, *args, &block) # Don't try to remove namespace from a Redis::Future, you can't. return result if result.is_a?(Redis::Future) # Remove the namespace from results that are keys. case after when :all result = rem_namespace(result) when :first result[0] = rem_namespace(result[0]) if result when :second result[1] = rem_namespace(result[1]) if result end result end private def namespaced_block(command, &block) redis.send(command) do |r| begin original, @redis = @redis, r yield self ensure @redis = original end end end def add_namespace(key) return key unless key && @namespace case key when Array key.map {|k| add_namespace k} when Hash Hash[*key.map {|k, v| [ add_namespace(k), v ]}.flatten] else "#{@namespace}:#{key}" end end def rem_namespace(key) return key unless key && @namespace case key when Array key.map {|k| rem_namespace k} when Hash Hash[*key.map {|k, v| [ rem_namespace(k), v ]}.flatten] when Enumerator create_enumerator do |yielder| key.each { |k| yielder.yield rem_namespace(k) } end else key.to_s.sub(/\A#{@namespace}:/, '') end end def create_enumerator(&block) # Enumerator in 1.8.7 *requires* a single argument, so we need to use # its Generator class, which matches the block syntax of 1.9.x's # Enumerator class. if RUBY_VERSION.start_with?('1.8') require 'generator' unless defined?(Generator) Generator.new(&block).to_enum else Enumerator.new(&block) end end end end redis-namespace-1.5.2/lib/redis/namespace/0000755000004100000410000000000012545202540020416 5ustar www-datawww-dataredis-namespace-1.5.2/lib/redis/namespace/version.rb0000644000004100000410000000012112545202540022422 0ustar www-datawww-data# encoding: utf-8 class Redis class Namespace VERSION = '1.5.2' end end redis-namespace-1.5.2/metadata.yml0000644000004100000410000000525712545202540017122 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: redis-namespace version: !ruby/object:Gem::Version version: 1.5.2 prerelease: platform: ruby authors: - Chris Wanstrath - Terence Lee - Steve Klabnik - Ryan Biesemeyer autorequire: bindir: bin cert_chain: [] date: 2015-03-30 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: redis requirement: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '3.0' - - ! '>=' - !ruby/object:Gem::Version version: 3.0.4 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '3.0' - - ! '>=' - !ruby/object:Gem::Version version: 3.0.4 - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '10.1' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '10.1' - !ruby/object:Gem::Dependency name: rspec requirement: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '2.14' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '2.14' description: ! 'Adds a Redis::Namespace class which can be used to namespace calls to Redis. This is useful when using a single instance of Redis with multiple, different applications. ' email: - chris@ozmm.org - hone02@gmail.com - steve@steveklabnik.com - me@yaauie.com executables: [] extensions: [] extra_rdoc_files: [] files: - README.md - Rakefile - LICENSE - lib/redis/namespace/version.rb - lib/redis/namespace.rb - lib/redis-namespace.rb - spec/deprecation_spec.rb - spec/redis_spec.rb - spec/spec_helper.rb homepage: http://github.com/resque/redis-namespace licenses: - MIT post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 1.8.23 signing_key: specification_version: 3 summary: Namespaces Redis commands. test_files: [] has_rdoc: false redis-namespace-1.5.2/LICENSE0000644000004100000410000000204312545202540015612 0ustar www-datawww-dataCopyright (c) 2009 Chris Wanstrath Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. redis-namespace-1.5.2/README.md0000644000004100000410000000076612545202540016076 0ustar www-datawww-dataredis-namespace --------------- Requires the redis gem. Namespaces all Redis calls. ``` ruby r = Redis::Namespace.new(:ns, :redis => @r) r['foo'] = 1000 ``` This will perform the equivalent of: redis-cli set ns:foo 1000 Useful when you have multiple systems using Redis differently in your app. Installation ============ $ gem install redis-namespace Testing ======= $ bundle install $ rake Author ===== Chris Wanstrath :: chris@ozmm.org Terence Lee :: hone02@gmail.com