mock_redis 0.20.0 → 0.25.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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +26 -5
  3. data/.rubocop_todo.yml +1 -1
  4. data/.travis.yml +3 -3
  5. data/CHANGELOG.md +37 -0
  6. data/Gemfile +2 -2
  7. data/LICENSE.md +21 -0
  8. data/README.md +37 -13
  9. data/lib/mock_redis.rb +0 -5
  10. data/lib/mock_redis/database.rb +53 -17
  11. data/lib/mock_redis/future.rb +1 -1
  12. data/lib/mock_redis/geospatial_methods.rb +4 -4
  13. data/lib/mock_redis/hash_methods.rb +22 -14
  14. data/lib/mock_redis/info_method.rb +2 -2
  15. data/lib/mock_redis/multi_db_wrapper.rb +2 -2
  16. data/lib/mock_redis/set_methods.rb +1 -0
  17. data/lib/mock_redis/stream.rb +22 -2
  18. data/lib/mock_redis/stream/id.rb +1 -1
  19. data/lib/mock_redis/stream_methods.rb +14 -1
  20. data/lib/mock_redis/string_methods.rb +28 -13
  21. data/lib/mock_redis/transaction_wrapper.rb +2 -2
  22. data/lib/mock_redis/utility_methods.rb +6 -3
  23. data/lib/mock_redis/version.rb +1 -1
  24. data/lib/mock_redis/zset_methods.rb +52 -9
  25. data/mock_redis.gemspec +1 -2
  26. data/spec/client_spec.rb +12 -0
  27. data/spec/commands/blpop_spec.rb +0 -6
  28. data/spec/commands/brpop_spec.rb +6 -5
  29. data/spec/commands/del_spec.rb +15 -0
  30. data/spec/commands/dump_spec.rb +19 -0
  31. data/spec/commands/exists_spec.rb +34 -5
  32. data/spec/commands/future_spec.rb +11 -1
  33. data/spec/commands/geoadd_spec.rb +1 -1
  34. data/spec/commands/hdel_spec.rb +16 -0
  35. data/spec/commands/hmset_spec.rb +26 -0
  36. data/spec/commands/hset_spec.rb +6 -6
  37. data/spec/commands/keys_spec.rb +17 -0
  38. data/spec/commands/mget_spec.rb +6 -0
  39. data/spec/commands/move_spec.rb +5 -5
  40. data/spec/commands/pipelined_spec.rb +20 -0
  41. data/spec/commands/restore_spec.rb +47 -0
  42. data/spec/commands/scan_spec.rb +9 -0
  43. data/spec/commands/set_spec.rb +12 -2
  44. data/spec/commands/setbit_spec.rb +1 -0
  45. data/spec/commands/setex_spec.rb +16 -0
  46. data/spec/commands/srandmember_spec.rb +1 -1
  47. data/spec/commands/srem_spec.rb +5 -0
  48. data/spec/commands/xadd_spec.rb +20 -0
  49. data/spec/commands/xrange_spec.rb +13 -0
  50. data/spec/commands/xread_spec.rb +50 -0
  51. data/spec/commands/xtrim_spec.rb +6 -0
  52. data/spec/commands/zinterstore_spec.rb +34 -0
  53. data/spec/commands/zpopmax_spec.rb +60 -0
  54. data/spec/commands/zpopmin_spec.rb +60 -0
  55. data/spec/commands/zrange_spec.rb +1 -1
  56. data/spec/commands/zrangebyscore_spec.rb +1 -1
  57. data/spec/commands/zrevrange_spec.rb +1 -1
  58. data/spec/commands/zrevrangebyscore_spec.rb +1 -1
  59. data/spec/commands/zunionstore_spec.rb +33 -0
  60. data/spec/mock_redis_spec.rb +4 -6
  61. data/spec/spec_helper.rb +4 -2
  62. data/spec/support/redis_multiplexer.rb +1 -0
  63. data/spec/transactions_spec.rb +16 -0
  64. metadata +16 -26
  65. data/LICENSE +0 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e3dccfea232f8d47d28dc16362a79232cd69a2a12e6d8127a43c83c03287ef4
