mock_redis 0.32.0 → 0.44.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (195) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -0
  3. data/README.md +4 -4
  4. data/lib/mock_redis/database.rb +42 -13
  5. data/lib/mock_redis/geospatial_methods.rb +2 -2
  6. data/lib/mock_redis/hash_methods.rb +1 -1
  7. data/lib/mock_redis/list_methods.rb +37 -2
  8. data/lib/mock_redis/memory_method.rb +11 -0
  9. data/lib/mock_redis/multi_db_wrapper.rb +1 -1
  10. data/lib/mock_redis/pipelined_wrapper.rb +12 -14
  11. data/lib/mock_redis/set_methods.rb +18 -2
  12. data/lib/mock_redis/stream.rb +3 -3
  13. data/lib/mock_redis/string_methods.rb +33 -6
  14. data/lib/mock_redis/transaction_wrapper.rb +5 -7
  15. data/lib/mock_redis/utility_methods.rb +4 -2
  16. data/lib/mock_redis/version.rb +1 -1
  17. data/lib/mock_redis/zset.rb +4 -2
  18. data/lib/mock_redis/zset_methods.rb +13 -4
  19. data/lib/mock_redis.rb +5 -6
  20. metadata +20 -352
  21. data/.github/workflows/lint.yml +0 -31
  22. data/.github/workflows/tests.yml +0 -63
  23. data/.gitignore +0 -5
  24. data/.mailmap +0 -2
  25. data/.overcommit.yml +0 -21
  26. data/.rspec +0 -1
  27. data/.rubocop.yml +0 -148
  28. data/.rubocop_todo.yml +0 -35
  29. data/.simplecov +0 -4
  30. data/Gemfile +0 -13
  31. data/Rakefile +0 -2
  32. data/mock_redis.gemspec +0 -32
  33. data/spec/client_spec.rb +0 -29
  34. data/spec/cloning_spec.rb +0 -95
  35. data/spec/commands/append_spec.rb +0 -24
  36. data/spec/commands/auth_spec.rb +0 -7
  37. data/spec/commands/bgrewriteaof_spec.rb +0 -7
  38. data/spec/commands/bgsave_spec.rb +0 -7
  39. data/spec/commands/bitcount_spec.rb +0 -25
  40. data/spec/commands/bitfield_spec.rb +0 -169
  41. data/spec/commands/blpop_spec.rb +0 -53
  42. data/spec/commands/brpop_spec.rb +0 -59
  43. data/spec/commands/brpoplpush_spec.rb +0 -52
  44. data/spec/commands/connected_spec.rb +0 -7
  45. data/spec/commands/connection_spec.rb +0 -15
  46. data/spec/commands/dbsize_spec.rb +0 -18
  47. data/spec/commands/decr_spec.rb +0 -34
  48. data/spec/commands/decrby_spec.rb +0 -34
  49. data/spec/commands/del_spec.rb +0 -52
  50. data/spec/commands/disconnect_spec.rb +0 -7
  51. data/spec/commands/dump_spec.rb +0 -19
  52. data/spec/commands/echo_spec.rb +0 -11
  53. data/spec/commands/eval_spec.rb +0 -7
  54. data/spec/commands/evalsha_spec.rb +0 -10
  55. data/spec/commands/exists_spec.rb +0 -43
  56. data/spec/commands/expire_spec.rb +0 -111
  57. data/spec/commands/expireat_spec.rb +0 -47
  58. data/spec/commands/flushall_spec.rb +0 -38
  59. data/spec/commands/flushdb_spec.rb +0 -38
  60. data/spec/commands/future_spec.rb +0 -30
  61. data/spec/commands/geoadd_spec.rb +0 -58
  62. data/spec/commands/geodist_spec.rb +0 -118
  63. data/spec/commands/geohash_spec.rb +0 -52
  64. data/spec/commands/geopos_spec.rb +0 -55
  65. data/spec/commands/get_spec.rb +0 -31
  66. data/spec/commands/getbit_spec.rb +0 -34
  67. data/spec/commands/getrange_spec.rb +0 -22
  68. data/spec/commands/getset_spec.rb +0 -23
  69. data/spec/commands/hdel_spec.rb +0 -77
  70. data/spec/commands/hexists_spec.rb +0 -27
  71. data/spec/commands/hget_spec.rb +0 -28
  72. data/spec/commands/hgetall_spec.rb +0 -32
  73. data/spec/commands/hincrby_spec.rb +0 -58
  74. data/spec/commands/hincrbyfloat_spec.rb +0 -58
  75. data/spec/commands/hkeys_spec.rb +0 -19
  76. data/spec/commands/hlen_spec.rb +0 -19
  77. data/spec/commands/hmget_spec.rb +0 -46
  78. data/spec/commands/hmset_spec.rb +0 -69
  79. data/spec/commands/hscan_each_spec.rb +0 -48
  80. data/spec/commands/hscan_spec.rb +0 -27
  81. data/spec/commands/hset_spec.rb +0 -42
  82. data/spec/commands/hsetnx_spec.rb +0 -44
  83. data/spec/commands/hvals_spec.rb +0 -19
  84. data/spec/commands/incr_spec.rb +0 -34
  85. data/spec/commands/incrby_spec.rb +0 -44
  86. data/spec/commands/incrbyfloat_spec.rb +0 -44
  87. data/spec/commands/info_spec.rb +0 -62
  88. data/spec/commands/keys_spec.rb +0 -139
  89. data/spec/commands/lastsave_spec.rb +0 -8
  90. data/spec/commands/lindex_spec.rb +0 -49
  91. data/spec/commands/linsert_spec.rb +0 -68
  92. data/spec/commands/llen_spec.rb +0 -16
  93. data/spec/commands/lpop_spec.rb +0 -34
  94. data/spec/commands/lpush_spec.rb +0 -43
  95. data/spec/commands/lpushx_spec.rb +0 -46
  96. data/spec/commands/lrange_spec.rb +0 -51
  97. data/spec/commands/lrem_spec.rb +0 -80
  98. data/spec/commands/lset_spec.rb +0 -43
  99. data/spec/commands/ltrim_spec.rb +0 -45
  100. data/spec/commands/mapped_hmget_spec.rb +0 -29
  101. data/spec/commands/mapped_hmset_spec.rb +0 -47
  102. data/spec/commands/mapped_mget_spec.rb +0 -22
  103. data/spec/commands/mapped_mset_spec.rb +0 -19
  104. data/spec/commands/mapped_msetnx_spec.rb +0 -26
  105. data/spec/commands/mget_spec.rb +0 -65
  106. data/spec/commands/move_spec.rb +0 -147
  107. data/spec/commands/mset_spec.rb +0 -43
  108. data/spec/commands/msetnx_spec.rb +0 -40
  109. data/spec/commands/persist_spec.rb +0 -48
  110. data/spec/commands/pexpire_spec.rb +0 -86
  111. data/spec/commands/pexpireat_spec.rb +0 -48
  112. data/spec/commands/ping_spec.rb +0 -11
  113. data/spec/commands/pipelined_spec.rb +0 -114
  114. data/spec/commands/psetex_spec.rb +0 -44
  115. data/spec/commands/pttl_spec.rb +0 -41
  116. data/spec/commands/quit_spec.rb +0 -7
  117. data/spec/commands/randomkey_spec.rb +0 -20
  118. data/spec/commands/rename_spec.rb +0 -42
  119. data/spec/commands/renamenx_spec.rb +0 -41
  120. data/spec/commands/restore_spec.rb +0 -47
  121. data/spec/commands/rpop_spec.rb +0 -34
  122. data/spec/commands/rpoplpush_spec.rb +0 -50
  123. data/spec/commands/rpush_spec.rb +0 -43
  124. data/spec/commands/rpushx_spec.rb +0 -46
  125. data/spec/commands/sadd_spec.rb +0 -45
  126. data/spec/commands/save_spec.rb +0 -7
  127. data/spec/commands/scan_each_spec.rb +0 -39
  128. data/spec/commands/scan_spec.rb +0 -64
  129. data/spec/commands/scard_spec.rb +0 -18
  130. data/spec/commands/script_spec.rb +0 -9
  131. data/spec/commands/sdiff_spec.rb +0 -47
  132. data/spec/commands/sdiffstore_spec.rb +0 -58
  133. data/spec/commands/select_spec.rb +0 -61
  134. data/spec/commands/set_spec.rb +0 -119
  135. data/spec/commands/setbit_spec.rb +0 -55
  136. data/spec/commands/setex_spec.rb +0 -38
  137. data/spec/commands/setnx_spec.rb +0 -25
  138. data/spec/commands/setrange_spec.rb +0 -30
  139. data/spec/commands/sinter_spec.rb +0 -39
  140. data/spec/commands/sinterstore_spec.rb +0 -53
  141. data/spec/commands/sismember_spec.rb +0 -29
  142. data/spec/commands/smembers_spec.rb +0 -28
  143. data/spec/commands/smove_spec.rb +0 -41
  144. data/spec/commands/sort_list_spec.rb +0 -21
  145. data/spec/commands/sort_set_spec.rb +0 -21
  146. data/spec/commands/sort_zset_spec.rb +0 -21
  147. data/spec/commands/spop_spec.rb +0 -40
  148. data/spec/commands/srandmember_spec.rb +0 -49
  149. data/spec/commands/srem_spec.rb +0 -45
  150. data/spec/commands/sscan_each_spec.rb +0 -48
  151. data/spec/commands/sscan_spec.rb +0 -39
  152. data/spec/commands/strlen_spec.rb +0 -18
  153. data/spec/commands/sunion_spec.rb +0 -42
  154. data/spec/commands/sunionstore_spec.rb +0 -59
  155. data/spec/commands/ttl_spec.rb +0 -40
  156. data/spec/commands/type_spec.rb +0 -36
  157. data/spec/commands/unwatch_spec.rb +0 -7
  158. data/spec/commands/watch_spec.rb +0 -21
  159. data/spec/commands/xadd_spec.rb +0 -122
  160. data/spec/commands/xlen_spec.rb +0 -22
  161. data/spec/commands/xrange_spec.rb +0 -164
  162. data/spec/commands/xread_spec.rb +0 -66
  163. data/spec/commands/xrevrange_spec.rb +0 -130
  164. data/spec/commands/xtrim_spec.rb +0 -36
  165. data/spec/commands/zadd_spec.rb +0 -129
  166. data/spec/commands/zcard_spec.rb +0 -19
  167. data/spec/commands/zcount_spec.rb +0 -39
  168. data/spec/commands/zincrby_spec.rb +0 -31
  169. data/spec/commands/zinterstore_spec.rb +0 -130
  170. data/spec/commands/zpopmax_spec.rb +0 -60
  171. data/spec/commands/zpopmin_spec.rb +0 -60
  172. data/spec/commands/zrange_spec.rb +0 -80
  173. data/spec/commands/zrangebyscore_spec.rb +0 -83
  174. data/spec/commands/zrank_spec.rb +0 -29
  175. data/spec/commands/zrem_spec.rb +0 -43
  176. data/spec/commands/zremrangebyrank_spec.rb +0 -27
  177. data/spec/commands/zremrangebyscore_spec.rb +0 -35
  178. data/spec/commands/zrevrange_spec.rb +0 -56
  179. data/spec/commands/zrevrangebyscore_spec.rb +0 -58
  180. data/spec/commands/zrevrank_spec.rb +0 -29
  181. data/spec/commands/zscan_each_spec.rb +0 -48
  182. data/spec/commands/zscan_spec.rb +0 -26
  183. data/spec/commands/zscore_spec.rb +0 -22
  184. data/spec/commands/zunionstore_spec.rb +0 -137
  185. data/spec/mock_redis_spec.rb +0 -93
  186. data/spec/spec_helper.rb +0 -71
  187. data/spec/support/redis_multiplexer.rb +0 -123
  188. data/spec/support/shared_examples/does_not_cleanup_empty_strings.rb +0 -14
  189. data/spec/support/shared_examples/only_operates_on_hashes.rb +0 -15
  190. data/spec/support/shared_examples/only_operates_on_lists.rb +0 -15
  191. data/spec/support/shared_examples/only_operates_on_sets.rb +0 -15
  192. data/spec/support/shared_examples/only_operates_on_strings.rb +0 -13
  193. data/spec/support/shared_examples/only_operates_on_zsets.rb +0 -59
  194. data/spec/support/shared_examples/sorts_enumerables.rb +0 -56
  195. data/spec/transactions_spec.rb +0 -163
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9f945150c69e6ff81838d4e029f1620ade56e9da6c3cb2ed0550cd61060335a
4
- data.tar.gz: fbd58f70dbf78b770a6d3e04351771a1d94a7cac87afbff95213c92bceb16eaa
3
+ metadata.gz: 7e495091d84ea1fec4bf5206d2a02e32c446ef0bd89b0feaed38f31d95b142b2
4
+ data.tar.gz: 696f65058fe29cd5fa849363b6cfa60454a432c0d392f530ac1494cba31ff062
5
5
  SHA512:
