redis 2.0.0 → 2.2.1

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.
Files changed (81) hide show
  1. data/.gitignore +8 -0
  2. data/CHANGELOG.md +49 -0
  3. data/README.md +208 -0
  4. data/Rakefile +183 -73
  5. data/TODO.md +4 -0
  6. data/benchmarking/logging.rb +62 -0
  7. data/benchmarking/pipeline.rb +51 -0
  8. data/benchmarking/speed.rb +21 -0
  9. data/benchmarking/suite.rb +24 -0
  10. data/benchmarking/thread_safety.rb +38 -0
  11. data/benchmarking/worker.rb +71 -0
  12. data/examples/basic.rb +15 -0
  13. data/examples/dist_redis.rb +43 -0
  14. data/examples/incr-decr.rb +17 -0
  15. data/examples/list.rb +26 -0
  16. data/examples/pubsub.rb +31 -0
  17. data/examples/sets.rb +36 -0
  18. data/examples/unicorn/config.ru +3 -0
  19. data/examples/unicorn/unicorn.rb +20 -0
  20. data/lib/redis/client.rb +149 -165
  21. data/lib/redis/connection/command_helper.rb +45 -0
  22. data/lib/redis/connection/hiredis.rb +49 -0
  23. data/lib/redis/connection/registry.rb +12 -0
  24. data/lib/redis/connection/ruby.rb +135 -0
  25. data/lib/redis/connection/synchrony.rb +129 -0
  26. data/lib/redis/connection.rb +9 -0
  27. data/lib/redis/distributed.rb +182 -7
  28. data/lib/redis/pipeline.rb +22 -1
  29. data/lib/redis/subscribe.rb +19 -4
  30. data/lib/redis/version.rb +3 -0
  31. data/lib/redis.rb +715 -155
  32. data/redis.gemspec +24 -0
  33. data/test/commands_on_hashes_test.rb +32 -0
  34. data/test/commands_on_lists_test.rb +60 -0
  35. data/test/commands_on_sets_test.rb +78 -0
  36. data/test/commands_on_sorted_sets_test.rb +109 -0
  37. data/test/commands_on_strings_test.rb +80 -0
  38. data/test/commands_on_value_types_test.rb +88 -0
  39. data/test/connection_handling_test.rb +88 -0
  40. data/test/db/.gitignore +1 -0
  41. data/test/distributed_blocking_commands_test.rb +53 -0
  42. data/test/distributed_commands_on_hashes_test.rb +12 -0
  43. data/test/distributed_commands_on_lists_test.rb +24 -0
  44. data/test/distributed_commands_on_sets_test.rb +85 -0
  45. data/test/distributed_commands_on_strings_test.rb +50 -0
  46. data/test/distributed_commands_on_value_types_test.rb +73 -0
  47. data/test/distributed_commands_requiring_clustering_test.rb +148 -0
  48. data/test/distributed_connection_handling_test.rb +25 -0
  49. data/test/distributed_internals_test.rb +27 -0
  50. data/test/distributed_key_tags_test.rb +53 -0
  51. data/test/distributed_persistence_control_commands_test.rb +24 -0
  52. data/test/distributed_publish_subscribe_test.rb +101 -0
  53. data/test/distributed_remote_server_control_commands_test.rb +43 -0
  54. data/test/distributed_sorting_test.rb +21 -0
  55. data/test/distributed_test.rb +59 -0
  56. data/test/distributed_transactions_test.rb +34 -0
  57. data/test/encoding_test.rb +16 -0
  58. data/test/error_replies_test.rb +53 -0
  59. data/test/helper.rb +145 -0
  60. data/test/internals_test.rb +160 -0
  61. data/test/lint/hashes.rb +114 -0
  62. data/test/lint/internals.rb +37 -0
  63. data/test/lint/lists.rb +93 -0
  64. data/test/lint/sets.rb +66 -0
  65. data/test/lint/sorted_sets.rb +167 -0
  66. data/test/lint/strings.rb +137 -0
  67. data/test/lint/value_types.rb +84 -0
  68. data/test/persistence_control_commands_test.rb +22 -0
  69. data/test/pipelining_commands_test.rb +123 -0
  70. data/test/publish_subscribe_test.rb +158 -0
  71. data/test/redis_mock.rb +80 -0
  72. data/test/remote_server_control_commands_test.rb +82 -0
  73. data/test/sorting_test.rb +44 -0
  74. data/test/synchrony_driver.rb +57 -0
  75. data/test/test.conf +8 -0
  76. data/test/thread_safety_test.rb +30 -0
  77. data/test/transactions_test.rb +100 -0
  78. data/test/unknown_commands_test.rb +14 -0
  79. data/test/url_param_test.rb +60 -0
  80. metadata +130 -22
  81. data/README.markdown +0 -120
