redis-client 0.4.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6bd28ab66722c23791118818d83505de8cbc1b141c75959f2408b8a0c90cb6ad
4
- data.tar.gz: 30f85f2bb0df340facd9a743f63dcfd2ff8fa8b8a3e363c9fdeb2f1101d5d076
3
+ metadata.gz: 770636c5814252674d680c45f75af4ea291b698c18382b49d93bec4e3d7ab45b
4
+ data.tar.gz: bdef4f0f80574a9aaf29613ec23d31d7a0c3d916ca3945ac412b36b55eecd73a
5
5
  SHA512:
6
- metadata.gz: 528b4cd6f9377f083a19ca1da7b117625ad32c1849c8cc53d795dc5af8e6d23c372fdb5714c043b7c796a6fcfda54ef28ee0142268846445cbfb60e00eabf625
7
- data.tar.gz: af0040e6a5cf498587818b5161122d57e3057a39a22d0eb5fc8252e4cb56e71ad61c0a381fbe35c75cd6f5e5e2998169487e33b74f7635f7e025f6e06d7351ca
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- redis-client (0.4.0)
4
+ redis-client (0.5.0)
5
5
  connection_pool
6
6
 
7
7
  GEM
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 redis commands to Ruby constructs,
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 tht all other configurtions take precedence, e.g. `RedisClient.config(url: "redis://localhost:3000" port: 6380)` will connect on port `6380`.
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 flatenned as well:
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 explictly cast the argument as a string.
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 explictly pass them as keyword arguments with `**`:
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 postional argument, but on Ruby 3 it will be interpreted as keyword
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` familly of methods:
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 uppon connection errors.
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
@@ -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
- uri = url && URI.parse(url)
114
- if uri
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.password
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&.user && uri&.password
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class RedisClient
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
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.each do |result|
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.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-05-06 00:00:00.000000000 Z
11
+ date: 2022-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: connection_pool