redis 4.6.0 → 4.7.1
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 +14 -1
- data/README.md +15 -0
- data/lib/redis/client.rb +3 -3
- data/lib/redis/cluster/command_loader.rb +5 -5
- data/lib/redis/cluster/node_loader.rb +8 -11
- data/lib/redis/cluster/option.rb +10 -3
- data/lib/redis/cluster/slot_loader.rb +9 -12
- data/lib/redis/commands/sorted_sets.rb +9 -1
- data/lib/redis/commands/transactions.rb +47 -0
- data/lib/redis/connection/hiredis.rb +0 -2
- data/lib/redis/connection/ruby.rb +6 -0
- data/lib/redis/errors.rb +9 -0
- data/lib/redis/pipeline.rb +33 -1
- data/lib/redis/version.rb +1 -1
- data/lib/redis.rb +7 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 185ccbe36adc1e1561474af65d0e0b8d246e9c161757c9218f5ac7b6c1d24bb9
|
4
|
+
data.tar.gz: 03a4203747503b971551500ea40f996cb9620e5d9a693231d7a63944a9f73bf8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3354c910dcf3feb76b7f4ed7adb5bcce4f1cfc3ef7c58347897ba4705337a89052d2db67a8a71765f34e16f01a7ab2c8894bba60a3a89e1f36b6487f3fe0089b
|
7
|
+
data.tar.gz: 0b23396f9ecf62dd952536b94107a7748bb5ee225e7c7ac1da36e813e7fffc588c5939abe1800fc440e173acc54a9ac3649037b62b3ff59aa946ffc9a3e11180
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 4.7.1
|
4
|
+
|
5
|
+
* Gracefully handle OpenSSL 3.0 EOF Errors (`OpenSSL::SSL::SSLError: SSL_read: unexpected eof while reading`). See #1106
|
6
|
+
This happens frequently on heroku-22.
|
7
|
+
|
8
|
+
# 4.7.0
|
9
|
+
|
10
|
+
* Support single endpoint architecture with SSL/TLS in cluster mode. See #1086.
|
11
|
+
* `zrem` and `zadd` act as noop when provided an empty list of keys. See #1097.
|
12
|
+
* Support IPv6 URLs.
|
13
|
+
* Add `Redis#with` for better compatibility with `connection_pool` usage.
|
14
|
+
* Fix the block form of `multi` called inside `pipelined`. Previously the `MUTLI/EXEC` wouldn't be sent. See #1073.
|
15
|
+
|
3
16
|
# 4.6.0
|
4
17
|
|
5
18
|
* Deprecate `Redis.current`.
|
@@ -37,7 +50,7 @@
|
|
37
50
|
* `Redis#synchronize` is now private like it should always have been.
|
38
51
|
|
39
52
|
* Add `Redis.silence_deprecations=` to turn off deprecation warnings.
|
40
|
-
If you don't wish to see warnings yet, you can set `Redis.silence_deprecations =
|
53
|
+
If you don't wish to see warnings yet, you can set `Redis.silence_deprecations = true`.
|
41
54
|
It is however heavily recommended to fix them instead when possible.
|
42
55
|
* Add `Redis.raise_deprecations=` to turn deprecation warnings into errors.
|
43
56
|
This makes it easier to identitify the source of deprecated APIs usage.
|
data/README.md
CHANGED
@@ -155,6 +155,21 @@ redis.mget('{key}1', '{key}2')
|
|
155
155
|
* The client support permanent node failures, and will reroute requests to promoted slaves.
|
156
156
|
* The client supports `MOVED` and `ASK` redirections transparently.
|
157
157
|
|
158
|
+
## Cluster mode with SSL/TLS
|
159
|
+
Since Redis can return FQDN of nodes in reply to client since `7.*` with CLUSTER commands, we can use cluster feature with SSL/TLS connection like this:
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
Redis.new(cluster: %w[rediss://foo.example.com:6379])
|
163
|
+
```
|
164
|
+
|
165
|
+
On the other hand, in Redis versions prior to `6.*`, you can specify options like the following if cluster mode is enabled and client has to connect to nodes via single endpoint with SSL/TLS.
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
Redis.new(cluster: %w[rediss://foo-endpoint.example.com:6379], fixed_hostname: 'foo-endpoint.example.com')
|
169
|
+
```
|
170
|
+
|
171
|
+
In case of the above architecture, if you don't pass the `fixed_hostname` option to the client and servers return IP addresses of nodes, the client may fail to verify certificates.
|
172
|
+
|
158
173
|
## Storing objects
|
159
174
|
|
160
175
|
Redis "string" types can be used to store serialized Ruby objects, for
|
data/lib/redis/client.rb
CHANGED
@@ -150,7 +150,7 @@ class Redis
|
|
150
150
|
end
|
151
151
|
|
152
152
|
def id
|
153
|
-
@options[:id] || "
|
153
|
+
@options[:id] || "#{@options[:ssl] ? 'rediss' : @options[:scheme]}://#{location}/#{db}"
|
154
154
|
end
|
155
155
|
|
156
156
|
def location
|
@@ -302,7 +302,7 @@ class Redis
|
|
302
302
|
e2 = TimeoutError.new("Connection timed out")
|
303
303
|
e2.set_backtrace(e1.backtrace)
|
304
304
|
raise e2
|
305
|
-
rescue Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED, Errno::EBADF, Errno::EINVAL => e
|
305
|
+
rescue Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED, Errno::EBADF, Errno::EINVAL, EOFError => e
|
306
306
|
raise ConnectionError, "Connection lost (%s)" % [e.class.name.split("::").last]
|
307
307
|
end
|
308
308
|
|
@@ -464,7 +464,7 @@ class Redis
|
|
464
464
|
defaults[:path] = uri.path
|
465
465
|
when "redis", "rediss"
|
466
466
|
defaults[:scheme] = uri.scheme
|
467
|
-
defaults[:host] = uri.host if uri.host
|
467
|
+
defaults[:host] = uri.host.sub(/\A\[(.*)\]\z/, '\1') if uri.host
|
468
468
|
defaults[:port] = uri.port if uri.port
|
469
469
|
defaults[:username] = CGI.unescape(uri.user) if uri.user && !uri.user.empty?
|
470
470
|
defaults[:password] = CGI.unescape(uri.password) if uri.password && !uri.password.empty?
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'redis/errors'
|
4
4
|
|
5
5
|
class Redis
|
6
6
|
class Cluster
|
@@ -10,15 +10,15 @@ class Redis
|
|
10
10
|
module_function
|
11
11
|
|
12
12
|
def load(nodes)
|
13
|
-
nodes.
|
13
|
+
errors = nodes.map do |node|
|
14
14
|
begin
|
15
15
|
return fetch_command_details(node)
|
16
|
-
rescue CannotConnectError, ConnectionError, CommandError
|
17
|
-
|
16
|
+
rescue CannotConnectError, ConnectionError, CommandError => error
|
17
|
+
error
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
raise
|
21
|
+
raise InitialSetupError, errors
|
22
22
|
end
|
23
23
|
|
24
24
|
def fetch_command_details(node)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'redis/errors'
|
4
4
|
|
5
5
|
class Redis
|
6
6
|
class Cluster
|
@@ -9,16 +9,15 @@ class Redis
|
|
9
9
|
module_function
|
10
10
|
|
11
11
|
def load_flags(nodes)
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
errors = nodes.map do |node|
|
13
|
+
begin
|
14
|
+
return fetch_node_info(node)
|
15
|
+
rescue CannotConnectError, ConnectionError, CommandError => error
|
16
|
+
error
|
17
|
+
end
|
17
18
|
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
raise CannotConnectError, 'Redis client could not connect to any cluster nodes'
|
20
|
+
raise InitialSetupError, errors
|
22
21
|
end
|
23
22
|
|
24
23
|
def fetch_node_info(node)
|
@@ -27,8 +26,6 @@ class Redis
|
|
27
26
|
.map { |str| str.split(' ') }
|
28
27
|
.map { |arr| [arr[1].split('@').first, (arr[2].split(',') & %w[master slave]).first] }
|
29
28
|
.to_h
|
30
|
-
rescue CannotConnectError, ConnectionError, CommandError
|
31
|
-
{} # can retry on another node
|
32
29
|
end
|
33
30
|
|
34
31
|
private_class_method :fetch_node_info
|
data/lib/redis/cluster/option.rb
CHANGED
@@ -17,6 +17,7 @@ class Redis
|
|
17
17
|
node_addrs = options.delete(:cluster)
|
18
18
|
@node_opts = build_node_options(node_addrs)
|
19
19
|
@replica = options.delete(:replica) == true
|
20
|
+
@fixed_hostname = options.delete(:fixed_hostname)
|
20
21
|
add_common_node_option_if_needed(options, @node_opts, :scheme)
|
21
22
|
add_common_node_option_if_needed(options, @node_opts, :username)
|
22
23
|
add_common_node_option_if_needed(options, @node_opts, :password)
|
@@ -24,8 +25,12 @@ class Redis
|
|
24
25
|
end
|
25
26
|
|
26
27
|
def per_node_key
|
27
|
-
@node_opts.map
|
28
|
-
|
28
|
+
@node_opts.map do |opt|
|
29
|
+
node_key = NodeKey.build_from_host_port(opt[:host], opt[:port])
|
30
|
+
options = @options.merge(opt)
|
31
|
+
options = options.merge(host: @fixed_hostname) if @fixed_hostname && !@fixed_hostname.empty?
|
32
|
+
[node_key, options]
|
33
|
+
end.to_h
|
29
34
|
end
|
30
35
|
|
31
36
|
def use_replica?
|
@@ -64,8 +69,10 @@ class Redis
|
|
64
69
|
raise InvalidClientOptionError, "Invalid uri scheme #{addr}" unless VALID_SCHEMES.include?(uri.scheme)
|
65
70
|
|
66
71
|
db = uri.path.split('/')[1]&.to_i
|
72
|
+
username = uri.user ? URI.decode_www_form_component(uri.user) : nil
|
73
|
+
password = uri.password ? URI.decode_www_form_component(uri.password) : nil
|
67
74
|
|
68
|
-
{ scheme: uri.scheme, username:
|
75
|
+
{ scheme: uri.scheme, username: username, password: password, host: uri.host, port: uri.port, db: db }
|
69
76
|
.reject { |_, v| v.nil? || v == '' }
|
70
77
|
rescue URI::InvalidURIError => err
|
71
78
|
raise InvalidClientOptionError, err.message
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
require 'redis/errors'
|
4
|
+
require 'redis/cluster/node_key'
|
5
5
|
|
6
6
|
class Redis
|
7
7
|
class Cluster
|
@@ -10,16 +10,15 @@ class Redis
|
|
10
10
|
module_function
|
11
11
|
|
12
12
|
def load(nodes)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
errors = nodes.map do |node|
|
14
|
+
begin
|
15
|
+
return fetch_slot_info(node)
|
16
|
+
rescue CannotConnectError, ConnectionError, CommandError => error
|
17
|
+
error
|
18
|
+
end
|
18
19
|
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
raise CannotConnectError, 'Redis client could not connect to any cluster nodes'
|
21
|
+
raise InitialSetupError, errors
|
23
22
|
end
|
24
23
|
|
25
24
|
def fetch_slot_info(node)
|
@@ -27,8 +26,6 @@ class Redis
|
|
27
26
|
node.call(%i[cluster slots])
|
28
27
|
.flat_map { |arr| parse_slot_info(arr, default_ip: node.host) }
|
29
28
|
.each_with_object(hash_with_default_arr) { |arr, h| h[arr[0]] << arr[1] }
|
30
|
-
rescue CannotConnectError, ConnectionError, CommandError
|
31
|
-
{} # can retry on another node
|
32
29
|
end
|
33
30
|
|
34
31
|
def parse_slot_info(arr, default_ip:)
|
@@ -60,8 +60,11 @@ class Redis
|
|
60
60
|
command << "INCR" if incr
|
61
61
|
|
62
62
|
if args.size == 1 && args[0].is_a?(Array)
|
63
|
+
members_to_add = args[0]
|
64
|
+
return 0 if members_to_add.empty?
|
65
|
+
|
63
66
|
# Variadic: return float if INCR, integer if !INCR
|
64
|
-
send_command(command +
|
67
|
+
send_command(command + members_to_add, &(incr ? Floatify : nil))
|
65
68
|
elsif args.size == 2
|
66
69
|
# Single pair: return float if INCR, boolean if !INCR
|
67
70
|
send_command(command + args, &(incr ? Floatify : Boolify))
|
@@ -102,6 +105,11 @@ class Redis
|
|
102
105
|
# - `Integer` when an array of pairs is specified, holding the number of
|
103
106
|
# members that were removed to the sorted set
|
104
107
|
def zrem(key, member)
|
108
|
+
if member.is_a?(Array)
|
109
|
+
members_to_remove = member
|
110
|
+
return 0 if members_to_remove.empty?
|
111
|
+
end
|
112
|
+
|
105
113
|
send_command([:zrem, key, member]) do |reply|
|
106
114
|
if member.is_a? Array
|
107
115
|
# Variadic: return integer
|
@@ -3,6 +3,53 @@
|
|
3
3
|
class Redis
|
4
4
|
module Commands
|
5
5
|
module Transactions
|
6
|
+
# Mark the start of a transaction block.
|
7
|
+
#
|
8
|
+
# Passing a block is optional.
|
9
|
+
#
|
10
|
+
# @example With a block
|
11
|
+
# redis.multi do |multi|
|
12
|
+
# multi.set("key", "value")
|
13
|
+
# multi.incr("counter")
|
14
|
+
# end # => ["OK", 6]
|
15
|
+
#
|
16
|
+
# @example Without a block
|
17
|
+
# redis.multi
|
18
|
+
# # => "OK"
|
19
|
+
# redis.set("key", "value")
|
20
|
+
# # => "QUEUED"
|
21
|
+
# redis.incr("counter")
|
22
|
+
# # => "QUEUED"
|
23
|
+
# redis.exec
|
24
|
+
# # => ["OK", 6]
|
25
|
+
#
|
26
|
+
# @yield [multi] the commands that are called inside this block are cached
|
27
|
+
# and written to the server upon returning from it
|
28
|
+
# @yieldparam [Redis] multi `self`
|
29
|
+
#
|
30
|
+
# @return [String, Array<...>]
|
31
|
+
# - when a block is not given, `OK`
|
32
|
+
# - when a block is given, an array with replies
|
33
|
+
#
|
34
|
+
# @see #watch
|
35
|
+
# @see #unwatch
|
36
|
+
def multi(&block) # :nodoc:
|
37
|
+
if block_given?
|
38
|
+
if block&.arity == 0
|
39
|
+
Pipeline.deprecation_warning("multi", Kernel.caller_locations(1, 5))
|
40
|
+
end
|
41
|
+
|
42
|
+
synchronize do |prior_client|
|
43
|
+
pipeline = Pipeline::Multi.new(prior_client)
|
44
|
+
pipelined_connection = PipelinedConnection.new(pipeline)
|
45
|
+
yield pipelined_connection
|
46
|
+
prior_client.call_pipeline(pipeline)
|
47
|
+
end
|
48
|
+
else
|
49
|
+
send_command([:multi])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
6
53
|
# Watch the given keys to determine execution of the MULTI/EXEC block.
|
7
54
|
#
|
8
55
|
# Using a block is optional, but is necessary for thread-safety.
|
@@ -15,8 +15,6 @@ class Redis
|
|
15
15
|
|
16
16
|
if config[:scheme] == "unix"
|
17
17
|
connection.connect_unix(config[:path], connect_timeout)
|
18
|
-
elsif config[:scheme] == "rediss" || config[:ssl]
|
19
|
-
raise NotImplementedError, "SSL not supported by hiredis driver"
|
20
18
|
else
|
21
19
|
connection.connect(config[:host], config[:port], connect_timeout)
|
22
20
|
end
|
@@ -384,6 +384,12 @@ class Redis
|
|
384
384
|
format_reply(reply_type, line)
|
385
385
|
rescue Errno::EAGAIN
|
386
386
|
raise TimeoutError
|
387
|
+
rescue OpenSSL::SSL::SSLError => ssl_error
|
388
|
+
if ssl_error.message.match?(/SSL_read: unexpected eof while reading/i)
|
389
|
+
raise EOFError, ssl_error.message
|
390
|
+
else
|
391
|
+
raise
|
392
|
+
end
|
387
393
|
end
|
388
394
|
|
389
395
|
def format_reply(reply_type, line)
|
data/lib/redis/errors.rb
CHANGED
@@ -45,6 +45,15 @@ class Redis
|
|
45
45
|
end
|
46
46
|
|
47
47
|
class Cluster
|
48
|
+
# Raised when client connected to redis as cluster mode
|
49
|
+
# and failed to fetch cluster state information by commands.
|
50
|
+
class InitialSetupError < BaseError
|
51
|
+
# @param errors [Array<Redis::BaseError>]
|
52
|
+
def initialize(errors)
|
53
|
+
super("Redis client could not fetch cluster information: #{errors.map(&:message).uniq.join(',')}")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
48
57
|
# Raised when client connected to redis as cluster mode
|
49
58
|
# and some cluster subcommands were called.
|
50
59
|
class OrchestrationCommandNotSupported < BaseError
|
data/lib/redis/pipeline.rb
CHANGED
@@ -22,6 +22,11 @@ class Redis
|
|
22
22
|
yield self
|
23
23
|
end
|
24
24
|
|
25
|
+
def call_pipeline(pipeline)
|
26
|
+
@pipeline.call_pipeline(pipeline)
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
25
30
|
private
|
26
31
|
|
27
32
|
def synchronize
|
@@ -69,6 +74,7 @@ class Redis
|
|
69
74
|
attr_reader :client
|
70
75
|
|
71
76
|
attr :futures
|
77
|
+
alias materialized_futures futures
|
72
78
|
|
73
79
|
def initialize(client)
|
74
80
|
@client = client.is_a?(Pipeline) ? client.client : client
|
@@ -112,7 +118,7 @@ class Redis
|
|
112
118
|
|
113
119
|
def call_pipeline(pipeline)
|
114
120
|
@shutdown = true if pipeline.shutdown?
|
115
|
-
@futures.concat(pipeline.
|
121
|
+
@futures.concat(pipeline.materialized_futures)
|
116
122
|
@db = pipeline.db
|
117
123
|
nil
|
118
124
|
end
|
@@ -169,6 +175,18 @@ class Redis
|
|
169
175
|
end
|
170
176
|
end
|
171
177
|
|
178
|
+
def materialized_futures
|
179
|
+
if empty?
|
180
|
+
[]
|
181
|
+
else
|
182
|
+
[
|
183
|
+
Future.new([:multi], nil, 0),
|
184
|
+
*futures,
|
185
|
+
MultiFuture.new(futures)
|
186
|
+
]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
172
190
|
def timeouts
|
173
191
|
if empty?
|
174
192
|
[]
|
@@ -271,4 +289,18 @@ class Redis
|
|
271
289
|
Future
|
272
290
|
end
|
273
291
|
end
|
292
|
+
|
293
|
+
class MultiFuture < Future
|
294
|
+
def initialize(futures)
|
295
|
+
@futures = futures
|
296
|
+
@command = [:exec]
|
297
|
+
end
|
298
|
+
|
299
|
+
def _set(replies)
|
300
|
+
@futures.each_with_index do |future, index|
|
301
|
+
future._set(replies[index])
|
302
|
+
end
|
303
|
+
replies
|
304
|
+
end
|
305
|
+
end
|
274
306
|
end
|
data/lib/redis/version.rb
CHANGED
data/lib/redis.rb
CHANGED
@@ -37,7 +37,7 @@ class Redis
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def current
|
40
|
-
deprecate!("`Redis.current
|
40
|
+
deprecate!("`Redis.current` is deprecated and will be removed in 5.0. (called from: #{caller(1, 1).first})")
|
41
41
|
@current ||= Redis.new
|
42
42
|
end
|
43
43
|
|
@@ -74,6 +74,8 @@ class Redis
|
|
74
74
|
# @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
|
75
75
|
# @option options [Array<String, Hash{Symbol => String, Integer}>] :cluster List of cluster nodes to contact
|
76
76
|
# @option options [Boolean] :replica Whether to use readonly replica nodes in Redis Cluster or not
|
77
|
+
# @option options [String] :fixed_hostname Specify a FQDN if cluster mode enabled and
|
78
|
+
# client has to connect nodes via single endpoint with SSL/TLS
|
77
79
|
# @option options [Class] :connector Class of custom connector
|
78
80
|
#
|
79
81
|
# @return [Redis] a new client instance
|
@@ -109,6 +111,10 @@ class Redis
|
|
109
111
|
end
|
110
112
|
alias disconnect! close
|
111
113
|
|
114
|
+
def with
|
115
|
+
yield self
|
116
|
+
end
|
117
|
+
|
112
118
|
# @deprecated Queues a command for pipelining.
|
113
119
|
#
|
114
120
|
# Commands in the queue are executed with the Redis#commit method.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ezra Zygmuntowicz
|
@@ -16,7 +16,7 @@ authors:
|
|
16
16
|
autorequire:
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
|
-
date: 2022-
|
19
|
+
date: 2022-07-01 00:00:00.000000000 Z
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
22
|
name: em-synchrony
|
@@ -119,9 +119,9 @@ licenses:
|
|
119
119
|
metadata:
|
120
120
|
bug_tracker_uri: https://github.com/redis/redis-rb/issues
|
121
121
|
changelog_uri: https://github.com/redis/redis-rb/blob/master/CHANGELOG.md
|
122
|
-
documentation_uri: https://www.rubydoc.info/gems/redis/4.
|
122
|
+
documentation_uri: https://www.rubydoc.info/gems/redis/4.7.1
|
123
123
|
homepage_uri: https://github.com/redis/redis-rb
|
124
|
-
source_code_uri: https://github.com/redis/redis-rb/tree/v4.
|
124
|
+
source_code_uri: https://github.com/redis/redis-rb/tree/v4.7.1
|
125
125
|
post_install_message:
|
126
126
|
rdoc_options: []
|
127
127
|
require_paths:
|