6
- metadata.gz: 9320958fc6e3360f26317dfcb47747bf8d39d626d54c0297cece31b9521fc4ad991acdccf885428d05f8b82765cfec12a37af4de33473f4106c7e3eb4e9c2b69
7
- data.tar.gz: 05c04acff0f82500f114a868ed40aef95fc115e367ed94008075ddd90de5550dd939ca12215ffb3c88021a6aeff18cd3f9ff51dfabcc3f14244b40465d6184f0
6
+ metadata.gz: 78b558108b350dc35df4d1f202e96326096394f9e23d89398d07cdbb33c6b088e0089467b14624a9e2c73210406457e291ab0cb09495a8d12a854df5ab8da1c3
7
+ data.tar.gz: f7e6d3021a4f2f5dd78e53d126ecea26fa7d3f8903bbcf96b0cf2a03b74049acbb169a18ff571c2aab18a0678cf57b90908d06b8c0e10039e00fce5b6050ad26
data/CHANGELOG.md CHANGED
@@ -1,5 +1,64 @@
1
1
  # MockRedis Changelog
2
2
 
3
+ ### 0.44.0
4
+
5
+ * Add suppore for `memory usage` command
6
+
7
+ ### 0.43.0
8
+
9
+ * Fix issue when using with `redis-store` gem
10
+
11
+ ### 0.42.0
12
+
13
+ * Drop support for Ruby 2.x
14
+ * Add support for `srem?`
15
+
16
+ ### 0.41.0
17
+
18
+ * Add support for `expire`-related command options `nx`/`xx`/`lt`/`gt`
19
+
20
+ ### 0.40.0
21
+
22
+ * Add support for `call` method name to be case insensitive
23
+ * Add support for `zmscore`
24
+
25
+ ### 0.39.0
26
+
27
+ * Add support for calling `call` in `pipelined` block
28
+ * Fix `sadd` to stringify array
29
+
30
+ ### 0.38.0
31
+
32
+ * Fix detection of stream key types
33
+ * Add support for `EXAT` AND `PXAT` arguments to `SET` command
34
+
35
+ ### 0.37.0
36
+
37
+ * Require Ruby 2.7 or newer, since Ruby 2.6 and older are EOL
38
+ * Remove unnecessary files from gem contents
39
+ * Add support for popping multiple items with `lpop`
40
+ * Add support for `scan` with type option
41
+ * Add support for `sadd?`
42
+ * Add support for scanning on keys containing hash tags (`{...}`)
43
+
44
+ ### 0.36.0
45
+
46
+ * Add support for `smismember`
47
+ * Add support for `lmove` and `blmove`
48
+ * Fix `zrem` to support passing array of integers
49
+
50
+ ### 0.35.0
51
+
52
+ * Add support for `getdel`
53
+
54
+ ### 0.34.0
55
+
56
+ * Add support for `with`
57
+
58
+ ### 0.33.0
59
+
60
+ * Add support for `GET` argument to `SET` command
61
+
3
62
  ### 0.32.0
