Ruby client library updated. Important changes in this new version!

This commit is contained in:
antirez 2009-03-27 12:14:35 +01:00
parent 1a4601492c
commit 29fac6170a
14 changed files with 739 additions and 850 deletions

3
client-libraries/ruby/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
nohup.out
redis/*
rdsrv

View File

@ -1,12 +0,0 @@
== redis
A ruby client library for the redis key value storage system:
http://code.google.com/p/redis/wiki/README
redis is a key value store with some interesting features:
1. fast
2. keys are strings but values can have types of "NONE","STRING","LIST","SET"
list's can be atomicaly push'd, pop'd and lpush'd, lpop'd and indexed so you
can store things like lists of comments under one key and still be able to
append comments without reading and putting back the whole list.

View File

@ -7,7 +7,7 @@ require 'tasks/redis.tasks'
GEM = 'redis' GEM = 'redis'
GEM_VERSION = '0.0.2' GEM_VERSION = '0.0.3'
AUTHORS = ['Ezra Zygmuntowicz', 'Taylor Weibley'] AUTHORS = ['Ezra Zygmuntowicz', 'Taylor Weibley']
EMAIL = "ez@engineyard.com" EMAIL = "ez@engineyard.com"
HOMEPAGE = "http://github.com/ezmobius/redis-rb" HOMEPAGE = "http://github.com/ezmobius/redis-rb"

View File

@ -0,0 +1,13 @@
require 'socket'
require 'pp'
require File.join(File.dirname(__FILE__), '../lib/redis')
#require File.join(File.dirname(__FILE__), '../lib/server')
#r = Redis.new
#loop do
# puts "--------------------------------------"
# sleep 12
#end

View File

@ -1,11 +0,0 @@
require 'benchmark'
$:.push File.join(File.dirname(__FILE__), 'lib')
require 'redis'
times = 20000
@r = Redis.new
(0..1000000).each{|x|
@r[x] = "Hello World"
puts x if (x > 0 and x % 10000) == 0
}

View File

@ -163,6 +163,9 @@ end
# Another name for Timeout::Error, defined for backwards compatibility with # Another name for Timeout::Error, defined for backwards compatibility with
# earlier versions of timeout.rb. # earlier versions of timeout.rb.
class Object
remove_const(:TimeoutError) if const_defined?(:TimeoutError)
end
TimeoutError = Timeout::Error # :nodoc: TimeoutError = Timeout::Error # :nodoc:
if __FILE__ == $0 if __FILE__ == $0

View File

@ -24,7 +24,7 @@ class DistRedis
end end
def method_missing(sym, *args, &blk) def method_missing(sym, *args, &blk)
if redis = node_for_key(args.first) if redis = node_for_key(args.first.to_s)
redis.send sym, *args, &blk redis.send sym, *args, &blk
else else
super super
@ -94,11 +94,11 @@ r = DistRedis.new 'localhost:6379', 'localhost:6380', 'localhost:6381', 'localho
r.push_tail 'listor', 'foo4' r.push_tail 'listor', 'foo4'
r.push_tail 'listor', 'foo5' r.push_tail 'listor', 'foo5'
p r.pop_tail 'listor' p r.pop_tail('listor')
p r.pop_tail 'listor' p r.pop_tail('listor')
p r.pop_tail 'listor' p r.pop_tail('listor')
p r.pop_tail 'listor' p r.pop_tail('listor')
p r.pop_tail 'listor' p r.pop_tail('listor')
puts "key distribution:" puts "key distribution:"

View File

@ -1,10 +1,15 @@
require 'digest/md5' require 'zlib'
class HashRing class HashRing
POINTS_PER_SERVER = 160 # this is the default in libmemcached
attr_reader :ring, :sorted_keys, :replicas, :nodes attr_reader :ring, :sorted_keys, :replicas, :nodes
# nodes is a list of objects that have a proper to_s representation. # nodes is a list of objects that have a proper to_s representation.
# replicas indicates how many virtual points should be used pr. node, # replicas indicates how many virtual points should be used pr. node,
# replicas are required to improve the distribution. # replicas are required to improve the distribution.
def initialize(nodes=[], replicas=3) def initialize(nodes=[], replicas=POINTS_PER_SERVER)
@replicas = replicas @replicas = replicas
@ring = {} @ring = {}
@nodes = [] @nodes = []
@ -18,7 +23,7 @@ class HashRing
def add_node(node) def add_node(node)
@nodes << node @nodes << node
@replicas.times do |i| @replicas.times do |i|
key = gen_key("#{node}:#{i}") key = Zlib.crc32("#{node}:#{i}")
@ring[key] = node @ring[key] = node
@sorted_keys << key @sorted_keys << key
end end
@ -27,7 +32,7 @@ class HashRing
def remove_node(node) def remove_node(node)
@replicas.times do |i| @replicas.times do |i|
key = gen_key("#{node}:#{count}") key = Zlib.crc32("#{node}:#{count}")
@ring.delete(key) @ring.delete(key)
@sorted_keys.reject! {|k| k == key} @sorted_keys.reject! {|k| k == key}
end end
@ -40,15 +45,9 @@ class HashRing
def get_node_pos(key) def get_node_pos(key)
return [nil,nil] if @ring.size == 0 return [nil,nil] if @ring.size == 0
key = gen_key(key) crc = Zlib.crc32(key)
nodes = @sorted_keys idx = HashRing.binary_search(@sorted_keys, crc)
nodes.size.times do |i| return [@ring[@sorted_keys[idx]], idx]
node = nodes[i]
if key <= node
return [@ring[node], i]
end
end
[@ring[nodes[0]], 0]
end end
def iter_nodes(key) def iter_nodes(key)
@ -59,11 +58,66 @@ class HashRing
end end
end end
def gen_key(key) class << self
key = Digest::MD5.hexdigest(key)
((key[3] << 24) | (key[2] << 16) | (key[1] << 8) | key[0]) # gem install RubyInline to use this code
# Native extension to perform the binary search within the hashring.
# There's a pure ruby version below so this is purely optional
# for performance. In testing 20k gets and sets, the native
# binary search shaved about 12% off the runtime (9sec -> 8sec).
begin
require 'inline'
inline do |builder|
builder.c <<-EOM
int binary_search(VALUE ary, unsigned int r) {
int upper = RARRAY_LEN(ary) - 1;
int lower = 0;
int idx = 0;
while (lower <= upper) {
idx = (lower + upper) / 2;
VALUE continuumValue = RARRAY_PTR(ary)[idx];
unsigned int l = NUM2UINT(continuumValue);
if (l == r) {
return idx;
}
else if (l > r) {
upper = idx - 1;
}
else {
lower = idx + 1;
}
}
return upper;
}
EOM
end
rescue Exception => e
# Find the closest index in HashRing with value <= the given value
def binary_search(ary, value, &block)
upper = ary.size - 1
lower = 0
idx = 0
while(lower <= upper) do
idx = (lower + upper) / 2
comp = ary[idx] <=> value
if comp == 0
return idx
elsif comp > 0
upper = idx - 1
else
lower = idx + 1
end
end
return upper
end
end
end end
end end
# ring = HashRing.new ['server1', 'server2', 'server3'] # ring = HashRing.new ['server1', 'server2', 'server3']

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,131 @@
##
# This class represents a redis server instance.
class Server
##
# The amount of time to wait before attempting to re-establish a
# connection with a server that is marked dead.
RETRY_DELAY = 30.0
##
# The host the redis server is running on.
attr_reader :host
##
# The port the redis server is listening on.
attr_reader :port
##
#
attr_reader :replica
##
# The time of next retry if the connection is dead.
attr_reader :retry
##
# A text status string describing the state of the server.
attr_reader :status
##
# Create a new Redis::Server object for the redis instance
# listening on the given host and port.
def initialize(host, port = DEFAULT_PORT)
raise ArgumentError, "No host specified" if host.nil? or host.empty?
raise ArgumentError, "No port specified" if port.nil? or port.to_i.zero?
@host = host
@port = port.to_i
@sock = nil
@retry = nil
@status = 'NOT CONNECTED'
@timeout = 1
end
##
# Return a string representation of the server object.
def inspect
"<Redis::Server: %s:%d (%s)>" % [@host, @port, @status]
end
##
# Try to connect to the redis server targeted by this object.
# Returns the connected socket object on success or nil on failure.
def socket
return @sock if @sock and not @sock.closed?
@sock = nil
# If the host was dead, don't retry for a while.
return if @retry and @retry > Time.now
# Attempt to connect if not already connected.
begin
@sock = connect_to(@host, @port, @timeout)
@sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
@retry = nil
@status = 'CONNECTED'
rescue Errno::EPIPE, Errno::ECONNREFUSED => e
puts "Socket died... socket: #{@sock.inspect}\n" if $debug
@sock.close
retry
rescue SocketError, SystemCallError, IOError => err
puts "Unable to open socket: #{err.class.name}, #{err.message}" if $debug
mark_dead err
end
return @sock
end
def connect_to(host, port, timeout=nil)
addrs = Socket.getaddrinfo('localhost', nil)
addr = addrs.detect { |ad| ad[0] == 'AF_INET' }
sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
#addr = Socket.getaddrinfo(host, nil)
#sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
if timeout
secs = Integer(timeout)
usecs = Integer((timeout - secs) * 1_000_000)
optval = [secs, usecs].pack("l_2")
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
end
sock.connect(Socket.pack_sockaddr_in('6379', addr[3]))
sock
end
##
# Close the connection to the redis server targeted by this
# object. The server is not considered dead.
def close
@sock.close if @sock && !@sock.closed?
@sock = nil
@retry = nil
@status = "NOT CONNECTED"
end
##
# Mark the server as dead and close its socket.
def mark_dead(error)
@sock.close if @sock && !@sock.closed?
@sock = nil
@retry = Time.now #+ RETRY_DELAY
reason = "#{error.class.name}: #{error.message}"
@status = sprintf "%s:%s DEAD (%s), will retry at %s", @host, @port, reason, @retry
puts @status
end
end

View File

@ -0,0 +1,22 @@
require 'rubygems'
require 'ruby-prof'
require "#{File.dirname(__FILE__)}/lib/redis"
mode = ARGV.shift || 'process_time'
n = (ARGV.shift || 200).to_i
r = Redis.new
RubyProf.measure_mode = RubyProf.const_get(mode.upcase)
RubyProf.start
n.times do |i|
key = "foo#{i}"
r[key] = key * 10
r[key]
end
results = RubyProf.stop
File.open("profile.#{mode}", 'w') do |out|
RubyProf::CallTreePrinter.new(results).print(out)
end

View File

@ -12,7 +12,7 @@ class Foo
end end
describe "redis" do describe "redis" do
before do before(:each) do
@r = Redis.new @r = Redis.new
@r.select_db(15) # use database 15 for testing so we dont accidentally step on you real data @r.select_db(15) # use database 15 for testing so we dont accidentally step on you real data
@r['foo'] = 'bar' @r['foo'] = 'bar'
@ -20,15 +20,9 @@ describe "redis" do
after do after do
@r.keys('*').each {|k| @r.delete k } @r.keys('*').each {|k| @r.delete k }
@r.quit
end end
it "should properly marshall objects" do
class MyFail; def fail; 'it will' end; end
@r['fail'] = MyFail.new
@r['fail'].fail.should == 'it will'
end
it "should be able to GET a key" do it "should be able to GET a key" do
@r['foo'].should == 'bar' @r['foo'].should == 'bar'
@ -45,14 +39,14 @@ describe "redis" do
@r.set_unless_exists 'foo', 'bar' @r.set_unless_exists 'foo', 'bar'
@r['foo'].should == 'nik' @r['foo'].should == 'nik'
end end
#
it "should be able to INCR(increment) a key" do it "should be able to INCR(increment) a key" do
@r.delete('counter') @r.delete('counter')
@r.incr('counter').should == 1 @r.incr('counter').should == 1
@r.incr('counter').should == 2 @r.incr('counter').should == 2
@r.incr('counter').should == 3 @r.incr('counter').should == 3
end end
#
it "should be able to DECR(decrement) a key" do it "should be able to DECR(decrement) a key" do
@r.delete('counter') @r.delete('counter')
@r.incr('counter').should == 1 @r.incr('counter').should == 1
@ -62,11 +56,11 @@ describe "redis" do
@r.decr('counter').should == 1 @r.decr('counter').should == 1
@r.decr('counter').should == 0 @r.decr('counter').should == 0
end end
#
it "should be able to RANDKEY(return a random key)" do it "should be able to RANDKEY(return a random key)" do
@r.randkey.should_not be_nil @r.randkey.should_not be_nil
end end
#
it "should be able to RENAME a key" do it "should be able to RENAME a key" do
@r.delete 'foo' @r.delete 'foo'
@r.delete 'bar' @r.delete 'bar'
@ -74,23 +68,23 @@ describe "redis" do
@r.rename! 'foo', 'bar' @r.rename! 'foo', 'bar'
@r['bar'].should == 'hi' @r['bar'].should == 'hi'
end end
#
it "should be able to RENAMENX(rename unless the new key already exists) a key" do it "should be able to RENAMENX(rename unless the new key already exists) a key" do
@r.delete 'foo' @r.delete 'foo'
@r.delete 'bar' @r.delete 'bar'
@r['foo'] = 'hi' @r['foo'] = 'hi'
@r['bar'] = 'ohai' @r['bar'] = 'ohai'
lambda {@r.rename 'foo', 'bar'}.should raise_error(RedisError) lambda {@r.rename 'foo', 'bar'}.should raise_error(RedisRenameError)
@r['bar'].should == 'ohai' @r['bar'].should == 'ohai'
end end
#
it "should be able to EXISTS(check if key exists)" do it "should be able to EXISTS(check if key exists)" do
@r['foo'] = 'nik' @r['foo'] = 'nik'
@r.key?('foo').should be_true @r.key?('foo').should be_true
@r.delete 'foo' @r.delete 'foo'
@r.key?('foo').should be_false @r.key?('foo').should be_false
end end
#
it "should be able to KEYS(glob for keys)" do it "should be able to KEYS(glob for keys)" do
@r.keys("f*").each do |key| @r.keys("f*").each do |key|
@r.delete key @r.delete key
@ -100,14 +94,14 @@ describe "redis" do
@r['foo'] = 'qux' @r['foo'] = 'qux'
@r.keys("f*").sort.should == ['f','fo', 'foo'].sort @r.keys("f*").sort.should == ['f','fo', 'foo'].sort
end end
#
it "should be able to check the TYPE of a key" do it "should be able to check the TYPE of a key" do
@r['foo'] = 'nik' @r['foo'] = 'nik'
@r.type?('foo').should == "string" @r.type?('foo').should == "string"
@r.delete 'foo' @r.delete 'foo'
@r.type?('foo').should == "none" @r.type?('foo').should == "none"
end end
#
it "should be able to push to the head of a list" do it "should be able to push to the head of a list" do
@r.push_head "list", 'hello' @r.push_head "list", 'hello'
@r.push_head "list", 42 @r.push_head "list", 42
@ -116,14 +110,14 @@ describe "redis" do
@r.pop_head('list').should == '42' @r.pop_head('list').should == '42'
@r.delete('list') @r.delete('list')
end end
#
it "should be able to push to the tail of a list" do it "should be able to push to the tail of a list" do
@r.push_tail "list", 'hello' @r.push_tail "list", 'hello'
@r.type?('list').should == "list" @r.type?('list').should == "list"
@r.list_length('list').should == 1 @r.list_length('list').should == 1
@r.delete('list') @r.delete('list')
end end
#
it "should be able to pop the tail of a list" do it "should be able to pop the tail of a list" do
@r.push_tail "list", 'hello' @r.push_tail "list", 'hello'
@r.push_tail "list", 'goodbye' @r.push_tail "list", 'goodbye'
@ -132,7 +126,7 @@ describe "redis" do
@r.pop_tail('list').should == 'goodbye' @r.pop_tail('list').should == 'goodbye'
@r.delete('list') @r.delete('list')
end end
#
it "should be able to pop the head of a list" do it "should be able to pop the head of a list" do
@r.push_tail "list", 'hello' @r.push_tail "list", 'hello'
@r.push_tail "list", 'goodbye' @r.push_tail "list", 'goodbye'
@ -141,7 +135,7 @@ describe "redis" do
@r.pop_head('list').should == 'hello' @r.pop_head('list').should == 'hello'
@r.delete('list') @r.delete('list')
end end
#
it "should be able to get the length of a list" do it "should be able to get the length of a list" do
@r.push_tail "list", 'hello' @r.push_tail "list", 'hello'
@r.push_tail "list", 'goodbye' @r.push_tail "list", 'goodbye'
@ -149,7 +143,7 @@ describe "redis" do
@r.list_length('list').should == 2 @r.list_length('list').should == 2
@r.delete('list') @r.delete('list')
end end
#
it "should be able to get a range of values from a list" do it "should be able to get a range of values from a list" do
@r.push_tail "list", 'hello' @r.push_tail "list", 'hello'
@r.push_tail "list", 'goodbye' @r.push_tail "list", 'goodbye'
@ -161,7 +155,7 @@ describe "redis" do
@r.list_range('list', 2, -1).should == ['1', '2', '3'] @r.list_range('list', 2, -1).should == ['1', '2', '3']
@r.delete('list') @r.delete('list')
end end
#
it "should be able to trim a list" do it "should be able to trim a list" do
@r.push_tail "list", 'hello' @r.push_tail "list", 'hello'
@r.push_tail "list", 'goodbye' @r.push_tail "list", 'goodbye'
@ -175,7 +169,7 @@ describe "redis" do
@r.list_range('list', 0, -1).should == ['hello', 'goodbye'] @r.list_range('list', 0, -1).should == ['hello', 'goodbye']
@r.delete('list') @r.delete('list')
end end
#
it "should be able to get a value by indexing into a list" do it "should be able to get a value by indexing into a list" do
@r.push_tail "list", 'hello' @r.push_tail "list", 'hello'
@r.push_tail "list", 'goodbye' @r.push_tail "list", 'goodbye'
@ -184,7 +178,7 @@ describe "redis" do
@r.list_index('list', 1).should == 'goodbye' @r.list_index('list', 1).should == 'goodbye'
@r.delete('list') @r.delete('list')
end end
#
it "should be able to set a value by indexing into a list" do it "should be able to set a value by indexing into a list" do
@r.push_tail "list", 'hello' @r.push_tail "list", 'hello'
@r.push_tail "list", 'hello' @r.push_tail "list", 'hello'
@ -194,7 +188,17 @@ describe "redis" do
@r.list_index('list', 1).should == 'goodbye' @r.list_index('list', 1).should == 'goodbye'
@r.delete('list') @r.delete('list')
end end
#
it "should be able to remove values from a list LREM" do
@r.push_tail "list", 'hello'
@r.push_tail "list", 'goodbye'
@r.type?('list').should == "list"
@r.list_length('list').should == 2
@r.list_rm('list', 1, 'hello').should == 1
@r.list_range('list', 0, -1).should == ['goodbye']
@r.delete('list')
end
#
it "should be able add members to a set" do it "should be able add members to a set" do
@r.set_add "set", 'key1' @r.set_add "set", 'key1'
@r.set_add "set", 'key2' @r.set_add "set", 'key2'
@ -203,7 +207,7 @@ describe "redis" do
@r.set_members('set').sort.should == ['key1', 'key2'].sort @r.set_members('set').sort.should == ['key1', 'key2'].sort
@r.delete('set') @r.delete('set')
end end
#
it "should be able delete members to a set" do it "should be able delete members to a set" do
@r.set_add "set", 'key1' @r.set_add "set", 'key1'
@r.set_add "set", 'key2' @r.set_add "set", 'key2'
@ -215,7 +219,7 @@ describe "redis" do
@r.set_members('set').should == Set.new(['key2']) @r.set_members('set').should == Set.new(['key2'])
@r.delete('set') @r.delete('set')
end end
#
it "should be able count the members of a set" do it "should be able count the members of a set" do
@r.set_add "set", 'key1' @r.set_add "set", 'key1'
@r.set_add "set", 'key2' @r.set_add "set", 'key2'
@ -223,7 +227,7 @@ describe "redis" do
@r.set_count('set').should == 2 @r.set_count('set').should == 2
@r.delete('set') @r.delete('set')
end end
#
it "should be able test for set membership" do it "should be able test for set membership" do
@r.set_add "set", 'key1' @r.set_add "set", 'key1'
@r.set_add "set", 'key2' @r.set_add "set", 'key2'
@ -234,7 +238,7 @@ describe "redis" do
@r.set_member?('set', 'notthere').should be_false @r.set_member?('set', 'notthere').should be_false
@r.delete('set') @r.delete('set')
end end
#
it "should be able to do set intersection" do it "should be able to do set intersection" do
@r.set_add "set", 'key1' @r.set_add "set", 'key1'
@r.set_add "set", 'key2' @r.set_add "set", 'key2'
@ -242,7 +246,7 @@ describe "redis" do
@r.set_intersect('set', 'set2').should == Set.new(['key2']) @r.set_intersect('set', 'set2').should == Set.new(['key2'])
@r.delete('set') @r.delete('set')
end end
#
it "should be able to do set intersection and store the results in a key" do it "should be able to do set intersection and store the results in a key" do
@r.set_add "set", 'key1' @r.set_add "set", 'key1'
@r.set_add "set", 'key2' @r.set_add "set", 'key2'
@ -251,7 +255,7 @@ describe "redis" do
@r.set_members('newone').should == Set.new(['key2']) @r.set_members('newone').should == Set.new(['key2'])
@r.delete('set') @r.delete('set')
end end
#
it "should be able to do crazy SORT queries" do it "should be able to do crazy SORT queries" do
@r['dog_1'] = 'louie' @r['dog_1'] = 'louie'
@r.push_tail 'dogs', 1 @r.push_tail 'dogs', 1
@ -264,4 +268,50 @@ describe "redis" do
@r.sort('dogs', :get => 'dog_*', :limit => [0,1]).should == ['louie'] @r.sort('dogs', :get => 'dog_*', :limit => [0,1]).should == ['louie']
@r.sort('dogs', :get => 'dog_*', :limit => [0,1], :order => 'desc alpha').should == ['taj'] @r.sort('dogs', :get => 'dog_*', :limit => [0,1], :order => 'desc alpha').should == ['taj']
end end
#
it "should provide info" do
[:last_save_time, :redis_version, :total_connections_received, :connected_clients, :total_commands_processed, :connected_slaves, :uptime_in_seconds, :used_memory, :uptime_in_days, :changes_since_last_save].each do |x|
@r.info.keys.should include(x)
end
end
#
it "should be able to flush the database" do
@r['key1'] = 'keyone'
@r['key2'] = 'keytwo'
@r.keys('*').sort.should == ['foo', 'key1', 'key2'] #foo from before
@r.flush_db
@r.keys('*').should == []
end
#
it "should be able to provide the last save time" do
savetime = @r.last_save
Time.at(savetime).class.should == Time
Time.at(savetime).should <= Time.now
end
it "should be able to MGET keys" do
@r['foo'] = 1000
@r['bar'] = 2000
@r.mget('foo', 'bar').should == ['1000', '2000']
@r.mget('foo', 'bar', 'baz').should == ['1000', '2000', nil]
end
it "should bgsave" do
lambda {@r.bgsave}.should_not raise_error(RedisError)
end
it "should handle multiple servers" do
require 'dist_redis'
@r = DistRedis.new('localhost:6379', '127.0.0.1:6379')
@r.select_db(15) # use database 15 for testing so we dont accidentally step on you real data
100.times do |idx|
@r[idx] = "foo#{idx}"
end
100.times do |idx|
@r[idx].should == "foo#{idx}"
end
end
end end

View File

@ -0,0 +1,16 @@
require 'benchmark'
require "#{File.dirname(__FILE__)}/lib/redis"
r = Redis.new
n = (ARGV.shift || 20000).to_i
elapsed = Benchmark.realtime do
# n sets, n gets
n.times do |i|
key = "foo#{i}"
r[key] = key * 10
r[key]
end
end
puts '%.2f Kops' % (2 * n / 1000 / elapsed)

View File

@ -52,6 +52,12 @@ namespace :redis do
task :stop do task :stop do
RedisRunner.stop RedisRunner.stop
end end
desc 'Restart redis'
task :restart do
RedisRunner.stop
RedisRunner.start
end
desc 'Attach to redis dtach socket' desc 'Attach to redis dtach socket'
task :attach do task :attach do
@ -60,9 +66,12 @@ namespace :redis do
desc 'Install the lastest redis from svn' desc 'Install the lastest redis from svn'
task :install => [:about, :download, :make] do task :install => [:about, :download, :make] do
sh 'sudo cp /tmp/redis/redis-server /usr/bin/' %w(redis-benchmark redis-cli redis-server).each do |bin|
sh 'sudo cp /tmp/redis/redis-benchmark /usr/bin/' sh "sudo cp /tmp/redis/#{bin} /usr/bin/"
puts 'Installed redis-server and redis-benchmark to /usr/bin/' end
puts "Installed redis-benchmark, redis-cli and redis-server to /usr/bin/"
unless File.exists?('/etc/redis.conf') unless File.exists?('/etc/redis.conf')
sh 'sudo cp /tmp/redis/redis.conf /etc/' sh 'sudo cp /tmp/redis/redis.conf /etc/'
puts "Installed redis.conf to /etc/ \n You should look at this file!" puts "Installed redis.conf to /etc/ \n You should look at this file!"
@ -76,8 +85,9 @@ namespace :redis do
desc "Download package" desc "Download package"
task :download do task :download do
system 'svn checkout http://redis.googlecode.com/svn/trunk /tmp/redis' unless File.exists?(RedisRunner.redisdir) sh 'rm -rf /tmp/redis/' if File.exists?("#{RedisRunner.redisdir}/.svn")
system 'svn up' if File.exists?("#{RedisRunner.redisdir}/.svn") sh 'git clone git://github.com/antirez/redis.git /tmp/redis' unless File.exists?(RedisRunner.redisdir)
sh "cd #{RedisRunner.redisdir} && git pull" if File.exists?("#{RedisRunner.redisdir}/.git")
end end
end end