mock_redis 0.55.0 → 0.56.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 +4 -4
- data/CHANGELOG.md +10 -0
- data/lib/mock_redis/database.rb +30 -2
- data/lib/mock_redis/geospatial_methods.rb +6 -3
- data/lib/mock_redis/string_methods.rb +3 -12
- data/lib/mock_redis/version.rb +1 -1
- data/lib/mock_redis/zset_methods.rb +142 -13
- data/lib/mock_redis.rb +10 -0
- metadata +17 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9941cda3e99b0e5f90f9d0e0d27eb242b8af20b52beb23f52075f77b0843a2c4
|
|
4
|
+
data.tar.gz: 293c9b589d9876fd4c907e6e76ec73bafcf6e20cbb4af4beb2d3150b956c6a15
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9a9f887b2dd27f3c242498a65c121d67d18cd98727c9c3b0a208aa4405903db077b824c8103cd326d5e900323fc631a9abb88553c7a73ab7321deb97be331d0a
|
|
7
|
+
data.tar.gz: c80f28279ed2aff3cac655ecaa3d8d7e56c08ae7f24d5a0403b8de63543e627b07b698eea6f3788d59734ac23575930e6d6c0c78f7c17c26e2bbac82c9ef35a9
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# MockRedis Changelog
|
|
2
2
|
|
|
3
|
+
### 0.56.0
|
|
4
|
+
|
|
5
|
+
* Add support for `redis-rb` 6 and return `GEOPOS` coordinates as floats under RESP3
|
|
6
|
+
* Add support for `username` option to Redis connection
|
|
7
|
+
* Fix `send_expires` when option is `nil`
|
|
8
|
+
* Fix `SET` command with positional arguments via `call` method
|
|
9
|
+
* Add `BYSCORE`, `BYLEX`, and `REV` options to `ZRANGE`
|
|
10
|
+
* Add `LT` and `CH` options to `ZADD` command
|
|
11
|
+
* Fix handling of nonpositive SET expirations before changing key state
|
|
12
|
+
|
|
3
13
|
### 0.55.0
|
|
4
14
|
|
|
5
15
|
* Add `GT` option support to `ZADD`
|
data/lib/mock_redis/database.rb
CHANGED
|
@@ -56,6 +56,8 @@ class MockRedis
|
|
|
56
56
|
|
|
57
57
|
if cmd_name.include?('expire')
|
|
58
58
|
send_expires(command)
|
|
59
|
+
elsif cmd_name == 'set'
|
|
60
|
+
send_set(command)
|
|
59
61
|
elsif cmd_name == 'info'
|
|
60
62
|
# call(:info) returns a string, not a parsed hash
|
|
61
63
|
info_raw(*command[1..])
|
|
@@ -362,8 +364,34 @@ class MockRedis
|
|
|
362
364
|
end
|
|
363
365
|
|
|
364
366
|
def send_expires(command)
|
|
365
|
-
|
|
366
|
-
|
|
367
|
+
cmd, key, ttl, option = *command
|
|
368
|
+
if option
|
|
369
|
+
public_send(cmd, key, ttl, option.downcase.to_sym => true)
|
|
370
|
+
else
|
|
371
|
+
public_send(cmd, key, ttl)
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def send_set(command)
|
|
376
|
+
_cmd, key, value, *options = *command
|
|
377
|
+
kwargs = {}
|
|
378
|
+
|
|
379
|
+
i = 0
|
|
380
|
+
while i < options.length
|
|
381
|
+
opt = options[i].to_s.downcase
|
|
382
|
+
case opt
|
|
383
|
+
when 'ex', 'px', 'exat', 'pxat'
|
|
384
|
+
kwargs[opt.to_sym] = options[i + 1].to_i
|
|
385
|
+
i += 2
|
|
386
|
+
when 'nx', 'xx', 'keepttl', 'get'
|
|
387
|
+
kwargs[opt.to_sym] = true
|
|
388
|
+
i += 1
|
|
389
|
+
else
|
|
390
|
+
i += 1
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
set(key, value, **kwargs)
|
|
367
395
|
end
|
|
368
396
|
|
|
369
397
|
def should_update_expiration?(expiry, new_expiry, nx:, xx:, lt:, gt:) # rubocop:disable Metrics/ParameterLists
|
|
@@ -71,9 +71,7 @@ class MockRedis
|
|
|
71
71
|
next nil unless score
|
|
72
72
|
hash = { bits: score.to_i, step: STEP }
|
|
73
73
|
lng, lat = geohash_decode(hash)
|
|
74
|
-
lng
|
|
75
|
-
lat = format_decoded_coord(lat)
|
|
76
|
-
[lng, lat]
|
|
74
|
+
[format_decoded_coord(lng), format_decoded_coord(lat)].map { |c| resp3? ? c.to_f : c }
|
|
77
75
|
end
|
|
78
76
|
end
|
|
79
77
|
|
|
@@ -203,6 +201,11 @@ class MockRedis
|
|
|
203
201
|
x | (y << 32)
|
|
204
202
|
end
|
|
205
203
|
|
|
204
|
+
# redis-rb 6 defaults to RESP3, which returns GEO coordinates as Floats
|
|
205
|
+
def resp3?
|
|
206
|
+
@base.options.fetch(:protocol) { Redis::VERSION.to_i >= 6 ? 3 : 2 }.to_i == 3
|
|
207
|
+
end
|
|
208
|
+
|
|
206
209
|
def format_decoded_coord(coord)
|
|
207
210
|
coord = format('%<coord>.17f', coord: coord)
|
|
208
211
|
l = 1
|
|
@@ -212,6 +212,9 @@ class MockRedis
|
|
|
212
212
|
def set(key, value, _hash = nil, ex: nil, px: nil, exat: nil, pxat: nil, nx: nil, xx: nil,
|
|
213
213
|
keepttl: nil, get: nil)
|
|
214
214
|
key = key.to_s
|
|
215
|
+
if [ex, px, exat, pxat].any? { |expiration| expiration && Integer(expiration) <= 0 }
|
|
216
|
+
raise Error.command_error('ERR invalid expire time in set', self)
|
|
217
|
+
end
|
|
215
218
|
retval = self.get(key) if get
|
|
216
219
|
|
|
217
220
|
return_true = false
|
|
@@ -233,30 +236,18 @@ class MockRedis
|
|
|
233
236
|
|
|
234
237
|
remove_expiration(key) unless keepttl
|
|
235
238
|
if ex
|
|
236
|
-
if ex == 0
|
|
237
|
-
raise Error.command_error('ERR invalid expire time in set', self)
|
|
238
|
-
end
|
|
239
239
|
expire(key, ex)
|
|
240
240
|
end
|
|
241
241
|
|
|
242
242
|
if px
|
|
243
|
-
if px == 0
|
|
244
|
-
raise Error.command_error('ERR invalid expire time in set', self)
|
|
245
|
-
end
|
|
246
243
|
pexpire(key, px)
|
|
247
244
|
end
|
|
248
245
|
|
|
249
246
|
if exat
|
|
250
|
-
if exat == 0
|
|
251
|
-
raise Error.command_error('ERR invalid expire time in set', self)
|
|
252
|
-
end
|
|
253
247
|
expireat(key, exat)
|
|
254
248
|
end
|
|
255
249
|
|
|
256
250
|
if pxat
|
|
257
|
-
if pxat == 0
|
|
258
|
-
raise Error.command_error('ERR invalid expire time in set', self)
|
|
259
|
-
end
|
|
260
251
|
pexpireat(key, pxat)
|
|
261
252
|
end
|
|
262
253
|
|
data/lib/mock_redis/version.rb
CHANGED
|
@@ -11,14 +11,28 @@ class MockRedis
|
|
|
11
11
|
zadd_options = {}
|
|
12
12
|
zadd_options = args.pop if args.last.is_a?(Hash)
|
|
13
13
|
|
|
14
|
-
if zadd_options
|
|
14
|
+
if zadd_options.include?(:nx) && zadd_options.include?(:xx)
|
|
15
15
|
raise Error.command_error(
|
|
16
16
|
'ERR XX and NX options at the same time are not compatible',
|
|
17
17
|
self
|
|
18
18
|
)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
if zadd_options
|
|
21
|
+
if zadd_options.include?(:gt) && zadd_options.include?(:nx)
|
|
22
|
+
raise Error.command_error(
|
|
23
|
+
'ERR GT, LT, and/or NX options at the same time are not compatible',
|
|
24
|
+
self
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
if zadd_options.include?(:lt) && zadd_options.include?(:nx)
|
|
29
|
+
raise Error.command_error(
|
|
30
|
+
'ERR GT, LT, and/or NX options at the same time are not compatible',
|
|
31
|
+
self
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
if zadd_options.include?(:gt) && zadd_options.include?(:lt)
|
|
22
36
|
raise Error.command_error(
|
|
23
37
|
'ERR GT, LT, and/or NX options at the same time are not compatible',
|
|
24
38
|
self
|
|
@@ -58,6 +72,24 @@ class MockRedis
|
|
|
58
72
|
return zincrby(key, score, member)
|
|
59
73
|
end
|
|
60
74
|
|
|
75
|
+
if zadd_options[:lt]
|
|
76
|
+
current_score = zset.score(member.to_s)
|
|
77
|
+
|
|
78
|
+
# NOTE: does nothing if the member doesn't exist and XX option is set.
|
|
79
|
+
return nil if current_score.nil? && zadd_options[:xx]
|
|
80
|
+
|
|
81
|
+
# NOTE: zincrby add the increment if the member doesn't exist and XX option is not set.
|
|
82
|
+
return zincrby(key, score, member) if current_score.nil? && !zadd_options[:xx]
|
|
83
|
+
|
|
84
|
+
new_score = current_score + score.to_f
|
|
85
|
+
|
|
86
|
+
# NOTE: does nothing if the new score is not less than the current score.
|
|
87
|
+
return nil if current_score && new_score >= current_score
|
|
88
|
+
|
|
89
|
+
# NOTE: zincrby update the score if the new score is less than the current score.
|
|
90
|
+
return zincrby(key, score, member)
|
|
91
|
+
end
|
|
92
|
+
|
|
61
93
|
if zadd_options[:xx]
|
|
62
94
|
member_present = zset.include?(member)
|
|
63
95
|
return member_present ? zincrby(key, score, member) : nil
|
|
@@ -79,8 +111,23 @@ class MockRedis
|
|
|
79
111
|
zset.add(new_score, member.to_s)
|
|
80
112
|
true
|
|
81
113
|
else
|
|
82
|
-
|
|
83
|
-
|
|
114
|
+
changed = new_score > current_score
|
|
115
|
+
zset.add(new_score, member.to_s) if changed
|
|
116
|
+
zadd_options[:ch] ? changed : false
|
|
117
|
+
end
|
|
118
|
+
elsif zadd_options[:lt]
|
|
119
|
+
current_score = zset.score(member.to_s)
|
|
120
|
+
new_score = score.to_f
|
|
121
|
+
|
|
122
|
+
if current_score.nil?
|
|
123
|
+
return false if zadd_options[:xx]
|
|
124
|
+
|
|
125
|
+
zset.add(new_score, member.to_s)
|
|
126
|
+
true
|
|
127
|
+
else
|
|
128
|
+
changed = new_score < current_score
|
|
129
|
+
zset.add(new_score, member.to_s) if changed
|
|
130
|
+
zadd_options[:ch] ? changed : false
|
|
84
131
|
end
|
|
85
132
|
elsif zadd_options[:xx]
|
|
86
133
|
# NOTE: gt and xx options are handled at the gt branch above, so its not handled here.
|
|
@@ -89,9 +136,14 @@ class MockRedis
|
|
|
89
136
|
elsif zadd_options[:nx]
|
|
90
137
|
!zset.include?(member) && !!zset.add(score, member.to_s)
|
|
91
138
|
else
|
|
92
|
-
|
|
139
|
+
is_new = !zscore(key, member)
|
|
140
|
+
old_score = zset.score(member.to_s)
|
|
93
141
|
zset.add(score, member.to_s)
|
|
94
|
-
|
|
142
|
+
if zadd_options[:ch]
|
|
143
|
+
is_new || (old_score && old_score != score.to_f) # rubocop:disable Lint/FloatComparison
|
|
144
|
+
else
|
|
145
|
+
is_new
|
|
146
|
+
end
|
|
95
147
|
end
|
|
96
148
|
end
|
|
97
149
|
end
|
|
@@ -116,13 +168,44 @@ class MockRedis
|
|
|
116
168
|
end
|
|
117
169
|
else
|
|
118
170
|
score_f = score.to_f
|
|
119
|
-
|
|
171
|
+
if score_f > current_score
|
|
172
|
+
zset.add(score_f, member.to_s)
|
|
173
|
+
retval += 1 if zadd_options[:ch]
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
retval
|
|
177
|
+
end
|
|
178
|
+
elsif zadd_options[:lt]
|
|
179
|
+
args.reduce(0) do |retval, (score, member)|
|
|
180
|
+
current_score = zset.score(member.to_s)
|
|
181
|
+
if current_score.nil?
|
|
182
|
+
unless zadd_options[:xx]
|
|
183
|
+
zset.add(score.to_f, member.to_s)
|
|
184
|
+
retval += 1
|
|
185
|
+
end
|
|
186
|
+
else
|
|
187
|
+
score_f = score.to_f
|
|
188
|
+
if score_f < current_score
|
|
189
|
+
zset.add(score_f, member.to_s)
|
|
190
|
+
retval += 1 if zadd_options[:ch]
|
|
191
|
+
end
|
|
120
192
|
end
|
|
121
193
|
retval
|
|
122
194
|
end
|
|
123
195
|
elsif zadd_options[:xx]
|
|
124
|
-
|
|
125
|
-
|
|
196
|
+
if zadd_options[:ch]
|
|
197
|
+
args.reduce(0) do |retval, (score, member)|
|
|
198
|
+
if zset.include?(member)
|
|
199
|
+
old_score = zset.score(member.to_s)
|
|
200
|
+
zset.add(score, member.to_s)
|
|
201
|
+
retval += 1 if old_score != score.to_f # rubocop:disable Lint/FloatComparison
|
|
202
|
+
end
|
|
203
|
+
retval
|
|
204
|
+
end
|
|
205
|
+
else
|
|
206
|
+
args.each { |score, member| zset.include?(member) && zset.add(score, member.to_s) }
|
|
207
|
+
0
|
|
208
|
+
end
|
|
126
209
|
elsif zadd_options[:nx]
|
|
127
210
|
args.reduce(0) do |retval, (score, member)|
|
|
128
211
|
unless zset.include?(member)
|
|
@@ -133,8 +216,13 @@ class MockRedis
|
|
|
133
216
|
end
|
|
134
217
|
else
|
|
135
218
|
args.reduce(0) do |retval, (score, member)|
|
|
136
|
-
|
|
219
|
+
is_new = !zset.include?(member)
|
|
220
|
+
old_score = zset.score(member.to_s)
|
|
137
221
|
zset.add(score, member.to_s)
|
|
222
|
+
# rubocop:disable Lint/FloatComparison
|
|
223
|
+
changed = is_new || (zadd_options[:ch] && old_score && old_score != score.to_f)
|
|
224
|
+
# rubocop:enable Lint/FloatComparison
|
|
225
|
+
retval += 1 if changed
|
|
138
226
|
retval
|
|
139
227
|
end
|
|
140
228
|
end
|
|
@@ -175,9 +263,50 @@ class MockRedis
|
|
|
175
263
|
|
|
176
264
|
def zrange(key, start, stop, options = {})
|
|
177
265
|
with_zset_at(key) do |z|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
266
|
+
if options[:byscore]
|
|
267
|
+
# With REV, arguments are (max, min) not (min, max)
|
|
268
|
+
min = options[:rev] ? stop : start
|
|
269
|
+
max = options[:rev] ? start : stop
|
|
270
|
+
assert_range_args(min, max)
|
|
271
|
+
results = z.in_range(min, max)
|
|
272
|
+
results = results.reverse if options[:rev]
|
|
273
|
+
results = apply_limit(results, options[:limit])
|
|
274
|
+
to_response(results, options)
|
|
275
|
+
elsif options[:bylex]
|
|
276
|
+
# With REV, arguments are (max, min) not (min, max)
|
|
277
|
+
lex_min = options[:rev] ? stop : start
|
|
278
|
+
lex_max = options[:rev] ? start : stop
|
|
279
|
+
results = z.sorted.select do |_score, member|
|
|
280
|
+
in_lex_range?(member, lex_min, lex_max)
|
|
281
|
+
end
|
|
282
|
+
results = results.reverse if options[:rev]
|
|
283
|
+
results = apply_limit(results, options[:limit])
|
|
284
|
+
to_response(results, options)
|
|
285
|
+
else
|
|
286
|
+
sorted = options[:rev] ? z.sorted.reverse : z.sorted
|
|
287
|
+
start_idx = [start.to_i, -sorted.size].max
|
|
288
|
+
stop_idx = stop.to_i
|
|
289
|
+
to_response(sorted[start_idx..stop_idx] || [], options)
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def in_lex_range?(member, min, max)
|
|
295
|
+
return false if min != '-' && !lex_compare(member, min, :min)
|
|
296
|
+
return false if max != '+' && !lex_compare(member, max, :max)
|
|
297
|
+
|
|
298
|
+
true
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def lex_compare(member, bound, direction)
|
|
302
|
+
if bound.start_with?('[')
|
|
303
|
+
value = bound[1..]
|
|
304
|
+
direction == :min ? member >= value : member <= value
|
|
305
|
+
elsif bound.start_with?('(')
|
|
306
|
+
value = bound[1..]
|
|
307
|
+
direction == :min ? member > value : member < value
|
|
308
|
+
else
|
|
309
|
+
false
|
|
181
310
|
end
|
|
182
311
|
end
|
|
183
312
|
|
data/lib/mock_redis.rb
CHANGED
|
@@ -21,6 +21,7 @@ class MockRedis
|
|
|
21
21
|
:port => 6379,
|
|
22
22
|
:path => nil,
|
|
23
23
|
:timeout => 5.0,
|
|
24
|
+
:username => nil,
|
|
24
25
|
:password => nil,
|
|
25
26
|
:logger => nil,
|
|
26
27
|
:db => 0,
|
|
@@ -62,6 +63,14 @@ class MockRedis
|
|
|
62
63
|
options[:db]
|
|
63
64
|
end
|
|
64
65
|
|
|
66
|
+
def username
|
|
67
|
+
options[:username]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def password
|
|
71
|
+
options[:password]
|
|
72
|
+
end
|
|
73
|
+
|
|
65
74
|
def logger
|
|
66
75
|
options[:logger]
|
|
67
76
|
end
|
|
@@ -125,6 +134,7 @@ class MockRedis
|
|
|
125
134
|
defaults[:scheme] = uri.scheme
|
|
126
135
|
defaults[:host] = uri.host
|
|
127
136
|
defaults[:port] = uri.port if uri.port
|
|
137
|
+
defaults[:username] = uri.user if uri.user
|
|
128
138
|
defaults[:password] = uri.password if uri.password
|
|
129
139
|
defaults[:db] = uri.path[1..].to_i if uri.path
|
|
130
140
|
end
|
metadata
CHANGED
|
@@ -1,30 +1,36 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mock_redis
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.56.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shane da Silva
|
|
8
8
|
- Samuel Merritt
|
|
9
|
-
autorequire:
|
|
9
|
+
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2026-
|
|
12
|
+
date: 2026-11-02 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: redis
|
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
|
17
17
|
requirements:
|
|
18
|
-
- - "
|
|
18
|
+
- - ">="
|
|
19
19
|
- !ruby/object:Gem::Version
|
|
20
20
|
version: '5'
|
|
21
|
+
- - "<"
|
|
22
|
+
- !ruby/object:Gem::Version
|
|
23
|
+
version: '7'
|
|
21
24
|
type: :runtime
|
|
22
25
|
prerelease: false
|
|
23
26
|
version_requirements: !ruby/object:Gem::Requirement
|
|
24
27
|
requirements:
|
|
25
|
-
- - "
|
|
28
|
+
- - ">="
|
|
26
29
|
- !ruby/object:Gem::Version
|
|
27
30
|
version: '5'
|
|
31
|
+
- - "<"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '7'
|
|
28
34
|
- !ruby/object:Gem::Dependency
|
|
29
35
|
name: rake
|
|
30
36
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -125,11 +131,11 @@ licenses:
|
|
|
125
131
|
- MIT
|
|
126
132
|
metadata:
|
|
127
133
|
bug_tracker_uri: https://github.com/sds/mock_redis/issues
|
|
128
|
-
changelog_uri: https://github.com/sds/mock_redis/blob/v0.
|
|
129
|
-
documentation_uri: https://www.rubydoc.info/gems/mock_redis/0.
|
|
134
|
+
changelog_uri: https://github.com/sds/mock_redis/blob/v0.56.0/CHANGELOG.md
|
|
135
|
+
documentation_uri: https://www.rubydoc.info/gems/mock_redis/0.56.0
|
|
130
136
|
homepage_uri: https://github.com/sds/mock_redis
|
|
131
|
-
source_code_uri: https://github.com/sds/mock_redis/tree/v0.
|
|
132
|
-
post_install_message:
|
|
137
|
+
source_code_uri: https://github.com/sds/mock_redis/tree/v0.56.0
|
|
138
|
+
post_install_message:
|
|
133
139
|
rdoc_options: []
|
|
134
140
|
require_paths:
|
|
135
141
|
- lib
|
|
@@ -144,8 +150,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
144
150
|
- !ruby/object:Gem::Version
|
|
145
151
|
version: '0'
|
|
146
152
|
requirements: []
|
|
147
|
-
rubygems_version: 3.
|
|
148
|
-
signing_key:
|
|
153
|
+
rubygems_version: 3.5.22
|
|
154
|
+
signing_key:
|
|
149
155
|
specification_version: 4
|
|
150
156
|
summary: Redis mock that just lives in memory; useful for testing.
|
|
151
157
|
test_files: []
|