mock_redis 0.23.0 → 0.27.1

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 (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