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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- OGM1OGIwOGQzM2Y5NzhlNWEzYjk5NTBlNGNkNjg5NzcyYWUwNWZkNw==
4
+ NDkwOWNiM2EzZDcwYjAxMWNhMzUyNjYwODA0OWVmZjc0YzFlMjgxNQ==
5
5
  data.tar.gz: !binary |-
6
- N2M4NzlhM2E0MDMzODcxZmUxZTUxYjI5MTBhZThiYjhjMzMxYzczMA==
6
+ NmI3MzIzMWNlOTgyMDY2MWM5NWM1YWE5NjkyNmNiZGI2NjZkNDc1Mg==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- NDk3ZjhjMDRmNTI1M2EyOTAwOGZjMDRmMTZiMDQwMzEzNGQ3MTBkMjc1NTYz
10
- OGEwNWU4NDcxZjYyYzVlYjBjN2QwNzU3MjZhYWIwMDA1OGQxMWQ4YTYwZmIz
11
- NTRkZDgxMzNkZDg4YzdjZGRhMTM1NGQyYjU4MDRhMzU0ODU2OTM=
9
+ Mjc3OGE1ZWI5YjYyMDllMGU1OTAyN2ZlZWRiMjQwYzViOWY2ZTQ5MmFlYTlh
10
+ OTNjYzFiZjkwZDI2ODg2NGMwZGY0N2QxOWJjODUyZjU0Yzk4ZWMxZDFiZjAw
11
+ MDVjMDk0ZGViOWMzOWJhMjI0YzQzODEzMDcwYWI5OTIyODYyMzU=
12
12
  data.tar.gz: !binary |-
13
- OWIzYzY1OGRkNDkyNDViMTY2MTg5NWMzNDNiNzE4NmFjMmIwNmY4YzAwMjRj
14
- NWM2YTkzNTk1MzAxYWRhNzUxZWM1M2JkOWFlMDJlYzI4MDliMmEyN2Y2MGY3
15
- OWI1YzBlN2FlNDQ4NTQxNmVhNWFiMmZjNGNjNDZlOWI2MWEyMjk=
13
+ MGJkYTVhNGM5NDEyZTZjZGJjN2I0YmExMGI4ODcyMGM0NTdhN2QxYmVkN2Uy
14
+ ZDQ2Mzc1NzlhZDQzNDA4NmI4YzdjNWY0Njg0NzRiNWQ1ZGI0MjMzMGJlY2Ji
15
+ ZWY1N2E1YWRkYzkwMWQ2ZDA5MGJlMDQzMTBlYmJkMmI0YTg0ZmI=
@@ -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
- if @options[:address] != nil
51
- @sock = TCPSocket.new( @options[:address], @options[:port] )
52
- @sock.setsockopt( Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true )
53
- @sock.setsockopt( Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true ) if @options[:keepalive]
54
- else
55
- @sock = UNIXSocket.open( @options[:socket] )
56
- end
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
- @connected = true
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
@@ -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
- :plain => 0x00, # the item is in plain encoding and data points to its buffer
81
- :lzf => 0x01, # PLAIN but compressed data with lzf
82
- :number => 0x02 # the item contains a number and data pointer is actually that number
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
@@ -25,6 +25,6 @@
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
- VERSION = '1.0.2'
28
+ VERSION = '1.0.3'
29
29
  end
30
30
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gibson
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simone Margaritelli