lib/redis/connection/memory.rb in fakeredis-0.2.2 vs lib/redis/connection/memory.rb in fakeredis-0.3.0

- old
+ new

@@ -1,5 +1,6 @@ +require 'set' require 'redis/connection/registry' require 'redis/connection/command_helper' class Redis module Connection @@ -57,10 +58,13 @@ end end end end + class ZSet < Hash + end + include Redis::Connection::CommandHelper def initialize @data = ExpiringHash.new @connected = false @@ -121,24 +125,12 @@ # * psubscribe # * publish # * substr # * unwatch # * watch - # * zadd - # * zcard - # * zcount - # * zincrby - # * zinterstore - # * zrange - # * zrangescore - # * zrank - # * zrem # * zremrangebyrank # * zremrangebyscore - # * zrevrange - # * zrevrangebyscore - # * zscore # * zunionstore def flushdb @data = ExpiringHash.new end @@ -218,17 +210,18 @@ end def hget(key, field) return unless @data[key] fail "Not a hash" unless @data[key].is_a?(Hash) - @data[key][field] + @data[key][field.to_s] end def hdel(key, field) return unless @data[key] fail "Not a hash" unless @data[key].is_a?(Hash) @data[key].delete(field) + remove_key_for_empty_collection(key) end def hkeys(key) case hash = @data[key] when nil then [] @@ -310,19 +303,22 @@ def lrem(key, count, value) fail "Not a list" unless @data[key].is_a?(Array) return unless @data[key] old_size = @data[key].size - if count == 0 - @data[key].delete(value) - old_size - @data[key].size - else - array = count > 0 ? @data[key].dup : @data[key].reverse - count.abs.times{ array.delete_at(array.index(value) || array.length) } - @data[key] = count > 0 ? array.dup : array.reverse - old_size - @data[key].size - end + diff = + if count == 0 + @data[key].delete(value) + old_size - @data[key].size + else + array = count > 0 ? @data[key].dup : @data[key].reverse + count.abs.times{ array.delete_at(array.index(value) || array.length) } + @data[key] = count > 0 ? array.dup : array.reverse + old_size - @data[key].size + end + remove_key_for_empty_collection(key) + diff end def rpush(key, value) @data[key] ||= [] fail "Not a list" unless @data[key].is_a?(Array) @@ -382,28 +378,32 @@ end def sadd(key, value) fail_unless_set(key) case set = @data[key] - when nil then @data[key] = Set.new([value.to_s]) - when Set then set.add(value.to_s) + when nil then @data[key] = Set.new([value.to_s]); true + when Set then !!set.add?(value.to_s) end end def srem(key, value) fail_unless_set(key) - case set = @data[key] - when nil then return - when Set then set.delete(value.to_s) - end + deleted = + case set = @data[key] + when nil then false + when Set then !!set.delete?(value.to_s) + end + + remove_key_for_empty_collection(key) + deleted end def smove(source, destination, value) fail_unless_set(destination) - if elem = self.srem(source, value) - self.sadd(destination, value) - end + result = self.srem(source, value) + self.sadd(destination, value) if result + result end def spop(key) fail_unless_set(key) elem = srandmember(key) @@ -477,22 +477,32 @@ end deleted_count = old_count - @data.keys.size end def setnx(key, value) - set(key, value) unless @data.key?(key) + if exists(key) + false + else + set(key, value) + true + end end def rename(key, new_key) return unless @data[key] @data[new_key] = @data[key] @data.expires[new_key] = @data.expires[key] if @data.expires.include?(key) @data.delete(key) end def renamenx(key, new_key) - rename(key, new_key) unless exists(new_key) + if exists(new_key) + false + else + rename(key, new_key) + true + end end def expire(key, ttl) return unless @data[key] @data.expires[key] = Time.now + ttl @@ -511,38 +521,41 @@ @data.expires[key] = Time.at(timestamp) true end def persist(key) - @data.expires.delete(key) + !!@data.expires.delete(key) end def hset(key, field, value) + field = field.to_s case hash = @data[key] - when nil then @data[key] = { field => value.to_s } - when Hash then hash[field] = value.to_s + when nil then @data[key] = { field => value.to_s }; true + when Hash then result = !hash.include?(field); hash[field] = value.to_s; result else fail "Not a hash" end end def hsetnx(key, field, value) - return if (@data[key][field] rescue false) + field = field.to_s + return false if (@data[key][field] rescue false) hset(key, field, value) end def hmset(key, *fields) @data[key] ||= {} - fail "Not a hash" unless @data[key].is_a?(Hash) + fail "Not a hash" unless @data[key].is_a?(Hash) fields.each_slice(2) do |field| @data[key][field[0].to_s] = field[1].to_s end end def hmget(key, *fields) values = [] fields.each do |field| - case hash = @data[key] + field = field.to_s + case hash = @data[key] when nil then values << nil when Hash then values << hash[field] else fail "Not a hash" end end @@ -565,14 +578,15 @@ end end def hincrby(key, field, increment) case hash = @data[key] - when nil then @data[key] = { field => value.to_s } + when nil then @data[key] = { field => increment.to_s } when Hash then hash[field] = (hash[field].to_i + increment.to_i).to_s else fail "Not a hash" end + @data[key][field].to_i end def hexists(key, field) return unless @data[key] fail "Not a hash" unless @data[key].is_a?(Hash) @@ -602,11 +616,11 @@ old_val.each_slice(8){|b| new_val = new_val + b.join("").to_i(2).chr } @data[key] = new_val end def setex(key, seconds, value) - @data[key] = value + @data[key] = value.to_s expire(key, seconds) end def setrange(key, offset, value) return unless @data[key] @@ -639,25 +653,29 @@ end def incr(key) @data[key] = (@data[key] || "0") @data[key] = (@data[key].to_i + 1).to_s + @data[key].to_i end def incrby(key, by) @data[key] = (@data[key] || "0") @data[key] = (@data[key].to_i + by.to_i).to_s + @data[key].to_i end def decr(key) @data[key] = (@data[key] || "0") @data[key] = (@data[key].to_i - 1).to_s + @data[key].to_i end def decrby(key, by) @data[key] = (@data[key] || "0") @data[key] = (@data[key].to_i - by.to_i).to_s + @data[key].to_i end def type(key) case value = @data[key] when nil then "none" @@ -684,16 +702,151 @@ @buffer = [] yield if block_given? "OK" end + def zadd(key, score, value) + fail_unless_zset(key) + @data[key] ||= ZSet.new + exists = @data[key].key?(value.to_s) + @data[key][value.to_s] = score.to_i + !exists + end + + def zrem(key, value) + fail_unless_zset(key) + exists = false + exists = @data[key].delete(value.to_s) if @data[key] + remove_key_for_empty_collection(key) + !!exists + end + + def zcard(key) + fail_unless_zset(key) + @data[key] ? @data[key].size : 0 + end + + def zscore(key, value) + fail_unless_zset(key) + @data[key] && @data[key][value.to_s].to_s + end + + def zcount(key, min, max) + fail_unless_zset(key) + return 0 unless @data[key] + zrange_select_by_score(key, min, max).size + end + + def zincrby(key, num, value) + fail_unless_zset(key) + @data[key][value.to_s] ||= 0 + @data[key][value.to_s] += num + @data[key][value.to_s].to_s + end + + def zrank(key, value) + fail_unless_zset(key) + @data[key].keys.sort_by {|k| @data[key][k] }.index(value.to_s) + end + + def zrevrank(key, value) + fail_unless_zset(key) + @data[key].keys.sort_by {|k| -@data[key][k] }.index(value.to_s) + end + + def zrange(key, start, stop, with_scores = nil) + fail_unless_zset(key) + return [] unless @data[key] + + if with_scores + @data[key].sort_by {|_,v| v } + else + @data[key].keys.sort_by {|k| @data[key][k] } + end[start..stop].flatten.map(&:to_s) + end + + def zrevrange(key, start, stop, with_scores = nil) + fail_unless_zset(key) + return [] unless @data[key] + + if with_scores + @data[key].sort_by {|_,v| -v } + 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) + fail_unless_zset(key) + return [] unless @data[key] + + range = zrange_select_by_score(key, min, max) + if with_scores + range.sort_by {|_,v| v } + else + range.keys.sort_by {|k| range[k] } + end.flatten.map(&:to_s) + end + + def zrevrangebyscore(key, max, min, with_scores = nil) + fail_unless_zset(key) + return [] unless @data[key] + + range = zrange_select_by_score(key, min, max) + if with_scores + range.sort_by {|_,v| -v } + else + range.keys.sort_by {|k| -range[k] } + end.flatten.map(&:to_s) + end + + def zinterstore(out, _, *keys) + fail_unless_zset(out) + + hashes = keys.map do |src| + case @data[src] + when Set + Hash[@data[src].zip([0] * @data[src].size)] + when Hash + @data[src] + else + {} + end + end + + @data[out] = ZSet.new + values = hashes.inject([]) {|r, h| r.empty? ? h.keys : r & h.keys } + values.each do |value| + @data[out][value] = hashes.inject(0) {|n, h| n + h[value].to_i } + end + + @data[out].size + end + private + def is_a_set?(key) @data[key].is_a?(Set) || @data[key].nil? end def fail_unless_set(key) fail "Not a set" unless is_a_set?(key) + end + + def is_a_zset?(key) + @data[key].is_a?(ZSet) || @data[key].nil? + end + + def fail_unless_zset(key) + fail "Not a sorted set" unless is_a_zset?(key) + end + + 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 end end end