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