redis-client 0.2.1 → 0.5.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/CHANGELOG.md +19 -0
- data/Gemfile +1 -2
- data/Gemfile.lock +2 -3
- data/README.md +71 -8
- data/Rakefile +43 -23
- data/lib/redis_client/command_builder.rb +91 -0
- data/lib/redis_client/config.rb +19 -50
- data/lib/redis_client/connection_mixin.rb +40 -0
- data/lib/redis_client/decorator.rb +84 -0
- 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 +183 -36
- data/redis-client.gemspec +2 -4
- metadata +12 -59
- data/.rubocop.yml +0 -190
- data/ext/redis_client/hiredis/export.clang +0 -2
- data/ext/redis_client/hiredis/export.gcc +0 -7
- data/ext/redis_client/hiredis/extconf.rb +0 -61
- data/ext/redis_client/hiredis/hiredis_connection.c +0 -708
- data/ext/redis_client/hiredis/vendor/.gitignore +0 -9
- data/ext/redis_client/hiredis/vendor/.travis.yml +0 -131
- data/ext/redis_client/hiredis/vendor/CHANGELOG.md +0 -364
- data/ext/redis_client/hiredis/vendor/CMakeLists.txt +0 -165
- data/ext/redis_client/hiredis/vendor/COPYING +0 -29
- data/ext/redis_client/hiredis/vendor/Makefile +0 -308
- data/ext/redis_client/hiredis/vendor/README.md +0 -664
- data/ext/redis_client/hiredis/vendor/adapters/ae.h +0 -130
- data/ext/redis_client/hiredis/vendor/adapters/glib.h +0 -156
- data/ext/redis_client/hiredis/vendor/adapters/ivykis.h +0 -84
- data/ext/redis_client/hiredis/vendor/adapters/libev.h +0 -179
- data/ext/redis_client/hiredis/vendor/adapters/libevent.h +0 -175
- data/ext/redis_client/hiredis/vendor/adapters/libuv.h +0 -117
- data/ext/redis_client/hiredis/vendor/adapters/macosx.h +0 -115
- data/ext/redis_client/hiredis/vendor/adapters/qt.h +0 -135
- data/ext/redis_client/hiredis/vendor/alloc.c +0 -86
- data/ext/redis_client/hiredis/vendor/alloc.h +0 -91
- data/ext/redis_client/hiredis/vendor/appveyor.yml +0 -24
- data/ext/redis_client/hiredis/vendor/async.c +0 -887
- data/ext/redis_client/hiredis/vendor/async.h +0 -147
- data/ext/redis_client/hiredis/vendor/async_private.h +0 -75
- data/ext/redis_client/hiredis/vendor/dict.c +0 -352
- data/ext/redis_client/hiredis/vendor/dict.h +0 -126
- data/ext/redis_client/hiredis/vendor/fmacros.h +0 -12
- data/ext/redis_client/hiredis/vendor/hiredis-config.cmake.in +0 -13
- data/ext/redis_client/hiredis/vendor/hiredis.c +0 -1174
- data/ext/redis_client/hiredis/vendor/hiredis.h +0 -336
- data/ext/redis_client/hiredis/vendor/hiredis.pc.in +0 -12
- data/ext/redis_client/hiredis/vendor/hiredis_ssl-config.cmake.in +0 -13
- data/ext/redis_client/hiredis/vendor/hiredis_ssl.h +0 -157
- data/ext/redis_client/hiredis/vendor/hiredis_ssl.pc.in +0 -12
- data/ext/redis_client/hiredis/vendor/net.c +0 -612
- data/ext/redis_client/hiredis/vendor/net.h +0 -56
- data/ext/redis_client/hiredis/vendor/read.c +0 -739
- data/ext/redis_client/hiredis/vendor/read.h +0 -129
- data/ext/redis_client/hiredis/vendor/sds.c +0 -1289
- data/ext/redis_client/hiredis/vendor/sds.h +0 -278
- data/ext/redis_client/hiredis/vendor/sdsalloc.h +0 -44
- data/ext/redis_client/hiredis/vendor/sockcompat.c +0 -248
- data/ext/redis_client/hiredis/vendor/sockcompat.h +0 -92
- data/ext/redis_client/hiredis/vendor/ssl.c +0 -544
- data/ext/redis_client/hiredis/vendor/test.c +0 -1401
- data/ext/redis_client/hiredis/vendor/test.sh +0 -78
- data/ext/redis_client/hiredis/vendor/win32.h +0 -56
- data/lib/redis_client/buffered_io.rb +0 -151
- data/lib/redis_client/hiredis_connection.rb +0 -80
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 770636c5814252674d680c45f75af4ea291b698c18382b49d93bec4e3d7ab45b
|
4
|
+
data.tar.gz: bdef4f0f80574a9aaf29613ec23d31d7a0c3d916ca3945ac412b36b55eecd73a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3367281b907b6e38e73ba1e9a74f63404e40f422b319c551645fec1efa694e87c09cd96c67d308c362407c84be59eb62435b9951740cd515727a554050ae3137
|
7
|
+
data.tar.gz: 9dc6f5e6638405c8841b267d0c9cf882d0721cbd04655984af71bddb66cc7e2f4698e7199e752582064e4db3ca8e3f45a77b56241df309ae0c2e231ebff1cf2b
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
- Fix handling of connection URLs with empty passwords (`redis://:pass@example.com`).
|
4
|
+
- Handle URLs with IPv6 hosts.
|
5
|
+
- Add `RedisClient::Config#server_url` as a quick way to identify which server the client is pointing to.
|
6
|
+
- Add `CommandError#command` to expose the command that caused the error.
|
7
|
+
- Raise a more explicit error when connecting to older redises without RESP3 support (5.0 and older).
|
8
|
+
- Properly reject empty commands early.
|
9
|
+
|
10
|
+
# 0.4.0
|
11
|
+
|
12
|
+
- The `hiredis` driver have been moved to the `hiredis-client` gem.
|
13
|
+
|
14
|
+
# 0.3.0
|
15
|
+
|
16
|
+
- `hiredis` is now the default driver when available.
|
17
|
+
- Add `RedisClient.default_driver=`.
|
18
|
+
- `#call` now takes an optional block to cast the return value.
|
19
|
+
- Treat `#call` keyword arguments as Redis flags.
|
20
|
+
- Fix `RedisClient#multi` returning some errors as values instead of raising them.
|
21
|
+
|
3
22
|
# 0.2.1
|
4
23
|
|
5
24
|
- Use a more robust way to detect the current compiler.
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
redis-client (0.
|
4
|
+
redis-client (0.5.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.
|
66
|
+
2.3.13
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
`redis-client` is a simple, low-level, client for Redis 6+.
|
4
4
|
|
5
|
-
Contrary to the `redis` gem, `redis-client` doesn't try to map all
|
5
|
+
Contrary to the `redis` gem, `redis-client` doesn't try to map all Redis commands to Ruby constructs,
|
6
6
|
it merely is a thin wrapper on top of the RESP3 protocol.
|
7
7
|
|
8
8
|
## Installation
|
@@ -63,7 +63,7 @@ redis.call("GET", "mykey")
|
|
63
63
|
### Configuration
|
64
64
|
|
65
65
|
- `url`: A Redis connection URL, e.g. `redis://example.com:6379/5`, a `rediss://` scheme enable SSL, and the path is interpreted as a database number.
|
66
|
-
Note
|
66
|
+
Note that all other configurations take precedence, e.g. `RedisClient.config(url: "redis://localhost:3000" port: 6380)` will connect on port `6380`.
|
67
67
|
- `host`: The server hostname or IP address. Defaults to `"localhost"`.
|
68
68
|
- `port`: The server port. Defaults to `6379`.
|
69
69
|
- `path`: The path to a UNIX socket, if set `url`, `host` and `port` are ignored.
|
@@ -142,21 +142,70 @@ is equivalent to:
|
|
142
142
|
redis.call("LPUSH", "list", "1", "2", "3", "4")
|
143
143
|
```
|
144
144
|
|
145
|
-
Hashes are
|
145
|
+
Hashes are flattened as well:
|
146
146
|
|
147
147
|
```ruby
|
148
|
-
redis.call("HMSET", "hash", foo
|
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
|
-
Any other type requires the caller to
|
157
|
+
Any other type requires the caller to explicitly cast the argument as a string.
|
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 explicitly 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 positional 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` family 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
|
+
```
|
160
209
|
|
161
210
|
### Blocking commands
|
162
211
|
|
@@ -248,7 +297,7 @@ end
|
|
248
297
|
|
249
298
|
If the transaction wasn't successful, `#multi` will return `nil`.
|
250
299
|
|
251
|
-
Note that transactions using optimistic locking aren't automatically retried
|
300
|
+
Note that transactions using optimistic locking aren't automatically retried upon connection errors.
|
252
301
|
|
253
302
|
### Publish / Subscribe
|
254
303
|
|
@@ -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(
|
42
|
-
|
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"] =
|
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
|
85
|
-
task default: %i[
|
86
|
-
|
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
|
112
|
+
task default: %i[test rubocop]
|
113
|
+
task ci: %i[test]
|
94
114
|
end
|
@@ -0,0 +1,91 @@
|
|
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
|
+
if command.empty?
|
44
|
+
raise ArgumentError, "can't issue an empty redis command"
|
45
|
+
end
|
46
|
+
|
47
|
+
command
|
48
|
+
end
|
49
|
+
else
|
50
|
+
def generate!(args, kwargs)
|
51
|
+
command = args.flat_map do |element|
|
52
|
+
case element
|
53
|
+
when Hash
|
54
|
+
element.flatten
|
55
|
+
when Set
|
56
|
+
element.to_a
|
57
|
+
else
|
58
|
+
element
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
kwargs.each do |key, value|
|
63
|
+
if value
|
64
|
+
if value == true
|
65
|
+
command << key.to_s
|
66
|
+
else
|
67
|
+
command << key.to_s << value
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
command.map! do |element|
|
73
|
+
case element
|
74
|
+
when String
|
75
|
+
element
|
76
|
+
when Integer, Float, Symbol
|
77
|
+
element.to_s
|
78
|
+
else
|
79
|
+
raise TypeError, "Unsupported command argument type: #{element.class}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
if command.empty?
|
84
|
+
raise ArgumentError, "can't issue an empty redis command"
|
85
|
+
end
|
86
|
+
|
87
|
+
command
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/redis_client/config.rb
CHANGED
@@ -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:
|
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 =
|
45
|
-
|
46
|
-
|
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,40 +78,15 @@ class RedisClient
|
|
84
78
|
false
|
85
79
|
end
|
86
80
|
|
87
|
-
def
|
88
|
-
@
|
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
|
-
)
|
81
|
+
def ssl_context
|
82
|
+
@ssl_context ||= @driver.ssl_context(@ssl_params)
|
95
83
|
end
|
96
84
|
|
97
|
-
def
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
85
|
+
def server_url
|
86
|
+
if path
|
87
|
+
"#{path}/#{db}"
|
88
|
+
else
|
89
|
+
"redis#{'s' if ssl?}://#{host}:#{port}/#{db}"
|
121
90
|
end
|
122
91
|
end
|
123
92
|
|
@@ -149,15 +118,15 @@ class RedisClient
|
|
149
118
|
path: nil,
|
150
119
|
**kwargs
|
151
120
|
)
|
152
|
-
|
153
|
-
|
121
|
+
if url
|
122
|
+
uri = URI.parse(url)
|
154
123
|
kwargs[:ssl] = uri.scheme == "rediss" unless kwargs.key?(:ssl)
|
155
124
|
|
156
|
-
kwargs[:username] ||= uri.user && uri.
|
125
|
+
kwargs[:username] ||= uri.user if uri.password && !uri.user.empty?
|
157
126
|
|
158
127
|
kwargs[:password] ||= if uri.user && !uri.password
|
159
128
|
URI.decode_www_form_component(uri.user)
|
160
|
-
elsif uri
|
129
|
+
elsif uri.user && uri.password
|
161
130
|
URI.decode_www_form_component(uri.password)
|
162
131
|
end
|
163
132
|
|
@@ -166,7 +135,7 @@ class RedisClient
|
|
166
135
|
|
167
136
|
super(**kwargs)
|
168
137
|
|
169
|
-
@host = host || uri&.host || DEFAULT_HOST
|
138
|
+
@host = host || uri&.host&.sub(/\A\[(.*)\]\z/, '\1') || DEFAULT_HOST
|
170
139
|
@port = port || uri&.port || DEFAULT_PORT
|
171
140
|
@path = path
|
172
141
|
end
|
@@ -0,0 +1,40 @@
|
|
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
|
+
result._set_command(command)
|
10
|
+
raise result
|
11
|
+
else
|
12
|
+
result
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def call_pipelined(commands, timeouts)
|
17
|
+
exception = nil
|
18
|
+
|
19
|
+
size = commands.size
|
20
|
+
results = Array.new(commands.size)
|
21
|
+
write_multi(commands)
|
22
|
+
|
23
|
+
size.times do |index|
|
24
|
+
timeout = timeouts && timeouts[index]
|
25
|
+
result = read(timeout)
|
26
|
+
if result.is_a?(CommandError)
|
27
|
+
result._set_command(commands[index])
|
28
|
+
exception ||= result
|
29
|
+
end
|
30
|
+
results[index] = result
|
31
|
+
end
|
32
|
+
|
33
|
+
if exception
|
34
|
+
raise exception
|
35
|
+
else
|
36
|
+
results
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
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
|