redis-client 0.2.0 → 0.4.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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/Gemfile +1 -2
  4. data/Gemfile.lock +2 -3
  5. data/README.md +66 -3
  6. data/Rakefile +43 -23
  7. data/lib/redis_client/command_builder.rb +83 -0
  8. data/lib/redis_client/config.rb +9 -48
  9. data/lib/redis_client/connection_mixin.rb +38 -0
  10. data/lib/redis_client/decorator.rb +84 -0
  11. data/lib/redis_client/pooled.rb +38 -30
  12. data/lib/redis_client/ruby_connection/buffered_io.rb +153 -0
  13. data/lib/redis_client/{resp3.rb → ruby_connection/resp3.rb} +0 -26
  14. data/lib/redis_client/{connection.rb → ruby_connection.rb} +26 -31
  15. data/lib/redis_client/version.rb +1 -1
  16. data/lib/redis_client.rb +162 -36
  17. data/redis-client.gemspec +2 -4
  18. metadata +12 -59
  19. data/.rubocop.yml +0 -190
  20. data/ext/redis_client/hiredis/export.clang +0 -2
  21. data/ext/redis_client/hiredis/export.gcc +0 -7
  22. data/ext/redis_client/hiredis/extconf.rb +0 -62
  23. data/ext/redis_client/hiredis/hiredis_connection.c +0 -708
  24. data/ext/redis_client/hiredis/vendor/.gitignore +0 -9
  25. data/ext/redis_client/hiredis/vendor/.travis.yml +0 -131
  26. data/ext/redis_client/hiredis/vendor/CHANGELOG.md +0 -364
  27. data/ext/redis_client/hiredis/vendor/CMakeLists.txt +0 -165
  28. data/ext/redis_client/hiredis/vendor/COPYING +0 -29
  29. data/ext/redis_client/hiredis/vendor/Makefile +0 -308
  30. data/ext/redis_client/hiredis/vendor/README.md +0 -664
  31. data/ext/redis_client/hiredis/vendor/adapters/ae.h +0 -130
  32. data/ext/redis_client/hiredis/vendor/adapters/glib.h +0 -156
  33. data/ext/redis_client/hiredis/vendor/adapters/ivykis.h +0 -84
  34. data/ext/redis_client/hiredis/vendor/adapters/libev.h +0 -179
  35. data/ext/redis_client/hiredis/vendor/adapters/libevent.h +0 -175
  36. data/ext/redis_client/hiredis/vendor/adapters/libuv.h +0 -117
  37. data/ext/redis_client/hiredis/vendor/adapters/macosx.h +0 -115
  38. data/ext/redis_client/hiredis/vendor/adapters/qt.h +0 -135
  39. data/ext/redis_client/hiredis/vendor/alloc.c +0 -86
  40. data/ext/redis_client/hiredis/vendor/alloc.h +0 -91
  41. data/ext/redis_client/hiredis/vendor/appveyor.yml +0 -24
  42. data/ext/redis_client/hiredis/vendor/async.c +0 -887
  43. data/ext/redis_client/hiredis/vendor/async.h +0 -147
  44. data/ext/redis_client/hiredis/vendor/async_private.h +0 -75
  45. data/ext/redis_client/hiredis/vendor/dict.c +0 -352
  46. data/ext/redis_client/hiredis/vendor/dict.h +0 -126
  47. data/ext/redis_client/hiredis/vendor/fmacros.h +0 -12
  48. data/ext/redis_client/hiredis/vendor/hiredis-config.cmake.in +0 -13
  49. data/ext/redis_client/hiredis/vendor/hiredis.c +0 -1174
  50. data/ext/redis_client/hiredis/vendor/hiredis.h +0 -336
  51. data/ext/redis_client/hiredis/vendor/hiredis.pc.in +0 -12
  52. data/ext/redis_client/hiredis/vendor/hiredis_ssl-config.cmake.in +0 -13
  53. data/ext/redis_client/hiredis/vendor/hiredis_ssl.h +0 -157
  54. data/ext/redis_client/hiredis/vendor/hiredis_ssl.pc.in +0 -12
  55. data/ext/redis_client/hiredis/vendor/net.c +0 -612
  56. data/ext/redis_client/hiredis/vendor/net.h +0 -56
  57. data/ext/redis_client/hiredis/vendor/read.c +0 -739
  58. data/ext/redis_client/hiredis/vendor/read.h +0 -129
  59. data/ext/redis_client/hiredis/vendor/sds.c +0 -1289
  60. data/ext/redis_client/hiredis/vendor/sds.h +0 -278
  61. data/ext/redis_client/hiredis/vendor/sdsalloc.h +0 -44
  62. data/ext/redis_client/hiredis/vendor/sockcompat.c +0 -248
  63. data/ext/redis_client/hiredis/vendor/sockcompat.h +0 -92
  64. data/ext/redis_client/hiredis/vendor/ssl.c +0 -544
  65. data/ext/redis_client/hiredis/vendor/test.c +0 -1401
  66. data/ext/redis_client/hiredis/vendor/test.sh +0 -78
  67. data/ext/redis_client/hiredis/vendor/win32.h +0 -56
  68. data/lib/redis_client/buffered_io.rb +0 -151
  69. data/lib/redis_client/hiredis_connection.rb +0 -80
