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 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