lib/redis/connection/memory.rb in fakeredis-0.3.1 vs lib/redis/connection/memory.rb in fakeredis-0.3.2
- old
+ new
@@ -114,23 +114,15 @@
# NOT IMPLEMENTED:
# * blpop
# * brpop
# * brpoplpush
# * discard
- # * mapped_hmset
- # * mapped_hmget
- # * mapped_mset
- # * mapped_msetnx
# * move
# * subscribe
# * psubscribe
# * publish
- # * substr
- # * unwatch
- # * watch
# * zremrangebyrank
- # * zremrangebyscore
# * zunionstore
def flushdb
@data = ExpiringHash.new
end
@@ -171,25 +163,27 @@
@data[key]
end
def getbit(key, offset)
return unless @data[key]
- @data[key].unpack('B8')[0].split("")[offset]
+ @data[key].unpack('B*')[0].split("")[offset].to_i
end
def getrange(key, start, ending)
return unless @data[key]
@data[key][start..ending]
end
+ alias :substr :getrange
def getset(key, value)
old_value = @data[key]
@data[key] = value
return old_value
end
def mget(*keys)
+ raise ArgumentError, "wrong number of arguments for 'mget' command" if keys.empty?
@data.values_at(*keys)
end
def append(key, value)
@data[key] = (@data[key] || "")
@@ -279,11 +273,11 @@
return unless @data[key]
index = @data[key].index(pivot)
case where
when :before then @data[key].insert(index, value)
when :after then @data[key].insert(index + 1, value)
- else raise ArgumentError.new
+ else raise ArgumentError
end
end
def lset(key, index, value)
data_type_check(key, Array)
@@ -353,103 +347,103 @@
return unless @data[key]
@data[key].shift
end
def smembers(key)
- data_type_check(key, Set)
+ data_type_check(key, ::Set)
return [] unless @data[key]
@data[key].to_a.reverse
end
def sismember(key, value)
- data_type_check(key, Set)
+ data_type_check(key, ::Set)
return false unless @data[key]
@data[key].include?(value.to_s)
end
def sadd(key, value)
- data_type_check(key, Set)
+ data_type_check(key, ::Set)
if @data[key]
!!@data[key].add?(value.to_s)
else
- @data[key] = Set.new([value.to_s])
+ @data[key] = ::Set.new([value.to_s])
true
end
end
def srem(key, value)
- data_type_check(key, Set)
+ data_type_check(key, ::Set)
deleted = !!(@data[key] && @data[key].delete?(value.to_s))
remove_key_for_empty_collection(key)
deleted
end
def smove(source, destination, value)
- data_type_check(destination, Set)
+ data_type_check(destination, ::Set)
result = self.srem(source, value)
self.sadd(destination, value) if result
result
end
def spop(key)
- data_type_check(key, Set)
+ data_type_check(key, ::Set)
elem = srandmember(key)
srem(key, elem)
elem
end
def scard(key)
- data_type_check(key, Set)
+ data_type_check(key, ::Set)
return 0 unless @data[key]
@data[key].size
end
def sinter(*keys)
- keys.each { |k| data_type_check(k, Set) }
- return Set.new if keys.any? { |k| @data[k].nil? }
- keys = keys.map { |k| @data[k] || Set.new }
+ keys.each { |k| data_type_check(k, ::Set) }
+ return ::Set.new if keys.any? { |k| @data[k].nil? }
+ keys = keys.map { |k| @data[k] || ::Set.new }
keys.inject do |set, key|
set & key
end.to_a
end
def sinterstore(destination, *keys)
- data_type_check(destination, Set)
+ data_type_check(destination, ::Set)
result = sinter(*keys)
- @data[destination] = Set.new(result)
+ @data[destination] = ::Set.new(result)
end
def sunion(*keys)
- keys.each { |k| data_type_check(k, Set) }
- keys = keys.map { |k| @data[k] || Set.new }
- keys.inject(Set.new) do |set, key|
+ keys.each { |k| data_type_check(k, ::Set) }
+ keys = keys.map { |k| @data[k] || ::Set.new }
+ keys.inject(::Set.new) do |set, key|
set | key
end.to_a
end
def sunionstore(destination, *keys)
- data_type_check(destination, Set)
+ data_type_check(destination, ::Set)
result = sunion(*keys)
- @data[destination] = Set.new(result)
+ @data[destination] = ::Set.new(result)
end
def sdiff(key1, *keys)
- [key1, *keys].each { |k| data_type_check(k, Set) }
- keys = keys.map { |k| @data[k] || Set.new }
+ [key1, *keys].each { |k| data_type_check(k, ::Set) }
+ keys = keys.map { |k| @data[k] || ::Set.new }
keys.inject(@data[key1]) do |memo, set|
memo - set
end.to_a
end
def sdiffstore(destination, key1, *keys)
- data_type_check(destination, Set)
+ data_type_check(destination, ::Set)
result = sdiff(key1, *keys)
- @data[destination] = Set.new(result)
+ @data[destination] = ::Set.new(result)
end
def srandmember(key)
- data_type_check(key, Set)
+ data_type_check(key, ::Set)
return nil unless @data[key]
@data[key].to_a[rand(@data[key].size)]
end
def del(*keys)
@@ -527,18 +521,20 @@
return false if @data[key] && @data[key][field]
hset(key, field, value)
end
def hmset(key, *fields)
+ raise ArgumentError, "wrong number of arguments for 'hmset' command" if fields.empty? || fields.size.odd?
data_type_check(key, Hash)
@data[key] ||= {}
fields.each_slice(2) do |field|
@data[key][field[0].to_s] = field[1].to_s
end
end
def hmget(key, *fields)
+ raise ArgumentError, "wrong number of arguments for 'hmget' command" if fields.empty?
data_type_check(key, Hash)
values = []
fields.map do |field|
field = field.to_s
if @data[key]
@@ -591,16 +587,19 @@
@data[key] = value.to_s
"OK"
end
def setbit(key, offset, bit)
- return unless @data[key]
- old_val = @data[key].unpack('B*')[0].split("")
+ old_val = @data[key] ? @data[key].unpack('B*')[0].split("") : []
+ size_increment = [((offset/8)+1)*8-old_val.length, 0].max
+ old_val += Array.new(size_increment).map{"0"}
+ original_val = old_val[offset]
old_val[offset] = bit.to_s
new_val = ""
old_val.each_slice(8){|b| new_val = new_val + b.join("").to_i(2).chr }
@data[key] = new_val
+ original_val
end
def setex(key, seconds, value)
@data[key] = value.to_s
expire(key, seconds)
@@ -625,17 +624,12 @@
return if keys.any?{|key| @data.key?(key) }
mset(*pairs)
true
end
- def mapped_mget(*keys)
- reply = mget(*keys)
- Hash[*keys.zip(reply).flatten]
- end
-
def sort(key)
- # TODO: Impleent
+ # TODO: Implement
end
def incr(key)
@data[key] = (@data[key] || "0")
@data[key] = (@data[key].to_i + 1).to_s
@@ -664,11 +658,11 @@
case value = @data[key]
when nil then "none"
when String then "string"
when Hash then "hash"
when Array then "list"
- when Set then "set"
+ when ::Set then "set"
end
end
def quit ; end
@@ -686,15 +680,23 @@
@buffer = []
yield if block_given?
"OK"
end
+ def watch(_)
+ "OK"
+ end
+
+ def unwatch
+ "OK"
+ end
+
def zadd(key, score, value)
data_type_check(key, ZSet)
@data[key] ||= ZSet.new
exists = @data[key].key?(value.to_s)
- @data[key][value.to_s] = score.to_i
+ @data[key][value.to_s] = score
!exists
end
def zrem(key, value)
data_type_check(key, ZSet)
@@ -720,10 +722,11 @@
zrange_select_by_score(key, min, max).size
end
def zincrby(key, num, value)
data_type_check(key, ZSet)
+ @data[key] ||= ZSet.new
@data[key][value.to_s] ||= 0
@data[key][value.to_s] += num
@data[key][value.to_s].to_s
end
@@ -757,40 +760,59 @@
else
@data[key].keys.sort_by {|k| -@data[key][k] }
end[start..stop].flatten.map(&:to_s)
end
- def zrangebyscore(key, min, max, with_scores = nil)
+ def zrangebyscore(key, min, max, *opts)
data_type_check(key, ZSet)
return [] unless @data[key]
range = zrange_select_by_score(key, min, max)
- if with_scores
+ vals = if opts.include?('WITHSCORES')
range.sort_by {|_,v| v }
else
range.keys.sort_by {|k| range[k] }
- end.flatten.map(&:to_s)
+ end
+
+ limit = get_limit(opts, vals)
+ vals = vals[*limit] if limit
+
+ vals.flatten.map(&:to_s)
end
- def zrevrangebyscore(key, max, min, with_scores = nil)
+ def zrevrangebyscore(key, max, min, *opts)
data_type_check(key, ZSet)
return [] unless @data[key]
range = zrange_select_by_score(key, min, max)
- if with_scores
+ vals = if opts.include?('WITHSCORES')
range.sort_by {|_,v| -v }
else
range.keys.sort_by {|k| -range[k] }
- end.flatten.map(&:to_s)
+ end
+
+ limit = get_limit(opts, vals)
+ vals = vals[*limit] if limit
+
+ vals.flatten.map(&:to_s)
end
+ def zremrangebyscore(key, min, max)
+ data_type_check(key, ZSet)
+ return 0 unless @data[key]
+
+ range = zrange_select_by_score(key, min, max)
+ range.each {|k,_| @data[key].delete(k) }
+ range.size
+ end
+
def zinterstore(out, _, *keys)
data_type_check(out, ZSet)
hashes = keys.map do |src|
case @data[src]
- when Set
+ when ::Set
Hash[@data[src].zip([0] * @data[src].size)]
when Hash
@data[src]
else
{}
@@ -804,22 +826,42 @@
end
@data[out].size
end
+ def zremrangebyrank(key, start, stop)
+ sorted_elements = @data[key].sort { |(v_a, r_a), (v_b, r_b)| r_a <=> r_b }
+ elements_to_delete = sorted_elements[start..stop]
+ elements_to_delete.each { |elem, rank| @data[key].delete(elem) }
+ elements_to_delete.size
+ end
+
private
def zrange_select_by_score(key, min, max)
@data[key].reject {|_,v| v < min || v > max }
end
def remove_key_for_empty_collection(key)
del(key) if @data[key] && @data[key].empty?
end
-
+
def data_type_check(key, klass)
if @data[key] && !@data[key].is_a?(klass)
fail "Operation against a key holding the wrong kind of value: Expected #{klass} at #{key}."
+ end
+ end
+
+ def get_limit(opts, vals)
+ index = opts.index('LIMIT')
+
+ if index
+ offset = opts[index + 1]
+
+ count = opts[index + 2]
+ count = vals.size if count < 0
+
+ [offset, count]
end
end
end
end
end