mock_redis 0.52.0 → 0.54.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: 9e933ae58d27e242f827d68d17dc8361327ce7c662cba7973ca4a44c4c9265a7
4
- data.tar.gz: f9bd080555a7c7f3aaec09d5e55327281b3a2860d5045d4e172812def4ea8c82
3
+ metadata.gz: afef9e22e0d014b5f2ebbf3c0d33ade376c134f26f4853099d2a792901740e8f
4
+ data.tar.gz: 926fa68ded405d6161072cc09f366bdd8fd069872b1a840735c0ab72984e51a8
5
5
  SHA512:
6
- metadata.gz: c09ef4f19dce4b289f5a205a1d34c284d0c78b98969cfeb8aa04eb76d5a10cf41e5d88f8bb2d85332a5d37d66d09c6262bc19f179ffce2e0e754113c8bfdaa71
7
- data.tar.gz: a8e824ef5dbf40a9406404eb5c8b5e2bd3cde46c0be994f36724be3beac97bb1f2d4168d557ede3c15f3085e8a03f155d4a88f48b83720b28c1779b8ebd5a0a0
6
+ metadata.gz: e069988875e87c06a4127670c362a7a43aeb793822ff3abde8708982ddf84bfbfba1ba50bf1681e0db85fbfcb75ba8bc9e204ae87cfacf57fe0c7d2289816e22
7
+ data.tar.gz: 69a994eb58218bce941320c3f74ac1faf01efb8c3a17bb6112136c189d89b26f4c6f1ef6d9d8fe4dc144c9e4a3acb44c0331db4dea3b7853df6a878d41a05539
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # MockRedis Changelog
2
2
 
3
+ ### 0.54.0
4
+
5
+ * Add `(B)ZMPOP`, `BZPOPMIN`, and `BZPOPMAX` commands
6
+
7
+ ### 0.53.0
8
+
9
+ * Fix `redis.call("info")`
10
+
3
11
  ### 0.52.0
4
12
 
5
13
  * Fix handling of `expire*` via `.call`
@@ -49,13 +49,18 @@ class MockRedis
49
49
  # i.e. `call("EXPIRE", "foo", 40, "NX")` (which redis-rb will simply transmit to redis-server)
50
50
  # will be passed to `#expire` without keywords transformation.
51
51
  def call(*command, &_block)
52
- # allow for single array argument or multiple arguments
53
- command = command[0] if command.length == 1
52
+ # flatten any nested arrays (eg from [:call, ["GET", "X"]] in pipelined commands)
53
+ command = command.flatten
54
54
 
55
- if command[0].downcase.to_s.include?('expire')
55
+ cmd_name = command[0].downcase.to_s
56
+
57
+ if cmd_name.include?('expire')
56
58
  send_expires(command)
59
+ elsif cmd_name == 'info'
60
+ # call(:info) returns a string, not a parsed hash
61
+ info_raw(*command[1..])
57
62
  else
58
- public_send(command[0].downcase, *command[1..])
63
+ public_send(cmd_name, *command[1..])
59
64
  end
60
65
  end
61
66
 
@@ -125,35 +125,65 @@ class MockRedis
125
125
  }.freeze
126
126
  # rubocop:enable Layout/LineLength
127
127
 