4
- data.tar.gz: a2918811810246e6ddf92643e88121550310a7628f396aba12ed163b61c979a5
3
+ metadata.gz: e4a2d7e6a155059cb97120f7f95c1e992a87a3933be5fe337a443dc0a9ac30df
4
+ data.tar.gz: 135172a723e9d5bfb058eec85863387093d56e3a6d7729f28da425f2b239e4bb
5
5
  SHA512:
6
- metadata.gz: e8233e6e66afbaa2f7801e33f7b16a7bbbb2eb4cb3caab73a0c19e94857542fe49a96a9c45e8e995b54bf71d50c90ef78cfb5e293e85d91a3ade26f0e3a3e4d8
7
- data.tar.gz: e30d5a2656ef3fb793cb9a0a2df9e1e24cd78aaa83a6cf5a3483353075f2a475379e294fc6b9c5905229db1a453d16cc7fa02095aed3ad59799312ff5d76ced5
6
+ metadata.gz: 1e1b5cba39d01b11b2c85442c5d9485285bf661316e5196448484bc78dd9d866c3829648cd547ac0996d454f113d054c3bc43728f917740870f5fe191c97b15c
7
+ data.tar.gz: bf5d20de1111be19c03cacb2639f303d0ced578656bfc5b4dae4e8cd6e28991be003e7a20702ab6825526fbee6f91ebcd676fbb27c9010792abd37a2e7eab218
@@ -3,10 +3,10 @@ inherit_from: .rubocop_todo.yml
3
3
  AllCops:
4
4
  TargetRubyVersion: 2.4
5
5
 
6
- Layout/AlignArguments:
6
+ Layout/ArgumentAlignment:
7
7
  Enabled: false
8
8
 
9
- Layout/AlignParameters:
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
@@ -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/UncommunicativeMethodParamName:
12
+ Naming/MethodParameterName:
13
13
  Exclude:
14
14
  - 'lib/mock_redis/database.rb'
15
15
  - 'lib/mock_redis/expire_wrapper.rb'
@@ -17,9 +17,9 @@ before_install:
17
17
  - echo PING | nc localhost 6379
18
18
 
19
19
  rvm:
20
- - 2.4.6
21
- - 2.5.5
22
- - 2.6.3
20
+ - 2.4
21
+ - 2.5
22
+ - 2.6
23
23
 
24
24
  before_script:
25
25
  - git config --local user.email "travis@travis.ci"
@@ -1,5 +1,42 @@
1
1
  # MockRedis Changelog
2
2
 