@@ -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/buffered_io"
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 Connection
9
- module Common
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
- def call_pipelined(commands, timeouts)
21
- exception = nil
13
+ class << self
14
+ def ssl_context(ssl_params)
15
+ params = ssl_params.dup || {}
22
16
 
23
- size = commands.size
24
- results = Array.new(commands.size)
25
- write_multi(commands)
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
- size.times do |index|
28
- timeout = timeouts && timeouts[index]
29
- result = read(timeout)
30
- if result.is_a?(CommandError)
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
- if exception
37
- raise exception
38
- else
39
- results
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.openssl_context)
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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class RedisClient
4
- VERSION = "0.2.0"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/redis_client.rb CHANGED
@@ -1,12 +1,57 @@
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 :ruby do
51
+ require "redis_client/ruby_connection"
52
+ RubyConnection
53
+ end
54
+
10
55
  module Common
11
56
  attr_reader :config, :id
12
57
  attr_accessor :connect_timeout, :read_timeout, :write_timeout
@@ -23,6 +68,7 @@ class RedisClient
23
68
  @connect_timeout = connect_timeout
24
69
  @read_timeout = read_timeout
25
70
  @write_timeout = write_timeout
71
+ @command_builder = config.command_builder
26
72
  end
27
73
 
28
74
  def timeout=(timeout)
@@ -54,10 +100,14 @@ class RedisClient
54
100
 
55
101
  AuthenticationError = Class.new(CommandError)
56
102
  PermissionError = Class.new(CommandError)
103
+ ReadOnlyError = Class.new(CommandError)
104
+ WrongTypeError = Class.new(CommandError)
57
105
 
58
106
  CommandError::ERRORS = {
59
107
  "WRONGPASS" => AuthenticationError,
60
108
  "NOPERM" => PermissionError,
109
+ "READONLY" => ReadOnlyError,
110
+ "WRONGTYPE" => WrongTypeError,
61
111
  }.freeze
62
112
 
63
113
  class << self
@@ -115,67 +165,89 @@ class RedisClient
115
165
  end
116
166
 
117
167
  def pubsub
118
- sub = PubSub.new(ensure_connected)
168
+ sub = PubSub.new(ensure_connected, @command_builder)
119
169
  @raw_connection = nil
120
170
  sub
121
171
  end
122
172
 
123
- def call(*command)
124
- command = RESP3.coerce_command!(command)
125
- ensure_connected do |connection|
173
+ def call(*command, **kwargs)
174
+ command = @command_builder.generate!(command, kwargs)
175
+ result = ensure_connected do |connection|
126
176
  Middlewares.call(command, config) do
127
177
  connection.call(command, nil)
128
178
  end
129
179
  end
180
+
181
+ if block_given?
182
+ yield result
183
+ else
184
+ result
185
+ end
130
186
  end
131
187
 
132
- def call_once(*command)
133
- command = RESP3.coerce_command!(command)
134
- ensure_connected(retryable: false) do |connection|
188
+ def call_once(*command, **kwargs)
189
+ command = @command_builder.generate!(command, kwargs)
190
+ result = ensure_connected(retryable: false) do |connection|
135
191
  Middlewares.call(command, config) do
136
192
  connection.call(command, nil)
137
193
  end
138
194
  end
195
+
196
+ if block_given?
197
+ yield result
198
+ else
199
+ result
200
+ end
139
201
  end
140
202
 
141
- def blocking_call(timeout, *command)
142
- command = RESP3.coerce_command!(command)
143
- ensure_connected do |connection|
203
+ def blocking_call(timeout, *command, **kwargs)
204
+ command = @command_builder.generate!(command, kwargs)
205
+ result = ensure_connected do |connection|
144
206
  Middlewares.call(command, config) do