128
- DEFAULT_INFO = [
129
- SERVER_INFO,
130
- CLIENTS_INFO,
131
- MEMORY_INFO,
132
- PERSISTENCE_INFO,
133
- STATS_INFO,
134
- REPLICATION_INFO,
135
- CPU_INFO,
136
- KEYSPACE_INFO,
137
- ].inject({}) { |memo, info| memo.merge(info) }
138
-
139
- ALL_INFO = [
140
- DEFAULT_INFO,
141
- COMMAND_STATS_COMBINED_INFO,
142
- ].inject({}) { |memo, info| memo.merge(info) }
128
+ SECTIONS = {
129
+ server: SERVER_INFO,
130
+ clients: CLIENTS_INFO,
131
+ memory: MEMORY_INFO,
132
+ persistence: PERSISTENCE_INFO,
133
+ stats: STATS_INFO,
134
+ replication: REPLICATION_INFO,
135
+ cpu: CPU_INFO,
136
+ keyspace: KEYSPACE_INFO,
137
+ commandstats: COMMAND_STATS_COMBINED_INFO,
138
+ }.freeze
139
+ SECTION_NAMES = {
140
+ server: 'Server',
141
+ clients: 'Clients',
142
+ memory: 'Memory',
143
+ persistence: 'Persistence',
144
+ stats: 'Stats',
145
+ replication: 'Replication',
146
+ cpu: 'Cpu',
147
+ keyspace: 'Keyspace',
148
+ commandstats: 'Commandstats',
149
+ }.freeze
150
+ DEFAULT_SECTIONS = [
151
+ :server, :clients, :memory, :persistence, :stats, :replication, :cpu, :keyspace
152
+ ].freeze
153
+ ALL_SECTIONS = DEFAULT_SECTIONS + [:commandstats].freeze
143
154
 
144
155
  def info(section = :default)
156
+ if section.to_s.downcase == 'commandstats'
157
+ # `redis.info(:commandstats)` gives a nested hash structure,
158
+ # unlike when commandstats is printed as part of `redis.info(:all)`
159
+ COMMAND_STATS_SOLO_INFO
160
+ else
161
+ sections = relevant_info_sections(section)
162
+ sections.inject({}) { |memo, name| memo.merge(SECTIONS[name]) }
163
+ end
164
+ end
165
+
166
+ private
167
+
168
+ # Format info hash as raw string (used by call("info"))
169
+ def info_raw(section = :default)
170
+ sections = relevant_info_sections(section)
171
+ sections.map do |name|
172
+ header = "# #{SECTION_NAMES[name]}"
173
+ lines = SECTIONS[name].map { |k, v| "#{k}:#{v}" }
174
+ [header, *lines].join("\n")
175
+ end.join("\n\n") << "\n"
176
+ end
177
+
178
+ def relevant_info_sections(section)
179
+ section = section.to_s.downcase.to_sym
145
180
  case section
146
- when :default; DEFAULT_INFO
147
- when :all; ALL_INFO
148
- when :server; SERVER_INFO
149
- when :clients; CLIENTS_INFO
150
- when :memory; MEMORY_INFO
151
- when :persistence; PERSISTENCE_INFO
152
- when :stats; STATS_INFO
153
- when :replication; REPLICATION_INFO
154
- when :cpu; CPU_INFO
155
- when :keyspace; KEYSPACE_INFO
156
- when :commandstats; COMMAND_STATS_SOLO_INFO
181
+ when :default
182
+ DEFAULT_SECTIONS
183
+ when :all
184
+ ALL_SECTIONS
185
+ else
186
+ [section]
157
187
  end
158
188
  end
159
189
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Defines the gem version.
4
4
  class MockRedis
5
- VERSION = '0.52.0'
5
+ VERSION = '0.54.0'
6
6
  end
@@ -253,6 +253,92 @@ class MockRedis
253
253
  zcard(destination)
254
254
  end
255
255
 
