mock_redis 0.23.0 → 0.27.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +26 -5
- data/.rubocop_todo.yml +1 -1
- data/.travis.yml +1 -0
- data/CHANGELOG.md +27 -0
- data/Gemfile +2 -2
- data/lib/mock_redis.rb +1 -1
- data/lib/mock_redis/connection_method.rb +13 -0
- data/lib/mock_redis/database.rb +21 -14
- data/lib/mock_redis/expire_wrapper.rb +1 -1
- data/lib/mock_redis/future.rb +1 -1
- data/lib/mock_redis/geospatial_methods.rb +5 -5
- data/lib/mock_redis/hash_methods.rb +9 -4
- data/lib/mock_redis/info_method.rb +2 -2
- data/lib/mock_redis/multi_db_wrapper.rb +3 -3
- data/lib/mock_redis/pipelined_wrapper.rb +1 -1
- data/lib/mock_redis/stream.rb +22 -2
- data/lib/mock_redis/stream/id.rb +1 -1
- data/lib/mock_redis/stream_methods.rb +16 -1
- data/lib/mock_redis/string_methods.rb +21 -17
- data/lib/mock_redis/transaction_wrapper.rb +3 -3
- data/lib/mock_redis/utility_methods.rb +1 -1
- data/lib/mock_redis/version.rb +1 -1
- data/mock_redis.gemspec +2 -1
- data/spec/commands/blpop_spec.rb +0 -6
- data/spec/commands/brpop_spec.rb +6 -5
- data/spec/commands/connection_spec.rb +15 -0
- data/spec/commands/del_spec.rb +17 -0
- data/spec/commands/exists_spec.rb +34 -5
- data/spec/commands/future_spec.rb +11 -1
- data/spec/commands/geoadd_spec.rb +1 -1
- data/spec/commands/hset_spec.rb +6 -6
- data/spec/commands/keys_spec.rb +17 -0
- data/spec/commands/mget_spec.rb +6 -0
- data/spec/commands/move_spec.rb +5 -5
- data/spec/commands/set_spec.rb +55 -7
- data/spec/commands/setbit_spec.rb +1 -0
- data/spec/commands/srandmember_spec.rb +1 -1
- data/spec/commands/xadd_spec.rb +23 -3
- data/spec/commands/xlen_spec.rb +3 -1
- data/spec/commands/xrange_spec.rb +13 -0
- data/spec/commands/xread_spec.rb +66 -0
- data/spec/commands/xtrim_spec.rb +6 -0
- data/spec/commands/zrange_spec.rb +1 -1
- data/spec/commands/zrangebyscore_spec.rb +1 -1
- data/spec/commands/zrevrange_spec.rb +1 -1
- data/spec/commands/zrevrangebyscore_spec.rb +1 -1
- data/spec/spec_helper.rb +2 -1
- data/spec/support/redis_multiplexer.rb +2 -1
- data/spec/transactions_spec.rb +16 -0
- metadata +24 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cbd4308bd9413896742121468e39d64096e55b7760453a4220696aaa0554e38e
|
4
|
+
data.tar.gz: 0f8e0c66ac4533044ad91ee89a383bba1297cc186fdf514c66923a6188024b0c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6fde8339234a0e1c8022cd63208e8e279cc834ff4280c6690d85a516259f7c29ac36fd3882a433a94af4e4e0bb584a7c332c823cc54db59556b17e7bf42d2f97
|
7
|
+
data.tar.gz: 4f7d508739c4eef78afa8e155d2ec4166f5b5b644f37f846a59f9b555c34bb948d545fca55c3d2c7dae2781fe90c24c7fabb804b3406f97030e8ee4e68eff9f4
|
data/.rubocop.yml
CHANGED
@@ -3,10 +3,10 @@ inherit_from: .rubocop_todo.yml
|
|
3
3
|
AllCops:
|
4
4
|
TargetRubyVersion: 2.4
|
5
5
|
|
6
|
-
Layout/
|
6
|
+
Layout/ArgumentAlignment:
|
7
7
|
Enabled: false
|
8
8
|
|
9
|
-
Layout/
|
9
|
+
Layout/ParameterAlignment:
|
10
10
|
Enabled: false
|
11
11
|
|
12
12
|
Layout/DotPosition:
|
@@ -15,9 +15,21 @@ Layout/DotPosition:
|
|
15
15
|
Layout/EmptyLineAfterGuardClause:
|
16
16
|
Enabled: false
|
17
17
|
|
18
|
+
Layout/LineLength:
|
19
|
+
Max: 100
|
20
|
+
|
21
|
+
Layout/SpaceAroundMethodCallOperator:
|
22
|
+
Enabled: true
|
23
|
+
|
18
24
|
Lint/AssignmentInCondition:
|
19
25
|
Enabled: false
|
20
26
|
|
27
|
+
Lint/RaiseException:
|
28
|
+
Enabled: true
|
29
|
+
|
30
|
+
Lint/StructNewOverride:
|
31
|
+
Enabled: true
|
32
|
+
|
21
33
|
# We use this a lot in specs where it's perfectly valid
|
22
34
|
Lint/Void:
|
23
35
|
Exclude:
|
@@ -35,9 +47,6 @@ Metrics/CyclomaticComplexity:
|
|
35
47
|
Metrics/ClassLength:
|
36
48
|
Enabled: false
|
37
49
|
|
38
|
-
Metrics/LineLength:
|
39
|
-
Max: 100
|
40
|
-
|
41
50
|
Metrics/MethodLength:
|
42
51
|
Enabled: false
|
43
52
|
|
@@ -57,6 +66,18 @@ Style/Documentation:
|
|
57
66
|
Style/DoubleNegation:
|
58
67
|
Enabled: false
|
59
68
|
|
69
|
+
Style/ExponentialNotation:
|
70
|
+
Enabled: true
|
71
|
+
|
72
|
+
Style/HashEachMethods:
|
73
|
+
Enabled: true
|
74
|
+
|
75
|
+
Style/HashTransformKeys:
|
76
|
+
Enabled: true
|
77
|
+
|
78
|
+
Style/HashTransformValues:
|
79
|
+
Enabled: true
|
80
|
+
|
60
81
|
# We have too much code that relies on modifying strings
|
61
82
|
Style/FrozenStringLiteralComment:
|
62
83
|
Enabled: false
|
data/.rubocop_todo.yml
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
# Offense count: 17
|
10
10
|
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
|
11
11
|
# AllowedNames: io, id, to, by, on, in, at, ip
|
12
|
-
Naming/
|
12
|
+
Naming/MethodParameterName:
|
13
13
|
Exclude:
|
14
14
|
- 'lib/mock_redis/database.rb'
|
15
15
|
- 'lib/mock_redis/expire_wrapper.rb'
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,32 @@
|
|
1
1
|
# MockRedis Changelog
|
2
2
|
|
3
|
+
### 0.27.1
|
4
|
+
|
5
|
+
* Fix missing `ruby2_keywords` gem
|
6
|
+
* Allow passing string `offset` to `getbit` ([#203](https://github.com/sds/mock_redis/pull/203))
|
7
|
+
|
8
|
+
### 0.27.0
|
9
|
+
|
10
|
+
* Fix handling of keyword arguments on Ruby 3 ([#199](https://github.com/sds/mock_redis/pull/199))
|
11
|
+
* Allow passing string `offset` to `setbit` ([#200](https://github.com/sds/mock_redis/pull/200))
|
12
|
+
* Add `connection` method ([#201](https://github.com/sds/mock_redis/pull/201))
|
13
|
+
|
14
|
+
### 0.26.0
|
15
|
+
|
16
|
+
* Add block and count support to `xread` ([#194](https://github.com/sds/mock_redis/pull/194))
|
17
|
+
|
18
|
+
### 0.25.0
|
19
|
+
|
20
|
+
* Add support for `xread` command ([#190](https://github.com/sds/mock_redis/pull/190))
|
21
|
+
* Fix `mget` to raise error when passing empty array ([#191](https://github.com/sds/mock_redis/pull/191))
|
22
|
+
* Fix `xadd` when `maxlen` is zero ([#192](https://github.com/sds/mock_redis/pull/192))
|
23
|
+
|
24
|
+
### 0.24.0
|
25
|
+
|
26
|
+
* Fix handling of blocks within `multi` blocks ([#185](https://github.com/sds/mock_redis/pull/185))
|
27
|
+
* Fix handling of multiple consecutive `?` characters in key pattern matching ([#186](https://github.com/sds/mock_redis/pull/186))
|
28
|
+
* Change `exists` to return an integer and add `exists?` ([#188](https://github.com/sds/mock_redis/pull/188))
|
29
|
+
|
3
30
|
### 0.23.0
|
4
31
|
|
5
32
|
* Raise error when `setex` called with negative timeout ([#174](https://github.com/sds/mock_redis/pull/174))
|
data/Gemfile
CHANGED
@@ -4,9 +4,9 @@ source 'http://rubygems.org'
|
|
4
4
|
gemspec
|
5
5
|
|
6
6
|
# Run all pre-commit hooks via Overcommit during CI runs
|
7
|
-
gem 'overcommit', '0.
|
7
|
+
gem 'overcommit', '0.53.0'
|
8
8
|
|
9
9
|
# Pin tool versions (which are executed by Overcommit) for Travis builds
|
10
|
-
gem 'rubocop', '0.
|
10
|
+
gem 'rubocop', '0.82.0'
|
11
11
|
|
12
12
|
gem 'coveralls', require: false
|
data/lib/mock_redis.rb
CHANGED
data/lib/mock_redis/database.rb
CHANGED
@@ -11,6 +11,7 @@ require 'mock_redis/info_method'
|
|
11
11
|
require 'mock_redis/utility_methods'
|
12
12
|
require 'mock_redis/geospatial_methods'
|
13
13
|
require 'mock_redis/stream_methods'
|
14
|
+
require 'mock_redis/connection_method'
|
14
15
|
|
15
16
|
class MockRedis
|
16
17
|
class Database
|
@@ -24,6 +25,7 @@ class MockRedis
|
|
24
25
|
include UtilityMethods
|
25
26
|
include GeospatialMethods
|
26
27
|
include StreamMethods
|
28
|
+
include ConnectionMethod
|
27
29
|
|
28
30
|
attr_reader :data, :expire_times
|
29
31
|
|
@@ -35,7 +37,7 @@ class MockRedis
|
|
35
37
|
|
36
38
|
def initialize_copy(_source)
|
37
39
|
@data = @data.clone
|
38
|
-
@data.
|
40
|
+
@data.each_key { |k| @data[k] = @data[k].clone }
|
39
41
|
@expire_times = @expire_times.map(&:clone)
|
40
42
|
end
|
41
43
|
|
@@ -106,7 +108,7 @@ class MockRedis
|
|
106
108
|
raise Redis::CommandError, 'ERR value is not an integer or out of range'
|
107
109
|
end
|
108
110
|
|
109
|
-
if exists(key)
|
111
|
+
if exists?(key)
|
110
112
|
timestamp = Rational(timestamp_ms.to_i, 1000)
|
111
113
|
set_expiration(key, @base.time_at(timestamp))
|
112
114
|
true
|
@@ -115,12 +117,17 @@ class MockRedis
|
|
115
117
|
end
|
116
118
|
end
|
117
119
|
|
118
|
-
def exists(
|
119
|
-
data.key?(key)
|
120
|
+
def exists(*keys)
|
121
|
+
keys.count { |key| data.key?(key) }
|
122
|
+
end
|
123
|
+
|
124
|
+
def exists?(*keys)
|
125
|
+
keys.each { |key| return true if data.key?(key) }
|
126
|
+
false
|
120
127
|
end
|
121
128
|
|
122
129
|
def flushdb
|
123
|
-
data.
|
130
|
+
data.each_key { |k| del(k) }
|
124
131
|
'OK'
|
125
132
|
end
|
126
133
|
|
@@ -130,7 +137,7 @@ class MockRedis
|
|
130
137
|
end
|
131
138
|
|
132
139
|
def restore(key, ttl, value, replace: false)
|
133
|
-
if !replace && exists(key)
|
140
|
+
if !replace && exists?(key)
|
134
141
|
raise Redis::CommandError, 'BUSYKEY Target key name already exists.'
|
135
142
|
end
|
136
143
|
data[key] = Marshal.load(value) # rubocop:disable Security/MarshalLoad
|
@@ -163,7 +170,7 @@ class MockRedis
|
|
163
170
|
end
|
164
171
|
|
165
172
|
def persist(key)
|
166
|
-
if exists(key) && has_expiration?(key)
|
173
|
+
if exists?(key) && has_expiration?(key)
|
167
174
|
remove_expiration(key)
|
168
175
|
true
|
169
176
|
else
|
@@ -204,7 +211,7 @@ class MockRedis
|
|
204
211
|
raise Redis::CommandError, 'ERR no such key'
|
205
212
|
end
|
206
213
|
|
207
|
-
if exists(newkey)
|
214
|
+
if exists?(newkey)
|
208
215
|
false
|
209
216
|
else
|
210
217
|
rename(key, newkey)
|
@@ -217,7 +224,7 @@ class MockRedis
|
|
217
224
|
end
|
218
225
|
|
219
226
|
def ttl(key)
|
220
|
-
if !exists(key)
|
227
|
+
if !exists?(key)
|
221
228
|
-2
|
222
229
|
elsif has_expiration?(key)
|
223
230
|
now, = @base.now
|
@@ -231,7 +238,7 @@ class MockRedis
|
|
231
238
|
now, miliseconds = @base.now
|
232
239
|
now_ms = now * 1000 + miliseconds
|
233
240
|
|
234
|
-
if !exists(key)
|
241
|
+
if !exists?(key)
|
235
242
|
-2
|
236
243
|
elsif has_expiration?(key)
|
237
244
|
(expiration(key).to_r * 1000).to_i - now_ms
|
@@ -248,7 +255,7 @@ class MockRedis
|
|
248
255
|
alias time now
|
249
256
|
|
250
257
|
def type(key)
|
251
|
-
if !exists(key)
|
258
|
+
if !exists?(key)
|
252
259
|
'none'
|
253
260
|
elsif hashy?(key)
|
254
261
|
'hash'
|
@@ -323,7 +330,7 @@ class MockRedis
|
|
323
330
|
Regexp.new(
|
324
331
|
"^#{pattern}$".
|
325
332
|
gsub(/([+|()])/, '\\\\\1').
|
326
|
-
gsub(/(
|
333
|
+
gsub(/(?<!\\)\?/, '\\1.').
|
327
334
|
gsub(/([^\\])\*/, '\\1.*')
|
328
335
|
)
|
329
336
|
end
|
@@ -351,8 +358,8 @@ class MockRedis
|
|
351
358
|
# This method isn't private, but it also isn't a Redis command, so
|
352
359
|
# it doesn't belong up above with all the Redis commands.
|
353
360
|
def expire_keys
|
354
|
-
|
355
|
-
now_ms =
|
361
|
+
now_sec, miliseconds = now
|
362
|
+
now_ms = now_sec * 1_000 + miliseconds
|
356
363
|
|
357
364
|
to_delete = expire_times.take_while do |(time, _key)|
|
358
365
|
(time.to_r * 1_000).to_i <= now_ms
|
data/lib/mock_redis/future.rb
CHANGED
@@ -12,7 +12,7 @@ class MockRedis
|
|
12
12
|
D_R = Math::PI / 180.0
|
13
13
|
EARTH_RADIUS_IN_METERS = 6_372_797.560856
|
14
14
|
|
15
|
-
def geoadd(key, *args)
|
15
|
+
ruby2_keywords def geoadd(key, *args)
|
16
16
|
points = parse_points(args)
|
17
17
|
|
18
18
|
scored_points = points.map do |point|
|
@@ -38,7 +38,7 @@ class MockRedis
|
|
38
38
|
lng2, lat2 = geohash_decode(hash2)
|
39
39
|
|
40
40
|
distance = geohash_distance(lng1, lat1, lng2, lat2) / to_meter
|
41
|
-
format('
|
41
|
+
format('%<distance>.4f', distance: distance)
|
42
42
|
end
|
43
43
|
|
44
44
|
def geohash(key, members)
|
@@ -95,8 +95,8 @@ class MockRedis
|
|
95
95
|
lat = Float(point[1])
|
96
96
|
|
97
97
|
unless LNG_RANGE.include?(lng) && LAT_RANGE.include?(lat)
|
98
|
-
lng = format('
|
99
|
-
lat = format('
|
98
|
+
lng = format('%<long>.6f', long: lng)
|
99
|
+
lat = format('%<lat>.6f', lat: lat)
|
100
100
|
raise Redis::CommandError,
|
101
101
|
"ERR invalid longitude,latitude pair #{lng},#{lat}"
|
102
102
|
end
|
@@ -201,7 +201,7 @@ class MockRedis
|
|
201
201
|
end
|
202
202
|
|
203
203
|
def format_decoded_coord(coord)
|
204
|
-
coord = format('
|
204
|
+
coord = format('%<coord>.17f', coord: coord)
|
205
205
|
l = 1
|
206
206
|
l += 1 while coord[-l] == '0'
|
207
207
|
coord = coord[0..-l]
|
@@ -128,10 +128,15 @@ class MockRedis
|
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
131
|
-
def hset(key,
|
132
|
-
|
133
|
-
with_hash_at(key)
|
134
|
-
|
131
|
+
def hset(key, *args)
|
132
|
+
added = 0
|
133
|
+
with_hash_at(key) do |hash|
|
134
|
+
args.each_slice(2) do |field, value|
|
135
|
+
added += 1 unless hash.key?(field.to_s)
|
136
|
+
hash[field.to_s] = value.to_s
|
137
|
+
end
|
138
|
+
end
|
139
|
+
added
|
135
140
|
end
|
136
141
|
|
137
142
|
def hsetnx(key, field, value)
|
@@ -83,7 +83,7 @@ class MockRedis
|
|
83
83
|
|
84
84
|
# The Ruby Redis client returns commandstats differently when it's called as
|
85
85
|
# "INFO commandstats".
|
86
|
-
# rubocop:disable
|
86
|
+
# rubocop:disable Layout/LineLength
|
87
87
|
COMMAND_STATS_SOLO_INFO = {
|
88
88
|
'auth' => { 'calls' => '572501', 'usec' => '2353163', 'usec_per_call' => '4.11' },
|
89
89
|
'client' => { 'calls' => '1', 'usec' => '80', 'usec_per_call' => '80.00' },
|
@@ -123,7 +123,7 @@ class MockRedis
|
|
123
123
|
'cmdstat_smembers' => 'calls=58,usec=231,usec_per_call=3.98',
|
124
124
|
'cmdstat_sunionstore' => 'calls=4185027,usec=11762454022,usec_per_call=2810.60',
|
125
125
|
}.freeze
|
126
|
-
# rubocop:enable
|
126
|
+
# rubocop:enable Layout/LineLength
|
127
127
|
|
128
128
|
DEFAULT_INFO = [
|
129
129
|
SERVER_INFO,
|
@@ -17,14 +17,14 @@ class MockRedis
|
|
17
17
|
super || current_db.respond_to?(method, include_private)
|
18
18
|
end
|
19
19
|
|
20
|
-
def method_missing(method, *args, &block)
|
20
|
+
ruby2_keywords def method_missing(method, *args, &block)
|
21
21
|
current_db.send(method, *args, &block)
|
22
22
|
end
|
23
23
|
|
24
24
|
def initialize_copy(source)
|
25
25
|
super
|
26
26
|
@databases = @databases.clone
|
27
|
-
@databases.
|
27
|
+
@databases.each_key do |k|
|
28
28
|
@databases[k] = @databases[k].clone
|
29
29
|
end
|
30
30
|
end
|
@@ -39,7 +39,7 @@ class MockRedis
|
|
39
39
|
src = current_db
|
40
40
|
dest = db(db_index)
|
41
41
|
|
42
|
-
if !src.exists(key) || dest.exists(key)
|
42
|
+
if !src.exists?(key) || dest.exists?(key)
|
43
43
|
false
|
44
44
|
else
|
45
45
|
case current_db.type(key)
|
@@ -18,7 +18,7 @@ class MockRedis
|
|
18
18
|
@pipelined_futures = @pipelined_futures.clone
|
19
19
|
end
|
20
20
|
|
21
|
-
def method_missing(method, *args, &block)
|
21
|
+
ruby2_keywords def method_missing(method, *args, &block)
|
22
22
|
if in_pipeline?
|
23
23
|
future = MockRedis::Future.new([method, *args], block)
|
24
24
|
@pipelined_futures << future
|
data/lib/mock_redis/stream.rb
CHANGED
@@ -23,14 +23,26 @@ class MockRedis
|
|
23
23
|
|
24
24
|
def add(id, values)
|
25
25
|
@last_id = MockRedis::Stream::Id.new(id, min: @last_id)
|
26
|
+
if @last_id.to_s == '0-0'
|
27
|
+
raise Redis::CommandError,
|
28
|
+
'ERR The ID specified in XADD must be greater than 0-0'
|
29
|
+
end
|
26
30
|
members.add [@last_id, Hash[values.map { |k, v| [k.to_s, v.to_s] }]]
|
27
31
|
@last_id.to_s
|
28
32
|
end
|
29
33
|
|
30
34
|
def trim(count)
|
31
35
|
deleted = @members.size - count
|
32
|
-
|
33
|
-
|
36
|
+
if deleted > 0
|
37
|
+
@members = if count == 0
|
38
|
+
Set.new
|
39
|
+
else
|
40
|
+
@members.to_a[-count..-1].to_set
|
41
|
+
end
|
42
|
+
deleted
|
43
|
+
else
|
44
|
+
0
|
45
|
+
end
|
34
46
|
end
|
35
47
|
|
36
48
|
def range(start, finish, reversed, *opts_in)
|
@@ -45,6 +57,14 @@ class MockRedis
|
|
45
57
|
items
|
46
58
|
end
|
47
59
|
|
60
|
+
def read(id, *opts_in)
|
61
|
+
opts = options opts_in, %w[count block]
|
62
|
+
stream_id = MockRedis::Stream::Id.new(id)
|
63
|
+
items = members.select { |m| (stream_id < m[0]) }.map { |m| [m[0].to_s, m[1]] }
|
64
|
+
return items.first(opts['count'].to_i) if opts.key?('count')
|
65
|
+
items
|
66
|
+
end
|
67
|
+
|
48
68
|
def each
|
49
69
|
members.each { |m| yield m }
|
50
70
|
end
|