145
207
  connection.call(command, timeout)
146
208
  end
147
209
  end
210
+
211
+ if block_given?
212
+ yield result
213
+ else
214
+ result
215
+ end
148
216
  end
149
217
 
150
- def scan(*args, &block)
218
+ def scan(*args, **kwargs, &block)
151
219
  unless block_given?
152
- return to_enum(__callee__, *args)
220
+ return to_enum(__callee__, *args, **kwargs)
153
221
  end
154
222
 
223
+ args = @command_builder.generate!(args, kwargs)
155
224
  scan_list(1, ["SCAN", 0, *args], &block)
156
225
  end
157
226
 
158
- def sscan(key, *args, &block)
227
+ def sscan(key, *args, **kwargs, &block)
159
228
  unless block_given?
160
- return to_enum(__callee__, key, *args)
229
+ return to_enum(__callee__, key, *args, **kwargs)
161
230
  end
162
231
 
232
+ args = @command_builder.generate!(args, kwargs)
163
233
  scan_list(2, ["SSCAN", key, 0, *args], &block)
164
234
  end
165
235
 
166
- def hscan(key, *args, &block)
236
+ def hscan(key, *args, **kwargs, &block)
167
237
  unless block_given?
168
- return to_enum(__callee__, key, *args)
238
+ return to_enum(__callee__, key, *args, **kwargs)
169
239
  end
170
240
 
241
+ args = @command_builder.generate!(args, kwargs)
171
242
  scan_pairs(2, ["HSCAN", key, 0, *args], &block)
172
243
  end
173
244
 
174
- def zscan(key, *args, &block)
245
+ def zscan(key, *args, **kwargs, &block)
175
246
  unless block_given?
176
- return to_enum(__callee__, key, *args)
247
+ return to_enum(__callee__, key, *args, **kwargs)
177
248
  end
178
249
 
250
+ args = @command_builder.generate!(args, kwargs)
179
251
  scan_pairs(2, ["ZSCAN", key, 0, *args], &block)
180
252
  end
181
253
 
@@ -190,23 +262,27 @@ class RedisClient
190
262
  end
191
263
 
192
264
  def pipelined
193
- pipeline = Pipeline.new
265
+ pipeline = Pipeline.new(@command_builder)
194
266
  yield pipeline
195
267
 
196
268
  if pipeline._size == 0
197
269
  []
198
270
  else
199
- ensure_connected(retryable: pipeline._retryable?) do |connection|
271
+ results = ensure_connected(retryable: pipeline._retryable?) do |connection|
200
272
  commands = pipeline._commands
201
273
  Middlewares.call_pipelined(commands, config) do
202
274
  connection.call_pipelined(commands, pipeline._timeouts)
203
275
  end
204
276
  end
277
+
278
+ pipeline._coerce!(results)
205
279
  end
206
280
  end
207
281
 
208
282
  def multi(watch: nil, &block)
209
- if watch
283
+ transaction = nil
284
+
285
+ results = if watch
210
286
  # WATCH is stateful, so we can't reconnect if it's used, the whole transaction
211
287
  # has to be redone.
212
288
  ensure_connected(retryable: false) do |connection|
@@ -214,7 +290,7 @@ class RedisClient
214
290
  begin
215
291
  if transaction = build_transaction(&block)
216
292
  commands = transaction._commands
217
- Middlewares.call_pipelined(commands, config) do
293
+ results = Middlewares.call_pipelined(commands, config) do
218
294
  connection.call_pipelined(commands, nil)
219
295
  end.last
220
296
  else
@@ -239,15 +315,22 @@ class RedisClient
239
315
  end
240
316
  end
241
317
  end
318
+
319
+ if transaction
320
+ transaction._coerce!(results)
321
+ else
322
+ results
323
+ end
242
324
  end
243
325
 
244
326
  class PubSub
245
- def initialize(raw_connection)
327
+ def initialize(raw_connection, command_builder)
246
328
  @raw_connection = raw_connection
329
+ @command_builder = command_builder
247
330
  end
248
331
 
249
- def call(*command)
250
- raw_connection.write(RESP3.coerce_command!(command))
332
+ def call(*command, **kwargs)
333
+ raw_connection.write(@command_builder.generate!(command, kwargs))
251
334
  nil
252
335
  end
253
336
 
@@ -273,20 +356,26 @@ class RedisClient
273
356
  end
274
357
 
275
358
  class Multi