@@ -0,0 +1,158 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+
5
+ setup do
6
+ init Redis.new(OPTIONS)
7
+ end
8
+
9
+ test "SUBSCRIBE and UNSUBSCRIBE" do |r|
10
+ listening = false
11
+
12
+ wire = Wire.new do
13
+ r.subscribe("foo") do |on|
14
+ on.subscribe do |channel, total|
15
+ @subscribed = true
16
+ @t1 = total
17
+ end
18
+
19
+ on.message do |channel, message|
20
+ if message == "s1"
21
+ r.unsubscribe
22
+ @message = message
23
+ end
24
+ end
25
+
26
+ on.unsubscribe do |channel, total|
27
+ @unsubscribed = true
28
+ @t2 = total
29
+ end
30
+
31
+ listening = true
32
+ end
33
+ end
34
+
35
+ Wire.pass while !listening
36
+
37
+ Redis.new(OPTIONS).publish("foo", "s1")
38
+
39
+ wire.join
40
+
41
+ assert @subscribed
42
+ assert 1 == @t1
43
+ assert @unsubscribed
44
+ assert 0 == @t2
45
+ assert "s1" == @message
46
+ end
47
+
48
+ test "PSUBSCRIBE and PUNSUBSCRIBE" do |r|
49
+ listening = false
50
+
51
+ wire = Wire.new do
52
+ r.psubscribe("f*") do |on|
53
+ on.psubscribe do |pattern, total|
54
+ @subscribed = true
55
+ @t1 = total
56
+ end
57
+
58
+ on.pmessage do |pattern, channel, message|
59
+ if message == "s1"
60
+ r.punsubscribe
61
+ @message = message
62
+ end
63
+ end
64
+
65
+ on.punsubscribe do |pattern, total|
66
+ @unsubscribed = true
67
+ @t2 = total
68
+ end
69
+
70
+ listening = true
71
+ end
72
+ end
73
+
74
+ Wire.pass while !listening
75
+
76
+ Redis.new(OPTIONS).publish("foo", "s1")
77
+
78
+ wire.join
79
+
80
+ assert @subscribed
81
+ assert 1 == @t1
82
+ assert @unsubscribed
83
+ assert 0 == @t2
84
+ assert "s1" == @message
85
+ end
86
+
87
+ test "SUBSCRIBE within SUBSCRIBE" do |r|
88
+ listening = false
89
+
90
+ @channels = []
91
+
92
+ wire = Wire.new do
93
+ r.subscribe("foo") do |on|
94
+ on.subscribe do |channel, total|
95
+ @channels << channel
96
+
97
+ r.subscribe("bar") if channel == "foo"
98
+ r.unsubscribe if channel == "bar"
99
+ end
100
+
101
+ listening = true
102
+ end
103
+ end
104
+
105
+ Wire.pass while !listening
106
+
107
+ Redis.new(OPTIONS).publish("foo", "s1")
108
+
109
+ wire.join
110
+
111
+ assert ["foo", "bar"] == @channels
112
+ end
113
+
114
+ test "other commands within a SUBSCRIBE" do |r|
115
+ assert_raise RuntimeError do
116
+ r.subscribe("foo") do |on|
117
+ on.subscribe do |channel, total|
118
+ r.set("bar", "s2")
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ test "SUBSCRIBE without a block" do |r|
125
+ assert_raise LocalJumpError do
126
+ r.subscribe("foo")
127
+ end
128
+ end
129
+
130
+ test "UNSUBSCRIBE without a SUBSCRIBE" do |r|
131
+ assert_raise RuntimeError do
132
+ r.unsubscribe
133
+ end
134
+
135
+ assert_raise RuntimeError do
136
+ r.punsubscribe
137
+ end
138
+ end
139
+
140
+ test "SUBSCRIBE past a timeout" do |r|
141
+ # For some reason, a thread here doesn't reproduce the issue.
142
+ sleep = %{sleep #{OPTIONS[:timeout] + 1}}
143
+ publish = %{echo "publish foo bar\r\n" | nc localhost #{OPTIONS[:port]}}
144
+ cmd = [sleep, publish].join("; ")
145
+
146
+ IO.popen(cmd, "r+") do |pipe|
147
+ received = false
148
+
149
+ r.subscribe "foo" do |on|
150
+ on.message do |channel, message|
151
+ received = true
152
+ r.unsubscribe
153
+ end
154
+ end
155
+
156
+ assert received
157
+ end
158
+ end
@@ -0,0 +1,80 @@
1
+ require "socket"
2
+
3
+ module RedisMock
4
+ class Server
5
+ VERBOSE = false
6
+
7
+ def initialize(port = 6380, &block)
8
+ @server = TCPServer.new("127.0.0.1", port)
9
+ @server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
10
+ @thread = Thread.new { run(&block) }
11
+ end
12
+
13
+ # This raises an exception in the thread calling @server.accept which
14
+ # in turn will cause the thread to terminate.
15
+ def shutdown
16
+ @server.close if @server
17
+ rescue => ex
18
+ $stderr.puts "Error closing mock server: #{ex.message}" if VERBOSE
19
+ $stderr.puts ex.backtrace if VERBOSE
20
+ end
21
+
22
+ def run
23
+ loop do
24
+ session = @server.accept
25
+
26
+ begin
27
+ while line = session.gets
28
+ parts = Array.new(line[1..-3].to_i) do
29
+ bytes = session.gets[1..-3].to_i
30
+ argument = session.read(bytes)
31
+ session.read(2) # Discard \r\n
32
+ argument
33
+ end
34
+
35
+ response = yield(*parts)
36
+
37
+ if response.nil?
38
+ session.shutdown(Socket::SHUT_RDWR)
39
+ break
40
+ else
41
+ session.write(response)
42
+ session.write("\r\n")
43
+ end
44
+ end
45
+ rescue Errno::ECONNRESET
46
+ # Ignore client closing the connection
47
+ end
48
+ end
49
+ rescue => ex
50
+ $stderr.puts "Error running mock server: #{ex.message}" if VERBOSE
51
+ $stderr.puts ex.backtrace if VERBOSE
52
+ end
53
+ end
54
+
55
+ module Helper
56
+ # Starts a mock Redis server in a thread on port 6380.
57
+ #
58
+ # The server will reply with a `+OK` to all commands, but you can
59
+ # customize it by providing a hash. For example:
60
+ #
61
+ # redis_mock(:ping => lambda { "+PONG" }) do
62
+ # assert_equal "PONG", Redis.new(:port => 6380).ping
63
+ # end
64
+ #
65
+ def redis_mock(replies = {})
66
+ begin
67
+ server = Server.new do |command, *args|
68
+ (replies[command.to_sym] || lambda { |*_| "+OK" }).call(*args)
69
+ end
70
+
71
+ sleep 0.1 # Give time for the socket to start listening.
72
+
73
+ yield
74
+
75
+ ensure
76
+ server.shutdown
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,82 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+ require File.expand_path("./redis_mock", File.dirname(__FILE__))
5
+
6
+ include RedisMock::Helper
7
+
8
+ setup do
9
+ init Redis.new(OPTIONS)
10
+ end
11
+
12
+ test "INFO" do |r|
13
+ %w(last_save_time redis_version total_connections_received connected_clients total_commands_processed connected_slaves uptime_in_seconds used_memory uptime_in_days changes_since_last_save).each do |x|
14
+ assert r.info.keys.include?(x)
15
+ end
16
+ end
17
+
18
+ test "INFO COMMANDSTATS" do |r|
19
+ # Only available on Redis >= 2.3.0
20
+ next if r.info["redis_version"] < "2.3.0"
21
+
22
+ r.config(:resetstat)
23
+ r.ping
24
+
25
+ result = r.info(:commandstats)
26
+ assert "1" == result["ping"]["calls"]
27
+ end
28
+
29
+ test "MONITOR" do |r|
30
+ log = []
31
+
32
+ wire = Wire.new do
33
+ Redis.new(OPTIONS).monitor do |line|
34
+ log << line
35
+ break if log.size == 3
36
+ end
37
+ end
38
+
39
+ Wire.pass while log.empty? # Faster than sleep
40
+
41
+ r.set "foo", "s1"
42
+
43
+ wire.join
44
+
45
+ assert log[-1][%q{(db 15) "set" "foo" "s1"}]
46
+ end
47
+
48
+ test "MONITOR returns value for break" do |r|
49
+ result = r.monitor do |line|
50
+ break line
51
+ end
52
+
53
+ assert result == "OK"
54
+ end
55
+
56
+ test "ECHO" do |r|
57
+ assert "foo bar baz\n" == r.echo("foo bar baz\n")
58
+ end
59
+
60
+ test "DEBUG" do |r|
61
+ r.set "foo", "s1"
62
+
63
+ assert r.debug(:object, "foo").kind_of?(String)
64
+ end
65
+
66
+ test "OBJECT" do |r|
67
+ r.lpush "list", "value"
68
+
69
+ assert r.object(:refcount, "list") == 1
70
+ assert r.object(:encoding, "list") == "ziplist"
71
+ assert r.object(:idletime, "list").kind_of?(Fixnum)
72
+ end
73
+
74
+ test "SYNC" do |r|
75
+ replies = {:sync => lambda { "+OK" }}
76
+
77
+ redis_mock(replies) do
78
+ redis = Redis.new(OPTIONS.merge(:port => 6380))
79
+
80
+ assert "OK" == redis.sync
81
+ end
82
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+
5
+ setup do
6
+ init Redis.new(OPTIONS)
7
+ end
8
+
9
+ test "SORT" do |r|
10
+ r.set("foo:1", "s1")
11
+ r.set("foo:2", "s2")
12
+
13
+ r.rpush("bar", "1")
14
+ r.rpush("bar", "2")
15
+
16
+ assert ["s1"] == r.sort("bar", :get => "foo:*", :limit => [0, 1])
17
+ assert ["s2"] == r.sort("bar", :get => "foo:*", :limit => [0, 1], :order => "desc alpha")
18
+ end
19
+
20
+ test "SORT with an array of GETs" do |r|
21
+ r.set("foo:1:a", "s1a")
22
+ r.set("foo:1:b", "s1b")
23
+
24
+ r.set("foo:2:a", "s2a")
25
+ r.set("foo:2:b", "s2b")
26
+
27
+ r.rpush("bar", "1")
28
+ r.rpush("bar", "2")
29
+
30
+ assert ["s1a", "s1b"] == r.sort("bar", :get => ["foo:*:a", "foo:*:b"], :limit => [0, 1])
31
+ assert ["s2a", "s2b"] == r.sort("bar", :get => ["foo:*:a", "foo:*:b"], :limit => [0, 1], :order => "desc alpha")
32
+ end
33
+
34
+ test "SORT with STORE" do |r|
35
+ r.set("foo:1", "s1")
36
+ r.set("foo:2", "s2")
37
+
38
+ r.rpush("bar", "1")
39
+ r.rpush("bar", "2")
40
+
41
+ r.sort("bar", :get => "foo:*", :store => "baz")
42
+ assert ["s1", "s2"] == r.lrange("baz", 0, -1)
43
+ end
44
+
@@ -0,0 +1,57 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'em-synchrony'
4
+
5
+ require 'redis'
6
+ require 'redis/connection/synchrony'
7
+
8
+ require File.expand_path("./helper", File.dirname(__FILE__))
9
+
10
+ #
11
+ # if running under Eventmachine + Synchrony (Ruby 1.9+), then
12
+ # we can simulate the blocking API while performing the network
13
+ # IO via the EM reactor.
14
+ #
15
+
16
+ EM.synchrony do
17
+ r = Redis.new
18
+ r.flushdb
19
+
20
+ r.rpush "foo", "s1"
21
+ r.rpush "foo", "s2"
22
+
23
+ assert 2 == r.llen("foo")
24
+ assert "s2" == r.rpop("foo")
25
+
26
+ r.set("foo", "bar")
27
+
28
+ assert "bar" == r.getset("foo", "baz")
29
+ assert "baz" == r.get("foo")
30
+
31
+ r.set("foo", "a")
32
+
33
+ assert_equal 1, r.getbit("foo", 1)
34
+ assert_equal 1, r.getbit("foo", 2)
35
+ assert_equal 0, r.getbit("foo", 3)
36
+ assert_equal 0, r.getbit("foo", 4)
37
+ assert_equal 0, r.getbit("foo", 5)
38
+ assert_equal 0, r.getbit("foo", 6)
39
+ assert_equal 1, r.getbit("foo", 7)
40
+
41
+ r.flushdb
42
+
43
+ # command pipelining
44
+ r.pipelined do
45
+ r.lpush "foo", "s1"
46
+ r.lpush "foo", "s2"
47
+ end
48
+
49
+ assert 2 == r.llen("foo")
50
+ assert "s2" == r.lpop("foo")
51
+ assert "s1" == r.lpop("foo")
52
+
53
+ assert "OK" == r.client.call(:quit)
54
+ assert "PONG" == r.ping
55
+
56
+ EM.stop
57
+ end
data/test/test.conf ADDED
@@ -0,0 +1,8 @@
1
+ dir ./test/db
2
+ pidfile ./redis.pid
3
+ port 6379
4
+ timeout 300
5
+ loglevel debug
6
+ logfile stdout
7
+ databases 16
8
+ daemonize yes
@@ -0,0 +1,30 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+
5
+ setup do
6
+ init Redis.new(OPTIONS)
7
+ end
8
+
9
+ test "thread safety" do
10
+ redis = Redis.connect(OPTIONS.merge(:thread_safe => true))
11
+
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
@@ -0,0 +1,100 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+
5
+ setup do
6
+ init Redis.new(OPTIONS)
7
+ end
8
+
9
+ test "MULTI/DISCARD" do |r|
10
+ r.multi
11
+
12
+ assert "QUEUED" == r.set("foo", "1")
13
+ assert "QUEUED" == r.get("foo")
14
+
15
+ r.discard
16
+
17
+ assert nil == r.get("foo")
18
+ end
19
+
20
+ test "MULTI/EXEC with a block" do |r|
21
+ r.multi do |multi|
22
+ multi.set "foo", "s1"
23
+ end
24
+
25
+ assert "s1" == r.get("foo")
26
+
27
+ assert_raise(RuntimeError) do
28
+ r.multi do |multi|
29
+ multi.set "bar", "s2"
30
+ raise "Some error"
31
+ multi.set "baz", "s3"
32
+ end
33
+ end
34
+
35
+ assert nil == r.get("bar")
36
+ assert nil == r.get("baz")
37
+ end
38
+
39
+ test "Don't raise (and ignore) immediate error in MULTI/EXEC" do |r|
40
+ result = r.multi do |m|
41
+ m.set("foo", "s1")
42
+ m.unknown_command
43
+ end
44
+
45
+ assert 1 == result.size
46
+ assert "OK" == result.first
47
+ assert "s1" == r.get("foo")
48
+ end
49
+
50
+ test "Don't raise delayed error in MULTI/EXEC" do |r|
51
+ result = r.multi do |m|
52
+ m.set("foo", "s1")
53
+ m.incr("foo") # not an integer
54
+ m.lpush("foo", "value") # wrong kind of value
55
+ end
56
+
57
+ assert result[1].message =~ /not an integer/i
58
+ assert result[2].message =~ /wrong kind of value/i
59
+ assert "s1" == r.get("foo")
60
+ end
61
+
62
+ test "MULTI with a block yielding the client" do |r|
63
+ r.multi do |multi|
64
+ multi.set "foo", "s1"
65
+ end
66
+
67
+ assert "s1" == r.get("foo")
68
+ end
69
+
70
+ test "WATCH with an unmodified key" do |r|
71
+ r.watch "foo"
72
+ r.multi do |multi|
73
+ multi.set "foo", "s1"
74
+ end
75
+
76
+ assert "s1" == r.get("foo")
77
+ end
78
+
79
+ test "WATCH with a modified key" do |r|
80
+ r.watch "foo"
81
+ r.set "foo", "s1"
82
+ res = r.multi do |multi|
83
+ multi.set "foo", "s2"
84
+ end
85
+
86
+ assert nil == res
87
+ assert "s1" == r.get("foo")
88
+ end
89
+
90
+ test "UNWATCH with a modified key" do |r|
91
+ r.watch "foo"
92
+ r.set "foo", "s1"
93
+ r.unwatch
94
+ r.multi do |multi|
95
+ multi.set "foo", "s2"
96
+ end
97
+
98
+ assert "s2" == r.get("foo")
99
+ end
100
+
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+
5
+ setup do
6
+ init Redis.new(OPTIONS)
7
+ end
8
+
9
+ test "should try to work" do |r|
10
+ assert_raise RuntimeError do
11
+ r.not_yet_implemented_command
12
+ end
13
+ end
14
+
@@ -0,0 +1,60 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+
5
+ test "URL defaults to 127.0.0.1:6379" do
6
+ redis = Redis.connect
7
+
8
+ assert "127.0.0.1" == redis.client.host
9
+ assert 6379 == redis.client.port
10
+ assert 0 == redis.client.db
11
+ assert nil == redis.client.password
12
+ end
13
+
14
+ test "allows to pass in a URL" do
15
+ redis = Redis.connect :url => "redis://:secr3t@foo.com:999/2"
16
+
17
+ assert "foo.com" == redis.client.host
18
+ assert 999 == redis.client.port
19
+ assert 2 == redis.client.db
20
+ assert "secr3t" == redis.client.password
21
+ end
22
+
23
+ test "override URL if path option is passed" do
24
+ redis = Redis.connect :url => "redis://:secr3t@foo.com/foo:999/2", :path => "/tmp/redis.sock"
25
+
26
+ assert "/tmp/redis.sock" == redis.client.path
27
+ assert nil == redis.client.host
28
+ assert nil == redis.client.port
29
+ end
30
+
31
+ test "overrides URL if another connection option is passed" do
32
+ redis = Redis.connect :url => "redis://:secr3t@foo.com:999/2", :port => 1000
33
+
34
+ assert "foo.com" == redis.client.host
35
+ assert 1000 == redis.client.port
36
+ assert 2 == redis.client.db
37
+ assert "secr3t" == redis.client.password
38
+ end
39
+
40
+ test "does not modify the passed options" do
41
+ options = { :url => "redis://:secr3t@foo.com:999/2" }
42
+
43
+ redis = Redis.connect(options)
44
+
45
+ assert({ :url => "redis://:secr3t@foo.com:999/2" } == options)
46
+ end
47
+
48
+ test "uses REDIS_URL over default if available" do
49
+ ENV["REDIS_URL"] = "redis://:secr3t@foo.com:999/2"
50
+
51
+ redis = Redis.connect
52
+
53
+ assert "foo.com" == redis.client.host
54
+ assert 999 == redis.client.port
55
+ assert 2 == redis.client.db
56
+ assert "secr3t" == redis.client.password
57
+
58
+ ENV.delete("REDIS_URL")
59
+ end
60
+