256
+ def bzpopmin(*args)
257
+ keys, timeout = extract_timeout(args)
258
+ nonempty_zset = first_nonempty_zset(keys)
259
+
260
+ if nonempty_zset
261
+ member, score = zpopmin(nonempty_zset)
262
+ [nonempty_zset, member, score]
263
+ elsif timeout > 0
264
+ nil
265
+ else
266
+ raise MockRedis::WouldBlock, "Can't block forever"
267
+ end
268
+ end
269
+
270
+ def bzpopmax(*args)
271
+ keys, timeout = extract_timeout(args)
272
+ nonempty_zset = first_nonempty_zset(keys)
273
+
274
+ if nonempty_zset
275
+ member, score = zpopmax(nonempty_zset)
276
+ [nonempty_zset, member, score]
277
+ elsif timeout > 0
278
+ nil
279
+ else
280
+ raise MockRedis::WouldBlock, "Can't block forever"
281
+ end
282
+ end
283
+
284
+ def zmpop(*keys, **options)
285
+ keys.each do |key|
286
+ assert_zsety(key)
287
+ end
288
+
289
+ modifier = options.is_a?(Hash) && options[:modifier]&.to_s&.downcase || 'min'
290
+ count = (options.is_a?(Hash) && options[:count]) || 1
291
+
292
+ unless %w[min max].include?(modifier)
293
+ raise ArgumentError, 'Pick either MIN or MAX'
294
+ end
295
+
296
+ keys.each do |key|
297
+ record_count = zcard(key)
298
+ next if record_count.zero?
299
+
300
+ values = [count, record_count].min.times.map do
301
+ modifier == 'min' ? zpopmin(key) : zpopmax(key)
302
+ end
303
+
304
+ return [key, values]
305
+ end
306
+
307
+ nil
308
+ end
309
+
310
+ def bzmpop(timeout, *keys, **options)
311
+ timeout = assert_valid_timeout(timeout)
312
+
313
+ keys.each do |key|
314
+ assert_zsety(key)
315
+ end
316
+
317
+ modifier = options.is_a?(Hash) && options[:modifier]&.to_s&.downcase || 'min'
318
+ count = (options.is_a?(Hash) && options[:count]) || 1
319
+
320
+ unless %w[min max].include?(modifier)
321
+ raise ArgumentError, 'Pick either MIN or MAX'
322
+ end
323
+
324
+ keys.each do |key|
325
+ record_count = zcard(key)
326
+ next if record_count.zero?
327
+
328
+ values = [count, record_count].min.times.map do
329
+ modifier == 'min' ? zpopmin(key) : zpopmax(key)
330
+ end
331
+
332
+ return [key, values]
333
+ end
334
+
335
+ if timeout > 0
336
+ nil
337
+ else
338
+ raise MockRedis::WouldBlock, "Can't block forever"
339
+ end
340
+ end
341
+
256
342
  private
257
343
 
258
344
  def apply_limit(collection, limit)
@@ -376,5 +462,9 @@ class MockRedis
376
462
  assert_scorey(value, 'ERR min or max is not a float')
377
463
  end
378
464
  end
465
+
466
+ def first_nonempty_zset(keys)
467
+ keys.find { |k| zcard(k) > 0 }
468
+ end
379
469
  end
380
470
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mock_redis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.52.0
4
+ version: 0.54.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane da Silva
8
8
  - Samuel Merritt
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-08-21 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -124,10 +124,10 @@ licenses:
124
124
  - MIT
125
125
  metadata:
126
126
  bug_tracker_uri: https://github.com/sds/mock_redis/issues
127
- changelog_uri: https://github.com/sds/mock_redis/blob/v0.52.0/CHANGELOG.md
128
- documentation_uri: https://www.rubydoc.info/gems/mock_redis/0.52.0
127
+ changelog_uri: https://github.com/sds/mock_redis/blob/v0.54.0/CHANGELOG.md
128
+ documentation_uri: https://www.rubydoc.info/gems/mock_redis/0.54.0
129
129
  homepage_uri: https://github.com/sds/mock_redis
130
- source_code_uri: https://github.com/sds/mock_redis/tree/v0.52.0
130
+ source_code_uri: https://github.com/sds/mock_redis/tree/v0.54.0
131
131
  rdoc_options: []
132
132
  require_paths:
133
133
  - lib
@@ -142,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
142
  - !ruby/object:Gem::Version
143
143
  version: '0'
144
144
  requirements: []
145
- rubygems_version: 3.6.2
145
+ rubygems_version: 3.6.9
146
146
  specification_version: 4
147
147
  summary: Redis mock that just lives in memory; useful for testing.
148
148
  test_files: []