lib/redis/connection/memory.rb in fakeredis-0.4.2 vs lib/redis/connection/memory.rb in fakeredis-0.4.3
- old
+ new
@@ -75,11 +75,11 @@
def timeout=(usecs)
end
def write(command)
- meffod = command.shift
+ meffod = command.shift.to_s.downcase.to_sym
if respond_to?(meffod)
reply = send(meffod, *command)
else
raise Redis::CommandError, "ERR unknown command '#{meffod}'"
end
@@ -129,11 +129,11 @@
"OK"
end
def info
{
- "redis_version" => "0.07",
+ "redis_version" => "2.6.16",
"connected_clients" => "1",
"connected_slaves" => "0",
"used_memory" => "3187",
"changes_since_last_save" => "0",
"last_save_time" => "1237655729",
@@ -183,11 +183,11 @@
set(key, value)
end
end
def mget(*keys)
- raise Redis::CommandError, "wrong number of arguments for 'mget' command" if keys.empty?
+ raise_argument_error('mget') if keys.empty?
# We work with either an array, or list of arguments
keys = keys.first if keys.size == 1
data.values_at(*keys)
end
@@ -210,10 +210,11 @@
data_type_check(key, Hash)
data[key] && data[key][field.to_s]
end
def hdel(key, field)
+ field = field.to_s
data_type_check(key, Hash)
data[key] && data[key].delete(field)
remove_key_for_empty_collection(key)
end
@@ -222,12 +223,11 @@
return [] if data[key].nil?
data[key].keys
end
def keys(pattern = "*")
- regexp = Regexp.new(pattern.split("*").map { |r| Regexp.escape(r) }.join(".*"))
- data.keys.select { |key| key =~ regexp }
+ data.keys.select { |key| File.fnmatch(pattern, key) }
end
def randomkey
data.keys[rand(dbsize)]
end
@@ -264,11 +264,22 @@
end
def ltrim(key, start, stop)
data_type_check(key, Array)
return unless data[key]
- data[key] = data[key][start..stop]
+
+ if start < 0 && data[key].count < start.abs
+ # Example: we have a list of 3 elements and
+ # we give it a ltrim list, -5, -1. This means
+ # it should trim to a max of 5. Since 3 < 5
+ # we should not touch the list. This is consistent
+ # with behavior of real Redis's ltrim with a negative
+ # start argument.
+ data[key]
+ else
+ data[key] = data[key][start..stop]
+ end
end
def lindex(key, index)
data_type_check(key, Array)
data[key] && data[key][index]
@@ -279,18 +290,18 @@
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 Redis::CommandError, "ERR syntax error"
+ else raise_syntax_error
end
end
def lset(key, index, value)
data_type_check(key, Array)
return unless data[key]
- raise(Redis::CommandError, "ERR index out of range") if index >= data[key].size
+ raise Redis::CommandError, "ERR index out of range" if index >= data[key].size
data[key][index] = value
end
def lrem(key, count, value)
data_type_check(key, Array)
@@ -372,10 +383,11 @@
end
def sadd(key, value)
data_type_check(key, ::Set)
value = Array(value)
+ raise_argument_error('sadd') if value.empty?
result = if data[key]
old_set = data[key].dup
data[key].merge(value.map(&:to_s))
(data[key] - old_set).size
@@ -415,10 +427,12 @@
return 0 unless data[key]
data[key].size
end
def sinter(*keys)
+ raise_argument_error('sinter') if keys.empty?
+
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
@@ -446,11 +460,11 @@
end
def sdiff(key1, *keys)
[key1, *keys].each { |k| data_type_check(k, ::Set) }
keys = keys.map { |k| data[k] || ::Set.new }
- keys.inject(data[key1]) do |memo, set|
+ keys.inject(data[key1] || Set.new) do |memo, set|
memo - set
end.to_a
end
def sdiffstore(destination, key1, *keys)
@@ -465,11 +479,12 @@
data[key].to_a[rand(data[key].size)]
end
def del(*keys)
keys = keys.flatten(1)
- raise Redis::CommandError, "ERR wrong number of arguments for 'del' command" if keys.empty?
+ raise_argument_error('del') if keys.empty?
+
old_count = data.keys.size
keys.each do |key|
data.delete(key)
end
old_count - data.keys.size
@@ -508,11 +523,11 @@
def ttl(key)
if data.expires.include?(key) && (ttl = data.expires[key].to_i - Time.now.to_i) > 0
ttl
else
- -1
+ exists(key) ? -1 : -2
end
end
def expireat(key, timestamp)
data.expires[key] = Time.at(timestamp)
@@ -544,20 +559,34 @@
end
def hmset(key, *fields)
# mapped_hmset gives us [[:k1, "v1", :k2, "v2"]] for `fields`. Fix that.
fields = fields[0] if mapped_param?(fields)
- raise Redis::CommandError, "ERR wrong number of arguments for HMSET" if fields.empty? || fields.size.odd?
+ raise_argument_error('hmset') if fields.empty?
+
+ is_list_of_arrays = fields.all?{|field| field.instance_of?(Array)}
+
+ raise_argument_error('hmset') if fields.size.odd? and !is_list_of_arrays
+ raise_argument_error('hmset') if is_list_of_arrays and !fields.all?{|field| field.length == 2}
+
data_type_check(key, Hash)
data[key] ||= {}
- fields.each_slice(2) do |field|
- data[key][field[0].to_s] = field[1].to_s
+
+ if is_list_of_arrays
+ fields.each do |pair|
+ data[key][pair[0].to_s] = pair[1].to_s
+ end
+ else
+ fields.each_slice(2) do |field|
+ data[key][field[0].to_s] = field[1].to_s
+ end
end
end
def hmget(key, *fields)
- raise Redis::CommandError, "wrong number of arguments for 'hmget' command" if fields.empty?
+ raise_argument_error('hmget') if fields.empty?
+
data_type_check(key, Hash)
fields.map do |field|
field = field.to_s
if data[key]
data[key][field]
@@ -579,22 +608,23 @@
data[key].values
end
def hincrby(key, field, increment)
data_type_check(key, Hash)
+ field = field.to_s
if data[key]
- data[key][field] = (data[key][field.to_s].to_i + increment.to_i).to_s
+ data[key][field] = (data[key][field].to_i + increment.to_i).to_s
else
data[key] = { field => increment.to_s }
end
data[key][field].to_i
end
def hexists(key, field)
data_type_check(key, Hash)
return false unless data[key]
- data[key].key?(field)
+ data[key].key?(field.to_s)
end
def sync ; end
def [](key)
@@ -623,10 +653,11 @@
end
def setex(key, seconds, value)
data[key] = value.to_s
expire(key, seconds)
+ "OK"
end
def setrange(key, offset, value)
return unless data[key]
s = data[key][offset,value.size]
@@ -634,10 +665,14 @@
end
def mset(*pairs)
# Handle pairs for mapped_mset command
pairs = pairs[0] if mapped_param?(pairs)
+ raise_argument_error('mset') if pairs.empty? || pairs.size == 1
+ # We have to reply with a different error message here to be consistent with redis-rb 3.0.6 / redis-server 2.8.1
+ raise_argument_error("mset", "mset_odd") if pairs.size.odd?
+
pairs.each_slice(2) do |pair|
data[pair[0].to_s] = pair[1].to_s
end
"OK"
end
@@ -678,10 +713,11 @@
def type(key)
case data[key]
when nil then "none"
when String then "string"
+ when ZSet then "zset"
when Hash then "hash"
when Array then "list"
when ::Set then "set"
end
end
@@ -711,24 +747,24 @@
end
def zadd(key, *args)
if !args.first.is_a?(Array)
if args.size < 2
- raise Redis::CommandError, "ERR wrong number of arguments for 'zadd' command"
+ raise_argument_error('zadd')
elsif args.size.odd?
- raise Redis::CommandError, "ERR syntax error"
+ raise_syntax_error
end
else
unless args.all? {|pair| pair.size == 2 }
- raise(Redis::CommandError, "ERR syntax error")
+ raise_syntax_error
end
end
data_type_check(key, ZSet)
data[key] ||= ZSet.new
- if args.size == 2
+ if args.size == 2 && !(Array === args.first)
score, value = args
exists = !data[key].key?(value.to_s)
data[key][value.to_s] = score
else
# Turn [1, 2, 3, 4] into [[1, 2], [3, 4]] unless it is already
@@ -744,11 +780,11 @@
data_type_check(key, ZSet)
values = Array(value)
return 0 unless data[key]
response = values.map do |v|
- data[key].delete(v) if data[key].has_key?(v)
+ data[key].delete(v.to_s) if data[key].has_key?(v.to_s)
end.compact.size
remove_key_for_empty_collection(key)
response
end
@@ -778,16 +814,20 @@
data[key][value.to_s].to_s
end
def zrank(key, value)
data_type_check(key, ZSet)
- data[key].keys.sort_by {|k| data[key][k] }.index(value.to_s)
+ z = data[key]
+ return unless z
+ z.keys.sort_by {|k| z[k] }.index(value.to_s)
end
def zrevrank(key, value)
data_type_check(key, ZSet)
- data[key].keys.sort_by {|k| -data[key][k] }.index(value.to_s)
+ z = data[key]
+ return unless z
+ z.keys.sort_by {|k| -z[k] }.index(value.to_s)
end
def zrange(key, start, stop, with_scores = nil)
data_type_check(key, ZSet)
return [] unless data[key]
@@ -832,10 +872,11 @@
vals.flatten.map(&:to_s)
end
def zrevrangebyscore(key, max, min, *opts)
+ opts = opts.flatten
data_type_check(key, ZSet)
return [] unless data[key]
range = data[key].select_by_score(min, max)
vals = if opts.include?('WITHSCORES')
@@ -880,18 +921,31 @@
elements_to_delete.each { |elem, rank| data[key].delete(elem) }
elements_to_delete.size
end
private
+ def raise_argument_error(command, match_string=command)
+ error_message = if %w(hmset mset_odd).include?(match_string.downcase)
+ "ERR wrong number of arguments for #{command.upcase}"
+ else
+ "ERR wrong number of arguments for '#{command}' command"
+ end
+ raise Redis::CommandError, error_message
+ end
+
+ def raise_syntax_error
+ raise Redis::CommandError, "ERR syntax error"
+ 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)
warn "Operation against a key holding the wrong kind of value: Expected #{klass} at #{key}."
- raise Redis::CommandError.new("ERR Operation against a key holding the wrong kind of value")
+ raise Redis::CommandError.new("WRONGTYPE Operation against a key holding the wrong kind of value")
end
end
def get_limit(opts, vals)
index = opts.index('LIMIT')