3
+ ### 0.25.0
4
+
5
+ * Add support for `xread` command ([#190](https://github.com/sds/mock_redis/pull/190))
6
+ * Fix `mget` to raise error when passing empty array ([#191](https://github.com/sds/mock_redis/pull/191))
7
+ * Fix `xadd` when `maxlen` is zero ([#192](https://github.com/sds/mock_redis/pull/192))
8
+
9
+ ### 0.24.0
10
+
11
+ * Fix handling of blocks within `multi` blocks ([#185](https://github.com/sds/mock_redis/pull/185))
12
+ * Fix handling of multiple consecutive `?` characters in key pattern matching ([#186](https://github.com/sds/mock_redis/pull/186))
13
+ * Change `exists` to return an integer and add `exists?` ([#188](https://github.com/sds/mock_redis/pull/188))
14
+
15
+ ### 0.23.0
16
+
17
+ * Raise error when `setex` called with negative timeout ([#174](https://github.com/sds/mock_redis/pull/174))
18
+ * Add support for `dump`/`restore` between MockRedis instances ([#176](https://github.com/sds/mock_redis/pull/176))
19
+ * Fix warnings for ZSET methods on Ruby 2.7 ([#177](https://github.com/sds/mock_redis/pull/177))
20
+ * Add support for returning time in pipelines ([#179](https://github.com/sds/mock_redis/pull/179))
21
+ * Fix SET methods to correct set milliseconds with `px` ([#180](https://github.com/sds/mock_redis/pull/180))
22
+ * Add support for unsorted sets within `zinterstore`/`zunionstore`([#182](https://github.com/sds/mock_redis/pull/182))
23
+
24
+ ### 0.22.0
25
+
26
+ * Gracefully handle cursors larger than the collection size in scan commands ([#171](https://github.com/sds/mock_redis/pull/171))
27
+ * Add `zpopmin` and `zpopmax` commands ([#172](https://github.com/sds/mock_redis/pull/172))
28
+ * Fix `hmset` to support array arguments ([#173](https://github.com/sds/mock_redis/pull/173))
29
+ * Fix `hmset` to always treat keys as strings ([#173](https://github.com/sds/mock_redis/pull/173))
30
+ * Remove unnecessary dependency on `rake` gem
31
+
32
+ ### 0.21.0
33
+
34
+ * Fix behavior of `time` to return array of two integers ([#161](https://github.com/sds/mock_redis/pull/161))
35
+ * Add support for `close` and `disconnect!` ([#163](https://github.com/sds/mock_redis/pull/163))
36
+ * Fix `set` to properly handle (and ignore) other options ([#164](https://github.com/sds/mock_redis/pull/163))
37
+ * Fix `srem` to allow array of integers as argument ([#166](https://github.com/sds/mock_redis/pull/166))
38
+ * Fix `hdel` to allow array as argument ([#168](https://github.com/sds/mock_redis/pull/168))
39
+
3
40
  ### 0.20.0
4
41
 
5
42
  * Add support for `count` parameter of `spop`
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.48.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.68.1'
10
+ gem 'rubocop', '0.82.0'
11
11
 
12
12
  gem 'coveralls', require: false
@@ -0,0 +1,21 @@
1
+ MockRedis released under the MIT license.
2
+
3
+ > Copyright (c) Shane da Silva. http://shane.io
4
+ >
5
+ > Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ > of this software and associated documentation files (the "Software"), to deal
7
+ > in the Software without restriction, including without limitation the rights
8
+ > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ > copies of the Software, and to permit persons to whom the Software is
10
+ > furnished to do so, subject to the following conditions:
11
+ >
12
+ > The above copyright notice and this permission notice shall be included in
13
+ > all copies or substantial portions of the Software.
14
+ >
15
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ > SOFTWARE.
data/README.md CHANGED
@@ -8,11 +8,20 @@ MockRedis provides the same interface as `redis-rb`, but it stores its
8
8
  data in memory instead of talking to a Redis server. It is intended
9
9
  for use in tests.
10
10
 
11
- The current implementation is tested against *Redis 4*. Older versions
12
- of Redis may return different results or not support some commands.
11
+ ## Requirements
12
+
13
+ * Ruby 2.4+
14
+
15
+ The current implementation is tested against **Redis 5**. Older versions may work, but can also return different results or not support some commands.
13
16
 
14
17
  ## Getting Started
15
18
 
19
+ Install the gem:
20
+
21
+ ```bash
22
+ gem install mock_redis
23
+ ```
24
+
16
25
  It's as easy as `require 'mock_redis'; mr = MockRedis.new`. Then you can
17
26
  call the same methods on it as you can call on a real `Redis` object.
18
27
 
@@ -90,17 +99,32 @@ please submit a pull request with your (tested!) implementation.
90
99
  * `#config(:get|:set|:resetstat)` isn't done. They can just return
91
100
  canned values.
92
101
 
93
- ## Compatibility
102
+ ## Running tests
103
+
104
+ We recommend running Redis within a Docker container to make development as simple as possible, but as long as you have a Redis instance listening on `localhost:6379` you should be good to go.
105
+
106
+ 1. Start Redis.
107
+ ```bash
108
+ docker run --rm -p 6379:6379 redis
109
+ ```
110
+ 2. Install dependencies.
111
+ ```bash
112
+ bundle install
113
+ ```
114
+ 3. Run tests.
115
+ ```bash
116
+ bundle exec rspec
117
+ ```
118
+
119
+ These tests were written with Redis running on `localhost` without any
120
+ passwords required. If you're using a different version of Redis, you
121
+ may see failures due to error message text being different and other
122
+ breaking changes over time.
123
+
124
+ ## Changelog
94
125
 
95
- As of version `0.19.0`, Ruby 2.2 and above are supported. For
96
- older versions of Ruby, use `0.18.0` or older.
126
+ If you're interested in seeing the changes and bug fixes between each version of `mock_redis`, read the [MockRedis Changelog](CHANGELOG.md).
97
127
 
98
- ## Running the Tests
128
+ ## License
99
129
 
100
- If you want to work on this, you'll probably want to run the
101
- tests. (Just kidding! There's no probably about it.) These tests were
102
- written with Redis running on `localhost` without any passwords
103
- required. If you're using a different version of Redis, you may see
104
- failures due to error message text being different. If you're running
105
- a really old version of Redis, you'll definitely see failures due to
106
- stuff that doesn't work!
130
+ This project is released under the [MIT license](LICENSE.md).
@@ -64,11 +64,6 @@ class MockRedis
64
64
  options[:db]
65
65
  end
66
66
 
67
- def now
68
- options[:time_class].now
69
- end
70
- alias time now
71
-
72
67
  def time_at(timestamp)
73
68
  options[:time_class].at(timestamp)
74
69
  end
@@ -35,7 +35,7 @@ class MockRedis
35
35
 
36
36
  def initialize_copy(_source)
37
37
  @data = @data.clone
38
- @data.keys.each { |k| @data[k] = @data[k].clone }
38
+ @data.each_key { |k| @data[k] = @data[k].clone }
39
39
  @expire_times = @expire_times.map(&:clone)
40
40
  end
41
41
 
@@ -56,6 +56,8 @@ class MockRedis
56
56
  def disconnect
57
57
  nil
58
58
  end
59
+ alias close disconnect
60
+ alias disconnect! close
59
61
 
60
62
  def connected?
61
63
  true
@@ -86,7 +88,8 @@ class MockRedis
86
88
  end
87
89
 
88
90
  def pexpire(key, ms)
89
- now_ms = (@base.now.to_r * 1000).to_i
91
+ now, miliseconds = @base.now
92
+ now_ms = (now * 1000) + miliseconds
90
93
  pexpireat(key, now_ms + ms.to_i)
91
94
  end
92
95
 
@@ -103,7 +106,7 @@ class MockRedis
103
106
  raise Redis::CommandError, 'ERR value is not an integer or out of range'
104
107
  end
105
108
 
106
- if exists(key)
109
+ if exists?(key)
107
110
  timestamp = Rational(timestamp_ms.to_i, 1000)
108
111
  set_expiration(key, @base.time_at(timestamp))
109
112
  true
@@ -112,12 +115,33 @@ class MockRedis
112
115
  end
113
116
  end
114
117
 
115
- def exists(key)
116
- data.key?(key)
118
+ def exists(*keys)
119
+ keys.count { |key| data.key?(key) }
120
+ end
121
+
122
+ def exists?(*keys)
123
+ keys.each { |key| return true if data.key?(key) }
124
+ false
117
125
  end
118
126
 
119
127
  def flushdb
120
- data.keys.each { |k| del(k) }
128
+ data.each_key { |k| del(k) }
129
+ 'OK'
130
+ end
131
+
132
+ def dump(key)
133
+ value = data[key]
134
+ value ? Marshal.dump(value) : nil
135
+ end
136
+
137
+ def restore(key, ttl, value, replace: false)
138
+ if !replace && exists?(key)
139
+ raise Redis::CommandError, 'BUSYKEY Target key name already exists.'
140
+ end
141
+ data[key] = Marshal.load(value) # rubocop:disable Security/MarshalLoad
142
+ if ttl > 0
143
+ pexpire(key, ttl)
144
+ end
121
145
  'OK'
122
146
  end
123
147
 
@@ -140,11 +164,11 @@ class MockRedis
140
164
  end
141
165
 
142
166
  def lastsave
143
- @base.now.to_i
167
+ now.first
144
168
  end
145
169
 
146
170
  def persist(key)
147
- if exists(key) && has_expiration?(key)
171
+ if exists?(key) && has_expiration?(key)
148
172
  remove_expiration(key)
149
173
  true
150
174
  else
@@ -185,7 +209,7 @@ class MockRedis
185
209
  raise Redis::CommandError, 'ERR no such key'
186
210
  end
187
211
 
188
- if exists(newkey)
212
+ if exists?(newkey)
189
213
  false
190
214
  else
191
215
  rename(key, newkey)
@@ -198,27 +222,38 @@ class MockRedis
198
222
  end
199
223
 
200
224
  def ttl(key)
201
- if !exists(key)
225
+ if !exists?(key)
202
226
  -2
203
227
  elsif has_expiration?(key)
204
- expiration(key).to_i - @base.now.to_i
228
+ now, = @base.now
229
+ expiration(key).to_i - now
205
230
  else
206
231
  -1
207
232
  end
208
233
  end
209
234
 
210
235
  def pttl(key)
211
- if !exists(key)
236
+ now, miliseconds = @base.now
237
+ now_ms = now * 1000 + miliseconds
238
+
239
+ if !exists?(key)
212
240
  -2
213
241
  elsif has_expiration?(key)
214
- (expiration(key).to_r * 1000).to_i - (@base.now.to_r * 1000).to_i
242
+ (expiration(key).to_r * 1000).to_i - now_ms
215
243
  else
216
244
  -1
217
245
  end
218
246
  end
219
247
 
248
+ def now
249
+ current_time = @base.options[:time_class].now
250
+ miliseconds = (current_time.to_r - current_time.to_i) * 1_000
251
+ [current_time.to_i, miliseconds.to_i]
252
+ end
253
+ alias time now
254
+
220
255
  def type(key)
221
- if !exists(key)
256
+ if !exists?(key)
222
257
  'none'
223
258
  elsif hashy?(key)
224
259
  'hash'
@@ -293,7 +328,7 @@ class MockRedis
293
328
  Regexp.new(
294
329
  "^#{pattern}$".
295
330
  gsub(/([+|()])/, '\\\\\1').
296
- gsub(/([^\\])\?/, '\\1.').
331
+ gsub(/(?<!\\)\?/, '\\1.').
297
332
  gsub(/([^\\])\*/, '\\1.*')
298
333
  )
299
334
  end
@@ -321,10 +356,11 @@ class MockRedis
321
356
  # This method isn't private, but it also isn't a Redis command, so
322
357
  # it doesn't belong up above with all the Redis commands.
323
358
  def expire_keys
324
- now = @base.now
359
+ now_sec, miliseconds = now
360
+ now_ms = now_sec * 1_000 + miliseconds
325
361
 
326
362
  to_delete = expire_times.take_while do |(time, _key)|
327
- (time.to_r * 1_000).to_i <= (now.to_r * 1_000).to_i
363
+ (time.to_r * 1_000).to_i <= now_ms
328
364
  end
329
365
 
330
366
  to_delete.each do |(_time, key)|
@@ -17,7 +17,7 @@ class MockRedis
17
17
 
18
18
  def store_result(result)
19
19
  @result_set = true
20
- @result = result
20
+ @result = @block ? @block.call(result) : result
21
21
  end
22
22
  end
23
23
  end
@@ -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('%.4f', distance)
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('%.6f', lng)
99
- lat = format('%.6f', lat)
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('%.17f', coord)
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]