redis-client 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +1 -1
- data/README.md +8 -8
- data/lib/redis_client/command_builder.rb +8 -0
- data/lib/redis_client/config.rb +13 -5
- data/lib/redis_client/connection_mixin.rb +2 -0
- data/lib/redis_client/version.rb +1 -1
- data/lib/redis_client.rb +22 -1
- metadata +2 -2
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,12 @@
|
|
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
|
+
|
3
10
|
# 0.4.0
|
4
11
|
|
5
12
|
- The `hiredis` driver have been moved to the `hiredis-client` gem.
|
data/Gemfile.lock
CHANGED
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,7 +142,7 @@ 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
148
|
redis.call("HMSET", "hash", { "foo" => "1", "bar" => "2" })
|
@@ -154,7 +154,7 @@ is equivalent to:
|
|
154
154
|
redis.call("HMSET", "hash", "foo", "1", "bar", "2")
|
155
155
|
```
|
156
156
|
|
157
|
-
Any other type requires the caller to
|
157
|
+
Any other type requires the caller to explicitly cast the argument as a string.
|
158
158
|
|
159
159
|
Keywords arguments are treated as Redis command flags:
|
160
160
|
|
@@ -170,7 +170,7 @@ redis.call("SET", "mykey", "value", "nx", "ex", "60")
|
|
170
170
|
redis.call("SET", "mykey", "value")
|
171
171
|
```
|
172
172
|
|
173
|
-
If flags are built dynamically, you'll have to
|
173
|
+
If flags are built dynamically, you'll have to explicitly pass them as keyword arguments with `**`:
|
174
174
|
|
175
175
|
```ruby
|
176
176
|
flags = {}
|
@@ -185,7 +185,7 @@ unclosed hash literals with string keys may be interpreted differently:
|
|
185
185
|
redis.call("HMSET", "hash", "foo" => "bar")
|
186
186
|
```
|
187
187
|
|
188
|
-
On Ruby 2 `"foo" => "bar"` will be passed as a
|
188
|
+
On Ruby 2 `"foo" => "bar"` will be passed as a positional argument, but on Ruby 3 it will be interpreted as keyword
|
189
189
|
arguments. To avoid such problem, make sure to enclose hash literals:
|
190
190
|
|
191
191
|
```ruby
|
@@ -196,7 +196,7 @@ redis.call("HMSET", "hash", { "foo" => "bar" })
|
|
196
196
|
|
197
197
|
Contrary to the `redis` gem, `redis-client` doesn't do any type casting on the return value of commands.
|
198
198
|
|
199
|
-
If you wish to cast the return value, you can pass a block to the `#call`
|
199
|
+
If you wish to cast the return value, you can pass a block to the `#call` family of methods:
|
200
200
|
|
201
201
|
```ruby
|
202
202
|
redis.call("INCR", "counter") # => 1
|
@@ -297,7 +297,7 @@ end
|
|
297
297
|
|
298
298
|
If the transaction wasn't successful, `#multi` will return `nil`.
|
299
299
|
|
300
|
-
Note that transactions using optimistic locking aren't automatically retried
|
300
|
+
Note that transactions using optimistic locking aren't automatically retried upon connection errors.
|
301
301
|
|
302
302
|
### Publish / Subscribe
|
303
303
|
|
@@ -40,6 +40,10 @@ class RedisClient
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
if command.empty?
|
44
|
+
raise ArgumentError, "can't issue an empty redis command"
|
45
|
+
end
|
46
|
+
|
43
47
|
command
|
44
48
|
end
|
45
49
|
else
|
@@ -76,6 +80,10 @@ class RedisClient
|
|
76
80
|
end
|
77
81
|
end
|
78
82
|
|
83
|
+
if command.empty?
|
84
|
+
raise ArgumentError, "can't issue an empty redis command"
|
85
|
+
end
|
86
|
+
|
79
87
|
command
|
80
88
|
end
|
81
89
|
end
|
data/lib/redis_client/config.rb
CHANGED
@@ -82,6 +82,14 @@ class RedisClient
|
|
82
82
|
@ssl_context ||= @driver.ssl_context(@ssl_params)
|
83
83
|
end
|
84
84
|
|
85
|
+
def server_url
|
86
|
+
if path
|
87
|
+
"#{path}/#{db}"
|
88
|
+
else
|
89
|
+
"redis#{'s' if ssl?}://#{host}:#{port}/#{db}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
85
93
|
private
|
86
94
|
|
87
95
|
def build_connection_prelude
|
@@ -110,15 +118,15 @@ class RedisClient
|
|
110
118
|
path: nil,
|
111
119
|
**kwargs
|
112
120
|
)
|
113
|
-
|
114
|
-
|
121
|
+
if url
|
122
|
+
uri = URI.parse(url)
|
115
123
|
kwargs[:ssl] = uri.scheme == "rediss" unless kwargs.key?(:ssl)
|
116
124
|
|
117
|
-
kwargs[:username] ||= uri.user && uri.
|
125
|
+
kwargs[:username] ||= uri.user if uri.password && !uri.user.empty?
|
118
126
|
|
119
127
|
kwargs[:password] ||= if uri.user && !uri.password
|
120
128
|
URI.decode_www_form_component(uri.user)
|
121
|
-
elsif uri
|
129
|
+
elsif uri.user && uri.password
|
122
130
|
URI.decode_www_form_component(uri.password)
|
123
131
|
end
|
124
132
|
|
@@ -127,7 +135,7 @@ class RedisClient
|
|
127
135
|
|
128
136
|
super(**kwargs)
|
129
137
|
|
130
|
-
@host = host || uri&.host || DEFAULT_HOST
|
138
|
+
@host = host || uri&.host&.sub(/\A\[(.*)\]\z/, '\1') || DEFAULT_HOST
|
131
139
|
@port = port || uri&.port || DEFAULT_PORT
|
132
140
|
@path = path
|
133
141
|
end
|
@@ -6,6 +6,7 @@ class RedisClient
|
|
6
6
|
write(command)
|
7
7
|
result = read(timeout)
|
8
8
|
if result.is_a?(CommandError)
|
9
|
+
result._set_command(command)
|
9
10
|
raise result
|
10
11
|
else
|
11
12
|
result
|
@@ -23,6 +24,7 @@ class RedisClient
|
|
23
24
|
timeout = timeouts && timeouts[index]
|
24
25
|
result = read(timeout)
|
25
26
|
if result.is_a?(CommandError)
|
27
|
+
result._set_command(commands[index])
|
26
28
|
exception ||= result
|
27
29
|
end
|
28
30
|
results[index] = result
|
data/lib/redis_client/version.rb
CHANGED
data/lib/redis_client.rb
CHANGED
@@ -78,6 +78,8 @@ class RedisClient
|
|
78
78
|
|
79
79
|
Error = Class.new(StandardError)
|
80
80
|
|
81
|
+
UnsupportedServer = Class.new(Error)
|
82
|
+
|
81
83
|
ConnectionError = Class.new(Error)
|
82
84
|
|
83
85
|
FailoverError = Class.new(ConnectionError)
|
@@ -89,6 +91,8 @@ class RedisClient
|
|
89
91
|
CheckoutTimeoutError = Class.new(ConnectTimeoutError)
|
90
92
|
|
91
93
|
class CommandError < Error
|
94
|
+
attr_reader :command
|
95
|
+
|
92
96
|
class << self
|
93
97
|
def parse(error_message)
|
94
98
|
code = error_message.split(' ', 2).first
|
@@ -96,6 +100,10 @@ class RedisClient
|
|
96
100
|
klass.new(error_message)
|
97
101
|
end
|
98
102
|
end
|
103
|
+
|
104
|
+
def _set_command(command)
|
105
|
+
@command = command
|
106
|
+
end
|
99
107
|
end
|
100
108
|
|
101
109
|
AuthenticationError = Class.new(CommandError)
|
@@ -140,6 +148,11 @@ class RedisClient
|
|
140
148
|
@disable_reconnection = false
|
141
149
|
end
|
142
150
|
|
151
|
+
def inspect
|
152
|
+
id_string = " id=#{id}" if id
|
153
|
+
"#<#{self.class.name} #{config.server_url}#{id_string}>"
|
154
|
+
end
|
155
|
+
|
143
156
|
def size
|
144
157
|
1
|
145
158
|
end
|
@@ -405,8 +418,9 @@ class RedisClient
|
|
405
418
|
|
406
419
|
def _coerce!(results)
|
407
420
|
if results
|
408
|
-
results.
|
421
|
+
results.each_with_index do |result, index|
|
409
422
|
if result.is_a?(CommandError)
|
423
|
+
result._set_command(@commands[index + 1])
|
410
424
|
raise result
|
411
425
|
end
|
412
426
|
end
|
@@ -558,6 +572,13 @@ class RedisClient
|
|
558
572
|
end
|
559
573
|
|
560
574
|
connection
|
575
|
+
rescue CommandError => error
|
576
|
+
if error.message.include?("ERR unknown command `HELLO`")
|
577
|
+
raise UnsupportedServer,
|
578
|
+
"Your Redis server version is too old. redis-client requires Redis 6+. (#{config.server_url})"
|
579
|
+
else
|
580
|
+
raise
|
581
|
+
end
|
561
582
|
end
|
562
583
|
end
|
563
584
|
|
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.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean Boussier
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: connection_pool
|