jodosha (owner)

Revisions

gist: 115000 Download_button fork
public
Public Clone URL: git://gist.github.com/115000.git
Embed All Files: show embed
redis_client.rb #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# RubyRedis is an alternative implementation of Ruby client library written
# by Salvatore Sanfilippo.
#
# The aim of this library is to create an alternative client library that is
# much simpler and does not implement every command explicitly but uses
# method_missing instead.
 
require 'socket'
 
class RedisClient
    BulkCommands = {
        "set"=>true, "setnx"=>true, "rpush"=>true, "lpush"=>true, "lset"=>true,
        "lrem"=>true, "sadd"=>true, "srem"=>true, "sismember"=>true,
        "echo"=>true, "getset"=>true, "smove"=>true
    }
 
    def initialize(opts={})
        opts = {:host => 'localhost', :port => '6379', :db => 0}.merge(opts)
        @host = opts[:host]
        @port = opts[:port]
        @db = opts[:db]
        connect_to_server
    end
 
    def to_s
        "Redis Client connected to #{@host}:#{@port} against DB #{@db}"
    end
 
    def connect_to_server
        @sock = TCPSocket.new(@host, @port, 0)
        call_command(["select",@db]) if @db != 0
    end
 
    def method_missing(*argv)
      method_name = argv.shift.to_s.downcase
      if BulkCommands[method_name]
        self.class.class_eval %{
def #{method_name}(*argv)
begin
argv = argv.flatten
bulk = argv[-1]
argv[-1] = bulk.length
@sock.write("#{method_name} \#\{argv.join(" ")\}\\r\\n\#\{bulk\}\\r\\n")
read_reply
rescue Errno::ECONNRESET
@sock.close
connect_to_server
retry
end
end
}
      else
        self.class.class_eval %{
def #{method_name}(*argv)
begin
@sock.write("#{method_name} \#\{argv.join(" ")\}\\r\\n")
read_reply
rescue Errno::ECONNRESET
@sock.close
connect_to_server
retry
end
end
}
      end
      send method_name, argv
    end
 
    def select(*args)
        raise "SELECT not allowed, use the :db option when creating the object"
    end
 
    def [](key)
        get(key)
    end
 
    def []=(key,value)
        set(key,value)
    end
 
    def read_reply
        line = @sock.gets
        raise Errno::ECONNRESET,"Connection lost" if !line
        case line[0..0]
        when "-"
            raise line.strip
        when "+"
            line[1..-1].strip
        when ":"
            line[1..-1].to_i
        when "$"
            bulklen = line[1..-1].to_i
            return nil if bulklen == -1
            data = @sock.read(bulklen)
            @sock.read(2) # CRLF
            data
        when "*"
            objects = line[1..-1].to_i
            return nil if bulklen == -1
            res = []
            objects.times {
                res << read_reply
            }
            res
        end
    end
    
    private
      def call_command(argv)
          # this wrapper to raw_call_command handle reconnection on socket
          # error. We try to reconnect just one time, otherwise let the error
          # araise.
          begin
              raw_call_command(argv)
          rescue Errno::ECONNRESET
              @sock.close
              connect_to_server
              raw_call_command(argv)
          end
      end
 
      def raw_call_command(argv)
          bulk = nil
          argv[0] = argv[0].to_s.downcase
          if BulkCommands[argv[0]]
              bulk = argv[-1]
              argv[-1] = bulk.length
          end
          @sock.write(argv.join(" ")+"\r\n")
          @sock.write(bulk+"\r\n") if bulk
          read_reply
      end
end