gibson 1.0.2 → 1.0.3
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.
- 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