hiredis 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -47
- data/ext/hiredis_ext/connection.c +104 -35
- data/ext/hiredis_ext/extconf.rb +1 -1
- data/ext/hiredis_ext/hiredis_ext.c +4 -2
- data/ext/hiredis_ext/hiredis_ext.h +0 -1
- data/ext/hiredis_ext/reader.c +0 -20
- data/lib/hiredis.rb +2 -10
- data/lib/hiredis/connection.rb +7 -14
- data/lib/hiredis/ext/connection.rb +16 -0
- data/lib/hiredis/ext/reader.rb +2 -0
- data/lib/hiredis/reader.rb +10 -1
- data/lib/hiredis/ruby/connection.rb +113 -0
- data/lib/hiredis/ruby/reader.rb +164 -0
- data/lib/hiredis/version.rb +1 -1
- data/vendor/hiredis/Makefile +4 -5
- data/vendor/hiredis/async.c +2 -0
- data/vendor/hiredis/async.h +3 -3
- data/vendor/hiredis/dict.c +27 -77
- data/vendor/hiredis/dict.h +12 -16
- data/vendor/hiredis/hiredis.c +26 -17
- data/vendor/hiredis/hiredis.h +4 -2
- data/vendor/hiredis/net.c +85 -18
- data/vendor/hiredis/net.h +2 -2
- data/vendor/hiredis/sds.c +129 -4
- data/vendor/hiredis/sds.h +2 -1
- data/vendor/hiredis/test.c +14 -5
- metadata +15 -29
- data/lib/hiredis/em.rb +0 -1
- data/lib/hiredis/em/base.rb +0 -36
- data/lib/hiredis/em/connection.rb +0 -25
data/Rakefile
CHANGED
@@ -1,57 +1,11 @@
|
|
1
1
|
require 'rake'
|
2
|
-
require 'rake/gempackagetask'
|
3
2
|
require 'rake/testtask'
|
4
|
-
|
5
|
-
gem 'rake-compiler', '~> 0.7.1'
|
6
3
|
require "rake/extensiontask"
|
7
4
|
|
8
|
-
$:.unshift File.join(File.dirname(__FILE__), 'lib')
|
9
|
-
require 'hiredis/version'
|
10
|
-
|
11
|
-
GEM = 'hiredis'
|
12
|
-
GEM_VERSION = Hiredis::VERSION
|
13
|
-
AUTHORS = ['Pieter Noordhuis']
|
14
|
-
EMAIL = "pcnoordhuis@gmail.com"
|
15
|
-
HOMEPAGE = "http://github.com/pietern/hiredis-rb"
|
16
|
-
SUMMARY = "Ruby extension that wraps Hiredis (blocking connection and reply parsing)"
|
17
|
-
|
18
|
-
spec = Gem::Specification.new do |s|
|
19
|
-
s.name = GEM
|
20
|
-
s.version = GEM_VERSION
|
21
|
-
s.platform = Gem::Platform::RUBY
|
22
|
-
s.has_rdoc = true
|
23
|
-
s.extra_rdoc_files = ["COPYING"]
|
24
|
-
s.summary = SUMMARY
|
25
|
-
s.description = s.summary
|
26
|
-
s.authors = AUTHORS
|
27
|
-
s.email = EMAIL
|
28
|
-
s.homepage = HOMEPAGE
|
29
|
-
s.require_path = 'lib'
|
30
|
-
s.extensions = FileList["ext/**/extconf.rb"]
|
31
|
-
|
32
|
-
ext_files = Dir.glob("ext/**/*.{rb,c,h}")
|
33
|
-
lib_files = Dir.glob("lib/**/*.rb")
|
34
|
-
hiredis_files = Dir.glob("vendor/hiredis/*.{c,h}") -
|
35
|
-
Dir.glob("vendor/hiredis/example*") +
|
36
|
-
Dir.glob("vendor/hiredis/COPYING") +
|
37
|
-
Dir.glob("vendor/hiredis/Makefile")
|
38
|
-
s.files = %w(COPYING Rakefile) + ext_files + lib_files + hiredis_files
|
39
|
-
|
40
|
-
s.add_runtime_dependency "rake-compiler", "~> 0.7.1"
|
41
|
-
s.add_runtime_dependency "redis", "~> 2.1.1"
|
42
|
-
end
|
43
|
-
|
44
|
-
desc "create a gemspec file"
|
45
|
-
task :gemspec do
|
46
|
-
File.open("#{GEM}.gemspec", "w") do |file|
|
47
|
-
file.puts spec.to_ruby
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
5
|
Rake::ExtensionTask.new('hiredis_ext') do |task|
|
52
6
|
# Pass --with-foo-config args to extconf.rb
|
53
7
|
task.config_options = ARGV[1..-1]
|
54
|
-
task.lib_dir = File.join(*['lib', 'hiredis'])
|
8
|
+
task.lib_dir = File.join(*['lib', 'hiredis', 'ext'])
|
55
9
|
end
|
56
10
|
|
57
11
|
namespace :hiredis do
|
@@ -28,24 +28,43 @@ static void parent_context_free(redisParentContext *pc) {
|
|
28
28
|
free(pc);
|
29
29
|
}
|
30
30
|
|
31
|
+
static void parent_context_raise(redisParentContext *pc) {
|
32
|
+
int err;
|
33
|
+
char errstr[1024];
|
34
|
+
|
35
|
+
/* Copy error and free context */
|
36
|
+
err = pc->context->err;
|
37
|
+
snprintf(errstr,sizeof(errstr),"%s",pc->context->errstr);
|
38
|
+
parent_context_try_free(pc);
|
39
|
+
|
40
|
+
switch(err) {
|
41
|
+
case REDIS_ERR_IO:
|
42
|
+
/* Raise native Ruby I/O error */
|
43
|
+
rb_sys_fail(0);
|
44
|
+
break;
|
45
|
+
case REDIS_ERR_EOF:
|
46
|
+
/* Raise our own EOF error */
|
47
|
+
rb_raise(error_eof,"%s",errstr);
|
48
|
+
break;
|
49
|
+
default:
|
50
|
+
/* Raise something else */
|
51
|
+
rb_raise(rb_eRuntimeError,"%s",errstr);
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
31
55
|
static VALUE connection_parent_context_alloc(VALUE klass) {
|
32
56
|
redisParentContext *pc = malloc(sizeof(*pc));
|
33
57
|
pc->context = NULL;
|
34
58
|
return Data_Wrap_Struct(klass, parent_context_mark, parent_context_free, pc);
|
35
59
|
}
|
36
60
|
|
37
|
-
static VALUE
|
61
|
+
static VALUE connection_generic_connect(VALUE self, redisContext *c) {
|
38
62
|
redisParentContext *pc;
|
39
|
-
redisContext *c;
|
40
|
-
char *host = StringValuePtr(_host);
|
41
|
-
int port = NUM2INT(_port);
|
42
63
|
int err;
|
43
64
|
char errstr[1024];
|
44
65
|
|
45
66
|
Data_Get_Struct(self,redisParentContext,pc);
|
46
|
-
parent_context_try_free(pc);
|
47
67
|
|
48
|
-
c = redisConnect(host,port);
|
49
68
|
if (c->err) {
|
50
69
|
/* Copy error and free context */
|
51
70
|
err = c->err;
|
@@ -66,6 +85,79 @@ static VALUE connection_connect(VALUE self, VALUE _host, VALUE _port) {
|
|
66
85
|
return Qnil;
|
67
86
|
}
|
68
87
|
|
88
|
+
static struct timeval __timeout_from_robj(VALUE usecs) {
|
89
|
+
int s = NUM2INT(usecs)/1000000;
|
90
|
+
int us = NUM2INT(usecs)-(s*1000000);
|
91
|
+
struct timeval timeout = { s, us };
|
92
|
+
return timeout;
|
93
|
+
}
|
94
|
+
|
95
|
+
static VALUE connection_connect(int argc, VALUE *argv, VALUE self) {
|
96
|
+
redisParentContext *pc;
|
97
|
+
redisContext *c;
|
98
|
+
VALUE *_host = NULL;
|
99
|
+
VALUE *_port = NULL;
|
100
|
+
VALUE *_timeout = NULL;
|
101
|
+
char *host;
|
102
|
+
int port;
|
103
|
+
struct timeval timeout;
|
104
|
+
|
105
|
+
if (argc == 2 || argc == 3) {
|
106
|
+
_host = &argv[0];
|
107
|
+
_port = &argv[1];
|
108
|
+
if (argc == 3)
|
109
|
+
_timeout = &argv[2];
|
110
|
+
} else {
|
111
|
+
rb_raise(rb_eArgError, "invalid number of arguments");
|
112
|
+
return Qnil;
|
113
|
+
}
|
114
|
+
|
115
|
+
Data_Get_Struct(self,redisParentContext,pc);
|
116
|
+
parent_context_try_free(pc);
|
117
|
+
|
118
|
+
host = StringValuePtr(*_host);
|
119
|
+
port = NUM2INT(*_port);
|
120
|
+
if (_timeout != NULL) {
|
121
|
+
timeout = __timeout_from_robj(*_timeout);
|
122
|
+
c = redisConnectWithTimeout(host,port,timeout);
|
123
|
+
} else {
|
124
|
+
c = redisConnect(host,port);
|
125
|
+
}
|
126
|
+
|
127
|
+
return connection_generic_connect(self,c);
|
128
|
+
}
|
129
|
+
|
130
|
+
static VALUE connection_connect_unix(int argc, VALUE *argv, VALUE self) {
|
131
|
+
redisParentContext *pc;
|
132
|
+
redisContext *c;
|
133
|
+
VALUE *_path = NULL;
|
134
|
+
VALUE *_timeout = NULL;
|
135
|
+
char *path;
|
136
|
+
struct timeval timeout;
|
137
|
+
|
138
|
+
if (argc == 1 || argc == 2) {
|
139
|
+
_path = &argv[0];
|
140
|
+
if (argc == 2)
|
141
|
+
_timeout = &argv[1];
|
142
|
+
} else {
|
143
|
+
rb_raise(rb_eArgError, "invalid number of arguments");
|
144
|
+
return Qnil;
|
145
|
+
}
|
146
|
+
|
147
|
+
Data_Get_Struct(self,redisParentContext,pc);
|
148
|
+
parent_context_try_free(pc);
|
149
|
+
|
150
|
+
path = StringValuePtr(*_path);
|
151
|
+
if (_timeout != NULL) {
|
152
|
+
timeout = __timeout_from_robj(*_timeout);
|
153
|
+
c = redisConnectUnixWithTimeout(path,timeout);
|
154
|
+
} else {
|
155
|
+
c = redisConnectUnix(path);
|
156
|
+
}
|
157
|
+
|
158
|
+
return connection_generic_connect(self,c);
|
159
|
+
}
|
160
|
+
|
69
161
|
static VALUE connection_is_connected(VALUE self) {
|
70
162
|
redisParentContext *pc;
|
71
163
|
Data_Get_Struct(self,redisParentContext,pc);
|
@@ -145,33 +237,13 @@ static int __get_reply(redisParentContext *pc, VALUE *reply) {
|
|
145
237
|
static VALUE connection_read(VALUE self) {
|
146
238
|
redisParentContext *pc;
|
147
239
|
VALUE reply;
|
148
|
-
int err;
|
149
|
-
char errstr[1024];
|
150
240
|
|
151
241
|
Data_Get_Struct(self,redisParentContext,pc);
|
152
242
|
if (!pc->context)
|
153
243
|
rb_raise(rb_eRuntimeError, "not connected");
|
154
244
|
|
155
|
-
if (__get_reply(pc,&reply) == -1)
|
156
|
-
|
157
|
-
err = pc->context->err;
|
158
|
-
snprintf(errstr,sizeof(errstr),"%s",pc->context->errstr);
|
159
|
-
parent_context_try_free(pc);
|
160
|
-
|
161
|
-
switch(err) {
|
162
|
-
case REDIS_ERR_IO:
|
163
|
-
/* Raise native Ruby I/O error */
|
164
|
-
rb_sys_fail(0);
|
165
|
-
break;
|
166
|
-
case REDIS_ERR_EOF:
|
167
|
-
/* Raise our own EOF error */
|
168
|
-
rb_raise(error_eof,"%s",errstr);
|
169
|
-
break;
|
170
|
-
default:
|
171
|
-
/* Raise something else */
|
172
|
-
rb_raise(rb_eRuntimeError,"%s",errstr);
|
173
|
-
}
|
174
|
-
}
|
245
|
+
if (__get_reply(pc,&reply) == -1)
|
246
|
+
parent_context_raise(pc);
|
175
247
|
|
176
248
|
return reply;
|
177
249
|
}
|
@@ -181,17 +253,13 @@ static VALUE connection_set_timeout(VALUE self, VALUE usecs) {
|
|
181
253
|
int s = NUM2INT(usecs)/1000000;
|
182
254
|
int us = NUM2INT(usecs)-(s*1000000);
|
183
255
|
struct timeval timeout = { s, us };
|
184
|
-
char errstr[1024];
|
185
256
|
|
186
257
|
Data_Get_Struct(self,redisParentContext,pc);
|
187
258
|
if (!pc->context)
|
188
259
|
rb_raise(rb_eRuntimeError, "not connected");
|
189
260
|
|
190
|
-
if (redisSetTimeout(pc->context,timeout) == REDIS_ERR)
|
191
|
-
|
192
|
-
parent_context_try_free(pc);
|
193
|
-
rb_raise(rb_eRuntimeError,"%s",errstr);
|
194
|
-
}
|
261
|
+
if (redisSetTimeout(pc->context,timeout) == REDIS_ERR)
|
262
|
+
parent_context_raise(pc);
|
195
263
|
|
196
264
|
return usecs;
|
197
265
|
}
|
@@ -202,7 +270,8 @@ VALUE error_eof;
|
|
202
270
|
void InitConnection(VALUE mod) {
|
203
271
|
klass_connection = rb_define_class_under(mod, "Connection", rb_cObject);
|
204
272
|
rb_define_alloc_func(klass_connection, connection_parent_context_alloc);
|
205
|
-
rb_define_method(klass_connection, "connect", connection_connect,
|
273
|
+
rb_define_method(klass_connection, "connect", connection_connect, -1);
|
274
|
+
rb_define_method(klass_connection, "connect_unix", connection_connect_unix, -1);
|
206
275
|
rb_define_method(klass_connection, "connected?", connection_is_connected, 0);
|
207
276
|
rb_define_method(klass_connection, "disconnect", connection_disconnect, 0);
|
208
277
|
rb_define_method(klass_connection, "timeout=", connection_set_timeout, 1);
|
data/ext/hiredis_ext/extconf.rb
CHANGED
@@ -4,8 +4,10 @@
|
|
4
4
|
#include "hiredis_ext.h"
|
5
5
|
|
6
6
|
VALUE mod_hiredis;
|
7
|
+
VALUE mod_ext;
|
7
8
|
void Init_hiredis_ext() {
|
8
9
|
mod_hiredis = rb_define_module("Hiredis");
|
9
|
-
|
10
|
-
|
10
|
+
mod_ext = rb_define_module_under(mod_hiredis,"Ext");
|
11
|
+
InitReader(mod_ext);
|
12
|
+
InitConnection(mod_ext);
|
11
13
|
}
|
@@ -10,7 +10,6 @@ extern VALUE mod_hiredis;
|
|
10
10
|
/* Defined in reader.c */
|
11
11
|
extern redisReplyObjectFunctions redisExtReplyObjectFunctions;
|
12
12
|
extern VALUE klass_reader;
|
13
|
-
extern ID ivar_hiredis_error; /* ivar used to store error reply ("-ERR message") */
|
14
13
|
extern void InitReader(VALUE module);
|
15
14
|
|
16
15
|
/* Defined in connection.c */
|
data/ext/hiredis_ext/reader.c
CHANGED
@@ -6,9 +6,6 @@ static VALUE enc_klass;
|
|
6
6
|
static ID enc_default_external = 0;
|
7
7
|
static ID str_force_encoding = 0;
|
8
8
|
|
9
|
-
/* Singleton method to test if the reply contains an error. */
|
10
|
-
ID ivar_hiredis_error;
|
11
|
-
|
12
9
|
/* Add VALUE to parent when the redisReadTask has a parent.
|
13
10
|
* Note that the parent should always be of type T_ARRAY. */
|
14
11
|
static void *tryParentize(const redisReadTask *task, VALUE v) {
|
@@ -20,10 +17,6 @@ static void *tryParentize(const redisReadTask *task, VALUE v) {
|
|
20
17
|
return (void*)v;
|
21
18
|
}
|
22
19
|
|
23
|
-
static VALUE object_contains_error(VALUE self) {
|
24
|
-
return Qtrue;
|
25
|
-
}
|
26
|
-
|
27
20
|
static void *createStringObject(const redisReadTask *task, char *str, size_t len) {
|
28
21
|
VALUE v, enc;
|
29
22
|
v = rb_str_new(str,len);
|
@@ -36,17 +29,6 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
|
|
36
29
|
|
37
30
|
if (task->type == REDIS_REPLY_ERROR) {
|
38
31
|
v = rb_funcall(rb_eRuntimeError,rb_intern("new"),1,v);
|
39
|
-
rb_ivar_set(v,ivar_hiredis_error,v);
|
40
|
-
|
41
|
-
if (task && task->parent != NULL) {
|
42
|
-
/* Also make the parent respond to this method. Redis currently
|
43
|
-
* only emits nested multi bulks of depth 2, so we don't need
|
44
|
-
* to cascade setting this ivar. Make sure to only set the first
|
45
|
-
* error reply on the parent. */
|
46
|
-
VALUE parent = (VALUE)task->parent->obj;
|
47
|
-
if (!rb_ivar_defined(parent,ivar_hiredis_error))
|
48
|
-
rb_ivar_set(parent,ivar_hiredis_error,v);
|
49
|
-
}
|
50
32
|
}
|
51
33
|
|
52
34
|
return tryParentize(task,v);
|
@@ -93,7 +75,6 @@ static VALUE reader_allocate(VALUE klass) {
|
|
93
75
|
|
94
76
|
static VALUE reader_feed(VALUE klass, VALUE str) {
|
95
77
|
void *reader;
|
96
|
-
unsigned int size;
|
97
78
|
|
98
79
|
if (TYPE(str) != T_STRING)
|
99
80
|
rb_raise(rb_eTypeError, "not a string");
|
@@ -122,7 +103,6 @@ void InitReader(VALUE mod) {
|
|
122
103
|
rb_define_alloc_func(klass_reader, reader_allocate);
|
123
104
|
rb_define_method(klass_reader, "feed", reader_feed, 1);
|
124
105
|
rb_define_method(klass_reader, "gets", reader_gets, 0);
|
125
|
-
ivar_hiredis_error = rb_intern("@__hiredis_error");
|
126
106
|
|
127
107
|
/* If the Encoding class is present, #default_external should be used to
|
128
108
|
* determine the encoding for new strings. The "enc_default_external"
|
data/lib/hiredis.rb
CHANGED
@@ -1,10 +1,2 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
|
4
|
-
# Make redis-rb use the Hiredis Connection class
|
5
|
-
class Redis
|
6
|
-
Connection = ::Hiredis::Connection
|
7
|
-
end
|
8
|
-
|
9
|
-
# Load redis
|
10
|
-
require 'redis'
|
1
|
+
require "hiredis/version"
|
2
|
+
require "hiredis/connection"
|
data/lib/hiredis/connection.rb
CHANGED
@@ -1,17 +1,10 @@
|
|
1
|
-
require 'hiredis/hiredis_ext'
|
2
|
-
|
3
1
|
module Hiredis
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
reply
|
13
|
-
rescue EOFError
|
14
|
-
raise Errno::ECONNRESET
|
15
|
-
end
|
2
|
+
begin
|
3
|
+
require "hiredis/ext/connection"
|
4
|
+
Connection = Ext::Connection
|
5
|
+
rescue LoadError
|
6
|
+
warn "WARNING: could not load hiredis extension, using (slower) pure Ruby implementation."
|
7
|
+
require "hiredis/ruby/connection"
|
8
|
+
Connection = Ruby::Connection
|
16
9
|
end
|
17
10
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "hiredis/ext/hiredis_ext"
|
2
|
+
require "hiredis/version"
|
3
|
+
|
4
|
+
module Hiredis
|
5
|
+
module Ext
|
6
|
+
class Connection
|
7
|
+
# Raise CONNRESET on EOF
|
8
|
+
alias :_read :read
|
9
|
+
def read
|
10
|
+
_read
|
11
|
+
rescue EOFError
|
12
|
+
raise Errno::ECONNRESET
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/hiredis/reader.rb
CHANGED
@@ -1 +1,10 @@
|
|
1
|
-
|
1
|
+
module Hiredis
|
2
|
+
begin
|
3
|
+
require "hiredis/ext/reader"
|
4
|
+
Reader = Ext::Reader
|
5
|
+
rescue LoadError
|
6
|
+
warn "WARNING: could not load hiredis extension, using (slower) pure Ruby implementation."
|
7
|
+
require "hiredis/ruby/reader"
|
8
|
+
Reader = Ruby::Reader
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require "socket"
|
2
|
+
require "timeout"
|
3
|
+
require "hiredis/ruby/reader"
|
4
|
+
require "hiredis/version"
|
5
|
+
|
6
|
+
module Hiredis
|
7
|
+
module Ruby
|
8
|
+
class Connection
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@sock = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def connected?
|
15
|
+
!! @sock
|
16
|
+
end
|
17
|
+
|
18
|
+
def connect(host, port, usecs = 0)
|
19
|
+
@reader = ::Hiredis::Ruby::Reader.new
|
20
|
+
|
21
|
+
begin
|
22
|
+
begin
|
23
|
+
Timeout.timeout(usecs.to_f / 1_000_000) do
|
24
|
+
@sock = TCPSocket.new(host, port)
|
25
|
+
@sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
|
26
|
+
end
|
27
|
+
rescue Timeout::Error
|
28
|
+
raise Errno::ETIMEDOUT
|
29
|
+
end
|
30
|
+
rescue SocketError => error
|
31
|
+
# Raise RuntimeError when host cannot be resolved
|
32
|
+
if error.message.start_with?("getaddrinfo:")
|
33
|
+
raise error.message
|
34
|
+
else
|
35
|
+
raise error
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def connect_unix(path, usecs = 0)
|
41
|
+
@reader = ::Hiredis::Ruby::Reader.new
|
42
|
+
|
43
|
+
begin
|
44
|
+
Timeout.timeout(usecs.to_f / 1_000_000) do
|
45
|
+
@sock = UNIXSocket.new(path)
|
46
|
+
end
|
47
|
+
rescue Timeout::Error
|
48
|
+
raise Errno::ETIMEDOUT
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def disconnect
|
53
|
+
@sock.close
|
54
|
+
rescue
|
55
|
+
ensure
|
56
|
+
@sock = nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def timeout=(usecs)
|
60
|
+
raise "not connected" unless connected?
|
61
|
+
|
62
|
+
secs = Integer(usecs / 1_000_000)
|
63
|
+
usecs = Integer(usecs - (secs * 1_000_000)) # 0 - 999_999
|
64
|
+
|
65
|
+
optval = [secs, usecs].pack("l_2")
|
66
|
+
|
67
|
+
begin
|
68
|
+
@sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
|
69
|
+
@sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
|
70
|
+
rescue Errno::ENOPROTOOPT
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
COMMAND_DELIMITER = "\r\n".freeze
|
75
|
+
|
76
|
+
def write(args)
|
77
|
+
command = []
|
78
|
+
command << "*#{args.size}"
|
79
|
+
args.each do |arg|
|
80
|
+
arg = arg.to_s
|
81
|
+
command << "$#{string_size arg}"
|
82
|
+
command << arg
|
83
|
+
end
|
84
|
+
|
85
|
+
@sock.syswrite(command.join(COMMAND_DELIMITER) + COMMAND_DELIMITER)
|
86
|
+
end
|
87
|
+
|
88
|
+
def read
|
89
|
+
raise "not connected" unless connected?
|
90
|
+
|
91
|
+
while (reply = @reader.gets) == false
|
92
|
+
@reader.feed @sock.sysread(1024)
|
93
|
+
end
|
94
|
+
|
95
|
+
reply
|
96
|
+
rescue EOFError
|
97
|
+
raise Errno::ECONNRESET
|
98
|
+
end
|
99
|
+
|
100
|
+
protected
|
101
|
+
|
102
|
+
if "".respond_to?(:bytesize)
|
103
|
+
def string_size(string)
|
104
|
+
string.to_s.bytesize
|
105
|
+
end
|
106
|
+
else
|
107
|
+
def string_size(string)
|
108
|
+
string.to_s.size
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|