mock_redis 0.21.0 → 0.26.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 (60) 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 +33 -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 -7
  10. data/lib/mock_redis/database.rb +42 -14
  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 +18 -6
  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/stream.rb +25 -2
  17. data/lib/mock_redis/stream/id.rb +1 -1
  18. data/lib/mock_redis/stream_methods.rb +16 -1
  19. data/lib/mock_redis/string_methods.rb +17 -9
  20. data/lib/mock_redis/transaction_wrapper.rb +2 -2
  21. data/lib/mock_redis/utility_methods.rb +6 -3
  22. data/lib/mock_redis/version.rb +1 -1
  23. data/lib/mock_redis/zset_methods.rb +52 -9
  24. data/mock_redis.gemspec +1 -2
  25. data/spec/commands/blpop_spec.rb +0 -6
  26. data/spec/commands/brpop_spec.rb +6 -5
  27. data/spec/commands/del_spec.rb +15 -0
  28. data/spec/commands/dump_spec.rb +19 -0
  29. data/spec/commands/exists_spec.rb +34 -5
  30. data/spec/commands/future_spec.rb +11 -1
  31. data/spec/commands/geoadd_spec.rb +1 -1
  32. data/spec/commands/hmset_spec.rb +26 -0
  33. data/spec/commands/hset_spec.rb +6 -6
  34. data/spec/commands/keys_spec.rb +17 -0
  35. data/spec/commands/mget_spec.rb +6 -0
  36. data/spec/commands/move_spec.rb +5 -5
  37. data/spec/commands/pipelined_spec.rb +20 -0
  38. data/spec/commands/restore_spec.rb +47 -0
  39. data/spec/commands/scan_spec.rb +9 -0
  40. data/spec/commands/set_spec.rb +8 -4
  41. data/spec/commands/setbit_spec.rb +1 -0
  42. data/spec/commands/setex_spec.rb +16 -0
  43. data/spec/commands/srandmember_spec.rb +1 -1
  44. data/spec/commands/xadd_spec.rb +20 -0
  45. data/spec/commands/xrange_spec.rb +13 -0
  46. data/spec/commands/xread_spec.rb +66 -0
  47. data/spec/commands/xtrim_spec.rb +6 -0
  48. data/spec/commands/zinterstore_spec.rb +34 -0
  49. data/spec/commands/zpopmax_spec.rb +60 -0
  50. data/spec/commands/zpopmin_spec.rb +60 -0
  51. data/spec/commands/zrange_spec.rb +1 -1
  52. data/spec/commands/zrangebyscore_spec.rb +1 -1
  53. data/spec/commands/zrevrange_spec.rb +1 -1
  54. data/spec/commands/zrevrangebyscore_spec.rb +1 -1
  55. data/spec/commands/zunionstore_spec.rb +33 -0
  56. data/spec/spec_helper.rb +4 -2
  57. data/spec/support/redis_multiplexer.rb +1 -0
  58. data/spec/transactions_spec.rb +16 -0
  59. metadata +16 -26
  60. data/LICENSE +0 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4443d90955b31a149afd0c66972a06993bf9b01f6bd826276c2d60edb49b3141
4
- data.tar.gz: 2d096c0b7438b81720adcd50ecb36a8fa19df7a05c822ed4beee6d06d459f1b4
3
+ metadata.gz: c087a47dd536c50c70b1238484a41a46669ca210b53063efd87fc863c83aaa67
4
+ data.tar.gz: 41927d6f1904768faaa5f9bbeff4fa803f7dc1045aa76da8f885594326eab326
5
5
  SHA512:
6
- metadata.gz: bdcb30c9756e77fcc4f857f0bdaa01d73faa28b8ea7cc37aa669c0f5cf2e2967d3e216a32ff50c7c78f6945b313f8525073060daf9f980187d566202f3dbcda2
7
- data.tar.gz: 17502b628f1a5a62fa2046b529ef0178d8b84453d3a8b83441e3f2eef72282d45a79e60bee34e9a871ea553deb08c1c286b915d4b2c6ffe52fab3e6e32a6cc56
6
+ metadata.gz: efe4f6cb8a7c3326e8ad06c1753a651dc41323ca1fb4a7f23a1ac080feb09a4192f5d140ddeb437ce7268c0325e9e8bc4270f6d68facc88696d9586b9342d388
7
+ data.tar.gz: 7392d8b37355b3c22dd7c51745df2f67497dfb88f2534a5cf5df606e07b6fe47cc7969e4e005da85b4a4e382ef8b7801e220f948db71313330079781a973ff75
@@ -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,38 @@
1
1
  # MockRedis Changelog
