redis-client 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eba18b5a126a8f4f9e76c842c41fd3ab2672c6d08be0d9023536e7fdbbf2ac1c
4
- data.tar.gz: 0a012bad73ae54550cae8dde282fdaf644a04a1bd748cd37993a31592ccf2259
3
+ metadata.gz: 6bd28ab66722c23791118818d83505de8cbc1b141c75959f2408b8a0c90cb6ad
4
+ data.tar.gz: 30f85f2bb0df340facd9a743f63dcfd2ff8fa8b8a3e363c9fdeb2f1101d5d076
5
5
  SHA512:
6
- metadata.gz: 37ccff70160eff897f9881784db776679e8b97cec95b89e6db3f13e5294799c3d6ca7dd97e83ce54699819943c7a10904fb76567a7944a8c83fc88408217d294
7
- data.tar.gz: c366d95353b081199751910676a9b0e682ff7cb0559c91d7c5112ec7f486a2edb0afa9317f9066953bd13b2f359f6fcb0c4fda268852aa7176557cc42d6b0f94
6
+ metadata.gz: 528b4cd6f9377f083a19ca1da7b117625ad32c1849c8cc53d795dc5af8e6d23c372fdb5714c043b7c796a6fcfda54ef28ee0142268846445cbfb60e00eabf625
7
+ data.tar.gz: af0040e6a5cf498587818b5161122d57e3057a39a22d0eb5fc8252e4cb56e71ad61c0a381fbe35c75cd6f5e5e2998169487e33b74f7635f7e025f6e06d7351ca
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Unreleased
2
2
 
3
+ # 0.4.0
4
+
5
+ - The `hiredis` driver have been moved to the `hiredis-client` gem.
6
+
7
+ # 0.3.0
8
+
9
+ - `hiredis` is now the default driver when available.
10
+ - Add `RedisClient.default_driver=`.
11
+ - `#call` now takes an optional block to cast the return value.
12
+ - Treat `#call` keyword arguments as Redis flags.
13
+ - Fix `RedisClient#multi` returning some errors as values instead of raising them.
14
+
15
+ # 0.2.1
16
+
17
+ - Use a more robust way to detect the current compiler.
18
+
3
19
  # 0.2.0
4
20
  - Added `RedisClient.register` as a public instrumentation API.
5
21
  - Fix `read_timeout=` and `write_timeout=` to apply even when the client or pool is already connected.
data/Gemfile CHANGED
@@ -3,9 +3,8 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  # Specify your gem's dependencies in redis-client.gemspec
6
- gemspec
6
+ gemspec name: "redis-client"
7
7
 
8
- gem "connection_pool"
9
8
  gem "minitest"
10
9
  gem "rake", "~> 13.0"
11
10
  gem "rake-compiler"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- redis-client (0.2.0)
4
+ redis-client (0.4.0)
5
5
  connection_pool
6
6
 
7
7
  GEM
@@ -51,7 +51,6 @@ PLATFORMS
51
51
  DEPENDENCIES
52
52
  benchmark-ips
53
53
  byebug
54
- connection_pool
55
54
  hiredis
56
55
  minitest
57
56
  rake (~> 13.0)
@@ -64,4 +63,4 @@ DEPENDENCIES
64
63
  toxiproxy
65
64
 
66
65
  BUNDLED WITH
67
- 2.3.8
66
+ 2.3.13
data/README.md CHANGED
@@ -145,19 +145,68 @@ redis.call("LPUSH", "list", "1", "2", "3", "4")
145
145
  Hashes are flatenned as well:
146
146
 
147
147
  ```ruby
148
- redis.call("HMSET", "hash", foo: 1, bar: 2)
149
- redis.call("SET", "key", "value", ex: 5)
148
+ redis.call("HMSET", "hash", { "foo" => "1", "bar" => "2" })
150
149
  ```
151
150
 
152
151
  is equivalent to:
153
152
 
