Skip to content

Commit b2a8529

Browse files
committed
Formalize Redis::Client's public API.
1 parent 1ffdaa9 commit b2a8529

File tree

2 files changed

+129
-12
lines changed

2 files changed

+129
-12
lines changed

lib/redis/client.rb

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,28 @@
33
require "cgi"
44

55
class Redis
6+
# Redis::Client handles the connection to Redis, sending and receiving commands.
7+
#
8+
# Usage:
9+
#
10+
# client = Redis::Client.new
11+
#
12+
# client.call(["PING"])
13+
# => "PONG"
14+
#
15+
# client.call(["SET", "key", "value"])
16+
# => "OK"
17+
#
18+
# client.call(["INCR", "key"])
19+
# => 1
20+
#
21+
# It provides an API for command pipelining:
22+
#
23+
# client.queue(["SET", "key", "value"])
24+
# client.queue(["GET", "key"])
25+
# client.commit
26+
# => ["OK", "value"]
27+
#
628
class Client
729

830
DEFAULTS = {
@@ -84,6 +106,47 @@ def initialize(options = {})
84106
else
85107
@connector = Connector.new(@options)
86108
end
109+
110+
@queue = []
111+
end
112+
113+
# Sends a command to Redis and returns its reply.
114+
#
115+
# Replies are converted to Ruby objects according to the RESP protocol, so
116+
# you can expect a Ruby array, integer or nil when Redis sends one. Higher
117+
# level transformations, such as converting an array of pairs into a Ruby
118+
# hash, are up to consumers.
119+
#
120+
# Redis error replies are raised as Ruby exceptions.
121+
def call(command, &block)
122+
reply = process([command]) { read }
123+
raise reply if reply.is_a?(CommandError)
124+
125+
if block
126+
block.call(reply)
127+
else
128+
reply
129+
end
130+
end
131+
132+
# Queues a command for pipelining.
133+
#
134+
# Commands in the queue are executed with the Redis::Client#commit method.
135+
#
136+
# See http://redis.io/topics/pipelining for more details.
137+
#
138+
def queue(command)
139+
@queue << command
140+
end
141+
142+
# Sends all commands in the queue.
143+
#
144+
# See http://redis.io/topics/pipelining for more details.
145+
#
146+
def commit
147+
call_pipelined(@queue)
148+
ensure
149+
@queue.clear
87150
end
88151

89152
def connect
@@ -108,17 +171,6 @@ def location
108171
path || "#{host}:#{port}"
109172
end
110173

111-
def call(command, &block)
112-
reply = process([command]) { read }
113-
raise reply if reply.is_a?(CommandError)
114-
115-
if block
116-
block.call(reply)
117-
else
118-
reply
119-
end
120-
end
121-
122174
def call_loop(command)
123175
error = nil
124176

@@ -174,15 +226,21 @@ def call_pipelined(commands)
174226
reconnect = @reconnect
175227

176228
begin
229+
exception = nil
230+
177231
process(commands) do
178232
result[0] = read
179233

180234
@reconnect = false
181235

182236
(commands.size - 1).times do |i|
183-
result[i + 1] = read
237+
reply = read
238+
result[i + 1] = reply
239+
exception = reply if exception.nil? && reply.is_a?(CommandError)
184240
end
185241
end
242+
243+
raise exception if exception
186244
ensure
187245
@reconnect = reconnect
188246
end

test/client_test.rb

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
require_relative "helper"
2+
3+
class TestClient < Test::Unit::TestCase
4+
5+
include Helper::Client
6+
7+
def test_call
8+
result = r.client.call(["PING"])
9+
assert_equal result, "PONG"
10+
end
11+
12+
def test_call_with_arguments
13+
result = r.client.call(["SET", "foo", "bar"])
14+
assert_equal result, "OK"
15+
end
16+
17+
def test_call_integers
18+
result = r.client.call(["INCR", "foo"])
19+
assert_equal result, 1
20+
end
21+
22+
def test_call_raise
23+
assert_raises(Redis::CommandError) do
24+
r.client.call(["INCR"])
25+
end
26+
end
27+
28+
def test_queue_commit
29+
r.client.queue(["SET", "foo", "bar"])
30+
r.client.queue(["GET", "foo"])
31+
result = r.client.commit
32+
33+
assert_equal result, ["OK", "bar"]
34+
end
35+
36+
def test_commit_raise
37+
r.client.queue(["SET", "foo", "bar"])
38+
r.client.queue(["INCR"])
39+
40+
assert_raise(Redis::CommandError) do
41+
r.client.commit
42+
end
43+
end
44+
45+
def test_queue_after_error
46+
r.client.queue(["SET", "foo", "bar"])
47+
r.client.queue(["INCR"])
48+
49+
assert_raise(Redis::CommandError) do
50+
r.client.commit
51+
end
52+
53+
r.client.queue(["SET", "foo", "bar"])
54+
r.client.queue(["INCR", "baz"])
55+
result = r.client.commit
56+
57+
assert_equal result, ["OK", 1]
58+
end
59+
end

0 commit comments

Comments
 (0)