mock_redis 0.20.0 → 0.25.0

Sign up to get free protection for your applications and to get access to all the features.
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]