Skip to content

Commit ebc8da6

Browse files
committed
Handle errors in parsing lambdas (#754)
1 parent 6fe2f94 commit ebc8da6

File tree

2 files changed

+157
-108
lines changed

2 files changed

+157
-108
lines changed

lib/redis.rb

Lines changed: 113 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
require "monitor"
24
require_relative "redis/errors"
35

@@ -3309,131 +3311,135 @@ def method_missing(command, *args)
33093311
# Commands returning 1 for true and 0 for false may be executed in a pipeline
33103312
# where the method call will return nil. Propagate the nil instead of falsely
33113313
# returning false.
3312-
Boolify =
3313-
lambda { |value|
3314-
value == 1 if value
3315-
}
3314+
Boolify = lambda { |value|
3315+
case value
3316+
when 1
3317+
true
3318+
when 0
3319+
false
3320+
else
3321+
value
3322+
end
3323+
}
33163324

3317-
BoolifySet =
3318-
lambda { |value|
3319-
if value && "OK" == value
3320-
true
3321-
else
3322-
false
3323-
end
3324-
}
3325+
BoolifySet = lambda { |value|
3326+
case value
3327+
when "OK"
3328+
true
3329+
when nil
3330+
false
3331+
else
3332+
value
3333+
end
3334+
}
33253335

3326-
Hashify =
3327-
lambda { |array|
3328-
hash = Hash.new
3329-
array.each_slice(2) do |field, value|
3330-
hash[field] = value
3331-
end
3332-
hash
3333-
}
3336+
Hashify = lambda { |value|
3337+
if value.respond_to?(:each_slice)
3338+
value.each_slice(2).to_h
3339+
else
3340+
value
3341+
end
3342+
}
3343+
3344+
Floatify = lambda { |value|
3345+
case value
3346+
when "inf"
3347+
Float::INFINITY
3348+
when "-inf"
3349+
-Float::INFINITY
3350+
when String
3351+
Float(value)
3352+
else
3353+
value
3354+
end
3355+
}
33343356

3335-
Floatify =
3336-
lambda { |str|
3337-
if str
3338-
if (inf = str.match(/^(-)?inf/i))
3339-
(inf[1] ? -1.0 : 1.0) / 0.0
3340-
else
3341-
Float(str)
3342-
end
3343-
end
3344-
}
3357+
FloatifyPairs = lambda { |value|
3358+
return value unless value.respond_to?(:each_slice)
33453359

3346-
FloatifyPairs =
3347-
lambda { |result|
3348-
result.each_slice(2).map do |member, score|
3349-
[member, Floatify.call(score)]
3350-
end
3351-
}
3360+
value.each_slice(2).map do |member, score|
3361+
[member, Floatify.call(score)]
3362+
end
3363+
}
33523364

3353-
HashifyInfo =
3354-
lambda { |reply|
3355-
Hash[reply.split("\r\n").map do |line|
3356-
line.split(':', 2) unless line =~ /^(#|$)/
3357-
end.compact]
3358-
}
3365+
HashifyInfo = lambda { |reply|
3366+
lines = reply.split("\r\n").grep_v(/^(#|$)/)
3367+
lines.map! { |line| line.split(':', 2) }
3368+
lines.compact!
3369+
lines.to_h
3370+
}
33593371

3360-
HashifyStreams =
3361-
lambda { |reply|
3362-
return {} if reply.nil?
3363-
reply.map do |stream_key, entries|
3364-
[stream_key, HashifyStreamEntries.call(entries)]
3365-
end.to_h
3366-
}
3372+
HashifyStreams = lambda { |reply|
3373+
case reply
3374+
when nil
3375+
{}
3376+
else
3377+
reply.map { |key, entries| [key, HashifyStreamEntries.call(entries)] }.to_h
3378+
end
3379+
}
33673380

3368-
HashifyStreamEntries =
3369-
lambda { |reply|
3370-
reply.map do |entry_id, values|
3371-
[entry_id, values.each_slice(2).to_h]
3372-
end
3381+
HashifyStreamEntries = lambda { |reply|
3382+
reply.map do |entry_id, values|
3383+
[entry_id, values.each_slice(2).to_h]
3384+
end
3385+
}
3386+
3387+
HashifyStreamPendings = lambda { |reply|
3388+
{
3389+
'size' => reply[0],
3390+
'min_entry_id' => reply[1],
3391+
'max_entry_id' => reply[2],
3392+
'consumers' => reply[3].nil? ? {} : reply[3].to_h
33733393
}
3394+
}
33743395

3375-
HashifyStreamPendings =
3376-
lambda { |reply|
3396+
HashifyStreamPendingDetails = lambda { |reply|
3397+
reply.map do |arr|
33773398
{
3378-
'size' => reply[0],
3379-
'min_entry_id' => reply[1],
3380-
'max_entry_id' => reply[2],
3381-
'consumers' => reply[3].nil? ? {} : Hash[reply[3]]
3399+
'entry_id' => arr[0],
3400+
'consumer' => arr[1],
3401+
'elapsed' => arr[2],
3402+
'count' => arr[3]
33823403
}
3383-
}
3404+
end
3405+
}
33843406

3385-
HashifyStreamPendingDetails =
3386-
lambda { |reply|
3387-
reply.map do |arr|
3388-
{
3389-
'entry_id' => arr[0],
3390-
'consumer' => arr[1],
3391-
'elapsed' => arr[2],
3392-
'count' => arr[3]
3393-
}
3394-
end
3407+
HashifyClusterNodeInfo = lambda { |str|
3408+
arr = str.split(' ')
3409+
{
3410+
'node_id' => arr[0],
3411+
'ip_port' => arr[1],
3412+
'flags' => arr[2].split(','),
3413+
'master_node_id' => arr[3],
3414+
'ping_sent' => arr[4],
3415+
'pong_recv' => arr[5],
3416+
'config_epoch' => arr[6],
3417+
'link_state' => arr[7],
3418+
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
33953419
}
3420+
}
33963421

3397-
HashifyClusterNodeInfo =
3398-
lambda { |str|
3399-
arr = str.split(' ')
3422+
HashifyClusterSlots = lambda { |reply|
3423+
reply.map do |arr|
3424+
first_slot, last_slot = arr[0..1]
3425+
master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
3426+
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
34003427
{
3401-
'node_id' => arr[0],
3402-
'ip_port' => arr[1],
3403-
'flags' => arr[2].split(','),
3404-
'master_node_id' => arr[3],
3405-
'ping_sent' => arr[4],
3406-
'pong_recv' => arr[5],
3407-
'config_epoch' => arr[6],
3408-
'link_state' => arr[7],
3409-
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
3428+
'start_slot' => first_slot,
3429+
'end_slot' => last_slot,
3430+
'master' => master,
3431+
'replicas' => replicas
34103432
}
3411-
}
3412-
3413-
HashifyClusterSlots =
3414-
lambda { |reply|
3415-
reply.map do |arr|
3416-
first_slot, last_slot = arr[0..1]
3417-
master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
3418-
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
3419-
{
3420-
'start_slot' => first_slot,
3421-
'end_slot' => last_slot,
3422-
'master' => master,
3423-
'replicas' => replicas
3424-
}
3425-
end
3426-
}
3433+
end
3434+
}
34273435

3428-
HashifyClusterNodes =
3429-
lambda { |reply|
3430-
reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
3431-
}
3436+
HashifyClusterNodes = lambda { |reply|
3437+
reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
3438+
}
34323439

3433-
HashifyClusterSlaves =
3434-
lambda { |reply|
3435-
reply.map { |str| HashifyClusterNodeInfo.call(str) }
3436-
}
3440+
HashifyClusterSlaves = lambda { |reply|
3441+
reply.map { |str| HashifyClusterNodeInfo.call(str) }
3442+
}
34373443

34383444
Noop = ->(reply) { reply }
34393445

test/transactions_test.rb

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def test_transformed_replies_inside_multi_exec_block
102102
assert @info.value.kind_of?(Hash)
103103
end
104104

105-
def test_raise_command_errors_in_multi_exec
105+
def test_raise_command_errors_when_reply_is_not_transformed
106106
assert_raise(Redis::CommandError) do
107107
r.multi do |m|
108108
m.set("foo", "s1")
@@ -124,6 +124,49 @@ def test_empty_multi_exec
124124
assert_equal [], result
125125
end
126126

127+
def test_raise_command_errors_when_reply_is_transformed_from_int_to_boolean
128+
assert_raise(Redis::CommandError) do
129+
r.multi do |m|
130+
m.set("foo", 1)
131+
m.sadd("foo", 2)
132+
end
133+
end
134+
end
135+
136+
def test_raise_command_errors_when_reply_is_transformed_from_ok_to_boolean
137+
assert_raise(Redis::CommandError) do
138+
r.multi do |m|
139+
m.set("foo", 1, ex: 0, nx: true)
140+
end
141+
end
142+
end
143+
144+
def test_raise_command_errors_when_reply_is_transformed_to_float
145+
assert_raise(Redis::CommandError) do
146+
r.multi do |m|
147+
m.set("foo", 1)
148+
m.zscore("foo", "b")
149+
end
150+
end
151+
end
152+
153+
def test_raise_command_errors_when_reply_is_transformed_to_floats
154+
assert_raise(Redis::CommandError) do
155+
r.multi do |m|
156+
m.zrange("a", "b", 5, :with_scores => true)
157+
end
158+
end
159+
end
160+
161+
def test_raise_command_errors_when_reply_is_transformed_to_hash
162+
assert_raise(Redis::CommandError) do
163+
r.multi do |m|
164+
m.set("foo", 1)
165+
m.hgetall("foo")
166+
end
167+
end
168+
end
169+
127170
def test_raise_command_errors_when_accessing_futures_after_multi_exec
128171
begin
129172
r.multi do |m|

0 commit comments

Comments
 (0)