2
2
 
3
+ ### 0.26.0
4
+
5
+ * Add block and count support to `xread` ([#194](https://github.com/sds/mock_redis/pull/194))
6
+
7
+ ### 0.25.0
8
+
9
+ * Add support for `xread` command ([#190](https://github.com/sds/mock_redis/pull/190))
10
+ * Fix `mget` to raise error when passing empty array ([#191](https://github.com/sds/mock_redis/pull/191))
11
+ * Fix `xadd` when `maxlen` is zero ([#192](https://github.com/sds/mock_redis/pull/192))
12
+
13
+ ### 0.24.0
14
+
15
+ * Fix handling of blocks within `multi` blocks ([#185](https://github.com/sds/mock_redis/pull/185))
16
+ * Fix handling of multiple consecutive `?` characters in key pattern matching ([#186](https://github.com/sds/mock_redis/pull/186))
17
+ * Change `exists` to return an integer and add `exists?` ([#188](https://github.com/sds/mock_redis/pull/188))
18
+
19
+ ### 0.23.0
20
+
21
+ * Raise error when `setex` called with negative timeout ([#174](https://github.com/sds/mock_redis/pull/174))
22
+ * Add support for `dump`/`restore` between MockRedis instances ([#176](https://github.com/sds/mock_redis/pull/176))
23
+ * Fix warnings for ZSET methods on Ruby 2.7 ([#177](https://github.com/sds/mock_redis/pull/177))
24
+ * Add support for returning time in pipelines ([#179](https://github.com/sds/mock_redis/pull/179))
25
+ * Fix SET methods to correct set milliseconds with `px` ([#180](https://github.com/sds/mock_redis/pull/180))
26
+ * Add support for unsorted sets within `zinterstore`/`zunionstore`([#182](https://github.com/sds/mock_redis/pull/182))
27
+
28
+ ### 0.22.0
29
+
30
+ * Gracefully handle cursors larger than the collection size in scan commands ([#171](https://github.com/sds/mock_redis/pull/171))
31
+ * Add `zpopmin` and `zpopmax` commands ([#172](https://github.com/sds/mock_redis/pull/172))
32
+ * Fix `hmset` to support array arguments ([#173](https://github.com/sds/mock_redis/pull/173))
33
+ * Fix `hmset` to always treat keys as strings ([#173](https://github.com/sds/mock_redis/pull/173))
34
+ * Remove unnecessary dependency on `rake` gem
35
+
3
36
  ### 0.21.0
4
37
 
5
38
  * Fix behavior of `time` to return array of two integers ([#161](https://github.com/sds/mock_redis/pull/161))
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,13 +64,6 @@ class MockRedis
64
64
  options[:db]
65
65
  end
66
66
 
67
- def now
68
- current_time = options[:time_class].now
69
- miliseconds = (current_time.to_r - current_time.to_i) * 1_000
70
- [current_time.to_i, miliseconds.to_i]
71
- end
72
- alias time now
73
-
74
67
  def time_at(timestamp)
75
68
  options[:time_class].at(timestamp)
76
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
 
@@ -106,7 +106,7 @@ class MockRedis
106
106
  raise Redis::CommandError, 'ERR value is not an integer or out of range'
107
107
  end
108
108
 
109
- if exists(key)
109
+ if exists?(key)
110
110
  timestamp = Rational(timestamp_ms.to_i, 1000)
111
111
  set_expiration(key, @base.time_at(timestamp))
112
112
  true
@@ -115,12 +115,33 @@ class MockRedis
115
115
  end
116
116
  end
117
117
 
118
- def exists(key)
119
- 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
120
125
  end
121
126
 
122
127
  def flushdb
123
- 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
124
145
  'OK'
125
146
  end
126
147
 
@@ -143,11 +164,11 @@ class MockRedis
143
164
  end
144
165
 
145
166
  def lastsave
146
- @base.now.first
167
+ now.first
147
168
  end
148
169
 
149
170
  def persist(key)
150
- if exists(key) && has_expiration?(key)
171
+ if exists?(key) && has_expiration?(key)
151
172
  remove_expiration(key)
152
173
  true
153
174
  else
@@ -188,7 +209,7 @@ class MockRedis
188
209
  raise Redis::CommandError, 'ERR no such key'
189
210
  end
190
211
 
191
- if exists(newkey)
212
+ if exists?(newkey)
192
213
  false
193
214
  else
194
215
  rename(key, newkey)
@@ -201,7 +222,7 @@ class MockRedis
201
222
  end
202
223
 
203
224
  def ttl(key)
204
- if !exists(key)
225
+ if !exists?(key)
205
226
  -2
206
227
  elsif has_expiration?(key)
207
228
  now, = @base.now
@@ -215,7 +236,7 @@ class MockRedis
215
236
  now, miliseconds = @base.now
216
237
  now_ms = now * 1000 + miliseconds
217
238
 
218
- if !exists(key)
239
+ if !exists?(key)
219
240
  -2
220
241
  elsif has_expiration?(key)
221
242
  (expiration(key).to_r * 1000).to_i - now_ms
@@ -224,8 +245,15 @@ class MockRedis
224
245
  end
225
246
  end
226
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
+
227
255
  def type(key)
228
- if !exists(key)
256
+ if !exists?(key)
229
257
  'none'
230
258
  elsif hashy?(key)
231
259
  'hash'
@@ -300,7 +328,7 @@ class MockRedis
300
328
  Regexp.new(
301
329
  "^#{pattern}$".
302
330
  gsub(/([+|()])/, '\\\\\1').
303
- gsub(/([^\\])\?/, '\\1.').
331
+ gsub(/(?<!\\)\?/, '\\1.').
304
332
  gsub(/([^\\])\*/, '\\1.*')
305
333
  )
306
334
  end
@@ -328,8 +356,8 @@ class MockRedis
328
356
  # This method isn't private, but it also isn't a Redis command, so
329
357
  # it doesn't belong up above with all the Redis commands.
330
358
  def expire_keys
331
- now, miliseconds = @base.now
332
- now_ms = now * 1_000 + miliseconds
359
+ now_sec, miliseconds = now
360
+ now_ms = now_sec * 1_000 + miliseconds
333
361
 
334
362
  to_delete = expire_times.take_while do |(time, _key)|
335
363
  (time.to_r * 1_000).to_i <= now_ms
@@ -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]
@@ -84,10 +84,17 @@ class MockRedis
84
84
  end
85
85
 
86
86
  def hmset(key, *kvpairs)
87
+ if key.is_a? Array
88
+ err_msg = 'ERR wrong number of arguments for \'hmset\' command'
89
+ kvpairs = key[1..-1]
90
+ key = key[0]
91
+ end
92
+
87
93
  kvpairs.flatten!
88
94
  assert_has_args(kvpairs, 'hmset')
95
+
89
96
  if kvpairs.length.odd?
90
- raise Redis::CommandError, 'ERR wrong number of arguments for HMSET'
97
+ raise Redis::CommandError, err_msg || 'ERR wrong number of arguments for HMSET'
91
98
  end
92
99
 
93
100
  kvpairs.each_slice(2) do |(k, v)|
@@ -121,10 +128,15 @@ class MockRedis
121
128
  end
122
129
  end
123
130
 
124
- def hset(key, field, value)
125
- field_exists = hexists(key, field)
126
- with_hash_at(key) { |h| h[field.to_s] = value.to_s }
127
- !field_exists
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
128
140
  end
129
141
 
130
142
  def hsetnx(key, field, value)
@@ -143,7 +155,7 @@ class MockRedis
143
155
  private
144
156
 
145
157
  def with_hash_at(key, &blk)
146
- with_thing_at(key, :assert_hashy, proc { {} }, &blk)
158
+ with_thing_at(key.to_s, :assert_hashy, proc { {} }, &blk)
147
159
  end
148
160
 
149
161
  def hashy?(key)