em-hiredis 0.0.1 → 0.1.0
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.
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/Gemfile +4 -2
- data/README.md +28 -5
- data/Rakefile +9 -0
- data/em-hiredis.gemspec +2 -2
- data/lib/em-hiredis.rb +22 -10
- data/lib/em-hiredis/client.rb +68 -30
- data/lib/em-hiredis/connection.rb +4 -4
- data/lib/em-hiredis/event_emitter.rb +1 -1
- data/lib/em-hiredis/version.rb +2 -2
- data/spec/connection_spec.rb +56 -0
- data/spec/live_redis_protocol_spec.rb +512 -0
- data/spec/redis_commands_spec.rb +910 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/support/connection_helper.rb +9 -0
- data/spec/support/redis_mock.rb +65 -0
- data/spec/url_param_spec.rb +43 -0
- metadata +20 -6
data/.gitignore
CHANGED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,13 +1,18 @@
|
|
1
1
|
Getting started
|
2
2
|
===============
|
3
3
|
|
4
|
-
Connect to redis
|
4
|
+
Connect to redis:
|
5
5
|
|
6
|
-
|
6
|
+
require 'em-hiredis'
|
7
|
+
redis = EM::Hiredis.connect
|
8
|
+
|
9
|
+
Or, connect to redis with a redis URL (for a different host, port, password, DB)
|
10
|
+
|
11
|
+
redis = EM::Hiredis.connect("redis://:secretpassword@example.com:9000/4")
|
7
12
|
|
8
13
|
The client is a deferrable which succeeds when the underlying connection is established so you can bind to this. This isn't necessary however - any commands sent before the connection is established (or while reconnecting) will be sent to redis on connect.
|
9
14
|
|
10
|
-
|
15
|
+
redis.callback { puts "Redis now connected" }
|
11
16
|
|
12
17
|
All redis commands are available without any remapping of names
|
13
18
|
|
@@ -40,8 +45,8 @@ Pubsub
|
|
40
45
|
|
41
46
|
This example should explain things. Once a redis connection is in a pubsub state, you must make sure you only send pubsub commands.
|
42
47
|
|
43
|
-
redis = EM::Hiredis
|
44
|
-
subscriber = EM::Hiredis
|
48
|
+
redis = EM::Hiredis.connect
|
49
|
+
subscriber = EM::Hiredis.connect
|
45
50
|
|
46
51
|
subscriber.subscribe('bar.0')
|
47
52
|
subscriber.psubscribe('bar.*')
|
@@ -59,3 +64,21 @@ This example should explain things. Once a redis connection is in a pubsub state
|
|
59
64
|
p [:publisherror, e]
|
60
65
|
}
|
61
66
|
}
|
67
|
+
|
68
|
+
Hacking
|
69
|
+
-------
|
70
|
+
|
71
|
+
Hacking on em-hiredis is pretty simple, make sure you have Bundler installed:
|
72
|
+
|
73
|
+
gem install bundler
|
74
|
+
bundle
|
75
|
+
|
76
|
+
To run all the tests:
|
77
|
+
|
78
|
+
rake
|
79
|
+
|
80
|
+
To run an individual test:
|
81
|
+
|
82
|
+
bundle exec rspec spec/redis_commands_spec.rb
|
83
|
+
|
84
|
+
Much thanks to the em-redis gem for getting this gem bootstrapped with some tests.
|
data/Rakefile
CHANGED
data/em-hiredis.gemspec
CHANGED
@@ -4,7 +4,7 @@ require "em-hiredis/version"
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "em-hiredis"
|
7
|
-
s.version =
|
7
|
+
s.version = EventMachine::Hiredis::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = ["Martyn Loughran"]
|
10
10
|
s.email = ["me@mloughran.com"]
|
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.summary = %q{Eventmachine redis client}
|
13
13
|
s.description = %q{Eventmachine redis client using hiredis native parser}
|
14
14
|
|
15
|
-
s.add_dependency 'hiredis', '~> 0.
|
15
|
+
s.add_dependency 'hiredis', '~> 0.3.0'
|
16
16
|
|
17
17
|
s.rubyforge_project = "em-hiredis"
|
18
18
|
|
data/lib/em-hiredis.rb
CHANGED
@@ -1,17 +1,29 @@
|
|
1
1
|
require 'eventmachine'
|
2
|
+
require 'uri'
|
2
3
|
|
3
|
-
module
|
4
|
+
module EventMachine
|
4
5
|
module Hiredis
|
5
|
-
|
6
|
-
|
6
|
+
def self.setup(uri = nil)
|
7
|
+
url = URI(uri || ENV["REDIS_URL"] || "redis://127.0.0.1:6379/0")
|
8
|
+
Client.new(url.host, url.port, url.password, url.path[1..-1])
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.connect(uri = nil)
|
12
|
+
client = setup(uri)
|
13
|
+
client.connect
|
14
|
+
client
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.logger=(logger)
|
18
|
+
@@logger = logger
|
19
|
+
end
|
7
20
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
21
|
+
def self.logger
|
22
|
+
@@logger ||= begin
|
23
|
+
require 'logger'
|
24
|
+
log = Logger.new(STDOUT)
|
25
|
+
log.level = Logger::WARN
|
26
|
+
log
|
15
27
|
end
|
16
28
|
end
|
17
29
|
end
|
data/lib/em-hiredis/client.rb
CHANGED
@@ -1,21 +1,25 @@
|
|
1
|
-
module
|
1
|
+
module EventMachine::Hiredis
|
2
2
|
class Client
|
3
3
|
PUBSUB_MESSAGES = %w{message pmessage}.freeze
|
4
4
|
|
5
|
-
include
|
5
|
+
include EventMachine::Hiredis::EventEmitter
|
6
6
|
include EM::Deferrable
|
7
7
|
|
8
|
+
attr_reader :host, :port, :password, :db
|
9
|
+
|
8
10
|
def self.connect(host = 'localhost', port = 6379)
|
9
|
-
new(host, port)
|
11
|
+
new(host, port).connect
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(host, port, password = nil, db = nil)
|
15
|
+
@host, @port, @password, @db = host, port, password, db
|
16
|
+
@subs, @psubs, @defs = [], [], []
|
10
17
|
end
|
11
18
|
|
12
|
-
def
|
13
|
-
@host, @port
|
14
|
-
@subs, @psubs = [], []
|
15
|
-
@defs = []
|
16
|
-
@connection = EM.connect(host, port, Connection, host, port)
|
19
|
+
def connect
|
20
|
+
@connection = EM.connect(@host, @port, Connection, @host, @port)
|
17
21
|
|
18
|
-
@connection.on(:closed)
|
22
|
+
@connection.on(:closed) do
|
19
23
|
if @connected
|
20
24
|
@defs.each { |d| d.fail("Redis disconnected") }
|
21
25
|
@defs = []
|
@@ -26,11 +30,14 @@ module EM::Hiredis
|
|
26
30
|
else
|
27
31
|
EM.add_timer(1) { reconnect }
|
28
32
|
end
|
29
|
-
|
33
|
+
end
|
30
34
|
|
31
|
-
@connection.on(:connected)
|
35
|
+
@connection.on(:connected) do
|
32
36
|
@connected = true
|
37
|
+
|
33
38
|
select(@db) if @db
|
39
|
+
auth(@password) if @password
|
40
|
+
|
34
41
|
@subs.each { |s| method_missing(:subscribe, s) }
|
35
42
|
@psubs.each { |s| method_missing(:psubscribe, s) }
|
36
43
|
succeed
|
@@ -39,9 +46,9 @@ module EM::Hiredis
|
|
39
46
|
@reconnecting = false
|
40
47
|
emit(:reconnected)
|
41
48
|
end
|
42
|
-
|
49
|
+
end
|
43
50
|
|
44
|
-
@connection.on(:message)
|
51
|
+
@connection.on(:message) do |reply|
|
45
52
|
if RuntimeError === reply
|
46
53
|
raise "Replies out of sync: #{reply.inspect}" if @defs.empty?
|
47
54
|
deferred = @defs.shift
|
@@ -57,23 +64,32 @@ module EM::Hiredis
|
|
57
64
|
emit(:pmessage, subscription, d1, d2)
|
58
65
|
end
|
59
66
|
else
|
60
|
-
|
61
|
-
|
62
|
-
|
67
|
+
if @defs.empty?
|
68
|
+
if @monitoring
|
69
|
+
emit(:monitor, reply)
|
70
|
+
else
|
71
|
+
raise "Replies out of sync: #{reply.inspect}"
|
72
|
+
end
|
73
|
+
else
|
74
|
+
deferred = @defs.shift
|
75
|
+
deferred.succeed(reply) if deferred
|
76
|
+
end
|
63
77
|
end
|
64
78
|
end
|
65
|
-
|
79
|
+
end
|
66
80
|
|
67
81
|
@connected = false
|
68
82
|
@reconnecting = false
|
83
|
+
|
84
|
+
return self
|
69
85
|
end
|
70
86
|
|
71
|
-
# Indicates that commands have been sent to redis but a reply has not yet
|
72
|
-
# been received
|
73
|
-
#
|
74
|
-
# This can be useful for example to avoid stopping the
|
87
|
+
# Indicates that commands have been sent to redis but a reply has not yet
|
88
|
+
# been received
|
89
|
+
#
|
90
|
+
# This can be useful for example to avoid stopping the
|
75
91
|
# eventmachine reactor while there are outstanding commands
|
76
|
-
#
|
92
|
+
#
|
77
93
|
def pending_commands?
|
78
94
|
@connected && @defs.size > 0
|
79
95
|
end
|
@@ -98,11 +114,35 @@ module EM::Hiredis
|
|
98
114
|
method_missing(:punsubscribe, channel)
|
99
115
|
end
|
100
116
|
|
101
|
-
def select(db)
|
117
|
+
def select(db, &blk)
|
102
118
|
@db = db
|
103
|
-
method_missing(:select, db)
|
119
|
+
method_missing(:select, db, &blk)
|
120
|
+
end
|
121
|
+
|
122
|
+
def auth(password, &blk)
|
123
|
+
@password = password
|
124
|
+
method_missing(:auth, password, &blk)
|
104
125
|
end
|
105
126
|
|
127
|
+
def monitor(&blk)
|
128
|
+
@monitoring = true
|
129
|
+
method_missing(:monitor, &blk)
|
130
|
+
end
|
131
|
+
|
132
|
+
def info(&blk)
|
133
|
+
hash_processor = lambda do |response|
|
134
|
+
info = {}
|
135
|
+
response.each_line do |line|
|
136
|
+
key, value = line.split(":", 2)
|
137
|
+
info[key.to_sym] = value.chomp
|
138
|
+
end
|
139
|
+
blk.call(info)
|
140
|
+
end
|
141
|
+
method_missing(:info, &hash_processor)
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
106
146
|
def method_missing(sym, *args)
|
107
147
|
deferred = EM::DefaultDeferrable.new
|
108
148
|
# Shortcut for defining the callback case with just a block
|
@@ -112,19 +152,17 @@ module EM::Hiredis
|
|
112
152
|
@connection.send_command(sym, *args)
|
113
153
|
@defs.push(deferred)
|
114
154
|
else
|
115
|
-
callback
|
155
|
+
callback do
|
116
156
|
@connection.send_command(sym, *args)
|
117
157
|
@defs.push(deferred)
|
118
|
-
|
158
|
+
end
|
119
159
|
end
|
120
160
|
|
121
|
-
|
161
|
+
deferred
|
122
162
|
end
|
123
163
|
|
124
|
-
private
|
125
|
-
|
126
164
|
def reconnect
|
127
|
-
|
165
|
+
EventMachine::Hiredis.logger.debug("Trying to reconnect to Redis")
|
128
166
|
@connection.reconnect @host, @port
|
129
167
|
end
|
130
168
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'hiredis/reader'
|
2
2
|
|
3
|
-
module
|
3
|
+
module EventMachine::Hiredis
|
4
4
|
class Connection < EM::Connection
|
5
|
-
include
|
5
|
+
include EventMachine::Hiredis::EventEmitter
|
6
6
|
|
7
7
|
def initialize(host, port)
|
8
8
|
super
|
@@ -10,7 +10,7 @@ module EM::Hiredis
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def connection_completed
|
13
|
-
|
13
|
+
EventMachine::Hiredis.logger.info("Connected to Redis")
|
14
14
|
@reader = ::Hiredis::Reader.new
|
15
15
|
emit(:connected)
|
16
16
|
end
|
@@ -23,7 +23,7 @@ module EM::Hiredis
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def unbind
|
26
|
-
|
26
|
+
EventMachine::Hiredis.logger.info("Disconnected from Redis")
|
27
27
|
emit(:closed)
|
28
28
|
end
|
29
29
|
|
data/lib/em-hiredis/version.rb
CHANGED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EventMachine::Hiredis, "connecting" do
|
4
|
+
let(:replies) do
|
5
|
+
# shove db number into PING reply since redis has no way
|
6
|
+
# of exposing the currently selected DB
|
7
|
+
replies = {
|
8
|
+
:select => lambda { |db| $db = db; "+OK" },
|
9
|
+
:ping => lambda { "+PONG #{$db}" },
|
10
|
+
:auth => lambda { |password| $auth = password; "+OK" },
|
11
|
+
:get => lambda { |key| $auth == "secret" ? "$3\r\nbar" : "$-1" },
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def connect_to_mock(url, &blk)
|
16
|
+
redis_mock(replies) do
|
17
|
+
connect(url, &blk)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "doesn't call select by default" do
|
22
|
+
connect_to_mock("redis://localhost:6380/") do |redis|
|
23
|
+
redis.ping do |response|
|
24
|
+
response.should == "PONG "
|
25
|
+
done
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it "selects the right db" do
|
31
|
+
connect_to_mock("redis://localhost:6380/9") do |redis|
|
32
|
+
redis.ping do |response|
|
33
|
+
response.should == "PONG 9"
|
34
|
+
done
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it "authenticates with a password" do
|
40
|
+
connect_to_mock("redis://:secret@localhost:6380/9") do |redis|
|
41
|
+
redis.get("foo") do |response|
|
42
|
+
response.should == "bar"
|
43
|
+
done
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it "rejects a bad password" do
|
49
|
+
connect_to_mock("redis://:failboat@localhost:6380/9") do |redis|
|
50
|
+
redis.get("foo") do |response|
|
51
|
+
response.should be_nil
|
52
|
+
done
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,512 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EventMachine::Hiredis, "connected to an empty db" do
|
4
|
+
it "sets a string value" do
|
5
|
+
connect do |redis|
|
6
|
+
redis.set("foo", "bar") do |r|
|
7
|
+
r.should == "OK"
|
8
|
+
done
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it "increments the value of a string" do
|
14
|
+
connect do |redis|
|
15
|
+
redis.incr "foo" do |r|
|
16
|
+
r.should == 1
|
17
|
+
redis.incr "foo" do |r|
|
18
|
+
r.should == 2
|
19
|
+
done
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "increments the value of a string by an amount" do
|
26
|
+
connect do |redis|
|
27
|
+
redis.incrby "foo", 10 do |r|
|
28
|
+
r.should == 10
|
29
|
+
done
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "decrements the value of a string" do
|
35
|
+
connect do |redis|
|
36
|
+
redis.incr "foo" do |r|
|
37
|
+
r.should == 1
|
38
|
+
redis.decr "foo" do |r|
|
39
|
+
r.should == 0
|
40
|
+
done
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "decrement the value of a string by an amount" do
|
47
|
+
connect do |redis|
|
48
|
+
redis.incrby "foo", 20 do |r|
|
49
|
+
r.should == 20
|
50
|
+
redis.decrby "foo", 10 do |r|
|
51
|
+
r.should == 10
|
52
|
+
done
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it "can 'lpush' to a nonexistent list" do
|
59
|
+
connect do |redis|
|
60
|
+
redis.lpush("foo", "bar") do |r|
|
61
|
+
r.should == 1
|
62
|
+
done
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it "can 'rpush' to a nonexistent list" do
|
68
|
+
connect do |redis|
|
69
|
+
redis.rpush("foo", "bar") do |r|
|
70
|
+
r.should == 1
|
71
|
+
done
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
it "gets the size of the database" do
|
78
|
+
connect do |redis|
|
79
|
+
redis.dbsize do |r|
|
80
|
+
r.should == 0
|
81
|
+
done
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it "adds a member to a nonexistent set" do
|
87
|
+
connect do |redis|
|
88
|
+
redis.sadd("set_foo", "bar") do |r|
|
89
|
+
r.should == 1
|
90
|
+
done
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it "reads info about the db" do
|
96
|
+
connect do |redis|
|
97
|
+
redis.info do |info|
|
98
|
+
info[:redis_version].should_not be_nil
|
99
|
+
done
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
it "can save the db" do
|
105
|
+
connect do |redis|
|
106
|
+
redis.save do |r|
|
107
|
+
r.should == "OK"
|
108
|
+
done
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
it "can save the db in the background" do
|
114
|
+
connect do |redis|
|
115
|
+
redis.bgsave do |r|
|
116
|
+
r.should == "Background saving started"
|
117
|
+
done
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe EventMachine::Hiredis, "connected to a db containing some simple string-valued keys" do
|
124
|
+
def set(&blk)
|
125
|
+
connect do |redis|
|
126
|
+
redis.flushdb
|
127
|
+
redis.set "a", "b"
|
128
|
+
redis.set "x", "y"
|
129
|
+
blk.call(redis)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
it "fetches the values of multiple keys" do
|
134
|
+
set do |redis|
|
135
|
+
redis.mget "a", "x" do |r|
|
136
|
+
r.should == ["b", "y"]
|
137
|
+
done
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
it "fetches all the keys" do
|
143
|
+
set do |redis|
|
144
|
+
redis.keys "*" do |r|
|
145
|
+
r.sort.should == ["a", "x"]
|
146
|
+
done
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
it "sets a value if a key doesn't exist" do
|
152
|
+
set do |redis|
|
153
|
+
redis.setnx "a", "foo" do |r|
|
154
|
+
r.should == 0
|
155
|
+
redis.setnx "zzz", "foo" do |r|
|
156
|
+
r.should == 1
|
157
|
+
done
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
it "tests for the existence of a key" do
|
164
|
+
set do |redis|
|
165
|
+
redis.exists "a" do |r|
|
166
|
+
r.should == 1
|
167
|
+
redis.exists "zzz" do |r|
|
168
|
+
r.should == 0
|
169
|
+
done
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
it "deletes a key" do
|
176
|
+
set do |redis|
|
177
|
+
redis.del "a" do |r|
|
178
|
+
r.should == 1
|
179
|
+
redis.exists "a" do |r|
|
180
|
+
r.should == 0
|
181
|
+
redis.del "a" do |r|
|
182
|
+
r.should == 0
|
183
|
+
done
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
it "detects the type of a key, existing or not" do
|
191
|
+
set do |redis|
|
192
|
+
redis.type "a" do |r|
|
193
|
+
r.should == "string"
|
194
|
+
redis.type "zzz" do |r|
|
195
|
+
r.should == "none"
|
196
|
+
done
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
it "renames a key" do
|
203
|
+
set do |redis|
|
204
|
+
redis.rename "a", "x" do |r|
|
205
|
+
redis.get "x" do |r|
|
206
|
+
r.should == "b"
|
207
|
+
done
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
it "renames a key unless it exists" do
|
214
|
+
set do |redis|
|
215
|
+
redis.renamenx "a", "x" do |r|
|
216
|
+
r.should == 0
|
217
|
+
redis.renamenx "a", "zzz" do |r|
|
218
|
+
r.should == 1
|
219
|
+
redis.get "zzz" do |r|
|
220
|
+
r.should == "b"
|
221
|
+
done
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe EventMachine::Hiredis, "connected to a db containing a list" do
|
230
|
+
def set(&blk)
|
231
|
+
connect do |redis|
|
232
|
+
redis.flushdb
|
233
|
+
redis.lpush "foo", "c"
|
234
|
+
redis.lpush "foo", "b"
|
235
|
+
redis.lpush "foo", "a"
|
236
|
+
blk.call(redis)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
it "sets a list member and 'lindex' to retrieve it" do
|
241
|
+
set do |redis|
|
242
|
+
redis.lset("foo", 1, "bar") do |r|
|
243
|
+
redis.lindex("foo", 1) do |r|
|
244
|
+
r.should == "bar"
|
245
|
+
done
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
it "pushes onto tail of the list" do
|
252
|
+
set do |redis|
|
253
|
+
redis.rpush "foo", "d" do |r|
|
254
|
+
r.should == 4
|
255
|
+
redis.rpop "foo" do |r|
|
256
|
+
r.should == "d"
|
257
|
+
done
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
it "pushes onto the head of the list" do
|
264
|
+
set do |redis|
|
265
|
+
redis.lpush "foo", "d" do |r|
|
266
|
+
r.should == 4
|
267
|
+
redis.lpop "foo" do |r|
|
268
|
+
r.should == "d"
|
269
|
+
done
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
it "pops off the tail of the list" do
|
276
|
+
set do |redis|
|
277
|
+
redis.rpop("foo") do |r|
|
278
|
+
r.should == "c"
|
279
|
+
done
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
it "pops off the tail of the list" do
|
285
|
+
set do |redis|
|
286
|
+
redis.lpop("foo") do |r|
|
287
|
+
r.should == "a"
|
288
|
+
done
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
it "gets a range of values from a list" do
|
294
|
+
set do |redis|
|
295
|
+
redis.lrange("foo", 0, 1) do |r|
|
296
|
+
r.should == ["a", "b"]
|
297
|
+
done
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
it "trims a list" do
|
303
|
+
set do |redis|
|
304
|
+
redis.ltrim("foo", 0, 1) do |r|
|
305
|
+
r.should == "OK"
|
306
|
+
redis.llen("foo") do |r|
|
307
|
+
r.should == 2
|
308
|
+
done
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
it "removes a list element" do
|
315
|
+
set do |redis|
|
316
|
+
redis.lrem("foo", 0, "a") do |r|
|
317
|
+
r.should == 1
|
318
|
+
redis.llen("foo") do |r|
|
319
|
+
r.should == 2
|
320
|
+
done
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
it "detects the type of a list" do
|
327
|
+
set do |redis|
|
328
|
+
redis.type "foo" do |r|
|
329
|
+
r.should == "list"
|
330
|
+
done
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
describe EventMachine::Hiredis, "connected to a db containing two sets" do
|
337
|
+
def set(&blk)
|
338
|
+
connect do |redis|
|
339
|
+
redis.flushdb
|
340
|
+
redis.sadd "foo", "a"
|
341
|
+
redis.sadd "foo", "b"
|
342
|
+
redis.sadd "foo", "c"
|
343
|
+
redis.sadd "bar", "c"
|
344
|
+
redis.sadd "bar", "d"
|
345
|
+
redis.sadd "bar", "e"
|
346
|
+
blk.call(redis)
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
it "finds a set's cardinality" do
|
351
|
+
set do |redis|
|
352
|
+
redis.scard("foo") do |r|
|
353
|
+
r.should == 3
|
354
|
+
done
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
it "adds a new member to a set unless it is a duplicate" do
|
360
|
+
set do |redis|
|
361
|
+
redis.sadd("foo", "d") do |r|
|
362
|
+
r.should == 1 # success
|
363
|
+
redis.sadd("foo", "a") do |r|
|
364
|
+
r.should == 0 # failure
|
365
|
+
redis.scard("foo") do |r|
|
366
|
+
r.should == 4
|
367
|
+
done
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
it "removes a set member if it exists" do
|
375
|
+
set do |redis|
|
376
|
+
redis.srem("foo", "a") do |r|
|
377
|
+
r.should == 1
|
378
|
+
redis.srem("foo", "z") do |r|
|
379
|
+
r.should == 0
|
380
|
+
redis.scard("foo") do |r|
|
381
|
+
r.should == 2
|
382
|
+
done
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
it "retrieves a set's members" do
|
390
|
+
set do |redis|
|
391
|
+
redis.smembers("foo") do |r|
|
392
|
+
r.sort.should == ["a", "b", "c"]
|
393
|
+
done
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
it "detects set membership" do
|
399
|
+
set do |redis|
|
400
|
+
redis.sismember("foo", "a") do |r|
|
401
|
+
r.should == 1
|
402
|
+
redis.sismember("foo", "z") do |r|
|
403
|
+
r.should == 0
|
404
|
+
done
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
it "finds the sets' intersection" do
|
411
|
+
set do |redis|
|
412
|
+
redis.sinter("foo", "bar") do |r|
|
413
|
+
r.should == ["c"]
|
414
|
+
done
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
it "finds and stores the sets' intersection" do
|
420
|
+
set do |redis|
|
421
|
+
redis.sinterstore("baz", "foo", "bar") do |r|
|
422
|
+
r.should == 1
|
423
|
+
redis.smembers("baz") do |r|
|
424
|
+
r.should == ["c"]
|
425
|
+
done
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
it "finds the sets' union" do
|
432
|
+
set do |redis|
|
433
|
+
redis.sunion("foo", "bar") do |r|
|
434
|
+
r.sort.should == ["a","b","c","d","e"]
|
435
|
+
done
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
it "finds and stores the sets' union" do
|
441
|
+
set do |redis|
|
442
|
+
redis.sunionstore("baz", "foo", "bar") do |r|
|
443
|
+
r.should == 5
|
444
|
+
redis.smembers("baz") do |r|
|
445
|
+
r.sort.should == ["a","b","c","d","e"]
|
446
|
+
done
|
447
|
+
end
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
it "detects the type of a set" do
|
453
|
+
set do |redis|
|
454
|
+
redis.type "foo" do |r|
|
455
|
+
r.should == "set"
|
456
|
+
done
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
describe EventMachine::Hiredis, "connected to a db containing three linked lists" do
|
463
|
+
def set(&blk)
|
464
|
+
connect do |redis|
|
465
|
+
redis.flushdb
|
466
|
+
redis.rpush "foo", "a"
|
467
|
+
redis.rpush "foo", "b"
|
468
|
+
redis.set "a_sort", "2"
|
469
|
+
redis.set "b_sort", "1"
|
470
|
+
redis.set "a_data", "foo"
|
471
|
+
redis.set "b_data", "bar"
|
472
|
+
blk.call(redis)
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
it "collates a sorted set of data" do
|
477
|
+
set do |redis|
|
478
|
+
redis.sort("foo", "BY", "*_sort", "GET", "*_data") do |r|
|
479
|
+
r.should == ["bar", "foo"]
|
480
|
+
done
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
it "gets keys selectively" do
|
486
|
+
set do |redis|
|
487
|
+
redis.keys "a_*" do |r|
|
488
|
+
r.sort.should == ["a_sort", "a_data"].sort
|
489
|
+
done
|
490
|
+
end
|
491
|
+
end
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
describe EventMachine::Hiredis, "when reconnecting" do
|
496
|
+
it "select previously selected dataset" do
|
497
|
+
connect do |redis|
|
498
|
+
#simulate disconnect
|
499
|
+
redis.set('foo', 'a') { redis.close_connection_after_writing }
|
500
|
+
|
501
|
+
EventMachine.add_timer(2) do
|
502
|
+
redis.get('foo') do |r|
|
503
|
+
r.should == 'a'
|
504
|
+
redis.get('non_existing') do |r|
|
505
|
+
r.should == nil
|
506
|
+
done
|
507
|
+
end
|
508
|
+
end
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
end
|