4
63
 
5
64
  * Add support for `psetex`
data/README.md CHANGED
@@ -10,7 +10,7 @@ for use in tests.
10
10
 
11
11
  ## Requirements
12
12
 
13
- Ruby 2.6+
13
+ Ruby 3.0+
14
14
 
15
15
  The current implementation is tested against Redis 6.2. Older versions may work, but can also return different results or not support some commands.
16
16
 
@@ -55,8 +55,8 @@ since it's an in-memory object confined to a single process. MockRedis
55
55
  makes every attempt to be Redis-compatible, but there are some
56
56
  necessary exceptions.
57
57
 
58
- * Blocking list commands (`#blpop`, `#brpop`, and `#brpoplpush`) work
59
- as expected if there is data for them to retrieve. If you use one of
58
+ * Blocking list commands (`#blmove`, `#blpop`, `#brpop`, and `#brpoplpush`)
59
+ work as expected if there is data for them to retrieve. If you use one of
60
60
  these commands with a nonzero timeout and there is no data for it to
61
61
  retrieve, then the command returns immediately. However, if you ask
62
62
  one of these commands for data with a 0 timeout (means "wait
@@ -105,7 +105,7 @@ We recommend running Redis within a Docker container to make development as simp
105
105
 
106
106
  1. Start Redis.
107
107
  ```bash
108
- docker run --rm -p 6379:6379 redis
108
+ docker run --rm -p 6379:6379 redis:6.2-alpine
109
109
  ```
110
110
  2. Install dependencies.
111
111
  ```bash
@@ -12,6 +12,7 @@ require 'mock_redis/utility_methods'
12
12
  require 'mock_redis/geospatial_methods'
13
13
  require 'mock_redis/stream_methods'
14
14
  require 'mock_redis/connection_method'
15
+ require 'mock_redis/memory_method'
15
16
 
16
17
  class MockRedis
17
18
  class Database
@@ -26,6 +27,7 @@ class MockRedis
26
27
  include GeospatialMethods
27
28
  include StreamMethods
28
29
  include ConnectionMethod
30
+ include MemoryMethod
29
31
 
30
32
  attr_reader :data, :expire_times
31
33
 
@@ -43,6 +45,13 @@ class MockRedis
43
45
 
44
46
  # Redis commands go below this line and above 'private'
45
47
 
48
+ # FIXME: Current implementation of `call` does not work propetly with kwarg-options.
49
+ # i.e. `call("EXPIRE", "foo", 40, "NX")` (which redis-rb will simply transmit to redis-server)
50
+ # will be passed to `#expire` without keywords transformation.
51
+ def call(command, &_block)
52
+ public_send(command[0].downcase, *command[1..])
53
+ end
54
+
46
55
  def auth(_)
47
56
  'OK'
48
57
  end
@@ -71,7 +80,7 @@ class MockRedis
71
80
 
72
81
  def del(*keys)
73
82
  keys = keys.flatten.map(&:to_s)
74
- assert_has_args(keys, 'del')
83
+ # assert_has_args(keys, 'del') # no longer errors in redis > v4.5
75
84
 
76
85
  keys.
77
86
  find_all { |key| data[key] }.
@@ -85,32 +94,42 @@ class MockRedis
85
94
  msg.to_s
86
95
  end
87
96
 
88
- def expire(key, seconds)
97
+ def expire(key, seconds, nx: nil, xx: nil, lt: nil, gt: nil) # rubocop:disable Metrics/ParameterLists
89
98
  assert_valid_integer(seconds)
90
99
 
91
- pexpire(key, seconds.to_i * 1000)
100
+ pexpire(key, seconds.to_i * 1000, nx: nx, xx: xx, lt: lt, gt: gt)
92
101
  end
93
102
 
94
- def pexpire(key, ms)
103
+ def pexpire(key, ms, nx: nil, xx: nil, lt: nil, gt: nil) # rubocop:disable Metrics/ParameterLists
95
104
  assert_valid_integer(ms)
96
105
 
97
106
  now, miliseconds = @base.now
98
107
  now_ms = (now * 1000) + miliseconds
99
- pexpireat(key, now_ms + ms.to_i)
108
+ pexpireat(key, now_ms + ms.to_i, nx: nx, xx: xx, lt: lt, gt: gt)
100
109
  end
101
110
 
102
- def expireat(key, timestamp)
111
+ def expireat(key, timestamp, nx: nil, xx: nil, lt: nil, gt: nil) # rubocop:disable Metrics/ParameterLists
103
112
  assert_valid_integer(timestamp)
104
113
 
105
- pexpireat(key, timestamp.to_i * 1000)
114
+ pexpireat(key, timestamp.to_i * 1000, nx: nx, xx: xx, lt: lt, gt: gt)
106
115
  end
107
116
 
108
- def pexpireat(key, timestamp_ms)
117
+ def pexpireat(key, timestamp_ms, nx: nil, xx: nil, lt: nil, gt: nil) # rubocop:disable Metrics/ParameterLists
109
118
  assert_valid_integer(timestamp_ms)
110
119
 
111
- if exists?(key)
112
- timestamp = Rational(timestamp_ms.to_i, 1000)
113
- set_expiration(key, @base.time_at(timestamp))
120
+ if nx && gt || gt && lt || lt && nx || nx && xx
121
+ raise Redis::CommandError, <<~TXT.chomp
122
+ ERR NX and XX, GT or LT options at the same time are not compatible
123
+ TXT
124
+ end
125
+
126
+ return false unless exists?(key)
127
+
128
+ expiry = expiration(key)
129
+ new_expiry = @base.time_at(Rational(timestamp_ms.to_i, 1000))
130
+
131
+ if should_update_expiration?(expiry, new_expiry, nx: nx, xx: xx, lt: lt, gt: gt)
132
+ set_expiration(key, new_expiry)
114
133
  true
115
134
  else
116
135
  false
@@ -267,6 +286,8 @@ class MockRedis
267
286
  'set'
268
287
  elsif zsety?(key)
269
288
  'zset'
289
+ elsif streamy?(key)
290
+ 'stream'
270
291
  else
271
292
  raise ArgumentError, "Not sure how #{data[key].inspect} got in here"
272
293
  end
@@ -318,7 +339,7 @@ class MockRedis
318
339
  end
319
340
 
320
341
  def expiration(key)
321
- expire_times.find { |(_, k)| k == key.to_s }.first
342
+ expire_times.find { |(_, k)| k == key.to_s }&.first
322
343
  end
323
344
 
324
345
  def has_expiration?(key)
@@ -333,10 +354,18 @@ class MockRedis
333
354
  !!Float(str) rescue false
334
355
  end
335
356
 
357
+ def should_update_expiration?(expiry, new_expiry, nx:, xx:, lt:, gt:) # rubocop:disable Metrics/ParameterLists
358
+ return false if nx && expiry || xx && !expiry
359
+ return false if lt && expiry && new_expiry > expiry
360
+ return false if gt && (!expiry || new_expiry < expiry)
361
+
362
+ true
363
+ end
364
+
336
365
  def redis_pattern_to_ruby_regex(pattern)
337
366
  Regexp.new(
338
367
  "^#{pattern}$".
339
- gsub(/([+|()])/, '\\\\\1').
368
+ gsub(/([+|(){}])/, '\\\\\1').
340
369
  gsub(/(?<!\\)\?/, '\\1.').
341
370
  gsub(/([^\\])\*/, '\\1.*')
342
371
  )
@@ -1,7 +1,7 @@
1
1
  class MockRedis
2
2
  module GeospatialMethods
3
- LNG_RANGE = (-180..180).freeze
4
- LAT_RANGE = (-85.05112878..85.05112878).freeze
3
+ LNG_RANGE = (-180..180)
4
+ LAT_RANGE = (-85.05112878..85.05112878)
5
5
  STEP = 26
6
6
  UNITS = {
7
7
  m: 1,
@@ -93,7 +93,7 @@ class MockRedis
93
93
  def hmset(key, *kvpairs)
94
94
  if key.is_a? Array
95
95
  err_msg = 'ERR wrong number of arguments for \'hmset\' command'
96
- kvpairs = key[1..-1]
96
+ kvpairs = key[1..]
97
97
  key = key[0]
98
98
  end
99
99
 
@@ -6,6 +6,20 @@ class MockRedis
6
6
  include Assertions
7
7
  include UtilityMethods
8
8
 
9
+ def blmove(source, destination, wherefrom, whereto, options = {})
10
+ options = { :timeout => options } if options.is_a?(Integer)
11
+ timeout = options.is_a?(Hash) && options[:timeout] || 0
12
+ assert_valid_timeout(timeout)
13
+
14
+ if llen(source) > 0
15
+ lmove(source, destination, wherefrom, whereto)
16
+ elsif timeout > 0
17
+ nil
18
+ else
19
+ raise MockRedis::WouldBlock, "Can't block forever"
20
+ end
21
+ end
22
+
9
23
  def blpop(*args)
10
24
  lists, timeout = extract_timeout(args)
11
25
  nonempty_list = first_nonempty_list(lists)
@@ -78,8 +92,29 @@ class MockRedis
78
92
  with_list_at(key, &:length)
79
93
  end
80
94
 
81
- def lpop(key)
82
- with_list_at(key, &:shift)
95
+ def lmove(source, destination, wherefrom, whereto)
96
+ assert_listy(source)
97
+ assert_listy(destination)
98
+
99
+ wherefrom = wherefrom.to_s.downcase
100
+ whereto = whereto.to_s.downcase
101
+
102
+ unless %w[left right].include?(wherefrom) && %w[left right].include?(whereto)
103
+ raise Redis::CommandError, 'ERR syntax error'
104
+ end
105
+
106
+ value = wherefrom == 'left' ? lpop(source) : rpop(source)
107
+ (whereto == 'left' ? lpush(destination, value) : rpush(destination, value)) unless value.nil?
108
+ value
109
+ end
110
+
111
+ def lpop(key, count = nil)
112
+ return with_list_at(key, &:shift) if count.nil?
113
+
114
+ record_count = llen(key)
115
+ return nil if record_count.zero?
116
+
117
+ [record_count, count].min.times.map { with_list_at(key, &:shift) }
83
118
  end
84
119
 
85
120
  def lpush(key, values)
@@ -0,0 +1,11 @@
1
+ class MockRedis
2
+ module MemoryMethod
3
+ def memory(usage, key = nil, *_options)
4
+ raise ArgumentError, "unhandled command `memory #{usage}`" if usage != 'usage'
5
+
6
+ return nil unless @data.key?(key)
7
+
8
+ 160
9
+ end
10
+ end
11
+ end
@@ -31,7 +31,7 @@ class MockRedis
31
31
 
32
32
  # Redis commands
33
33
  def flushall
34
- @databases.values.each(&:flushdb)
34
+ @databases.each_value(&:flushdb)
35
35
  'OK'
36
36
  end
37
37
 
@@ -41,22 +41,20 @@ class MockRedis
41
41
  end
42
42
 
43
43
  responses = @pipelined_futures.flat_map do |future|
44
- begin
45
- result = if future.block
46
- send(*future.command, &future.block)
47
- else
48
- send(*future.command)
49
- end
50
- future.store_result(result)
44
+ result = if future.block
45
+ send(*future.command, &future.block)
46
+ else
47
+ send(*future.command)
48
+ end
49
+ future.store_result(result)
51
50
 
52
- if future.block
53
- result
54
- else
55
- [result]
56
- end
57
- rescue StandardError => e
58
- e
51
+ if future.block
52
+ result
53
+ else
54
+ [result]
59
55
  end
56
+ rescue StandardError => e
57
+ e
60
58
  end
61
59
  @pipelined_futures = []
62
60
  responses
@@ -8,7 +8,7 @@ class MockRedis
8
8
 
9
9
  def sadd(key, members)
10
10
  members_class = members.class
11
- members = [members].flatten.map(&:to_s)
11
+ members = Array(members).map(&:to_s)
12
12
  assert_has_args(members, 'sadd')
13
13
 
14
14
  with_set_at(key) do |s|
@@ -27,6 +27,11 @@ class MockRedis
27
27
  end
28
28
  end
29
29
 
30
+ def sadd?(key, members)
31
+ res = sadd(key, members)
32
+ res.is_a?(Numeric) ? res > 0 : res
33
+ end
34
+
30
35
  def scard(key)
31
36
  with_set_at(key, &:length)
32
37
  end
@@ -64,6 +69,12 @@ class MockRedis
64
69
  with_set_at(key) { |s| s.include?(member.to_s) }
65
70
  end
66
71
 
72
+ def smismember(key, *members)
73
+ with_set_at(key) do |set|
74
+ members.flatten.map { |m| set.include?(m.to_s) }
75
+ end
76
+ end
77
+
67
78
  def smembers(key)
68
79
  with_set_at(key, &:to_a).map(&:dup).reverse
69
80
  end
@@ -126,6 +137,11 @@ class MockRedis
126
137
  end
127
138
  end
128
139
 
140
+ def srem?(key, members)
141
+ res = srem(key, members)
142
+ res.is_a?(Numeric) ? res > 0 : res
143
+ end
144
+
129
145
  def sscan(key, cursor, opts = {})
130
146
  common_scan(smembers(key), cursor, opts)
131
147
  end
@@ -165,7 +181,7 @@ class MockRedis
165
181
  with_set_at(keys.first, &blk)
166
182
  else
167
183
  with_set_at(keys.first) do |set|
168
- with_sets_at(*(keys[1..-1])) do |*sets|
184
+ with_sets_at(*(keys[1..])) do |*sets|
169
185
  yield(*([set] + sets))
170
186
  end
171
187
  end
@@ -37,7 +37,7 @@ class MockRedis
37
37
  @members = if count == 0
38
38
  Set.new
39
39
  else
40
- @members.to_a[-count..-1].to_set
40
+ @members.to_a[-count..].to_set
41
41
  end
42
42
  deleted
43
43
  else
@@ -71,8 +71,8 @@ class MockRedis
71
71
  items
72
72
  end
73
73
 
74
- def each
75
- members.each { |m| yield m }
74
+ def each(&block)
75
+ members.each(&block)
76
76
  end
77
77
 
78
78
  private
@@ -38,7 +38,7 @@ class MockRedis
38
38
  type, offset = args.shift(2)
39
39
 
40
40
  is_signed = type.slice(0) == 'i'
41
- type_size = type[1..-1].to_i
41
+ type_size = type[1..].to_i
42
42
 
43
43
  if (type_size > 64 && is_signed) || (type_size >= 64 && !is_signed)
44
44
  raise Redis::CommandError,
@@ -47,7 +47,7 @@ class MockRedis
47
47
  end
48
48
 
49
49
  if offset.to_s[0] == '#'
50
- offset = offset[1..-1].to_i * type_size
50
+ offset = offset[1..].to_i * type_size
51
51
  end
52
52
 
53
53
  bits = []
@@ -107,6 +107,12 @@ class MockRedis
107
107
  end
108
108
  end
109
109
 
110
+ def getdel(key)
111
+ value = get(key)
112
+ del(key)
113
+ value
114
+ end
115
+
110
116
  def getrange(key, start, stop)
111
117
  assert_stringy(key)
112
118
  (data[key] || '')[start..stop]
@@ -204,10 +210,13 @@ class MockRedis
204
210
  msetnx(*hash.to_a.flatten)
205
211
  end
206
212
 
207
- # Parameer list required to ensure the ArgumentError is returned correctly
213
+ # Parameter list required to ensure the ArgumentError is returned correctly
208
214
  # rubocop:disable Metrics/ParameterLists
209
- def set(key, value, ex: nil, px: nil, nx: nil, xx: nil, keepttl: nil)
215
+ def set(key, value, _hash = nil, ex: nil, px: nil, exat: nil, pxat: nil, nx: nil, xx: nil,
216
+ keepttl: nil, get: nil)
210
217
  key = key.to_s
218
+ retval = self.get(key) if get
219
+
211
220
  return_true = false
212
221
  if nx
213
222
  if exists?(key)
@@ -240,7 +249,25 @@ class MockRedis
240
249
  pexpire(key, px)
241
250
  end
242
251
 
243
- return_true ? true : 'OK'
252
+ if exat
253
+ if exat == 0
254
+ raise Redis::CommandError, 'ERR invalid expire time in set'
255
+ end
256
+ expireat(key, exat)
257
+ end
258
+
259
+ if pxat
260
+ if pxat == 0
261
+ raise Redis::CommandError, 'ERR invalid expire time in set'
262
+ end
263
+ pexpireat(key, pxat)
264
+ end
265
+
266
+ if get
267
+ retval
268
+ else
269
+ return_true ? true : 'OK'
270
+ end
244
271
  end
245
272
  # rubocop:enable Metrics/ParameterLists
246
273
 
@@ -353,7 +380,7 @@ class MockRedis
353
380
  old_value = (data[key] || '')
354
381
 
355
382
  prefix = zero_pad(old_value[0...offset], offset)
356
- data[key] = prefix + value + (old_value[(offset + value.length)..-1] || '')
383
+ data[key] = prefix + value + (old_value[(offset + value.length)..] || '')
357
384
  data[key].length
358
385
  end
359
386
 
@@ -57,13 +57,11 @@ class MockRedis
57
57
  @multi_block_given = false
58
58
 
59
59
  responses = @transaction_futures.map do |future|
60
- begin
61
- result = send(*future.command)
62
- future.store_result(result)
63
- future.value
64
- rescue StandardError => e
65
- e
66
- end
60
+ result = send(*future.command)
61
+ future.store_result(result)
62
+ future.value
63
+ rescue StandardError => e
64
+ e
67
65
  end
68
66
 
69
67
  @transaction_futures = []
@@ -28,6 +28,7 @@ class MockRedis
28
28
  cursor = cursor.to_i
29
29
  match = opts[:match] || '*'
30
30
  key = opts[:key] || lambda { |x| x }
31
+ type_opt = opts[:type]
31
32
  filtered_values = []
32
33
 
33
34
  limit = cursor + count
@@ -35,7 +36,8 @@ class MockRedis
35
36
 
36
37
  unless values[cursor...limit].nil?
37
38
  filtered_values = values[cursor...limit].select do |val|
38
- redis_pattern_to_ruby_regex(match).match(key.call(val))
39
+ redis_pattern_to_ruby_regex(match).match(key.call(val)) &&
40
+ (type_opt.nil? || type(val) == type_opt)
39
41
  end
40
42
  end
41
43
 
@@ -68,7 +70,7 @@ class MockRedis
68
70
  end
69
71
 
70
72
  def left_pad(str, size)
71
- str = '0' + str while str.length < size
73
+ str = "0#{str}" while str.length < size
72
74
 
73
75
  str
74
76
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Defines the gem version.
4
4
  class MockRedis
5
- VERSION = '0.32.0'
5
+ VERSION = '0.44.0'
6
6
  end
@@ -10,9 +10,11 @@ class MockRedis
10
10
 
11
11
  def_delegators :members, :empty?, :include?, :size
12
12
 
13
- def initialize
13
+ def initialize(enum = nil)
14
14
  @members = Set.new
15
- @scores = {}
15
+ @members.merge(enum) if enum
16
+
17
+ @scores = {}
16
18
  end
17
19
 
18
20
  def initialize_copy(source)
@@ -26,7 +26,7 @@ class MockRedis
26
26
  end
27
27
 
28
28
  def zadd_one_member(key, score, member, zadd_options = {})
29
- assert_scorey(score) unless score.to_s =~ /(\+|\-)inf/
29
+ assert_scorey(score) unless score.to_s =~ /(\+|-)inf/
30
30
 
31
31
  with_zset_at(key) do |zset|
32
32
  if zadd_options[:incr]
@@ -147,7 +147,7 @@ class MockRedis
147
147
  else
148
148
  retval = args.map { |member| !!zscore(key, member.to_s) }.count(true)
149
149
  with_zset_at(key) do |z|
150
- args.each { |member| z.delete?(member) }
150
+ args.each { |member| z.delete?(member.to_s) }
151
151
  end
152
152
  end
153
153
  end
@@ -233,6 +233,15 @@ class MockRedis
233
233
  end
234
234
  end
235
235
 
236
+ def zmscore(key, *members)
237
+ with_zset_at(key) do |z|
238
+ members.map do |member|
239
+ score = z.score(member.to_s)
240
+ score&.to_f
241
+ end
242
+ end
243
+ end
244
+
236
245
  def zunionstore(destination, keys, options = {})
237
246
  assert_has_args(keys, 'zunionstore')
238
247
 
@@ -316,7 +325,7 @@ class MockRedis
316
325
  with_zset_at(keys.first, coercible: coercible, &blk)
317
326
  else
318
327
  with_zset_at(keys.first, coercible: coercible) do |set|
319
- with_zsets_at(*(keys[1..-1]), coercible: coercible) do |*sets|
328
+ with_zsets_at(*(keys[1..]), coercible: coercible) do |*sets|
320
329
  yield(*([set] + sets))
321
330
  end
322
331
  end
@@ -351,7 +360,7 @@ class MockRedis
351
360
  end
352
361
 
353
362
  def assert_scorey(value, message = 'ERR value is not a valid float')
354
- return if value.to_s =~ /\(?(\-|\+)inf/
363
+ return if value.to_s =~ /\(?(-|\+)inf/
355
364
 
356
365
  value = $1 if value.to_s =~ /\((.*)/
357
366
  unless looks_like_float?(value)
data/lib/mock_redis.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'set'
2
- require 'ruby2_keywords'
3
2
 
4
3
  require 'mock_redis/assertions'
5
4
  require 'mock_redis/database'
@@ -50,10 +49,6 @@ class MockRedis
50
49
  end
51
50
  alias location id
52
51
 
53
- def call(command, &_block)
54
- send(*command)
55
- end
56
-
57
52
  def host
58
53
  options[:host]
59
54
  end
@@ -86,6 +81,10 @@ class MockRedis
86
81
  self
87
82
  end
88
83
 
84
+ def with
85
+ yield self
86
+ end
87
+
89
88
  def respond_to?(method, include_private = false)
90
89
  super || @db.respond_to?(method, include_private)
91
90
  end
@@ -126,7 +125,7 @@ class MockRedis
126
125
  defaults[:host] = uri.host
127
126
  defaults[:port] = uri.port if uri.port
128
127
  defaults[:password] = uri.password if uri.password
129
- defaults[:db] = uri.path[1..-1].to_i if uri.path
128
+ defaults[:db] = uri.path[1..].to_i if uri.path
130
129
  end
131
130
  end
132
131