mock_redis 0.21.0 → 0.26.0

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