redis-client 0.2.1 → 0.3.0
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 +4 -4
- data/.rubocop.yml +1 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +2 -2
- data/README.md +70 -3
- data/Rakefile +2 -1
- data/ext/redis_client/hiredis/extconf.rb +16 -8
- data/lib/redis_client/command_builder.rb +83 -0
- data/lib/redis_client/config.rb +9 -48
- data/lib/redis_client/connection_mixin.rb +38 -0
- data/lib/redis_client/decorator.rb +84 -0
- data/lib/redis_client/hiredis_connection.rb +15 -2
- data/lib/redis_client/pooled.rb +38 -30
- data/lib/redis_client/ruby_connection/buffered_io.rb +153 -0
- data/lib/redis_client/{resp3.rb → ruby_connection/resp3.rb} +0 -26
- data/lib/redis_client/{connection.rb → ruby_connection.rb} +26 -31
- data/lib/redis_client/version.rb +1 -1
- data/lib/redis_client.rb +167 -36
- metadata +8 -5
- data/lib/redis_client/buffered_io.rb +0 -151
@@ -0,0 +1,153 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "io/wait" unless IO.method_defined?(:wait_readable) && IO.method_defined?(:wait_writable)
|
4
|
+
|
5
|
+
class RedisClient
|
6
|
+
class RubyConnection
|
7
|
+
class BufferedIO
|
8
|
+
EOL = "\r\n".b.freeze
|
9
|
+
EOL_SIZE = EOL.bytesize
|
10
|
+
|
11
|
+
attr_accessor :read_timeout, :write_timeout
|
12
|
+
|
13
|
+
def initialize(io, read_timeout:, write_timeout:, chunk_size: 4096)
|
14
|
+
@io = io
|
15
|
+
@buffer = "".b
|
16
|
+
@offset = 0
|
17
|
+
@chunk_size = chunk_size
|
18
|
+
@read_timeout = read_timeout
|
19
|
+
@write_timeout = write_timeout
|
20
|
+
@blocking_reads = false
|
21
|
+
end
|
22
|
+
|
23
|
+
def close
|
24
|
+
@io.to_io.close
|
25
|
+
end
|
26
|
+
|
27
|
+
def closed?
|
28
|
+
@io.to_io.closed?
|
29
|
+
end
|
30
|
+
|
31
|
+
def eof?
|
32
|
+
@offset >= @buffer.bytesize && @io.eof?
|
33
|
+
end
|
34
|
+
|
35
|
+
def with_timeout(new_timeout)
|
36
|
+
new_timeout = false if new_timeout == 0
|
37
|
+
|
38
|
+
previous_read_timeout = @read_timeout
|
39
|
+
previous_blocking_reads = @blocking_reads
|
40
|
+
|
41
|
+
if new_timeout
|
42
|
+
@read_timeout = new_timeout
|
43
|
+
else
|
44
|
+
@blocking_reads = true
|
45
|
+
end
|
46
|
+
|
47
|
+
begin
|
48
|
+
yield
|
49
|
+
ensure
|
50
|
+
@read_timeout = previous_read_timeout
|
51
|
+
@blocking_reads = previous_blocking_reads
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def skip(offset)
|
56
|
+
ensure_remaining(offset)
|
57
|
+
@offset += offset
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def write(string)
|
62
|
+
total = remaining = string.bytesize
|
63
|
+
loop do
|
64
|
+
case bytes_written = @io.write_nonblock(string, exception: false)
|
65
|
+
when Integer
|
66
|
+
remaining -= bytes_written
|
67
|
+
if remaining > 0
|
68
|
+
string = string.byteslice(bytes_written..-1)
|
69
|
+
else
|
70
|
+
return total
|
71
|
+
end
|
72
|
+
when :wait_readable
|
73
|
+
@io.to_io.wait_readable(@read_timeout) or raise ReadTimeoutError
|
74
|
+
when :wait_writable
|
75
|
+
@io.to_io.wait_writable(@write_timeout) or raise WriteTimeoutError
|
76
|
+
when nil
|
77
|
+
raise Errno::ECONNRESET
|
78
|
+
else
|
79
|
+
raise "Unexpected `write_nonblock` return: #{bytes.inspect}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def getbyte
|
85
|
+
ensure_remaining(1)
|
86
|
+
byte = @buffer.getbyte(@offset)
|
87
|
+
@offset += 1
|
88
|
+
byte
|
89
|
+
end
|
90
|
+
|
91
|
+
def gets_chomp
|
92
|
+
fill_buffer(false) if @offset >= @buffer.bytesize
|
93
|
+
until eol_index = @buffer.index(EOL, @offset)
|
94
|
+
fill_buffer(false)
|
95
|
+
end
|
96
|
+
|
97
|
+
line = @buffer.byteslice(@offset, eol_index - @offset)
|
98
|
+
@offset = eol_index + EOL_SIZE
|
99
|
+
line
|
100
|
+
end
|
101
|
+
|
102
|
+
def read_chomp(bytes)
|
103
|
+
ensure_remaining(bytes + EOL_SIZE)
|
104
|
+
str = @buffer.byteslice(@offset, bytes)
|
105
|
+
@offset += bytes + EOL_SIZE
|
106
|
+
str
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def ensure_remaining(bytes)
|
112
|
+
needed = bytes - (@buffer.bytesize - @offset)
|
113
|
+
if needed > 0
|
114
|
+
fill_buffer(true, needed)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def fill_buffer(strict, size = @chunk_size)
|
119
|
+
remaining = size
|
120
|
+
empty_buffer = @offset >= @buffer.bytesize
|
121
|
+
|
122
|
+
loop do
|
123
|
+
bytes = if empty_buffer
|
124
|
+
@io.read_nonblock([remaining, @chunk_size].max, @buffer, exception: false)
|
125
|
+
else
|
126
|
+
@io.read_nonblock([remaining, @chunk_size].max, exception: false)
|
127
|
+
end
|
128
|
+
case bytes
|
129
|
+
when String
|
130
|
+
if empty_buffer
|
131
|
+
@offset = 0
|
132
|
+
empty_buffer = false
|
133
|
+
else
|
134
|
+
@buffer << bytes
|
135
|
+
end
|
136
|
+
remaining -= bytes.bytesize
|
137
|
+
return if !strict || remaining <= 0
|
138
|
+
when :wait_readable
|
139
|
+
unless @io.to_io.wait_readable(@read_timeout)
|
140
|
+
raise ReadTimeoutError unless @blocking_reads
|
141
|
+
end
|
142
|
+
when :wait_writable
|
143
|
+
@io.to_io.wait_writable(@write_timeout) or raise WriteTimeoutError
|
144
|
+
when nil
|
145
|
+
raise Errno::ECONNRESET
|
146
|
+
else
|
147
|
+
raise "Unexpected `read_nonblock` return: #{bytes.inspect}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -58,32 +58,6 @@ class RedisClient
|
|
58
58
|
String.new(encoding: Encoding::BINARY, capacity: 128)
|
59
59
|
end
|
60
60
|
|
61
|
-
def coerce_command!(command)
|
62
|
-
command = command.flat_map do |element|
|
63
|
-
case element
|
64
|
-
when Hash
|
65
|
-
element.flatten
|
66
|
-
when Set
|
67
|
-
element.to_a
|
68
|
-
else
|
69
|
-
element
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
command.map! do |element|
|
74
|
-
case element
|
75
|
-
when String
|
76
|
-
element
|
77
|
-
when Integer, Float, Symbol
|
78
|
-
element.to_s
|
79
|
-
else
|
80
|
-
raise TypeError, "Unsupported command argument type: #{element.class}"
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
command
|
85
|
-
end
|
86
|
-
|
87
61
|
def dump_any(object, buffer)
|
88
62
|
method = DUMP_TYPES.fetch(object.class) do
|
89
63
|
raise TypeError, "Unsupported command argument type: #{object.class}"
|
@@ -2,47 +2,42 @@
|
|
2
2
|
|
3
3
|
require "socket"
|
4
4
|
require "openssl"
|
5
|
-
require "redis_client/
|
5
|
+
require "redis_client/connection_mixin"
|
6
|
+
require "redis_client/ruby_connection/buffered_io"
|
7
|
+
require "redis_client/ruby_connection/resp3"
|
6
8
|
|
7
9
|
class RedisClient
|
8
|
-
class
|
9
|
-
|
10
|
-
def call(command, timeout)
|
11
|
-
write(command)
|
12
|
-
result = read(timeout)
|
13
|
-
if result.is_a?(CommandError)
|
14
|
-
raise result
|
15
|
-
else
|
16
|
-
result
|
17
|
-
end
|
18
|
-
end
|
10
|
+
class RubyConnection
|
11
|
+
include ConnectionMixin
|
19
12
|
|
20
|
-
|
21
|
-
|
13
|
+
class << self
|
14
|
+
def ssl_context(ssl_params)
|
15
|
+
params = ssl_params.dup || {}
|
22
16
|
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
cert = params[:cert]
|
18
|
+
if cert.is_a?(String)
|
19
|
+
cert = File.read(cert) if File.exist?(cert)
|
20
|
+
params[:cert] = OpenSSL::X509::Certificate.new(cert)
|
21
|
+
end
|
26
22
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
exception ||= result
|
32
|
-
end
|
33
|
-
results[index] = result
|
23
|
+
key = params[:key]
|
24
|
+
if key.is_a?(String)
|
25
|
+
key = File.read(key) if File.exist?(key)
|
26
|
+
params[:key] = OpenSSL::PKey.read(key)
|
34
27
|
end
|
35
28
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
29
|
+
context = OpenSSL::SSL::SSLContext.new
|
30
|
+
context.set_params(params)
|
31
|
+
if context.verify_mode != OpenSSL::SSL::VERIFY_NONE
|
32
|
+
if context.respond_to?(:verify_hostname) # Missing on JRuby
|
33
|
+
context.verify_hostname
|
34
|
+
end
|
40
35
|
end
|
36
|
+
|
37
|
+
context
|
41
38
|
end
|
42
39
|
end
|
43
40
|
|
44
|
-
include Common
|
45
|
-
|
46
41
|
SUPPORTS_RESOLV_TIMEOUT = Socket.method(:tcp).parameters.any? { |p| p.last == :resolv_timeout }
|
47
42
|
|
48
43
|
def initialize(config, connect_timeout:, read_timeout:, write_timeout:)
|
@@ -60,7 +55,7 @@ class RedisClient
|
|
60
55
|
end
|
61
56
|
|
62
57
|
if config.ssl
|
63
|
-
socket = OpenSSL::SSL::SSLSocket.new(socket, config.
|
58
|
+
socket = OpenSSL::SSL::SSLSocket.new(socket, config.ssl_context)
|
64
59
|
socket.hostname = config.host
|
65
60
|
loop do
|
66
61
|
case status = socket.connect_nonblock(exception: false)
|
data/lib/redis_client/version.rb
CHANGED
data/lib/redis_client.rb
CHANGED
@@ -1,12 +1,62 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "set"
|
4
|
+
|
3
5
|
require "redis_client/version"
|
6
|
+
require "redis_client/command_builder"
|
4
7
|
require "redis_client/config"
|
5
8
|
require "redis_client/sentinel_config"
|
6
|
-
require "redis_client/connection"
|
7
9
|
require "redis_client/middlewares"
|
8
10
|
|
9
11
|
class RedisClient
|
12
|
+
@driver_definitions = {}
|
13
|
+
@drivers = {}
|
14
|
+
|
15
|
+
@default_driver = nil
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def register_driver(name, &block)
|
19
|
+
@driver_definitions[name] = block
|
20
|
+
end
|
21
|
+
|
22
|
+
def driver(name)
|
23
|
+
return name if name.is_a?(Class)
|
24
|
+
|
25
|
+
name = name.to_sym
|
26
|
+
unless @driver_definitions.key?(name)
|
27
|
+
raise ArgumentError, "Unknown driver #{name.inspect}, expected one of: `#{DRIVER_DEFINITIONS.keys.inspect}`"
|
28
|
+
end
|
29
|
+
|
30
|
+
@drivers[name] ||= @driver_definitions[name]&.call
|
31
|
+
end
|
32
|
+
|
33
|
+
def default_driver
|
34
|
+
unless @default_driver
|
35
|
+
@driver_definitions.each_key do |name|
|
36
|
+
if @default_driver = driver(name)
|
37
|
+
break
|
38
|
+
end
|
39
|
+
rescue LoadError
|
40
|
+
end
|
41
|
+
end
|
42
|
+
@default_driver
|
43
|
+
end
|
44
|
+
|
45
|
+
def default_driver=(name)
|
46
|
+
@default_driver = driver(name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
register_driver :hiredis do
|
51
|
+
require "redis_client/hiredis_connection"
|
52
|
+
HiredisConnection
|
53
|
+
end
|
54
|
+
|
55
|
+
register_driver :ruby do
|
56
|
+
require "redis_client/ruby_connection"
|
57
|
+
RubyConnection
|
58
|
+
end
|
59
|
+
|
10
60
|
module Common
|
11
61
|
attr_reader :config, :id
|
12
62
|
attr_accessor :connect_timeout, :read_timeout, :write_timeout
|
@@ -23,6 +73,7 @@ class RedisClient
|
|
23
73
|
@connect_timeout = connect_timeout
|
24
74
|
@read_timeout = read_timeout
|
25
75
|
@write_timeout = write_timeout
|
76
|
+
@command_builder = config.command_builder
|
26
77
|
end
|
27
78
|
|
28
79
|
def timeout=(timeout)
|
@@ -54,10 +105,14 @@ class RedisClient
|
|
54
105
|
|
55
106
|
AuthenticationError = Class.new(CommandError)
|
56
107
|
PermissionError = Class.new(CommandError)
|
108
|
+
ReadOnlyError = Class.new(CommandError)
|
109
|
+
WrongTypeError = Class.new(CommandError)
|
57
110
|
|
58
111
|
CommandError::ERRORS = {
|
59
112
|
"WRONGPASS" => AuthenticationError,
|
60
113
|
"NOPERM" => PermissionError,
|
114
|
+
"READONLY" => ReadOnlyError,
|
115
|
+
"WRONGTYPE" => WrongTypeError,
|
61
116
|
}.freeze
|
62
117
|
|
63
118
|
class << self
|
@@ -115,67 +170,89 @@ class RedisClient
|
|
115
170
|
end
|
116
171
|
|
117
172
|
def pubsub
|
118
|
-
sub = PubSub.new(ensure_connected)
|
173
|
+
sub = PubSub.new(ensure_connected, @command_builder)
|
119
174
|
@raw_connection = nil
|
120
175
|
sub
|
121
176
|
end
|
122
177
|
|
123
|
-
def call(*command)
|
124
|
-
command =
|
125
|
-
ensure_connected do |connection|
|
178
|
+
def call(*command, **kwargs)
|
179
|
+
command = @command_builder.generate!(command, kwargs)
|
180
|
+
result = ensure_connected do |connection|
|
126
181
|
Middlewares.call(command, config) do
|
127
182
|
connection.call(command, nil)
|
128
183
|
end
|
129
184
|
end
|
185
|
+
|
186
|
+
if block_given?
|
187
|
+
yield result
|
188
|
+
else
|
189
|
+
result
|
190
|
+
end
|
130
191
|
end
|
131
192
|
|
132
|
-
def call_once(*command)
|
133
|
-
command =
|
134
|
-
ensure_connected(retryable: false) do |connection|
|
193
|
+
def call_once(*command, **kwargs)
|
194
|
+
command = @command_builder.generate!(command, kwargs)
|
195
|
+
result = ensure_connected(retryable: false) do |connection|
|
135
196
|
Middlewares.call(command, config) do
|
136
197
|
connection.call(command, nil)
|
137
198
|
end
|
138
199
|
end
|
200
|
+
|
201
|
+
if block_given?
|
202
|
+
yield result
|
203
|
+
else
|
204
|
+
result
|
205
|
+
end
|
139
206
|
end
|
140
207
|
|
141
|
-
def blocking_call(timeout, *command)
|
142
|
-
command =
|
143
|
-
ensure_connected do |connection|
|
208
|
+
def blocking_call(timeout, *command, **kwargs)
|
209
|
+
command = @command_builder.generate!(command, kwargs)
|
210
|
+
result = ensure_connected do |connection|
|
144
211
|
Middlewares.call(command, config) do
|
145
212
|
connection.call(command, timeout)
|
146
213
|
end
|
147
214
|
end
|
215
|
+
|
216
|
+
if block_given?
|
217
|
+
yield result
|
218
|
+
else
|
219
|
+
result
|
220
|
+
end
|
148
221
|
end
|
149
222
|
|
150
|
-
def scan(*args, &block)
|
223
|
+
def scan(*args, **kwargs, &block)
|
151
224
|
unless block_given?
|
152
|
-
return to_enum(__callee__, *args)
|
225
|
+
return to_enum(__callee__, *args, **kwargs)
|
153
226
|
end
|
154
227
|
|
228
|
+
args = @command_builder.generate!(args, kwargs)
|
155
229
|
scan_list(1, ["SCAN", 0, *args], &block)
|
156
230
|
end
|
157
231
|
|
158
|
-
def sscan(key, *args, &block)
|
232
|
+
def sscan(key, *args, **kwargs, &block)
|
159
233
|
unless block_given?
|
160
|
-
return to_enum(__callee__, key, *args)
|
234
|
+
return to_enum(__callee__, key, *args, **kwargs)
|
161
235
|
end
|
162
236
|
|
237
|
+
args = @command_builder.generate!(args, kwargs)
|
163
238
|
scan_list(2, ["SSCAN", key, 0, *args], &block)
|
164
239
|
end
|
165
240
|
|
166
|
-
def hscan(key, *args, &block)
|
241
|
+
def hscan(key, *args, **kwargs, &block)
|
167
242
|
unless block_given?
|
168
|
-
return to_enum(__callee__, key, *args)
|
243
|
+
return to_enum(__callee__, key, *args, **kwargs)
|
169
244
|
end
|
170
245
|
|
246
|
+
args = @command_builder.generate!(args, kwargs)
|
171
247
|
scan_pairs(2, ["HSCAN", key, 0, *args], &block)
|
172
248
|
end
|
173
249
|
|
174
|
-
def zscan(key, *args, &block)
|
250
|
+
def zscan(key, *args, **kwargs, &block)
|
175
251
|
unless block_given?
|
176
|
-
return to_enum(__callee__, key, *args)
|
252
|
+
return to_enum(__callee__, key, *args, **kwargs)
|
177
253
|
end
|
178
254
|
|
255
|
+
args = @command_builder.generate!(args, kwargs)
|
179
256
|
scan_pairs(2, ["ZSCAN", key, 0, *args], &block)
|
180
257
|
end
|
181
258
|
|
@@ -190,23 +267,27 @@ class RedisClient
|
|
190
267
|
end
|
191
268
|
|
192
269
|
def pipelined
|
193
|
-
pipeline = Pipeline.new
|
270
|
+
pipeline = Pipeline.new(@command_builder)
|
194
271
|
yield pipeline
|
195
272
|
|
196
273
|
if pipeline._size == 0
|
197
274
|
[]
|
198
275
|
else
|
199
|
-
ensure_connected(retryable: pipeline._retryable?) do |connection|
|
276
|
+
results = ensure_connected(retryable: pipeline._retryable?) do |connection|
|
200
277
|
commands = pipeline._commands
|
201
278
|
Middlewares.call_pipelined(commands, config) do
|
202
279
|
connection.call_pipelined(commands, pipeline._timeouts)
|
203
280
|
end
|
204
281
|
end
|
282
|
+
|
283
|
+
pipeline._coerce!(results)
|
205
284
|
end
|
206
285
|
end
|
207
286
|
|
208
287
|
def multi(watch: nil, &block)
|
209
|
-
|
288
|
+
transaction = nil
|
289
|
+
|
290
|
+
results = if watch
|
210
291
|
# WATCH is stateful, so we can't reconnect if it's used, the whole transaction
|
211
292
|
# has to be redone.
|
212
293
|
ensure_connected(retryable: false) do |connection|
|
@@ -214,7 +295,7 @@ class RedisClient
|
|
214
295
|
begin
|
215
296
|
if transaction = build_transaction(&block)
|
216
297
|
commands = transaction._commands
|
217
|
-
Middlewares.call_pipelined(commands, config) do
|
298
|
+
results = Middlewares.call_pipelined(commands, config) do
|
218
299
|
connection.call_pipelined(commands, nil)
|
219
300
|
end.last
|
220
301
|
else
|
@@ -239,15 +320,22 @@ class RedisClient
|
|
239
320
|
end
|
240
321
|
end
|
241
322
|
end
|
323
|
+
|
324
|
+
if transaction
|
325
|
+
transaction._coerce!(results)
|
326
|
+
else
|
327
|
+
results
|
328
|
+
end
|
242
329
|
end
|
243
330
|
|
244
331
|
class PubSub
|
245
|
-
def initialize(raw_connection)
|
332
|
+
def initialize(raw_connection, command_builder)
|
246
333
|
@raw_connection = raw_connection
|
334
|
+
@command_builder = command_builder
|
247
335
|
end
|
248
336
|
|
249
|
-
def call(*command)
|
250
|
-
raw_connection.write(
|
337
|
+
def call(*command, **kwargs)
|
338
|
+
raw_connection.write(@command_builder.generate!(command, kwargs))
|
251
339
|
nil
|
252
340
|
end
|
253
341
|
|
@@ -273,20 +361,26 @@ class RedisClient
|
|
273
361
|
end
|
274
362
|
|
275
363
|
class Multi
|
276
|
-
def initialize
|
364
|
+
def initialize(command_builder)
|
365
|
+
@command_builder = command_builder
|
277
366
|
@size = 0
|
278
367
|
@commands = []
|
368
|
+
@blocks = nil
|
279
369
|
@retryable = true
|
280
370
|
end
|
281
371
|
|
282
|
-
def call(*command)
|
283
|
-
|
372
|
+
def call(*command, **kwargs, &block)
|
373
|
+
command = @command_builder.generate!(command, kwargs)
|
374
|
+
(@blocks ||= [])[@commands.size] = block if block_given?
|
375
|
+
@commands << command
|
284
376
|
nil
|
285
377
|
end
|
286
378
|
|
287
|
-
def call_once(*command)
|
379
|
+
def call_once(*command, **kwargs)
|
380
|
+
command = @command_builder.generate!(command, kwargs)
|
288
381
|
@retryable = false
|
289
|
-
@commands
|
382
|
+
(@blocks ||= [])[@commands.size] = block if block_given?
|
383
|
+
@commands << command
|
290
384
|
nil
|
291
385
|
end
|
292
386
|
|
@@ -294,6 +388,10 @@ class RedisClient
|
|
294
388
|
@commands
|
295
389
|
end
|
296
390
|
|
391
|
+
def _blocks
|
392
|
+
@blocks
|
393
|
+
end
|
394
|
+
|
297
395
|
def _size
|
298
396
|
@commands.size
|
299
397
|
end
|
@@ -309,18 +407,38 @@ class RedisClient
|
|
309
407
|
def _retryable?
|
310
408
|
@retryable
|
311
409
|
end
|
410
|
+
|
411
|
+
def _coerce!(results)
|
412
|
+
if results
|
413
|
+
results.each do |result|
|
414
|
+
if result.is_a?(CommandError)
|
415
|
+
raise result
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
@blocks&.each_with_index do |block, index|
|
420
|
+
if block
|
421
|
+
results[index - 1] = block.call(results[index - 1])
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
results
|
427
|
+
end
|
312
428
|
end
|
313
429
|
|
314
430
|
class Pipeline < Multi
|
315
|
-
def initialize
|
431
|
+
def initialize(_command_builder)
|
316
432
|
super
|
317
433
|
@timeouts = nil
|
318
434
|
end
|
319
435
|
|
320
|
-
def blocking_call(timeout, *command)
|
436
|
+
def blocking_call(timeout, *command, **kwargs)
|
437
|
+
command = @command_builder.generate!(command, kwargs)
|
321
438
|
@timeouts ||= []
|
322
439
|
@timeouts[@commands.size] = timeout
|
323
|
-
@commands
|
440
|
+
(@blocks ||= [])[@commands.size] = block if block_given?
|
441
|
+
@commands << command
|
324
442
|
nil
|
325
443
|
end
|
326
444
|
|
@@ -331,12 +449,24 @@ class RedisClient
|
|
331
449
|
def _empty?
|
332
450
|
@commands.empty?
|
333
451
|
end
|
452
|
+
|
453
|
+
def _coerce!(results)
|
454
|
+
return results unless results
|
455
|
+
|
456
|
+
@blocks&.each_with_index do |block, index|
|
457
|
+
if block
|
458
|
+
results[index] = block.call(results[index])
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
results
|
463
|
+
end
|
334
464
|
end
|
335
465
|
|
336
466
|
private
|
337
467
|
|
338
468
|
def build_transaction
|
339
|
-
transaction = Multi.new
|
469
|
+
transaction = Multi.new(@command_builder)
|
340
470
|
transaction.call("MULTI")
|
341
471
|
yield transaction
|
342
472
|
transaction.call("EXEC")
|
@@ -436,5 +566,6 @@ class RedisClient
|
|
436
566
|
end
|
437
567
|
end
|
438
568
|
|
439
|
-
require "redis_client/resp3"
|
440
569
|
require "redis_client/pooled"
|
570
|
+
|
571
|
+
RedisClient.default_driver
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean Boussier
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: connection_pool
|
@@ -89,13 +89,16 @@ files:
|
|
89
89
|
- ext/redis_client/hiredis/vendor/win32.h
|
90
90
|
- lib/redis-client.rb
|
91
91
|
- lib/redis_client.rb
|
92
|
-
- lib/redis_client/
|
92
|
+
- lib/redis_client/command_builder.rb
|
93
93
|
- lib/redis_client/config.rb
|
94
|
-
- lib/redis_client/
|
94
|
+
- lib/redis_client/connection_mixin.rb
|
95
|
+
- lib/redis_client/decorator.rb
|
95
96
|
- lib/redis_client/hiredis_connection.rb
|
96
97
|
- lib/redis_client/middlewares.rb
|
97
98
|
- lib/redis_client/pooled.rb
|
98
|
-
- lib/redis_client/
|
99
|
+
- lib/redis_client/ruby_connection.rb
|
100
|
+
- lib/redis_client/ruby_connection/buffered_io.rb
|
101
|
+
- lib/redis_client/ruby_connection/resp3.rb
|
99
102
|
- lib/redis_client/sentinel_config.rb
|
100
103
|
- lib/redis_client/version.rb
|
101
104
|
- redis-client.gemspec
|