/
hash_methods.rb
181 lines (149 loc) · 4.26 KB
/
hash_methods.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
require 'mock_redis/assertions'
require 'mock_redis/utility_methods'
class MockRedis
module HashMethods
include Assertions
include UtilityMethods
def hdel(key, *fields)
with_hash_at(key) do |hash|
orig_size = hash.size
fields = Array(fields).flatten.map(&:to_s)
if fields.empty?
raise Redis::CommandError, "ERR wrong number of arguments for 'hdel' command"
end
hash.delete_if { |k, _v| fields.include?(k) }
orig_size - hash.size
end
end
def hexists(key, field)
with_hash_at(key) { |h| h.key?(field.to_s) }
end
def hget(key, field)
with_hash_at(key) { |h| h[field.to_s] }
end
def hgetall(key)
with_hash_at(key) { |h| h }
end
def hincrby(key, field, increment)
with_hash_at(key) do |hash|
field = field.to_s
unless can_incr?(data[key][field])
raise Redis::CommandError, 'ERR hash value is not an integer'
end
unless looks_like_integer?(increment.to_s)
raise Redis::CommandError, 'ERR value is not an integer or out of range'
end
new_value = (hash[field] || '0').to_i + increment.to_i
hash[field] = new_value.to_s
new_value
end
end
def hincrbyfloat(key, field, increment)
with_hash_at(key) do |hash|
field = field.to_s
unless can_incr_float?(data[key][field])
raise Redis::CommandError, 'ERR hash value is not a float'
end
unless looks_like_float?(increment.to_s)
raise Redis::CommandError, 'ERR value is not a valid float'
end
new_value = (hash[field] || '0').to_f + increment.to_f
new_value = new_value.to_i if new_value % 1 == 0
hash[field] = new_value.to_s
new_value
end
end
def hkeys(key)
with_hash_at(key, &:keys)
end
def hlen(key)
hkeys(key).length
end
def hmget(key, *fields)
assert_has_args(fields, 'hmget')
fields.flatten.map { |f| hget(key, f) }
end
def mapped_hmget(key, *fields)
reply = hmget(key, *fields)
if reply.is_a?(Array)
Hash[fields.zip(reply)]
else
reply
end
end
def hmset(key, *kvpairs)
if key.is_a? Array
err_msg = 'ERR wrong number of arguments for \'hmset\' command'
kvpairs = key[1..-1]
key = key[0]
end
kvpairs.flatten!
assert_has_args(kvpairs, 'hmset')
if kvpairs.length.odd?
raise Redis::CommandError, err_msg || 'ERR wrong number of arguments for \'hmset\' command'
end
kvpairs.each_slice(2) do |(k, v)|
hset(key, k, v)
end
'OK'
end
def mapped_hmset(key, hash)
kvpairs = hash.to_a.flatten
assert_has_args(kvpairs, 'hmset')
if kvpairs.length.odd?
raise Redis::CommandError, "ERR wrong number of arguments for 'hmset' command"
end
hmset(key, *kvpairs)
end
def hscan(key, cursor, opts = {})
opts = opts.merge(key: lambda { |x| x[0] })
common_scan(hgetall(key).to_a, cursor, opts)
end
def hscan_each(key, opts = {}, &block)
return to_enum(:hscan_each, key, opts) unless block_given?
cursor = 0
loop do
cursor, values = hscan(key, cursor, opts)
values.each(&block)
break if cursor == '0'
end
end
def hset(key, *args)
added = 0
with_hash_at(key) do |hash|
if args.length == 1 && args[0].is_a?(Hash)
args = args[0].to_a.flatten
end
args.each_slice(2) do |field, value|
added += 1 unless hash.key?(field.to_s)
hash[field.to_s] = value.to_s
end
end
added
end
def hsetnx(key, field, value)
if hget(key, field)
false
else
hset(key, field, value)
true
end
end
def hvals(key)
with_hash_at(key, &:values)
end
private
def with_hash_at(key, &blk)
with_thing_at(key.to_s, :assert_hashy, proc { {} }, &blk)
end
def hashy?(key)
data[key].nil? || data[key].is_a?(Hash)
end
def assert_hashy(key)
unless hashy?(key)
raise Redis::CommandError,
'WRONGTYPE Operation against a key holding the wrong kind of value'
end
end
end
end