154
153
  ```ruby
155
154
  redis.call("HMSET", "hash", "foo", "1", "bar", "2")
156
- redis.call("SET", "key", "value", "ex", "5")
157
155
  ```
158
156
 
159
157
  Any other type requires the caller to explictly cast the argument as a string.
160
158
 
159
+ Keywords arguments are treated as Redis command flags:
160
+
161
+ ```ruby
162
+ redis.call("SET", "mykey", "value", nx: true, ex: 60)
163
+ redis.call("SET", "mykey", "value", nx: false, ex: nil)
164
+ ```
165
+
166
+ is equivalent to:
167
+
168
+ ```ruby
169
+ redis.call("SET", "mykey", "value", "nx", "ex", "60")
170
+ redis.call("SET", "mykey", "value")
171
+ ```
172
+
173
+ If flags are built dynamically, you'll have to explictly pass them as keyword arguments with `**`:
174
+
175
+ ```ruby
176
+ flags = {}
177
+ flags[:nx] = true if something?
178
+ redis.call("SET", "mykey", "value", **flags)
179
+ ```
180
+
181
+ **Important Note**: because of the keyword argument semantic change between Ruby 2 and Ruby 3,
182
+ unclosed hash literals with string keys may be interpreted differently:
183
+
184
+ ```ruby
185
+ redis.call("HMSET", "hash", "foo" => "bar")
186
+ ```
187
+
188
+ On Ruby 2 `"foo" => "bar"` will be passed as a postional argument, but on Ruby 3 it will be interpreted as keyword
189
+ arguments. To avoid such problem, make sure to enclose hash literals:
190
+
191
+ ```ruby
192
+ redis.call("HMSET", "hash", { "foo" => "bar" })
193
+ ```
194
+
195
+ ### Commands return values
196
+
197
+ Contrary to the `redis` gem, `redis-client` doesn't do any type casting on the return value of commands.
198
+
199
+ If you wish to cast the return value, you can pass a block to the `#call` familly of methods:
200
+
201
+ ```ruby
202
+ redis.call("INCR", "counter") # => 1
203
+ redis.call("GET", "counter") # => "1"
204
+ redis.call("GET", "counter", &:to_i) # => 1
205
+
206
+ redis.call("EXISTS", "counter") # => 1
207
+ redis.call("EXISTS", "counter") { |c| c > 0 } # => true
208
+ ```
209
+
161
210
  ### Blocking commands
162
211
 
163
212
  For blocking commands such as `BRPOP`, a custom timeout duration can be passed as first argument of the `#blocking_call` method:
@@ -338,6 +387,20 @@ redis.call("GET", "counter") # Will be retried up to 3 times.
338
387
  redis.call_once("INCR", "counter") # Won't be retried.