276
- def initialize
359
+ def initialize(command_builder)
360
+ @command_builder = command_builder
277
361
  @size = 0
278
362
  @commands = []
363
+ @blocks = nil
279
364
  @retryable = true
280
365
  end
281
366
 
282
- def call(*command)
283
- @commands << RESP3.coerce_command!(command)
367
+ def call(*command, **kwargs, &block)
368
+ command = @command_builder.generate!(command, kwargs)
369
+ (@blocks ||= [])[@commands.size] = block if block_given?
370
+ @commands << command
284
371
  nil
285
372
  end
286
373
 
287
- def call_once(*command)
374
+ def call_once(*command, **kwargs)
375
+ command = @command_builder.generate!(command, kwargs)
288
376
  @retryable = false
289
- @commands << RESP3.coerce_command!(command)
377
+ (@blocks ||= [])[@commands.size] = block if block_given?
378
+ @commands << command
290
379
  nil
291
380
  end
292
381
 
@@ -294,6 +383,10 @@ class RedisClient
294
383
  @commands
295
384
  end
296
385
 
386
+ def _blocks
387
+ @blocks
388
+ end
389
+
297
390
  def _size
298
391
  @commands.size
299
392
  end
@@ -309,18 +402,38 @@ class RedisClient
309
402
  def _retryable?
310
403
  @retryable
311
404
  end
405
+
406
+ def _coerce!(results)
407
+ if results
408
+ results.each do |result|
409
+ if result.is_a?(CommandError)
410
+ raise result
411
+ end
412
+ end
413
+
414
+ @blocks&.each_with_index do |block, index|
415
+ if block
416
+ results[index - 1] = block.call(results[index - 1])
417
+ end
418
+ end
419
+ end
420
+
421
+ results
422
+ end
312
423
  end
313
424
 
314
425
  class Pipeline < Multi
315
- def initialize
426
+ def initialize(_command_builder)
316
427
  super
317
428
  @timeouts = nil
318
429
  end
319
430
 
320
- def blocking_call(timeout, *command)
431
+ def blocking_call(timeout, *command, **kwargs)
432
+ command = @command_builder.generate!(command, kwargs)
321
433
  @timeouts ||= []
322
434
  @timeouts[@commands.size] = timeout
323
- @commands << RESP3.coerce_command!(command)
435
+ (@blocks ||= [])[@commands.size] = block if block_given?
436
+ @commands << command
324
437
  nil
325
438
  end
326
439
 
@@ -331,12 +444,24 @@ class RedisClient
331
444
  def _empty?
332
445
  @commands.empty?
333
446
  end
447
+
448
+ def _coerce!(results)
449
+ return results unless results
450
+
451
+ @blocks&.each_with_index do |block, index|
452
+ if block
453
+ results[index] = block.call(results[index])
454
+ end
455
+ end
456
+
457
+ results
458
+ end
334
459
  end
335
460
 
336
461
  private
337
462
 
338
463
  def build_transaction
339
- transaction = Multi.new
464
+ transaction = Multi.new(@command_builder)
340
465
  transaction.call("MULTI")
341
466
  yield transaction
342
467
  transaction.call("EXEC")
@@ -436,5 +561,6 @@ class RedisClient
436
561
  end
437
562
  end
438
563
 
439
- require "redis_client/resp3"
440
564
  require "redis_client/pooled"
565
+
566
+ RedisClient.default_driver
data/redis-client.gemspec CHANGED
@@ -10,6 +10,7 @@ Gem::Specification.new do |spec|
10
10
 
11
11
  spec.summary = "Simple low-level client for Redis 6+"
12
12
  spec.homepage = "https://github.com/redis-rb/redis-client"
13
+ spec.license = "MIT"
13
14
  spec.required_ruby_version = ">= 2.5.0"
14
15
 
15
16
  spec.metadata["allowed_push_host"] = "https://rubygems.org"
@@ -22,13 +23,10 @@ Gem::Specification.new do |spec|
22
23
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
24
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
25
  `git ls-files -z`.split("\x0").reject do |f|
25
- (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features|benchmark)/|\.(?:git|travis|circleci)|appveyor)})
26
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|hiredis-client|test|spec|features|benchmark)/|\.(?:git|rubocop))})
26
27
  end
27
28
  end
28
- spec.bindir = "exe"
29
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
29
  spec.require_paths = ["lib"]
31
- spec.extensions = ["ext/redis_client/hiredis/extconf.rb"]
32
30
 
33
31
  spec.add_runtime_dependency "connection_pool"
34
32
  end