mock_redis 0.5.4 → 0.31.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.
- checksums.yaml +7 -0
- data/.github/workflows/lint.yml +31 -0
- data/.github/workflows/tests.yml +63 -0
- data/.gitignore +1 -1
- data/.overcommit.yml +21 -0
- data/.rspec +1 -1
- data/.rubocop.yml +148 -0
- data/.rubocop_todo.yml +35 -0
- data/.simplecov +4 -0
- data/CHANGELOG.md +278 -0
- data/Gemfile +9 -5
- data/LICENSE.md +21 -0
- data/README.md +52 -16
- data/Rakefile +0 -8
- data/lib/mock_redis/assertions.rb +0 -1
- data/lib/mock_redis/connection_method.rb +13 -0
- data/lib/mock_redis/database.rb +193 -257
- data/lib/mock_redis/expire_wrapper.rb +2 -2
- data/lib/mock_redis/future.rb +23 -0
- data/lib/mock_redis/geospatial_methods.rb +240 -0
- data/lib/mock_redis/hash_methods.rb +83 -24
- data/lib/mock_redis/indifferent_hash.rb +11 -0
- data/lib/mock_redis/info_method.rb +160 -0
- data/lib/mock_redis/list_methods.rb +34 -19
- data/lib/mock_redis/multi_db_wrapper.rb +8 -7
- data/lib/mock_redis/pipelined_wrapper.rb +42 -16
- data/lib/mock_redis/set_methods.rb +62 -19
- data/lib/mock_redis/sort_method.rb +81 -0
- data/lib/mock_redis/stream/id.rb +58 -0
- data/lib/mock_redis/stream.rb +88 -0
- data/lib/mock_redis/stream_methods.rb +102 -0
- data/lib/mock_redis/string_methods.rb +235 -42
- data/lib/mock_redis/transaction_wrapper.rb +62 -28
- data/lib/mock_redis/utility_methods.rb +62 -11
- data/lib/mock_redis/version.rb +4 -1
- data/lib/mock_redis/zset.rb +24 -29
- data/lib/mock_redis/zset_methods.rb +187 -59
- data/lib/mock_redis.rb +77 -27
- data/mock_redis.gemspec +23 -15
- data/spec/client_spec.rb +29 -0
- data/spec/cloning_spec.rb +17 -18
- data/spec/commands/append_spec.rb +4 -4
- data/spec/commands/auth_spec.rb +1 -1
- data/spec/commands/bgrewriteaof_spec.rb +2 -2
- data/spec/commands/bgsave_spec.rb +2 -2
- data/spec/commands/bitcount_spec.rb +25 -0
- data/spec/commands/bitfield_spec.rb +169 -0
- data/spec/commands/blpop_spec.rb +19 -21
- data/spec/commands/brpop_spec.rb +25 -20
- data/spec/commands/brpoplpush_spec.rb +16 -17
- data/spec/commands/connected_spec.rb +7 -0
- data/spec/commands/connection_spec.rb +15 -0
- data/spec/commands/dbsize_spec.rb +3 -3
- data/spec/commands/decr_spec.rb +8 -8
- data/spec/commands/decrby_spec.rb +8 -8
- data/spec/commands/del_spec.rb +35 -3
- data/spec/commands/disconnect_spec.rb +7 -0
- data/spec/commands/dump_spec.rb +19 -0
- data/spec/commands/echo_spec.rb +4 -4
- data/spec/commands/eval_spec.rb +7 -0
- data/spec/commands/evalsha_spec.rb +10 -0
- data/spec/commands/exists_spec.rb +36 -7
- data/spec/commands/expire_spec.rb +48 -20
- data/spec/commands/expireat_spec.rb +12 -13
- data/spec/commands/flushall_spec.rb +5 -5
- data/spec/commands/flushdb_spec.rb +5 -5
- data/spec/commands/future_spec.rb +30 -0
- data/spec/commands/geoadd_spec.rb +58 -0
- data/spec/commands/geodist_spec.rb +118 -0
- data/spec/commands/geohash_spec.rb +52 -0
- data/spec/commands/geopos_spec.rb +55 -0
- data/spec/commands/get_spec.rb +14 -6
- data/spec/commands/getbit_spec.rb +7 -7
- data/spec/commands/getrange_spec.rb +9 -9
- data/spec/commands/getset_spec.rb +7 -7
- data/spec/commands/hdel_spec.rb +41 -11
- data/spec/commands/hexists_spec.rb +11 -11
- data/spec/commands/hget_spec.rb +7 -7
- data/spec/commands/hgetall_spec.rb +15 -5
- data/spec/commands/hincrby_spec.rb +16 -16
- data/spec/commands/hincrbyfloat_spec.rb +58 -0
- data/spec/commands/hkeys_spec.rb +5 -5
- data/spec/commands/hlen_spec.rb +5 -5
- data/spec/commands/hmget_spec.rb +19 -9
- data/spec/commands/hmset_spec.rb +38 -12
- data/spec/commands/hscan_each_spec.rb +48 -0
- data/spec/commands/hscan_spec.rb +27 -0
- data/spec/commands/hset_spec.rb +26 -12
- data/spec/commands/hsetnx_spec.rb +16 -16
- data/spec/commands/hvals_spec.rb +5 -5
- data/spec/commands/incr_spec.rb +8 -8
- data/spec/commands/incrby_spec.rb +13 -13
- data/spec/commands/incrbyfloat_spec.rb +13 -13
- data/spec/commands/info_spec.rb +54 -5
- data/spec/commands/keys_spec.rb +83 -31
- data/spec/commands/lastsave_spec.rb +2 -2
- data/spec/commands/lindex_spec.rb +20 -10
- data/spec/commands/linsert_spec.rb +14 -14
- data/spec/commands/llen_spec.rb +4 -4
- data/spec/commands/lpop_spec.rb +6 -6
- data/spec/commands/lpush_spec.rb +21 -15
- data/spec/commands/lpushx_spec.rb +24 -11
- data/spec/commands/lrange_spec.rb +24 -8
- data/spec/commands/lrem_spec.rb +16 -16
- data/spec/commands/lset_spec.rb +17 -12
- data/spec/commands/ltrim_spec.rb +17 -7
- data/spec/commands/mapped_hmget_spec.rb +13 -9
- data/spec/commands/mapped_hmset_spec.rb +12 -12
- data/spec/commands/mapped_mget_spec.rb +22 -0
- data/spec/commands/mapped_mset_spec.rb +19 -0
- data/spec/commands/mapped_msetnx_spec.rb +26 -0
- data/spec/commands/mget_spec.rb +48 -17
- data/spec/commands/move_spec.rb +37 -37
- data/spec/commands/mset_spec.rb +20 -6
- data/spec/commands/msetnx_spec.rb +14 -14
- data/spec/commands/persist_spec.rb +15 -16
- data/spec/commands/pexpire_spec.rb +86 -0
- data/spec/commands/pexpireat_spec.rb +48 -0
- data/spec/commands/ping_spec.rb +6 -2
- data/spec/commands/pipelined_spec.rb +98 -7
- data/spec/commands/pttl_spec.rb +41 -0
- data/spec/commands/randomkey_spec.rb +3 -3
- data/spec/commands/rename_spec.rb +16 -12
- data/spec/commands/renamenx_spec.rb +13 -15
- data/spec/commands/restore_spec.rb +47 -0
- data/spec/commands/rpop_spec.rb +6 -6
- data/spec/commands/rpoplpush_spec.rb +13 -8
- data/spec/commands/rpush_spec.rb +21 -15
- data/spec/commands/rpushx_spec.rb +24 -11
- data/spec/commands/sadd_spec.rb +14 -10
- data/spec/commands/scan_each_spec.rb +39 -0
- data/spec/commands/scan_spec.rb +64 -0
- data/spec/commands/scard_spec.rb +3 -3
- data/spec/commands/script_spec.rb +9 -0
- data/spec/commands/sdiff_spec.rb +13 -13
- data/spec/commands/sdiffstore_spec.rb +13 -13
- data/spec/commands/select_spec.rb +13 -5
- data/spec/commands/set_spec.rb +112 -0
- data/spec/commands/setbit_spec.rb +25 -16
- data/spec/commands/setex_spec.rb +20 -4
- data/spec/commands/setnx_spec.rb +6 -6
- data/spec/commands/setrange_spec.rb +12 -12
- data/spec/commands/sinter_spec.rb +11 -13
- data/spec/commands/sinterstore_spec.rb +12 -12
- data/spec/commands/sismember_spec.rb +10 -10
- data/spec/commands/smembers_spec.rb +15 -5
- data/spec/commands/smove_spec.rb +13 -13
- data/spec/commands/sort_list_spec.rb +21 -0
- data/spec/commands/sort_set_spec.rb +21 -0
- data/spec/commands/sort_zset_spec.rb +21 -0
- data/spec/commands/spop_spec.rb +19 -4
- data/spec/commands/srandmember_spec.rb +28 -4
- data/spec/commands/srem_spec.rb +17 -12
- data/spec/commands/sscan_each_spec.rb +48 -0
- data/spec/commands/sscan_spec.rb +39 -0
- data/spec/commands/strlen_spec.rb +4 -5
- data/spec/commands/sunion_spec.rb +13 -11
- data/spec/commands/sunionstore_spec.rb +12 -12
- data/spec/commands/ttl_spec.rb +11 -6
- data/spec/commands/type_spec.rb +1 -1
- data/spec/commands/watch_spec.rb +9 -4
- data/spec/commands/xadd_spec.rb +122 -0
- data/spec/commands/xlen_spec.rb +22 -0
- data/spec/commands/xrange_spec.rb +164 -0
- data/spec/commands/xread_spec.rb +66 -0
- data/spec/commands/xrevrange_spec.rb +130 -0
- data/spec/commands/xtrim_spec.rb +36 -0
- data/spec/commands/zadd_spec.rb +100 -11
- data/spec/commands/zcard_spec.rb +4 -4
- data/spec/commands/zcount_spec.rb +18 -10
- data/spec/commands/zincrby_spec.rb +6 -6
- data/spec/commands/zinterstore_spec.rb +54 -20
- data/spec/commands/zpopmax_spec.rb +60 -0
- data/spec/commands/zpopmin_spec.rb +60 -0
- data/spec/commands/zrange_spec.rb +54 -13
- data/spec/commands/zrangebyscore_spec.rb +42 -27
- data/spec/commands/zrank_spec.rb +4 -4
- data/spec/commands/zrem_spec.rb +18 -12
- data/spec/commands/zremrangebyrank_spec.rb +5 -5
- data/spec/commands/zremrangebyscore_spec.rb +12 -5
- data/spec/commands/zrevrange_spec.rb +35 -10
- data/spec/commands/zrevrangebyscore_spec.rb +26 -15
- data/spec/commands/zrevrank_spec.rb +4 -4
- data/spec/commands/zscan_each_spec.rb +48 -0
- data/spec/commands/zscan_spec.rb +26 -0
- data/spec/commands/zscore_spec.rb +7 -7
- data/spec/commands/zunionstore_spec.rb +54 -21
- data/spec/mock_redis_spec.rb +61 -0
- data/spec/spec_helper.rb +35 -8
- data/spec/support/redis_multiplexer.rb +62 -37
- data/spec/support/shared_examples/does_not_cleanup_empty_strings.rb +14 -0
- data/spec/support/shared_examples/only_operates_on_hashes.rb +5 -3
- data/spec/support/shared_examples/only_operates_on_lists.rb +5 -3
- data/spec/support/shared_examples/only_operates_on_sets.rb +5 -3
- data/spec/support/shared_examples/only_operates_on_strings.rb +4 -4
- data/spec/support/shared_examples/only_operates_on_zsets.rb +18 -16
- data/spec/support/shared_examples/sorts_enumerables.rb +56 -0
- data/spec/transactions_spec.rb +79 -29
- metadata +162 -42
- data/LICENSE +0 -19
- data/spec/commands/hash_operator_spec.rb +0 -21
data/lib/mock_redis/database.rb
CHANGED
@@ -5,6 +5,13 @@ require 'mock_redis/list_methods'
|
|
5
5
|
require 'mock_redis/set_methods'
|
6
6
|
require 'mock_redis/string_methods'
|
7
7
|
require 'mock_redis/zset_methods'
|
8
|
+
require 'mock_redis/sort_method'
|
9
|
+
require 'mock_redis/indifferent_hash'
|
10
|
+
require 'mock_redis/info_method'
|
11
|
+
require 'mock_redis/utility_methods'
|
12
|
+
require 'mock_redis/geospatial_methods'
|
13
|
+
require 'mock_redis/stream_methods'
|
14
|
+
require 'mock_redis/connection_method'
|
8
15
|
|
9
16
|
class MockRedis
|
10
17
|
class Database
|
@@ -13,277 +20,157 @@ class MockRedis
|
|
13
20
|
include SetMethods
|
14
21
|
include StringMethods
|
15
22
|
include ZsetMethods
|
23
|
+
include SortMethod
|
24
|
+
include InfoMethod
|
25
|
+
include UtilityMethods
|
26
|
+
include GeospatialMethods
|
27
|
+
include StreamMethods
|
28
|
+
include ConnectionMethod
|
16
29
|
|
17
30
|
attr_reader :data, :expire_times
|
18
31
|
|
19
|
-
def initialize(*
|
20
|
-
@
|
32
|
+
def initialize(base, *_args)
|
33
|
+
@base = base
|
34
|
+
@data = MockRedis::IndifferentHash.new
|
21
35
|
@expire_times = []
|
22
36
|
end
|
23
37
|
|
24
|
-
def initialize_copy(
|
38
|
+
def initialize_copy(_source)
|
25
39
|
@data = @data.clone
|
26
|
-
@data.
|
27
|
-
@expire_times = @expire_times.map
|
40
|
+
@data.each_key { |k| @data[k] = @data[k].clone }
|
41
|
+
@expire_times = @expire_times.map(&:clone)
|
28
42
|
end
|
29
43
|
|
30
44
|
# Redis commands go below this line and above 'private'
|
31
45
|
|
32
|
-
def auth(_)
|
46
|
+
def auth(_)
|
47
|
+
'OK'
|
48
|
+
end
|
49
|
+
|
50
|
+
def bgrewriteaof
|
51
|
+
'Background append only file rewriting started'
|
52
|
+
end
|
33
53
|
|
34
|
-
def
|
54
|
+
def bgsave
|
55
|
+
'Background saving started'
|
56
|
+
end
|
57
|
+
|
58
|
+
def disconnect
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
alias close disconnect
|
62
|
+
alias disconnect! close
|
35
63
|
|
36
|
-
def
|
64
|
+
def connected?
|
65
|
+
true
|
66
|
+
end
|
37
67
|
|
38
68
|
def dbsize
|
39
69
|
data.keys.length
|
40
70
|
end
|
41
71
|
|
42
72
|
def del(*keys)
|
73
|
+
keys = keys.flatten.map(&:to_s)
|
74
|
+
assert_has_args(keys, 'del')
|
75
|
+
|
43
76
|
keys.
|
44
|
-
find_all{|key| data[key]}.
|
45
|
-
each {|k| persist(k)}.
|
46
|
-
each {|k| data.delete(k)}.
|
77
|
+
find_all { |key| data[key] }.
|
78
|
+
each { |k| persist(k) }.
|
79
|
+
each { |k| data.delete(k) }.
|
47
80
|
length
|
48
81
|
end
|
82
|
+
alias unlink del
|
49
83
|
|
50
84
|
def echo(msg)
|
51
85
|
msg.to_s
|
52
86
|
end
|
53
87
|
|
54
88
|
def expire(key, seconds)
|
55
|
-
|
89
|
+
assert_valid_integer(seconds)
|
90
|
+
|
91
|
+
pexpire(key, seconds.to_i * 1000)
|
92
|
+
end
|
93
|
+
|
94
|
+
def pexpire(key, ms)
|
95
|
+
assert_valid_integer(ms)
|
96
|
+
|
97
|
+
now, miliseconds = @base.now
|
98
|
+
now_ms = (now * 1000) + miliseconds
|
99
|
+
pexpireat(key, now_ms + ms.to_i)
|
56
100
|
end
|
57
101
|
|
58
102
|
def expireat(key, timestamp)
|
59
|
-
|
60
|
-
raise Redis::CommandError, "ERR value is not an integer or out of range"
|
61
|
-
end
|
103
|
+
assert_valid_integer(timestamp)
|
62
104
|
|
63
|
-
|
64
|
-
|
105
|
+
pexpireat(key, timestamp.to_i * 1000)
|
106
|
+
end
|
107
|
+
|
108
|
+
def pexpireat(key, timestamp_ms)
|
109
|
+
assert_valid_integer(timestamp_ms)
|
110
|
+
|
111
|
+
if exists?(key)
|
112
|
+
timestamp = Rational(timestamp_ms.to_i, 1000)
|
113
|
+
set_expiration(key, @base.time_at(timestamp))
|
65
114
|
true
|
66
115
|
else
|
67
116
|
false
|
68
117
|
end
|
69
118
|
end
|
70
119
|
|
71
|
-
def exists(
|
72
|
-
data.
|
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
|
73
127
|
end
|
74
128
|
|
75
129
|
def flushdb
|
76
|
-
data.
|
130
|
+
data.each_key { |k| del(k) }
|
131
|
+
'OK'
|
132
|
+
end
|
133
|
+
|
134
|
+
def dump(key)
|
135
|
+
value = data[key]
|
136
|
+
value ? Marshal.dump(value) : nil
|
137
|
+
end
|
138
|
+
|
139
|
+
def restore(key, ttl, value, replace: false)
|
140
|
+
if !replace && exists?(key)
|
141
|
+
raise Redis::CommandError, 'BUSYKEY Target key name already exists.'
|
142
|
+
end
|
143
|
+
data[key] = Marshal.load(value) # rubocop:disable Security/MarshalLoad
|
144
|
+
if ttl > 0
|
145
|
+
pexpire(key, ttl)
|
146
|
+
end
|
77
147
|
'OK'
|
78
148
|
end
|
79
149
|
|
80
|
-
def
|
81
|
-
astats = [
|
82
|
-
["2", "2699"],
|
83
|
-
["6", "1"],
|
84
|
-
["7", "1"],
|
85
|
-
["8", "17197"],
|
86
|
-
["9", "109875"],
|
87
|
-
["10", "94348"],
|
88
|
-
["11", "32580"],
|
89
|
-
["12", "52347"],
|
90
|
-
["13", "86475"],
|
91
|
-
["14", "58175"],
|
92
|
-
["15", "53408"],
|
93
|
-
["16", "876949"],
|
94
|
-
["17", "71157"],
|
95
|
-
["18", "5104"],
|
96
|
-
["19", "2705"],
|
97
|
-
["20", "2903"],
|
98
|
-
["21", "1024"],
|
99
|
-
["22", "2546"],
|
100
|
-
["23", "952"],
|
101
|
-
["24", "186080"],
|
102
|
-
["25", "611"],
|
103
|
-
["26", "40936"],
|
104
|
-
["27", "960"],
|
105
|
-
["28", "1323"],
|
106
|
-
["29", "14216"],
|
107
|
-
["30", "52412"],
|
108
|
-
["31", "21130"],
|
109
|
-
["32", "47959"],
|
110
|
-
["33", "6891"],
|
111
|
-
["34", "9712"],
|
112
|
-
["35", "3366"],
|
113
|
-
["36", "5737"],
|
114
|
-
["37", "11274"],
|
115
|
-
["38", "8057"],
|
116
|
-
["39", "2957"],
|
117
|
-
["40", "51200"],
|
118
|
-
["42", "8220"],
|
119
|
-
["43", "8278"],
|
120
|
-
["44", "6539"],
|
121
|
-
["45", "764"],
|
122
|
-
["47", "1018"],
|
123
|
-
["48", "19250"],
|
124
|
-
["49", "713"],
|
125
|
-
["51", "51"],
|
126
|
-
["53", "2"],
|
127
|
-
["55", "3922"],
|
128
|
-
["56", "153"],
|
129
|
-
["57", "614"],
|
130
|
-
["58", "1"],
|
131
|
-
["59", "1775"],
|
132
|
-
["61", "32865"],
|
133
|
-
["63", "2530"],
|
134
|
-
["64", "565"],
|
135
|
-
["65", "1322"],
|
136
|
-
["67", "1572"],
|
137
|
-
["69", "1421"],
|
138
|
-
["71", "1220"],
|
139
|
-
["72", "241"],
|
140
|
-
["73", "5432"],
|
141
|
-
["74", "1122"],
|
142
|
-
["75", "2555"],
|
143
|
-
["77", "1539"],
|
144
|
-
["78", "612"],
|
145
|
-
["79", "902"],
|
146
|
-
["81", "1678"],
|
147
|
-
["83", "51"],
|
148
|
-
["84", "612"],
|
149
|
-
["85", "706"],
|
150
|
-
["87", "410"],
|
151
|
-
["88", "5435"],
|
152
|
-
["89", "813"],
|
153
|
-
["90", "612"],
|
154
|
-
["93", "153"],
|
155
|
-
["94", "612"],
|
156
|
-
["96", "159"],
|
157
|
-
["97", "306"],
|
158
|
-
["99", "153"],
|
159
|
-
["101", "456"],
|
160
|
-
["103", "741"],
|
161
|
-
["105", "447"],
|
162
|
-
["107", "754"],
|
163
|
-
["109", "414"],
|
164
|
-
["111", "475"],
|
165
|
-
["113", "757"],
|
166
|
-
["115", "287"],
|
167
|
-
["117", "420"],
|
168
|
-
["118", "765"],
|
169
|
-
["119", "642"],
|
170
|
-
["120", "159"],
|
171
|
-
["121", "926"],
|
172
|
-
["122", "612"],
|
173
|
-
["123", "251"],
|
174
|
-
["125", "390"],
|
175
|
-
["127", "354"],
|
176
|
-
["128", "617"],
|
177
|
-
["129", "528"],
|
178
|
-
["131", "298"],
|
179
|
-
["132", "612"],
|
180
|
-
["133", "809"],
|
181
|
-
["135", "244"],
|
182
|
-
["136", "306"],
|
183
|
-
["137", "504"],
|
184
|
-
["139", "201"],
|
185
|
-
["141", "1124"],
|
186
|
-
["143", "139"],
|
187
|
-
["144", "159"],
|
188
|
-
["145", "1322"],
|
189
|
-
["147", "410"],
|
190
|
-
["149", "253"],
|
191
|
-
["151", "304"],
|
192
|
-
["153", "312"],
|
193
|
-
["155", "249"],
|
194
|
-
["157", "306"],
|
195
|
-
["159", "348"],
|
196
|
-
["161", "255"],
|
197
|
-
["163", "458"],
|
198
|
-
["165", "5"],
|
199
|
-
["167", "306"],
|
200
|
-
["168", "47"],
|
201
|
-
["169", "214"],
|
202
|
-
["171", "250"],
|
203
|
-
["173", "5"],
|
204
|
-
["177", "10"],
|
205
|
-
["179", "158"],
|
206
|
-
["181", "5"],
|
207
|
-
["183", "10"],
|
208
|
-
["185", "51"],
|
209
|
-
["187", "49"],
|
210
|
-
["191", "5"],
|
211
|
-
["192", "47"],
|
212
|
-
["193", "51"],
|
213
|
-
["197", "112"],
|
214
|
-
["199", "5"],
|
215
|
-
["201", "5"],
|
216
|
-
["203", "5"],
|
217
|
-
["209", "5"],
|
218
|
-
["213", "51"],
|
219
|
-
["217", "102"],
|
220
|
-
["225", "357"],
|
221
|
-
["229", "51"],
|
222
|
-
["233", "204"],
|
223
|
-
["237", "51"],
|
224
|
-
["239", "1"],
|
225
|
-
["247", "46"],
|
226
|
-
["255", "102"],
|
227
|
-
[">=256", "6201"],
|
228
|
-
]
|
229
|
-
|
230
|
-
{
|
231
|
-
"allocation_stats" => astats.map {|(a,b)| "#{a}=#{b}"}.join(','),
|
232
|
-
"aof_enabled" => "0",
|
233
|
-
"arch_bits" => "64",
|
234
|
-
"bgrewriteaof_in_progress" => "0",
|
235
|
-
"bgsave_in_progress" => "0",
|
236
|
-
"blocked_clients" => "0",
|
237
|
-
"changes_since_last_save" => "0",
|
238
|
-
"client_biggest_input_buf" => "0",
|
239
|
-
"client_longest_output_list" => "0",
|
240
|
-
"connected_clients" => "1",
|
241
|
-
"connected_slaves" => "0",
|
242
|
-
"db0" => "keys=8,expires=0",
|
243
|
-
"evicted_keys" => "0",
|
244
|
-
"expired_keys" => "0",
|
245
|
-
"hash_max_zipmap_entries" => "512",
|
246
|
-
"hash_max_zipmap_value" => "64",
|
247
|
-
"keyspace_hits" => "62645",
|
248
|
-
"keyspace_misses" => "29757",
|
249
|
-
"last_save_time" => "1310596333",
|
250
|
-
"loading" => "0",
|
251
|
-
"lru_clock" => "1036434",
|
252
|
-
"mem_fragmentation_ratio" => "2.04",
|
253
|
-
"multiplexing_api" => "kqueue",
|
254
|
-
"process_id" => "14508",
|
255
|
-
"pubsub_channels" => "0",
|
256
|
-
"pubsub_patterns" => "0",
|
257
|
-
"redis_git_dirty" => "0",
|
258
|
-
"redis_git_sha1" => "00000000",
|
259
|
-
"redis_version" => "2.2.11",
|
260
|
-
"role" => "master",
|
261
|
-
"total_commands_processed" => "196800",
|
262
|
-
"total_connections_received" => "4359",
|
263
|
-
"uptime_in_days" => "0",
|
264
|
-
"uptime_in_seconds" => "84215",
|
265
|
-
"use_tcmalloc" => "0",
|
266
|
-
"used_cpu_sys" => "5.54",
|
267
|
-
"used_cpu_sys_childrens" => "0.00",
|
268
|
-
"used_cpu_user" => "7.65",
|
269
|
-
"used_cpu_user_childrens" => "0.02",
|
270
|
-
"used_memory" => "931456",
|
271
|
-
"used_memory_human" => "909.62K",
|
272
|
-
"used_memory_rss" => "1904640",
|
273
|
-
"vm_enabled" => "0",
|
274
|
-
}
|
275
|
-
end
|
276
|
-
|
277
|
-
def keys(format)
|
150
|
+
def keys(format = '*')
|
278
151
|
data.keys.grep(redis_pattern_to_ruby_regex(format))
|
279
152
|
end
|
280
153
|
|
154
|
+
def scan(cursor, opts = {})
|
155
|
+
common_scan(data.keys, cursor, opts)
|
156
|
+
end
|
157
|
+
|
158
|
+
def scan_each(opts = {}, &block)
|
159
|
+
return to_enum(:scan_each, opts) unless block_given?
|
160
|
+
cursor = 0
|
161
|
+
loop do
|
162
|
+
cursor, keys = scan(cursor, opts)
|
163
|
+
keys.each(&block)
|
164
|
+
break if cursor == '0'
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
281
168
|
def lastsave
|
282
|
-
|
169
|
+
now.first
|
283
170
|
end
|
284
171
|
|
285
172
|
def persist(key)
|
286
|
-
if exists(key) && has_expiration?(key)
|
173
|
+
if exists?(key) && has_expiration?(key)
|
287
174
|
remove_expiration(key)
|
288
175
|
true
|
289
176
|
else
|
@@ -291,8 +178,8 @@ class MockRedis
|
|
291
178
|
end
|
292
179
|
end
|
293
180
|
|
294
|
-
def ping
|
295
|
-
|
181
|
+
def ping(response = 'PONG')
|
182
|
+
response
|
296
183
|
end
|
297
184
|
|
298
185
|
def quit
|
@@ -304,22 +191,27 @@ class MockRedis
|
|
304
191
|
end
|
305
192
|
|
306
193
|
def rename(key, newkey)
|
307
|
-
|
308
|
-
raise Redis::CommandError,
|
309
|
-
elsif key == newkey
|
310
|
-
raise Redis::CommandError, "ERR source and destination objects are the same"
|
194
|
+
unless data.include?(key)
|
195
|
+
raise Redis::CommandError, 'ERR no such key'
|
311
196
|
end
|
312
|
-
|
197
|
+
|
198
|
+
if key != newkey
|
199
|
+
data[newkey] = data.delete(key)
|
200
|
+
if has_expiration?(key)
|
201
|
+
set_expiration(newkey, expiration(key))
|
202
|
+
remove_expiration(key)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
313
206
|
'OK'
|
314
207
|
end
|
315
208
|
|
316
209
|
def renamenx(key, newkey)
|
317
|
-
|
318
|
-
raise Redis::CommandError,
|
319
|
-
elsif key == newkey
|
320
|
-
raise Redis::CommandError, "ERR source and destination objects are the same"
|
210
|
+
unless data.include?(key)
|
211
|
+
raise Redis::CommandError, 'ERR no such key'
|
321
212
|
end
|
322
|
-
|
213
|
+
|
214
|
+
if exists?(newkey)
|
323
215
|
false
|
324
216
|
else
|
325
217
|
rename(key, newkey)
|
@@ -332,15 +224,38 @@ class MockRedis
|
|
332
224
|
end
|
333
225
|
|
334
226
|
def ttl(key)
|
335
|
-
if
|
336
|
-
|
227
|
+
if !exists?(key)
|
228
|
+
-2
|
229
|
+
elsif has_expiration?(key)
|
230
|
+
now, = @base.now
|
231
|
+
expiration(key).to_i - now
|
337
232
|
else
|
338
233
|
-1
|
339
234
|
end
|
340
235
|
end
|
341
236
|
|
237
|
+
def pttl(key)
|
238
|
+
now, miliseconds = @base.now
|
239
|
+
now_ms = now * 1000 + miliseconds
|
240
|
+
|
241
|
+
if !exists?(key)
|
242
|
+
-2
|
243
|
+
elsif has_expiration?(key)
|
244
|
+
(expiration(key).to_r * 1000).to_i - now_ms
|
245
|
+
else
|
246
|
+
-1
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def now
|
251
|
+
current_time = @base.options[:time_class].now
|
252
|
+
miliseconds = (current_time.to_r - current_time.to_i) * 1_000
|
253
|
+
[current_time.to_i, miliseconds.to_i]
|
254
|
+
end
|
255
|
+
alias time now
|
256
|
+
|
342
257
|
def type(key)
|
343
|
-
if !exists(key)
|
258
|
+
if !exists?(key)
|
344
259
|
'none'
|
345
260
|
elsif hashy?(key)
|
346
261
|
'hash'
|
@@ -357,13 +272,26 @@ class MockRedis
|
|
357
272
|
end
|
358
273
|
end
|
359
274
|
|
275
|
+
def script(subcommand, *args); end
|
276
|
+
|
277
|
+
def evalsha(*args); end
|
278
|
+
|
279
|
+
def eval(*args); end
|
280
|
+
|
360
281
|
private
|
361
282
|
|
283
|
+
def assert_valid_integer(integer)
|
284
|
+
unless looks_like_integer?(integer.to_s)
|
285
|
+
raise Redis::CommandError, 'ERR value is not an integer or out of range'
|
286
|
+
end
|
287
|
+
integer
|
288
|
+
end
|
289
|
+
|
362
290
|
def assert_valid_timeout(timeout)
|
363
|
-
if !
|
364
|
-
raise Redis::CommandError,
|
291
|
+
if !looks_like_integer?(timeout.to_s)
|
292
|
+
raise Redis::CommandError, 'ERR timeout is not an integer or out of range'
|
365
293
|
elsif timeout < 0
|
366
|
-
raise Redis::CommandError,
|
294
|
+
raise Redis::CommandError, 'ERR timeout is negative'
|
367
295
|
end
|
368
296
|
timeout
|
369
297
|
end
|
@@ -377,16 +305,24 @@ class MockRedis
|
|
377
305
|
end
|
378
306
|
|
379
307
|
def extract_timeout(arglist)
|
380
|
-
|
381
|
-
|
308
|
+
options = arglist.last
|
309
|
+
if options.is_a?(Hash) && options[:timeout]
|
310
|
+
timeout = assert_valid_timeout(options[:timeout])
|
311
|
+
[arglist[0..-2], timeout]
|
312
|
+
elsif options.is_a?(Integer)
|
313
|
+
timeout = assert_valid_timeout(options)
|
314
|
+
[arglist[0..-2], timeout]
|
315
|
+
else
|
316
|
+
[arglist, 0]
|
317
|
+
end
|
382
318
|
end
|
383
319
|
|
384
320
|
def expiration(key)
|
385
|
-
expire_times.find {|(_,k)| k == key}.first
|
321
|
+
expire_times.find { |(_, k)| k == key.to_s }.first
|
386
322
|
end
|
387
323
|
|
388
324
|
def has_expiration?(key)
|
389
|
-
expire_times.any? {|(_,k)| k == key}
|
325
|
+
expire_times.any? { |(_, k)| k == key.to_s }
|
390
326
|
end
|
391
327
|
|
392
328
|
def looks_like_integer?(str)
|
@@ -400,23 +336,23 @@ class MockRedis
|
|
400
336
|
def redis_pattern_to_ruby_regex(pattern)
|
401
337
|
Regexp.new(
|
402
338
|
"^#{pattern}$".
|
403
|
-
gsub(/([
|
404
|
-
gsub(/(
|
339
|
+
gsub(/([+|()])/, '\\\\\1').
|
340
|
+
gsub(/(?<!\\)\?/, '\\1.').
|
341
|
+
gsub(/([^\\])\*/, '\\1.*')
|
342
|
+
)
|
405
343
|
end
|
406
344
|
|
407
345
|
def remove_expiration(key)
|
408
|
-
expire_times.delete_if do |(
|
409
|
-
key == k
|
346
|
+
expire_times.delete_if do |(_t, k)|
|
347
|
+
key.to_s == k
|
410
348
|
end
|
411
349
|
end
|
412
350
|
|
413
351
|
def set_expiration(key, time)
|
414
352
|
remove_expiration(key)
|
415
|
-
|
416
|
-
|
417
|
-
expire_times.
|
418
|
-
a.first <=> b.first
|
419
|
-
end
|
353
|
+
found = expire_times.each_with_index.to_a.bsearch { |item, _| item.first >= time }
|
354
|
+
index = found ? found.last : -1
|
355
|
+
expire_times.insert(index, [time, key.to_s])
|
420
356
|
end
|
421
357
|
|
422
358
|
def zero_pad(string, desired_length)
|
@@ -425,20 +361,20 @@ class MockRedis
|
|
425
361
|
end
|
426
362
|
|
427
363
|
public
|
364
|
+
|
428
365
|
# This method isn't private, but it also isn't a Redis command, so
|
429
366
|
# it doesn't belong up above with all the Redis commands.
|
430
367
|
def expire_keys
|
431
|
-
|
368
|
+
now_sec, miliseconds = now
|
369
|
+
now_ms = now_sec * 1_000 + miliseconds
|
432
370
|
|
433
|
-
to_delete = expire_times.take_while do |(time,
|
434
|
-
time <=
|
371
|
+
to_delete = expire_times.take_while do |(time, _key)|
|
372
|
+
(time.to_r * 1_000).to_i <= now_ms
|
435
373
|
end
|
436
374
|
|
437
|
-
to_delete.each do |(
|
375
|
+
to_delete.each do |(_time, key)|
|
438
376
|
del(key)
|
439
377
|
end
|
440
|
-
|
441
|
-
expire_times.slice!(0, to_delete.length)
|
442
378
|
end
|
443
379
|
end
|
444
380
|
end
|
@@ -4,7 +4,7 @@ class MockRedis
|
|
4
4
|
class ExpireWrapper
|
5
5
|
include UndefRedisMethods
|
6
6
|
|
7
|
-
def respond_to?(method, include_private=false)
|
7
|
+
def respond_to?(method, include_private = false)
|
8
8
|
super || @db.respond_to?(method)
|
9
9
|
end
|
10
10
|
|
@@ -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
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class MockRedis
|
2
|
+
class FutureNotReady < RuntimeError; end
|
3
|
+
|
4
|
+
class Future
|
5
|
+
attr_reader :command, :block
|
6
|
+
|
7
|
+
def initialize(command, block = nil)
|
8
|
+
@command = command
|
9
|
+
@block = block
|
10
|
+
@result_set = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def value
|
14
|
+
raise FutureNotReady unless @result_set
|
15
|
+
@result
|
16
|
+
end
|
17
|
+
|
18
|
+
def store_result(result)
|
19
|
+
@result_set = true
|
20
|
+
@result = @block ? @block.call(result) : result
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|