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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +26 -5
  3. data/.rubocop_todo.yml +1 -1
  4. data/.travis.yml +1 -0
  5. data/CHANGELOG.md +27 -0
  6. data/Gemfile +2 -2
  7. data/lib/mock_redis.rb +1 -1
  8. data/lib/mock_redis/connection_method.rb +13 -0
  9. data/lib/mock_redis/database.rb +21 -14
  10. data/lib/mock_redis/expire_wrapper.rb +1 -1
  11. data/lib/mock_redis/future.rb +1 -1
  12. data/lib/mock_redis/geospatial_methods.rb +5 -5
  13. data/lib/mock_redis/hash_methods.rb +9 -4
  14. data/lib/mock_redis/info_method.rb +2 -2
  15. data/lib/mock_redis/multi_db_wrapper.rb +3 -3
  16. data/lib/mock_redis/pipelined_wrapper.rb +1 -1
  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 +16 -1
  20. data/lib/mock_redis/string_methods.rb +21 -17
  21. data/lib/mock_redis/transaction_wrapper.rb +3 -3
  22. data/lib/mock_redis/utility_methods.rb +1 -1
  23. data/lib/mock_redis/version.rb +1 -1
  24. data/mock_redis.gemspec +2 -1
  25. data/spec/commands/blpop_spec.rb +0 -6
  26. data/spec/commands/brpop_spec.rb +6 -5
  27. data/spec/commands/connection_spec.rb +15 -0
  28. data/spec/commands/del_spec.rb +17 -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/hset_spec.rb +6 -6
  33. data/spec/commands/keys_spec.rb +17 -0
  34. data/spec/commands/mget_spec.rb +6 -0
  35. data/spec/commands/move_spec.rb +5 -5
  36. data/spec/commands/set_spec.rb +55 -7
  37. data/spec/commands/setbit_spec.rb +1 -0
  38. data/spec/commands/srandmember_spec.rb +1 -1
  39. data/spec/commands/xadd_spec.rb +23 -3
  40. data/spec/commands/xlen_spec.rb +3 -1
  41. data/spec/commands/xrange_spec.rb +13 -0
  42. data/spec/commands/xread_spec.rb +66 -0
  43. data/spec/commands/xtrim_spec.rb +6 -0
  44. data/spec/commands/zrange_spec.rb +1 -1
  45. data/spec/commands/zrangebyscore_spec.rb +1 -1
  46. data/spec/commands/zrevrange_spec.rb +1 -1
  47. data/spec/commands/zrevrangebyscore_spec.rb +1 -1
  48. data/spec/spec_helper.rb +2 -1
  49. data/spec/support/redis_multiplexer.rb +2 -1
  50. data/spec/transactions_spec.rb +16 -0
  51. metadata +24 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd9fd0d5b4ccad106eee03697d49082baa7e34065ff76f67b7e18da9991847a9
4
- data.tar.gz: 11f940cdee40f2646c3e6087faac7ac1c70e0aae5eaff7ddfc8839f0fb50e1b6
3
+ metadata.gz: cbd4308bd9413896742121468e39d64096e55b7760453a4220696aaa0554e38e
4
+ data.tar.gz: 0f8e0c66ac4533044ad91ee89a383bba1297cc186fdf514c66923a6188024b0c
5
5
  SHA512:
6
- metadata.gz: 347930a11a59575cd3a4bbc48cb689d46d25b27c6cfbdc28c28b6bfdfa1d833c317dd5ce7566ba016aae7b13b909629d078486bd95dee68f4e7e6b5cfc7e1e44
7
- data.tar.gz: 689b8dc84b58c38201bf3220641f467cd56b3ef2d6c4efc12829ec08c3a84f6dbc1e2c8207fdb18808c10bdb71f878cc571d21df2548a9d240881fff8b9ddd68
6
+ metadata.gz: 6fde8339234a0e1c8022cd63208e8e279cc834ff4280c6690d85a516259f7c29ac36fd3882a433a94af4e4e0bb584a7c332c823cc54db59556b17e7bf42d2f97
7
+ data.tar.gz: 4f7d508739c4eef78afa8e155d2ec4166f5b5b644f37f846a59f9b555c34bb948d545fca55c3d2c7dae2781fe90c24c7fabb804b3406f97030e8ee4e68eff9f4
@@ -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'
@@ -20,6 +20,7 @@ rvm:
20
20
  - 2.4
21
21
  - 2.5
22
22
  - 2.6
23
+ - 2.7
23
24
 
24
25
  before_script:
25
26
  - git config --local user.email "travis@travis.ci"
@@ -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.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
@@ -84,7 +84,7 @@ class MockRedis
84
84
  super || @db.respond_to?(method, include_private)
85
85
  end
86
86
 
87
- def method_missing(method, *args, &block)
87
+ ruby2_keywords def method_missing(method, *args, &block)
88
88
  @db.send(method, *args, &block)
89
89
  end
90
90
 
@@ -0,0 +1,13 @@
1
+ class MockRedis
2
+ module ConnectionMethod
3
+ def connection
4
+ {
5
+ :host => @base.host,
6
+ :port => @base.port,
7
+ :db => @base.db,
8
+ :id => @base.id,
9
+ :location => "#{@base.host}:#{@base.port}"
10
+ }
11
+ end
12
+ end
13
+ end
@@ -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.keys.each { |k| @data[k] = @data[k].clone }
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(key)
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.keys.each { |k| del(k) }
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(/([^\\])\?/, '\\1.').
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
- now, miliseconds = self.now
355
- now_ms = now * 1_000 + miliseconds
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
@@ -12,7 +12,7 @@ class MockRedis
12
12
  @db = db
13
13
  end
14
14
 
15
- def method_missing(method, *args, &block)
15
+ ruby2_keywords def method_missing(method, *args, &block)
16
16
  @db.expire_keys
17
17
  @db.send(method, *args, &block)
18
18
  end
@@ -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
@@ -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('%.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]
@@ -128,10 +128,15 @@ class MockRedis
128
128
  end
129
129
  end
130
130
 
131
- def hset(key, field, value)
132
- field_exists = hexists(key, field)
133
- with_hash_at(key) { |h| h[field.to_s] = value.to_s }
134
- !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
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 Metrics/LineLength
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 Metrics/LineLength
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.keys.each do |k|
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
@@ -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
- @members = @members.to_a[-count..-1].to_set
33
- deleted
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