discourse-redis 3.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.travis.yml +59 -0
- data/.travis/Gemfile +11 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +349 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +328 -0
- data/Rakefile +87 -0
- data/benchmarking/logging.rb +71 -0
- data/benchmarking/pipeline.rb +51 -0
- data/benchmarking/speed.rb +21 -0
- data/benchmarking/suite.rb +24 -0
- data/benchmarking/worker.rb +71 -0
- data/examples/basic.rb +15 -0
- data/examples/consistency.rb +114 -0
- data/examples/dist_redis.rb +43 -0
- data/examples/incr-decr.rb +17 -0
- data/examples/list.rb +26 -0
- data/examples/pubsub.rb +37 -0
- data/examples/sentinel.rb +41 -0
- data/examples/sentinel/start +49 -0
- data/examples/sets.rb +36 -0
- data/examples/unicorn/config.ru +3 -0
- data/examples/unicorn/unicorn.rb +20 -0
- data/lib/redis.rb +2731 -0
- data/lib/redis/client.rb +575 -0
- data/lib/redis/connection.rb +9 -0
- data/lib/redis/connection/command_helper.rb +44 -0
- data/lib/redis/connection/hiredis.rb +64 -0
- data/lib/redis/connection/registry.rb +12 -0
- data/lib/redis/connection/ruby.rb +322 -0
- data/lib/redis/connection/synchrony.rb +124 -0
- data/lib/redis/distributed.rb +873 -0
- data/lib/redis/errors.rb +40 -0
- data/lib/redis/hash_ring.rb +132 -0
- data/lib/redis/pipeline.rb +141 -0
- data/lib/redis/subscribe.rb +83 -0
- data/lib/redis/version.rb +3 -0
- data/redis.gemspec +34 -0
- data/test/bitpos_test.rb +69 -0
- data/test/blocking_commands_test.rb +42 -0
- data/test/command_map_test.rb +30 -0
- data/test/commands_on_hashes_test.rb +21 -0
- data/test/commands_on_hyper_log_log_test.rb +21 -0
- data/test/commands_on_lists_test.rb +20 -0
- data/test/commands_on_sets_test.rb +77 -0
- data/test/commands_on_sorted_sets_test.rb +137 -0
- data/test/commands_on_strings_test.rb +101 -0
- data/test/commands_on_value_types_test.rb +133 -0
- data/test/connection_handling_test.rb +250 -0
- data/test/distributed_blocking_commands_test.rb +46 -0
- data/test/distributed_commands_on_hashes_test.rb +10 -0
- data/test/distributed_commands_on_hyper_log_log_test.rb +33 -0
- data/test/distributed_commands_on_lists_test.rb +22 -0
- data/test/distributed_commands_on_sets_test.rb +83 -0
- data/test/distributed_commands_on_sorted_sets_test.rb +18 -0
- data/test/distributed_commands_on_strings_test.rb +59 -0
- data/test/distributed_commands_on_value_types_test.rb +95 -0
- data/test/distributed_commands_requiring_clustering_test.rb +164 -0
- data/test/distributed_connection_handling_test.rb +23 -0
- data/test/distributed_internals_test.rb +79 -0
- data/test/distributed_key_tags_test.rb +52 -0
- data/test/distributed_persistence_control_commands_test.rb +26 -0
- data/test/distributed_publish_subscribe_test.rb +92 -0
- data/test/distributed_remote_server_control_commands_test.rb +66 -0
- data/test/distributed_scripting_test.rb +102 -0
- data/test/distributed_sorting_test.rb +20 -0
- data/test/distributed_test.rb +58 -0
- data/test/distributed_transactions_test.rb +32 -0
- data/test/encoding_test.rb +18 -0
- data/test/error_replies_test.rb +59 -0
- data/test/fork_safety_test.rb +65 -0
- data/test/helper.rb +232 -0
- data/test/helper_test.rb +24 -0
- data/test/internals_test.rb +437 -0
- data/test/lint/blocking_commands.rb +150 -0
- data/test/lint/hashes.rb +162 -0
- data/test/lint/hyper_log_log.rb +60 -0
- data/test/lint/lists.rb +143 -0
- data/test/lint/sets.rb +125 -0
- data/test/lint/sorted_sets.rb +316 -0
- data/test/lint/strings.rb +260 -0
- data/test/lint/value_types.rb +122 -0
- data/test/persistence_control_commands_test.rb +26 -0
- data/test/pipelining_commands_test.rb +242 -0
- data/test/publish_subscribe_test.rb +254 -0
- data/test/remote_server_control_commands_test.rb +118 -0
- data/test/scanning_test.rb +413 -0
- data/test/scripting_test.rb +78 -0
- data/test/sentinel_command_test.rb +80 -0
- data/test/sentinel_test.rb +255 -0
- data/test/sorting_test.rb +59 -0
- data/test/support/connection/hiredis.rb +1 -0
- data/test/support/connection/ruby.rb +1 -0
- data/test/support/connection/synchrony.rb +17 -0
- data/test/support/redis_mock.rb +119 -0
- data/test/support/wire/synchrony.rb +24 -0
- data/test/support/wire/thread.rb +5 -0
- data/test/synchrony_driver.rb +88 -0
- data/test/test.conf.erb +9 -0
- data/test/thread_safety_test.rb +32 -0
- data/test/transactions_test.rb +264 -0
- data/test/unknown_commands_test.rb +14 -0
- data/test/url_param_test.rb +138 -0
- metadata +182 -0
@@ -0,0 +1 @@
|
|
1
|
+
require "support/wire/thread"
|
@@ -0,0 +1 @@
|
|
1
|
+
require "support/wire/thread"
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require "socket"
|
2
|
+
|
3
|
+
module RedisMock
|
4
|
+
class Server
|
5
|
+
def initialize(options = {}, &block)
|
6
|
+
@server = TCPServer.new(options[:host] || "127.0.0.1", 0)
|
7
|
+
@server.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
|
8
|
+
end
|
9
|
+
|
10
|
+
def port
|
11
|
+
@server.addr[1]
|
12
|
+
end
|
13
|
+
|
14
|
+
def start(&block)
|
15
|
+
@thread = Thread.new { run(&block) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def shutdown
|
19
|
+
@thread.kill
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
begin
|
24
|
+
loop do
|
25
|
+
session = @server.accept
|
26
|
+
|
27
|
+
begin
|
28
|
+
return if yield(session) == :exit
|
29
|
+
ensure
|
30
|
+
session.close
|
31
|
+
end
|
32
|
+
end
|
33
|
+
rescue => ex
|
34
|
+
$stderr.puts "Error running mock server: #{ex.message}"
|
35
|
+
$stderr.puts ex.backtrace
|
36
|
+
retry
|
37
|
+
ensure
|
38
|
+
@server.close
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Starts a mock Redis server in a thread.
|
44
|
+
#
|
45
|
+
# The server will use the lambda handler passed as argument to handle
|
46
|
+
# connections. For example:
|
47
|
+
#
|
48
|
+
# handler = lambda { |session| session.close }
|
49
|
+
# RedisMock.start_with_handler(handler) do
|
50
|
+
# # Every connection will be closed immediately
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
def self.start_with_handler(blk, options = {})
|
54
|
+
server = Server.new(options)
|
55
|
+
port = server.port
|
56
|
+
|
57
|
+
begin
|
58
|
+
server.start(&blk)
|
59
|
+
yield(port)
|
60
|
+
ensure
|
61
|
+
server.shutdown
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Starts a mock Redis server in a thread.
|
66
|
+
#
|
67
|
+
# The server will reply with a `+OK` to all commands, but you can
|
68
|
+
# customize it by providing a hash. For example:
|
69
|
+
#
|
70
|
+
# RedisMock.start(:ping => lambda { "+PONG" }) do |port|
|
71
|
+
# assert_equal "PONG", Redis.new(:port => port).ping
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
def self.start(commands, options = {}, &blk)
|
75
|
+
handler = lambda do |session|
|
76
|
+
while line = session.gets
|
77
|
+
argv = Array.new(line[1..-3].to_i) do
|
78
|
+
bytes = session.gets[1..-3].to_i
|
79
|
+
arg = session.read(bytes)
|
80
|
+
session.read(2) # Discard \r\n
|
81
|
+
arg
|
82
|
+
end
|
83
|
+
|
84
|
+
command = argv.shift
|
85
|
+
blk = commands[command.to_sym]
|
86
|
+
blk ||= lambda { |*_| "+OK" }
|
87
|
+
|
88
|
+
response = blk.call(*argv)
|
89
|
+
|
90
|
+
# Convert a nil response to :close
|
91
|
+
response ||= :close
|
92
|
+
|
93
|
+
if response == :exit
|
94
|
+
break :exit
|
95
|
+
elsif response == :close
|
96
|
+
break :close
|
97
|
+
elsif response.is_a?(Array)
|
98
|
+
session.write("*%d\r\n" % response.size)
|
99
|
+
|
100
|
+
response.each do |resp|
|
101
|
+
if resp.is_a?(Array)
|
102
|
+
session.write("*%d\r\n" % resp.size)
|
103
|
+
resp.each do |r|
|
104
|
+
session.write("$%d\r\n%s\r\n" % [r.length, r])
|
105
|
+
end
|
106
|
+
else
|
107
|
+
session.write("$%d\r\n%s\r\n" % [resp.length, resp])
|
108
|
+
end
|
109
|
+
end
|
110
|
+
else
|
111
|
+
session.write(response)
|
112
|
+
session.write("\r\n") unless response.end_with?("\r\n")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
start_with_handler(handler, options, &blk)
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Wire < Fiber
|
2
|
+
# We cannot run this fiber explicitly because EM schedules it. Resuming the
|
3
|
+
# current fiber on the next tick to let the reactor do work.
|
4
|
+
def self.pass
|
5
|
+
f = Fiber.current
|
6
|
+
EM.next_tick { f.resume }
|
7
|
+
Fiber.yield
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.sleep(sec)
|
11
|
+
EM::Synchrony.sleep(sec)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(&blk)
|
15
|
+
super
|
16
|
+
|
17
|
+
# Schedule run in next tick
|
18
|
+
EM.next_tick { resume }
|
19
|
+
end
|
20
|
+
|
21
|
+
def join
|
22
|
+
self.class.pass while alive?
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'em-synchrony'
|
4
|
+
require 'em-synchrony/connection_pool'
|
5
|
+
|
6
|
+
require 'redis'
|
7
|
+
require 'redis/connection/synchrony'
|
8
|
+
|
9
|
+
|
10
|
+
require File.expand_path("./helper", File.dirname(__FILE__))
|
11
|
+
|
12
|
+
PORT = 6381
|
13
|
+
OPTIONS = {:port => PORT, :db => 15}
|
14
|
+
|
15
|
+
#
|
16
|
+
# if running under Eventmachine + Synchrony (Ruby 1.9+), then
|
17
|
+
# we can simulate the blocking API while performing the network
|
18
|
+
# IO via the EM reactor.
|
19
|
+
#
|
20
|
+
|
21
|
+
EM.synchrony do
|
22
|
+
r = Redis.new OPTIONS
|
23
|
+
r.flushdb
|
24
|
+
|
25
|
+
r.rpush "foo", "s1"
|
26
|
+
r.rpush "foo", "s2"
|
27
|
+
|
28
|
+
assert_equal 2, r.llen("foo")
|
29
|
+
assert_equal "s2", r.rpop("foo")
|
30
|
+
|
31
|
+
r.set("foo", "bar")
|
32
|
+
|
33
|
+
assert_equal "bar", r.getset("foo", "baz")
|
34
|
+
assert_equal "baz", r.get("foo")
|
35
|
+
|
36
|
+
r.set("foo", "a")
|
37
|
+
|
38
|
+
assert_equal 1, r.getbit("foo", 1)
|
39
|
+
assert_equal 1, r.getbit("foo", 2)
|
40
|
+
assert_equal 0, r.getbit("foo", 3)
|
41
|
+
assert_equal 0, r.getbit("foo", 4)
|
42
|
+
assert_equal 0, r.getbit("foo", 5)
|
43
|
+
assert_equal 0, r.getbit("foo", 6)
|
44
|
+
assert_equal 1, r.getbit("foo", 7)
|
45
|
+
|
46
|
+
r.flushdb
|
47
|
+
|
48
|
+
# command pipelining
|
49
|
+
r.pipelined do
|
50
|
+
r.lpush "foo", "s1"
|
51
|
+
r.lpush "foo", "s2"
|
52
|
+
end
|
53
|
+
|
54
|
+
assert_equal 2, r.llen("foo")
|
55
|
+
assert_equal "s2", r.lpop("foo")
|
56
|
+
assert_equal "s1", r.lpop("foo")
|
57
|
+
|
58
|
+
assert_equal "OK", r.client.call(:quit)
|
59
|
+
assert_equal "PONG", r.ping
|
60
|
+
|
61
|
+
|
62
|
+
rpool = EM::Synchrony::ConnectionPool.new(size: 5) { Redis.new OPTIONS }
|
63
|
+
|
64
|
+
result = rpool.watch 'foo' do |rd|
|
65
|
+
assert_kind_of Redis, rd
|
66
|
+
|
67
|
+
rd.set "foo", "s1"
|
68
|
+
rd.multi do |multi|
|
69
|
+
multi.set "foo", "s2"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
assert_equal nil, result
|
74
|
+
assert_equal "s1", rpool.get("foo")
|
75
|
+
|
76
|
+
result = rpool.watch "foo" do |rd|
|
77
|
+
assert_kind_of Redis, rd
|
78
|
+
|
79
|
+
rd.multi do |multi|
|
80
|
+
multi.set "foo", "s3"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
assert_equal ["OK"], result
|
85
|
+
assert_equal "s3", rpool.get("foo")
|
86
|
+
|
87
|
+
EM.stop
|
88
|
+
end
|
data/test/test.conf.erb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
4
|
+
|
5
|
+
class TestThreadSafety < Test::Unit::TestCase
|
6
|
+
|
7
|
+
include Helper::Client
|
8
|
+
|
9
|
+
driver(:ruby, :hiredis) do
|
10
|
+
def test_thread_safety
|
11
|
+
redis = Redis.new(OPTIONS)
|
12
|
+
redis.set "foo", 1
|
13
|
+
redis.set "bar", 2
|
14
|
+
|
15
|
+
sample = 100
|
16
|
+
|
17
|
+
t1 = Thread.new do
|
18
|
+
$foos = Array.new(sample) { redis.get "foo" }
|
19
|
+
end
|
20
|
+
|
21
|
+
t2 = Thread.new do
|
22
|
+
$bars = Array.new(sample) { redis.get "bar" }
|
23
|
+
end
|
24
|
+
|
25
|
+
t1.join
|
26
|
+
t2.join
|
27
|
+
|
28
|
+
assert_equal ["1"], $foos.uniq
|
29
|
+
assert_equal ["2"], $bars.uniq
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,264 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
4
|
+
|
5
|
+
class TestTransactions < Test::Unit::TestCase
|
6
|
+
|
7
|
+
include Helper::Client
|
8
|
+
|
9
|
+
def test_multi_discard
|
10
|
+
r.multi
|
11
|
+
|
12
|
+
assert_equal "QUEUED", r.set("foo", "1")
|
13
|
+
assert_equal "QUEUED", r.get("foo")
|
14
|
+
|
15
|
+
r.discard
|
16
|
+
|
17
|
+
assert_equal nil, r.get("foo")
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_multi_exec_with_a_block
|
21
|
+
r.multi do |multi|
|
22
|
+
multi.set "foo", "s1"
|
23
|
+
end
|
24
|
+
|
25
|
+
assert_equal "s1", r.get("foo")
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_multi_exec_with_a_block_doesn_t_return_replies_for_multi_and_exec
|
29
|
+
r1, r2, nothing_else = r.multi do |multi|
|
30
|
+
multi.set "foo", "s1"
|
31
|
+
multi.get "foo"
|
32
|
+
end
|
33
|
+
|
34
|
+
assert_equal "OK", r1
|
35
|
+
assert_equal "s1", r2
|
36
|
+
assert_equal nil, nothing_else
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_assignment_inside_multi_exec_block
|
40
|
+
r.multi do |m|
|
41
|
+
@first = m.sadd("foo", 1)
|
42
|
+
@second = m.sadd("foo", 1)
|
43
|
+
end
|
44
|
+
|
45
|
+
assert_equal true, @first.value
|
46
|
+
assert_equal false, @second.value
|
47
|
+
end
|
48
|
+
|
49
|
+
# Although we could support accessing the values in these futures,
|
50
|
+
# it doesn't make a lot of sense.
|
51
|
+
def test_assignment_inside_multi_exec_block_with_delayed_command_errors
|
52
|
+
assert_raise(Redis::CommandError) do
|
53
|
+
r.multi do |m|
|
54
|
+
@first = m.set("foo", "s1")
|
55
|
+
@second = m.incr("foo") # not an integer
|
56
|
+
@third = m.lpush("foo", "value") # wrong kind of value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
assert_equal "OK", @first.value
|
61
|
+
assert_raise(Redis::CommandError) { @second.value }
|
62
|
+
assert_raise(Redis::FutureNotReady) { @third.value }
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_assignment_inside_multi_exec_block_with_immediate_command_errors
|
66
|
+
assert_raise(Redis::CommandError) do
|
67
|
+
r.multi do |m|
|
68
|
+
m.doesnt_exist
|
69
|
+
@first = m.sadd("foo", 1)
|
70
|
+
@second = m.sadd("foo", 1)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
assert_raise(Redis::FutureNotReady) { @first.value }
|
75
|
+
assert_raise(Redis::FutureNotReady) { @second.value }
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_raise_immediate_errors_in_multi_exec
|
79
|
+
assert_raise(RuntimeError) do
|
80
|
+
r.multi do |multi|
|
81
|
+
multi.set "bar", "s2"
|
82
|
+
raise "Some error"
|
83
|
+
multi.set "baz", "s3"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
assert_equal nil, r.get("bar")
|
88
|
+
assert_equal nil, r.get("baz")
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_transformed_replies_as_return_values_for_multi_exec_block
|
92
|
+
info, _ = r.multi do |m|
|
93
|
+
r.info
|
94
|
+
end
|
95
|
+
|
96
|
+
assert info.kind_of?(Hash)
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_transformed_replies_inside_multi_exec_block
|
100
|
+
r.multi do |m|
|
101
|
+
@info = r.info
|
102
|
+
end
|
103
|
+
|
104
|
+
assert @info.value.kind_of?(Hash)
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_raise_command_errors_in_multi_exec
|
108
|
+
assert_raise(Redis::CommandError) do
|
109
|
+
r.multi do |m|
|
110
|
+
m.set("foo", "s1")
|
111
|
+
m.incr("foo") # not an integer
|
112
|
+
m.lpush("foo", "value") # wrong kind of value
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
assert_equal "s1", r.get("foo")
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_raise_command_errors_when_accessing_futures_after_multi_exec
|
120
|
+
begin
|
121
|
+
r.multi do |m|
|
122
|
+
m.set("foo", "s1")
|
123
|
+
@counter = m.incr("foo") # not an integer
|
124
|
+
end
|
125
|
+
rescue Exception
|
126
|
+
# Not gonna deal with it
|
127
|
+
end
|
128
|
+
|
129
|
+
# We should test for Redis::Error here, but hiredis doesn't yet do
|
130
|
+
# custom error classes.
|
131
|
+
err = nil
|
132
|
+
begin
|
133
|
+
@counter.value
|
134
|
+
rescue => err
|
135
|
+
end
|
136
|
+
|
137
|
+
assert err.kind_of?(RuntimeError)
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_multi_with_a_block_yielding_the_client
|
141
|
+
r.multi do |multi|
|
142
|
+
multi.set "foo", "s1"
|
143
|
+
end
|
144
|
+
|
145
|
+
assert_equal "s1", r.get("foo")
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_raise_command_error_when_exec_fails
|
149
|
+
redis_mock(:exec => lambda { |*_| "-ERROR" }) do |redis|
|
150
|
+
assert_raise(Redis::CommandError) do
|
151
|
+
redis.multi do |m|
|
152
|
+
m.set "foo", "s1"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_watch
|
159
|
+
res = r.watch "foo"
|
160
|
+
|
161
|
+
assert_equal "OK", res
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_watch_with_an_unmodified_key
|
165
|
+
r.watch "foo"
|
166
|
+
r.multi do |multi|
|
167
|
+
multi.set "foo", "s1"
|
168
|
+
end
|
169
|
+
|
170
|
+
assert_equal "s1", r.get("foo")
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_watch_with_an_unmodified_key_passed_as_array
|
174
|
+
r.watch ["foo", "bar"]
|
175
|
+
r.multi do |multi|
|
176
|
+
multi.set "foo", "s1"
|
177
|
+
end
|
178
|
+
|
179
|
+
assert_equal "s1", r.get("foo")
|
180
|
+
end
|
181
|
+
|
182
|
+
def test_watch_with_a_modified_key
|
183
|
+
r.watch "foo"
|
184
|
+
r.set "foo", "s1"
|
185
|
+
res = r.multi do |multi|
|
186
|
+
multi.set "foo", "s2"
|
187
|
+
end
|
188
|
+
|
189
|
+
assert_equal nil, res
|
190
|
+
assert_equal "s1", r.get("foo")
|
191
|
+
end
|
192
|
+
|
193
|
+
def test_watch_with_a_modified_key_passed_as_array
|
194
|
+
r.watch ["foo", "bar"]
|
195
|
+
r.set "foo", "s1"
|
196
|
+
res = r.multi do |multi|
|
197
|
+
multi.set "foo", "s2"
|
198
|
+
end
|
199
|
+
|
200
|
+
assert_equal nil, res
|
201
|
+
assert_equal "s1", r.get("foo")
|
202
|
+
end
|
203
|
+
|
204
|
+
def test_watch_with_a_block_and_an_unmodified_key
|
205
|
+
result = r.watch "foo" do |rd|
|
206
|
+
|
207
|
+
assert_same r, rd
|
208
|
+
|
209
|
+
rd.multi do |multi|
|
210
|
+
multi.set "foo", "s1"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
assert_equal ["OK"], result
|
215
|
+
assert_equal "s1", r.get("foo")
|
216
|
+
end
|
217
|
+
|
218
|
+
def test_watch_with_a_block_and_a_modified_key
|
219
|
+
result = r.watch "foo" do |rd|
|
220
|
+
|
221
|
+
assert_same r, rd
|
222
|
+
|
223
|
+
rd.set "foo", "s1"
|
224
|
+
rd.multi do |multi|
|
225
|
+
multi.set "foo", "s2"
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
assert_equal nil, result
|
230
|
+
assert_equal "s1", r.get("foo")
|
231
|
+
end
|
232
|
+
|
233
|
+
def test_watch_with_a_block_that_raises_an_exception
|
234
|
+
r.set("foo", "s1")
|
235
|
+
|
236
|
+
begin
|
237
|
+
r.watch "foo" do
|
238
|
+
raise "test"
|
239
|
+
end
|
240
|
+
rescue RuntimeError
|
241
|
+
end
|
242
|
+
|
243
|
+
r.set("foo", "s2")
|
244
|
+
|
245
|
+
# If the watch was still set from within the block above, this multi/exec
|
246
|
+
# would fail. This proves that raising an exception above unwatches.
|
247
|
+
r.multi do |multi|
|
248
|
+
multi.set "foo", "s3"
|
249
|
+
end
|
250
|
+
|
251
|
+
assert_equal "s3", r.get("foo")
|
252
|
+
end
|
253
|
+
|
254
|
+
def test_unwatch_with_a_modified_key
|
255
|
+
r.watch "foo"
|
256
|
+
r.set "foo", "s1"
|
257
|
+
r.unwatch
|
258
|
+
r.multi do |multi|
|
259
|
+
multi.set "foo", "s2"
|
260
|
+
end
|
261
|
+
|
262
|
+
assert_equal "s2", r.get("foo")
|
263
|
+
end
|
264
|
+
end
|