gibson 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/lib/gibson/connection.rb +41 -10
- data/lib/gibson/gibson.rb +26 -0
- data/lib/gibson/protocol.rb +17 -3
- data/lib/gibson/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NDkwOWNiM2EzZDcwYjAxMWNhMzUyNjYwODA0OWVmZjc0YzFlMjgxNQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NmI3MzIzMWNlOTgyMDY2MWM5NWM1YWE5NjkyNmNiZGI2NjZkNDc1Mg==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
Mjc3OGE1ZWI5YjYyMDllMGU1OTAyN2ZlZWRiMjQwYzViOWY2ZTQ5MmFlYTlh
|
10
|
+
OTNjYzFiZjkwZDI2ODg2NGMwZGY0N2QxOWJjODUyZjU0Yzk4ZWMxZDFiZjAw
|
11
|
+
MDVjMDk0ZGViOWMzOWJhMjI0YzQzODEzMDcwYWI5OTIyODYyMzU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MGJkYTVhNGM5NDEyZTZjZGJjN2I0YmExMGI4ODcyMGM0NTdhN2QxYmVkN2Uy
|
14
|
+
ZDQ2Mzc1NzlhZDQzNDA4NmI4YzdjNWY0Njg0NzRiNWQ1ZGI0MjMzMGJlY2Ji
|
15
|
+
ZWY1N2E1YWRkYzkwMWQ2ZDA5MGJlMDQzMTBlYmJkMmI0YTg0ZmI=
|
data/lib/gibson/connection.rb
CHANGED
@@ -26,55 +26,86 @@
|
|
26
26
|
# POSSIBILITY OF SUCH DAMAGE.
|
27
27
|
module Gibson
|
28
28
|
require 'socket'
|
29
|
-
|
29
|
+
require 'timeout'
|
30
|
+
|
30
31
|
class Connection
|
32
|
+
##
|
33
|
+
# Connection default options.
|
31
34
|
DEFAULTS = {
|
35
|
+
# The UNIX socket path, if this option is set a UNIX socket connection will be used.
|
32
36
|
:socket => '/var/run/gibson.sock',
|
37
|
+
# The ip address to connect to, if this option is set a TCP socket connection will be used.
|
33
38
|
:address => nil,
|
39
|
+
# The tcp port to connect to.
|
34
40
|
:port => 10128,
|
41
|
+
# The connection and I/O timeout in milliseconds.
|
35
42
|
:timeout => 100,
|
43
|
+
# If a TCP connection will be used, set this to true to use the SO_KEEPALIVE flag on the socket.
|
36
44
|
:keepalive => false
|
37
45
|
}
|
38
46
|
|
47
|
+
##
|
48
|
+
# Create a new Connection instance with custom options.
|
49
|
+
# If no options are specified, Connection.DEFAULTS will be used.
|
50
|
+
# For instance:
|
51
|
+
# Gibson::Client.new # will create a connection to the default /var/run/gibson.sock UNIX socket.
|
52
|
+
# Gibson::Client.new :address => '127.0.0.1' # will connect to localhost:10128
|
39
53
|
def initialize(opts = {})
|
40
54
|
@sock = nil
|
41
55
|
@connected = false
|
42
56
|
@options = DEFAULTS.merge(opts)
|
43
57
|
end
|
44
58
|
|
59
|
+
##
|
60
|
+
# Return true if connection is enstablished, otherwise false.
|
45
61
|
def connected?
|
46
62
|
@connected
|
47
63
|
end
|
48
64
|
|
65
|
+
##
|
66
|
+
# Attempt a connection with the specified options until @options[:timeout]
|
67
|
+
# is reached.
|
49
68
|
def connect
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
69
|
+
Timeout.timeout(@options[:timeout]) do
|
70
|
+
if @options[:address] != nil
|
71
|
+
@sock = TCPSocket.new( @options[:address], @options[:port] )
|
72
|
+
@sock.setsockopt( Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true )
|
73
|
+
@sock.setsockopt( Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true ) if @options[:keepalive]
|
74
|
+
else
|
75
|
+
@sock = UNIXSocket.open( @options[:socket] )
|
76
|
+
end
|
57
77
|
|
58
|
-
|
78
|
+
@connected = true
|
79
|
+
end
|
59
80
|
end
|
60
81
|
|
82
|
+
##
|
83
|
+
# Close the connection.
|
61
84
|
def close
|
62
|
-
@sock.close
|
85
|
+
@sock.close if connected?
|
63
86
|
end
|
64
87
|
|
88
|
+
##
|
89
|
+
# Wait for the socket to be in a writable state for @options[:timeout] milliseconds.
|
65
90
|
def wait_writable
|
66
91
|
IO.select(nil, [@sock], nil, @options[:timeout] ) || raise(Timeout::Error, "IO timeout")
|
67
92
|
end
|
68
93
|
|
94
|
+
##
|
95
|
+
# Wait for the socket to be in a readable state for @options[:timeout] milliseconds.
|
69
96
|
def wait_readable
|
70
97
|
IO.select( [@sock], nil, nil, @options[:timeout] ) || raise(Timeout::Error, "IO timeout")
|
71
98
|
end
|
72
99
|
|
100
|
+
##
|
101
|
+
# Write data to the socket.
|
73
102
|
def write(data)
|
74
103
|
wait_writable
|
75
104
|
@sock.write data
|
76
105
|
end
|
77
106
|
|
107
|
+
##
|
108
|
+
# Read specified amount of data from the socket.
|
78
109
|
def read(n)
|
79
110
|
wait_readable
|
80
111
|
@sock.recv n
|
data/lib/gibson/gibson.rb
CHANGED
@@ -29,16 +29,23 @@ require 'gibson/connection'
|
|
29
29
|
|
30
30
|
module Gibson
|
31
31
|
class Client
|
32
|
+
##
|
33
|
+
# Create a new Gibson::Client instance, the options are passed to
|
34
|
+
# Gibson::Connection initialize method.
|
32
35
|
def initialize(opts = {})
|
33
36
|
@connection = nil
|
34
37
|
@options = opts
|
35
38
|
end
|
36
39
|
|
40
|
+
##
|
41
|
+
# Create the connection.
|
37
42
|
def connect
|
38
43
|
@connection = Connection.new( @options )
|
39
44
|
@connection.connect
|
40
45
|
end
|
41
46
|
|
47
|
+
##
|
48
|
+
# Decode a REPL_VAL reply.
|
42
49
|
def decode_val( encoding, size, data )
|
43
50
|
# plain string
|
44
51
|
if encoding == Protocol::ENCODINGS[:plain]
|
@@ -56,6 +63,8 @@ module Gibson
|
|
56
63
|
end
|
57
64
|
end
|
58
65
|
|
66
|
+
##
|
67
|
+
# Decode a REPL_KVAL reply.
|
59
68
|
def decode_kval( data, size )
|
60
69
|
left = size - 4
|
61
70
|
count, data = data.unpack( 'L<a' + left.to_s )
|
@@ -83,6 +92,8 @@ module Gibson
|
|
83
92
|
obj
|
84
93
|
end
|
85
94
|
|
95
|
+
##
|
96
|
+
# Reply decoding wrapper.
|
86
97
|
def decode( code, encoding, size, data )
|
87
98
|
if code == Protocol::REPLIES[:val]
|
88
99
|
decode_val encoding, size, data
|
@@ -101,6 +112,10 @@ module Gibson
|
|
101
112
|
end
|
102
113
|
end
|
103
114
|
|
115
|
+
##
|
116
|
+
# Send a query to the server given its opcode and arguments payload.
|
117
|
+
# Return the decoded data, or raise one of the RuntimeErrors defined
|
118
|
+
# inside Gibson::Protocol.
|
104
119
|
def query( opcode, payload = '' )
|
105
120
|
connect if @connection == nil or not @connection.connected?
|
106
121
|
|
@@ -115,10 +130,21 @@ module Gibson
|
|
115
130
|
decode code, encoding, size, data
|
116
131
|
end
|
117
132
|
|
133
|
+
##
|
134
|
+
# This method will be called for every undefined method call
|
135
|
+
# of Gibson::Client mapping the method to its opcode and creating
|
136
|
+
# its argument payload.
|
137
|
+
# For instance a call to:
|
138
|
+
# client = Gibson::Client.new
|
139
|
+
# client.set 0, 'foo', 'bar'
|
140
|
+
# Will be executed as:
|
141
|
+
# client.query Protocol::COMMANDS[:set], '0 foo bar'
|
118
142
|
def method_missing(name, *arguments)
|
119
143
|
if Protocol::COMMANDS.has_key? name
|
120
144
|
query Protocol::COMMANDS[name], arguments.join(' ')
|
121
145
|
end
|
122
146
|
end
|
147
|
+
|
148
|
+
private :decode_val, :decode_kval, :decode
|
123
149
|
end
|
124
150
|
end
|
data/lib/gibson/protocol.rb
CHANGED
@@ -25,13 +25,19 @@
|
|
25
25
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
26
26
|
# POSSIBILITY OF SUCH DAMAGE.
|
27
27
|
module Gibson
|
28
|
+
# A generic protocol error.
|
28
29
|
class GenericError < RuntimeError; end
|
30
|
+
# Key or prefix not found.
|
29
31
|
class NotFoundError < RuntimeError; end
|
32
|
+
# Specified value is not a number.
|
30
33
|
class NaNError < RuntimeError; end
|
34
|
+
# The server is out of memory.
|
31
35
|
class OutOfMemoryError < RuntimeError; end
|
36
|
+
# The object is locked and can't be modified.
|
32
37
|
class LockedError < RuntimeError; end
|
33
38
|
|
34
39
|
class Protocol
|
40
|
+
# Query opcodes.
|
35
41
|
COMMANDS = {
|
36
42
|
:set => 1,
|
37
43
|
:ttl => 2,
|
@@ -57,6 +63,7 @@ module Gibson
|
|
57
63
|
:end => 0xff
|
58
64
|
}
|
59
65
|
|
66
|
+
# Server replies opcodes.
|
60
67
|
REPLIES = {
|
61
68
|
:error => 0, # Generic error
|
62
69
|
:not_found => 1, # Key/Prefix not found
|
@@ -68,6 +75,7 @@ module Gibson
|
|
68
75
|
:kval => 7 # Ok, [ key => value, ... ] follows
|
69
76
|
}
|
70
77
|
|
78
|
+
# Error code to exception map.
|
71
79
|
ERRORS = {
|
72
80
|
0 => GenericError,
|
73
81
|
1 => NotFoundError,
|
@@ -76,12 +84,18 @@ module Gibson
|
|
76
84
|
4 => LockedError
|
77
85
|
}
|
78
86
|
|
87
|
+
# Incoming data encodings.
|
79
88
|
ENCODINGS = {
|
80
|
-
|
81
|
-
:
|
82
|
-
|
89
|
+
# the item is in plain encoding and data points to its buffer
|
90
|
+
:plain => 0x00,
|
91
|
+
# PLAIN but compressed data with lzf
|
92
|
+
:lzf => 0x01,
|
93
|
+
# the item contains a number and data pointer is actually that number
|
94
|
+
:number => 0x02
|
83
95
|
}
|
84
96
|
|
97
|
+
##
|
98
|
+
# Return true if the specified code is an error code, otherwise false.
|
85
99
|
def self.error? (code)
|
86
100
|
code >= REPLIES[:error] && code <= REPLIES[:locked]
|
87
101
|
end
|
data/lib/gibson/version.rb
CHANGED