339
388
  ```
340
389
 
390
+ ### Drivers
391
+
392
+ `redis-client` ships with a pure Ruby socket implementation.
393
+
394
+ For increased performance, you can enable the `hiredis` binding by adding `hiredis-client` to your Gemfile:
395
+
396
+ ```ruby
397
+ gem "hiredis-client"
398
+ ```
399
+
400
+ The hiredis binding is only available on Linux, macOS and other POSIX platforms. You can install the gem on other platforms, but it won't have any effect.
401
+
402
+ The default driver can be set through `RedisClient.default_driver=`:
403
+
341
404
  ## Notable differences with the `redis` gem
342
405
 
343
406
  ### Thread Safety
data/Rakefile CHANGED
@@ -1,33 +1,53 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
4
3
  require "rake/extensiontask"
5
4
  require "rake/testtask"
6
5
  require 'rubocop/rake_task'
7
6
 
8
7
  RuboCop::RakeTask.new
9
8
 
9
+ require "rake/clean"
10
+ CLOBBER.include "pkg"
11
+ require "bundler/gem_helper"
12
+ Bundler::GemHelper.install_tasks(name: "redis-client")
13
+ Bundler::GemHelper.install_tasks(dir: "hiredis-client", name: "hiredis-client")
14
+
10
15
  gemspec = Gem::Specification.load("redis-client.gemspec")
11
16
  Rake::ExtensionTask.new do |ext|
12
17
  ext.name = "hiredis_connection"
13
- ext.ext_dir = "ext/redis_client/hiredis"
14
- ext.lib_dir = "lib/redis_client"
18
+ ext.ext_dir = "hiredis-client/ext/redis_client/hiredis"
19
+ ext.lib_dir = "hiredis-client/lib/redis_client"
15
20
  ext.gem_spec = gemspec
16
21
  CLEAN.add("#{ext.ext_dir}/vendor/*.{a,o}")
17
22
  end
18
23
 
19
- Rake::TestTask.new(:test) do |t|
20
- t.libs << "test"
21
- t.libs << "lib"
22
- t.test_files = FileList["test/**/*_test.rb"].exclude("test/sentinel/*_test.rb")
23
- end
24
-
25
24
  namespace :test do
25
+ Rake::TestTask.new(:ruby) do |t|
26
+ t.libs << "test"
27
+ t.libs << "lib"
28
+ t.test_files = FileList["test/**/*_test.rb"].exclude("test/sentinel/*_test.rb")
29
+ end
30
+
26
31
  Rake::TestTask.new(:sentinel) do |t|
32
+ t.libs << "test/sentinel"
27
33
  t.libs << "test"
28
34
  t.libs << "lib"
29
35
  t.test_files = FileList["test/sentinel/*_test.rb"]
30
36
  end
37
+
38
+ Rake::TestTask.new(:hiredis) do |t|
39
+ t.libs << "test/hiredis"
40
+ t.libs << "test"
41
+ t.libs << "lib"
42
+ t.test_files = FileList["test/**/*_test.rb"].exclude("test/sentinel/*_test.rb")
43
+ end
44
+ end
45
+
46
+ hiredis_supported = RUBY_ENGINE == "ruby" && !RUBY_PLATFORM.match?(/mswin/)
47
+ if hiredis_supported
48
+ task test: %i[test:ruby test:hiredis test:sentinel]
49
+ else
50
+ task test: %i[test:ruby test:sentinel]
31
51
  end
32
52
 
33
53
  namespace :hiredis do
@@ -36,10 +56,14 @@ namespace :hiredis do
36
56
  archive_path = "tmp/hiredis-#{version}.tar.gz"
37
57
  url = "https://github.com/redis/hiredis/archive/refs/tags/v#{version}.tar.gz"
38
58
  system("curl", "-L", url, out: archive_path) or raise "Downloading of #{url} failed"
39
- system("rm", "-rf", "ext/redis_client/hiredis/vendor/")
40
- system("mkdir", "-p", "ext/redis_client/hiredis/vendor/")
41
- system("tar", "xvzf", archive_path, "-C", "ext/redis_client/hiredis/vendor", "--strip-components", "1")
42
- system("rm", "-rf", "ext/redis_client/hiredis/vendor/examples")
59
+ system("rm", "-rf", "hiredis-client/ext/redis_client/hiredis/vendor/")
60
+ system("mkdir", "-p", "hiredis-client/ext/redis_client/hiredis/vendor/")
61
+ system(
62
+ "tar", "xvzf", archive_path,
63
+ "-C", "hiredis-client/ext/redis_client/hiredis/vendor",
64
+ "--strip-components", "1",
65
+ )
66
+ system("rm", "-rf", "hiredis-client/ext/redis_client/hiredis/vendor/examples")
43
67
  end
44
68
  end
45
69
 
@@ -57,7 +81,7 @@ namespace :benchmark do
57
81
  env = {}
58
82
  args = []
59
83
  args << "--yjit" if mode == :yjit
60
- env["DRIVER"] = "hiredis" if mode == :hiredis
84
+ env["DRIVER"] = mode == :hiredis ? "hiredis" : "ruby"
61
85
  system(env, RbConfig.ruby, *args, "benchmark/#{suite}.rb", out: output)
62
86
  end
63
87
 
@@ -81,14 +105,10 @@ namespace :benchmark do
81
105
  end
82
106
  end
83
107
 
84
- if RUBY_PLATFORM == "java"
85
- task default: %i[test test:sentinel rubocop]
86
- else
87
- task default: %i[compile test test:sentinel rubocop]
88
- end
89
-
90
- if ENV["DRIVER"] == "hiredis"
91
- task ci: %i[compile test test:sentinel]
108
+ if hiredis_supported
109
+ task default: %i[compile test rubocop]
110
+ task ci: %i[compile test]
92
111
  else
93
- task ci: %i[test test:sentinel]
112
+ task default: %i[test rubocop]
113
+ task ci: %i[test]
94
114
  end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RedisClient
4
+ module CommandBuilder
5
+ extend self
6
+
7
+ if Symbol.method_defined?(:name)
8
+ def generate!(args, kwargs)
9
+ command = args.flat_map do |element|
10
+ case element
11
+ when Hash
12
+ element.flatten
13
+ when Set
14
+ element.to_a
15
+ else
16
+ element
17
+ end
18
+ end
19
+
20
+ kwargs.each do |key, value|
21
+ if value
22
+ if value == true
23
+ command << key.name
24
+ else
25
+ command << key.name << value
26
+ end
27
+ end
28
+ end
29
+
30
+ command.map! do |element|
31
+ case element
32
+ when String
33
+ element
34
+ when Symbol
35
+ element.name
36
+ when Integer, Float
37
+ element.to_s
38
+ else
39
+ raise TypeError, "Unsupported command argument type: #{element.class}"
40
+ end
41
+ end
42
+
43
+ command
44
+ end
45
+ else
46
+ def generate!(args, kwargs)
47
+ command = args.flat_map do |element|
48
+ case element
49
+ when Hash
50
+ element.flatten
51
+ when Set
52
+ element.to_a
53
+ else
54
+ element
55
+ end
56
+ end
57
+
58
+ kwargs.each do |key, value|
59
+ if value
60
+ if value == true
61
+ command << key.to_s
62
+ else
63
+ command << key.to_s << value
64
+ end
65
+ end
66
+ end
67
+
68
+ command.map! do |element|
69
+ case element
70
+ when String
71
+ element
72
+ when Integer, Float, Symbol
73
+ element.to_s
74
+ else
75
+ raise TypeError, "Unsupported command argument type: #{element.class}"
76
+ end
77
+ end
78
+
79
+ command
80
+ end
81
+ end
82
+ end
83
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "openssl"
3
4
  require "uri"
4
5
 
5
6
  class RedisClient
@@ -11,7 +12,7 @@ class RedisClient
11
12
  DEFAULT_DB = 0
12
13
 
13
14
  module Common
14
- attr_reader :db, :username, :password, :id, :ssl, :ssl_params,
15
+ attr_reader :db, :username, :password, :id, :ssl, :ssl_params, :command_builder,
15
16
  :connect_timeout, :read_timeout, :write_timeout, :driver, :connection_prelude
16
17
 
17
18
  alias_method :ssl?, :ssl
@@ -27,7 +28,8 @@ class RedisClient
27
28
  connect_timeout: timeout,
28
29
  ssl: nil,
29
30
  ssl_params: nil,
30
- driver: :ruby,
31
+ driver: nil,
32
+ command_builder: CommandBuilder,
31
33
  reconnect_attempts: false
32
34
  )
33
35
  @username = username || DEFAULT_USERNAME
@@ -41,17 +43,9 @@ class RedisClient
41
43
  @read_timeout = read_timeout
42
44
  @write_timeout = write_timeout
43
45
 
44
- @driver = case driver
45
- when :ruby
46
- Connection
47
- when :hiredis
48
- unless defined?(RedisClient::HiredisConnection)
49
- require "redis_client/hiredis_connection"
50
- end
51
- HiredisConnection
52
- else
53
- raise ArgumentError, "Unknown driver #{driver.inspect}, expected one of: `:ruby`, `:hiredis`"
54
- end
46
+ @driver = driver ? RedisClient.driver(driver) : RedisClient.default_driver
47
+
48
+ @command_builder = command_builder
55
49
 
56
50
  reconnect_attempts = Array.new(reconnect_attempts, 0).freeze if reconnect_attempts.is_a?(Integer)
57
51
  @reconnect_attempts = reconnect_attempts
@@ -84,41 +78,8 @@ class RedisClient
84
78
  false
85
79
  end
86
80
 
87
- def hiredis_ssl_context
88
- @hiredis_ssl_context ||= HiredisConnection::SSLContext.new(
89
- ca_file: @ssl_params[:ca_file],
90
- ca_path: @ssl_params[:ca_path],
91
- cert: @ssl_params[:cert],
92
- key: @ssl_params[:key],
93
- hostname: @ssl_params[:hostname],
94
- )
95
- end
96
-
97
- def openssl_context
98
- @openssl_context ||= begin
99
- params = @ssl_params.dup || {}
100
-
101
- cert = params[:cert]
102
- if cert.is_a?(String)
103
- cert = File.read(cert) if File.exist?(cert)
104
- params[:cert] = OpenSSL::X509::Certificate.new(cert)
105
- end
106
-
107
- key = params[:key]
108
- if key.is_a?(String)
109
- key = File.read(key) if File.exist?(key)
110
- params[:key] = OpenSSL::PKey.read(key)
111
- end
112
-
113
- context = OpenSSL::SSL::SSLContext.new
114
- context.set_params(params)
115
- if context.verify_mode != OpenSSL::SSL::VERIFY_NONE
116
- if context.respond_to?(:verify_hostname) # Missing on JRuby
117
- context.verify_hostname
118
- end
119
- end
120
- context
121
- end
81
+ def ssl_context
82
+ @ssl_context ||= @driver.ssl_context(@ssl_params)
122
83
  end
123
84
 
124
85
  private
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RedisClient
4
+ module ConnectionMixin
5
+ def call(command, timeout)
6
+ write(command)
7
+ result = read(timeout)
8
+ if result.is_a?(CommandError)
9
+ raise result
10
+ else
11
+ result
12
+ end
13
+ end
14
+
15
+ def call_pipelined(commands, timeouts)
16
+ exception = nil
17
+
18
+ size = commands.size
19
+ results = Array.new(commands.size)
20
+ write_multi(commands)
21
+
22
+ size.times do |index|
23
+ timeout = timeouts && timeouts[index]
24
+ result = read(timeout)
25
+ if result.is_a?(CommandError)
26
+ exception ||= result
27
+ end
28
+ results[index] = result
29
+ end
30
+
31
+ if exception
32
+ raise exception
33
+ else
34
+ results
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RedisClient
4
+ module Decorator
5
+ class << self
6
+ def create(commands_mixin)
7
+ client_decorator = Class.new(Client)
8
+ client_decorator.include(commands_mixin)
9
+
10
+ pipeline_decorator = Class.new(Pipeline)
11
+ pipeline_decorator.include(commands_mixin)
12
+ client_decorator.const_set(:Pipeline, pipeline_decorator)
13
+
14
+ client_decorator
15
+ end
16
+ end
17
+
18
+ module CommandsMixin
19
+ def initialize(client)
20
+ @client = client
21
+ end
22
+
23
+ %i(call call_once blocking_call).each do |method|
24
+ class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
25
+ def #{method}(*args, &block)
26
+ @client.#{method}(*args, &block)
27
+ end
28
+ ruby2_keywords :#{method} if respond_to?(:ruby2_keywords, true)
29
+ RUBY
30
+ end
31
+ end
32
+
33
+ class Pipeline
34
+ include CommandsMixin
35
+ end
36
+
37
+ class Client
38
+ include CommandsMixin
39
+
40
+ def initialize(_client)
41
+ super
42
+ @_pipeline_class = self.class::Pipeline
43
+ end
44
+
45
+ def with(*args)
46
+ @client.with(*args) { |c| yield self.class.new(c) }
47
+ end
48
+ ruby2_keywords :with if respond_to?(:ruby2_keywords, true)
49
+
50
+ def pipelined
51
+ @client.pipelined { |p| yield @_pipeline_class.new(p) }
52
+ end
53
+
54
+ def multi(**kwargs)
55
+ @client.multi(**kwargs) { |p| yield @_pipeline_class.new(p) }
56
+ end
57
+
58
+ %i(close scan hscan sscan zscan).each do |method|
59
+ class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
60
+ def #{method}(*args, &block)
61
+ @client.#{method}(*args, &block)
62
+ end
63
+ ruby2_keywords :#{method} if respond_to?(:ruby2_keywords, true)
64
+ RUBY
65
+ end
66
+
67
+ %i(id config size connect_timeout read_timeout write_timeout).each do |reader|
68
+ class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
69
+ def #{reader}
70
+ @client.#{reader}
71
+ end
72
+ RUBY
73
+ end
74
+
75
+ %i(timeout connect_timeout read_timeout write_timeout).each do |writer|
76
+ class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
77
+ def #{writer}=(value)
78
+ @client.#{writer} = value
79
+ end
80
+ RUBY
81
+ end
82
+ end
83
+ end
84
+ end
@@ -49,40 +49,48 @@ class RedisClient
49
49
  pool.size
50
50
  end
51
51
 
52
- %w(pipelined).each do |method|
53
- class_eval <<~RUBY, __FILE__, __LINE__ + 1
54
- def #{method}(&block)
55
- with { |r| r.#{method}(&block) }
56
- end
57
- RUBY
58
- end
59
-
60
- %w(multi).each do |method|
61
- class_eval <<~RUBY, __FILE__, __LINE__ + 1
62
- def #{method}(**kwargs, &block)
63
- with { |r| r.#{method}(**kwargs, &block) }
64
- end
65
- RUBY
66
- end
52
+ methods = %w(pipelined multi pubsub call call_once blocking_call)
53
+ iterable_methods = %w(scan sscan hscan zscan)
54
+ begin
55
+ methods.each do |method|
56
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
57
+ def #{method}(...)
58
+ with { |r| r.#{method}(...) }
59
+ end
60
+ RUBY
61
+ end
67
62
 
68
- %w(call call_once blocking_call pubsub).each do |method|
69
- class_eval <<~RUBY, __FILE__, __LINE__ + 1
70
- def #{method}(*args)
71
- with { |r| r.#{method}(*args) }
72
- end
73
- RUBY
74
- end
63
+ iterable_methods.each do |method|
64
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
65
+ def #{method}(...)
66
+ unless block_given?
67
+ return to_enum(__callee__, ...)
68
+ end
75
69
 
76
- %w(scan sscan hscan zscan).each do |method|
77
- class_eval <<~RUBY, __FILE__, __LINE__ + 1
78
- def #{method}(*args, &block)
79
- unless block_given?
80
- return to_enum(__callee__, *args)
70
+ with { |r| r.#{method}(...) }
71
+ end
72
+ RUBY
73
+ end
74
+ rescue SyntaxError
75
+ methods.each do |method|
76
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
77
+ def #{method}(*args, &block)
78
+ with { |r| r.#{method}(*args, &block) }
81
79
  end
80
+ RUBY
81
+ end
82
82
 
83
- with { |r| r.#{method}(*args, &block) }
84
- end
85
- RUBY
83
+ iterable_methods.each do |method|
84
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
85
+ def #{method}(*args, &block)
86
+ unless block_given?
87
+ return to_enum(__callee__, *args)
88
+ end
89
+
90
+ with { |r| r.#{method}(*args, &block) }
91
+ end
92
+ RUBY
93
+ end
86
94
  end
87
95
 
88
96
  private