redic-rb 1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gems +2 -0
- data/.gitignore +1 -0
- data/CHANGELOG +35 -0
- data/CONTRIBUTING +19 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +59 -0
- data/LICENSE +19 -0
- data/README.md +142 -0
- data/bin/console +12 -0
- data/bin/setup +8 -0
- data/lib/redic/client.rb +116 -0
- data/lib/redic/connection.rb +17 -0
- data/lib/redic.rb +75 -0
- data/makefile +2 -0
- data/redic-rb.gemspec +19 -0
- data/tests/multithreaded_test.rb +54 -0
- data/tests/redic_test.rb +232 -0
- data/tests/unix_test.rb +27 -0
- metadata +108 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1e6a500c5ac8d333481dcc65b4c7f9eb9dddc322f80fdc691d73aa0d426fadee
|
4
|
+
data.tar.gz: '09da31f87e4089f3100256ad018ec6f0e687c9ed0e46c316b8e404831c3045d4'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a080ef7608225c3587c66f4f6115af50710686218b26e2a95dff8cb67d6baba9a3f8f8566c30872a41a6d014a04afd003650f21f4ef7920c4231e4f78df07e75
|
7
|
+
data.tar.gz: 894767f8f0a73c5440ef5dffc237ce4190b92237eb485803d8de9acbb682de65ee04dccd03046abeec6b2bcb3d40e701425d9bcc697f16a0a9ce7bff7bf1404e
|
data/.gems
ADDED
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
/.gs
|
data/CHANGELOG
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
1.5.1
|
2
|
+
|
3
|
+
- Allow timeout to be changed.
|
4
|
+
|
5
|
+
1.5.0
|
6
|
+
|
7
|
+
- Raise runtime errors when using `call!`.
|
8
|
+
|
9
|
+
1.4.1
|
10
|
+
|
11
|
+
- Reconnect only if URLs differ.
|
12
|
+
|
13
|
+
1.4.0
|
14
|
+
|
15
|
+
- Close connections with Redic#quit.
|
16
|
+
|
17
|
+
1.3.0
|
18
|
+
|
19
|
+
- Improve support for multithreaded environments.
|
20
|
+
|
21
|
+
1.2.0
|
22
|
+
|
23
|
+
- Add Redic#configure.
|
24
|
+
|
25
|
+
1.1.1
|
26
|
+
|
27
|
+
- Redic can now connect to sentinels.
|
28
|
+
|
29
|
+
1.1.0
|
30
|
+
|
31
|
+
- Redic is now compatible with all rubies.
|
32
|
+
|
33
|
+
1.0.0
|
34
|
+
|
35
|
+
- Add a configurable timeout.
|
data/CONTRIBUTING
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
This code tries to solve a particular problem with a very simple
|
2
|
+
implementation. We try to keep the code to a minimum while making
|
3
|
+
it as clear as possible. The design is very likely finished, and
|
4
|
+
if some feature is missing it is possible that it was left out on
|
5
|
+
purpose. That said, new usage patterns may arise, and when that
|
6
|
+
happens we are ready to adapt if necessary.
|
7
|
+
|
8
|
+
A good first step for contributing is to meet us on IRC and discuss
|
9
|
+
ideas. We spend a lot of time on #lesscode at freenode, always ready
|
10
|
+
to talk about code and simplicity. If connecting to IRC is not an
|
11
|
+
option, you can create an issue explaining the proposed change and
|
12
|
+
a use case. We pay a lot of attention to use cases, because our
|
13
|
+
goal is to keep the code base simple. Usually the result of a
|
14
|
+
conversation is the creation of a different tool.
|
15
|
+
|
16
|
+
Please don't start the conversation with a pull request. The code
|
17
|
+
should come at last, and even though it may help to convey an idea,
|
18
|
+
more often than not it draws the attention to a particular
|
19
|
+
implementation.
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
redic-rb (1.6)
|
5
|
+
redis (~> 4.1)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
awesome_print (1.8.0)
|
11
|
+
byebug (11.0.0)
|
12
|
+
clap (1.0.0)
|
13
|
+
coderay (1.1.2)
|
14
|
+
coolline (0.5.0)
|
15
|
+
unicode_utils (~> 1.4)
|
16
|
+
cutest (1.2.3)
|
17
|
+
clap
|
18
|
+
hirb (0.7.3)
|
19
|
+
hirb-unicode-steakknife (0.0.8)
|
20
|
+
hirb (~> 0.5)
|
21
|
+
unicode-display_width (~> 1.1)
|
22
|
+
method_source (0.9.2)
|
23
|
+
pry (0.12.2)
|
24
|
+
coderay (~> 1.1.0)
|
25
|
+
method_source (~> 0.9.0)
|
26
|
+
pry-byebug (3.7.0)
|
27
|
+
byebug (~> 11.0)
|
28
|
+
pry (~> 0.10)
|
29
|
+
pry-coolline (0.2.5)
|
30
|
+
coolline (~> 0.5)
|
31
|
+
pry-doc (0.13.5)
|
32
|
+
pry (~> 0.11)
|
33
|
+
yard (~> 0.9.11)
|
34
|
+
pry-rails (0.3.9)
|
35
|
+
pry (>= 0.10.4)
|
36
|
+
redis (4.1.0)
|
37
|
+
spirit_hands (2.1.5)
|
38
|
+
awesome_print (~> 1.6)
|
39
|
+
hirb (~> 0.7)
|
40
|
+
hirb-unicode-steakknife (~> 0.0)
|
41
|
+
pry (~> 0.10)
|
42
|
+
pry-byebug (~> 3.4)
|
43
|
+
pry-coolline (~> 0.2)
|
44
|
+
pry-doc (~> 0.8)
|
45
|
+
pry-rails (~> 0.3)
|
46
|
+
unicode-display_width (1.5.0)
|
47
|
+
unicode_utils (1.4.0)
|
48
|
+
yard (0.9.18)
|
49
|
+
|
50
|
+
PLATFORMS
|
51
|
+
ruby
|
52
|
+
|
53
|
+
DEPENDENCIES
|
54
|
+
cutest (~> 1.2)
|
55
|
+
redic-rb!
|
56
|
+
spirit_hands (~> 2.1)
|
57
|
+
|
58
|
+
BUNDLED WITH
|
59
|
+
1.16.1
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2013-2014 Michel Martens, Cyril David
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
Redic
|
2
|
+
=====
|
3
|
+
|
4
|
+
Lightweight Redis Client
|
5
|
+
|
6
|
+
Description
|
7
|
+
-----------
|
8
|
+
|
9
|
+
Lightweight Redis Client inspired by [redigo][redigo], a Redis
|
10
|
+
client library for golang.
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
# Accepts a Redis URL and defaults to "redis://127.0.0.1:6379".
|
16
|
+
redis = Redic.new
|
17
|
+
|
18
|
+
# Processes the command and returns the response.
|
19
|
+
redis.call("SET", "foo", "bar")
|
20
|
+
|
21
|
+
assert_equal "bar", redis.call("GET", "foo")
|
22
|
+
|
23
|
+
# Pipelining is implemented by buffering commands,
|
24
|
+
# then calling Redic#commit
|
25
|
+
redis.queue("SET", "foo", "bar")
|
26
|
+
redis.queue("GET", "foo")
|
27
|
+
|
28
|
+
assert_equal ["OK", "bar"], redis.commit
|
29
|
+
```
|
30
|
+
|
31
|
+
You can provide the password and the database to be selected. The
|
32
|
+
format for Redis URLs is `redis://user:pass@host:port/db`. As
|
33
|
+
Redis only needs a password for authentication, the user can be
|
34
|
+
omitted:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
# Connect to localhost:6380 using "bar" as password and use the
|
38
|
+
# database 2. Both AUTH and SELECT commands are issued after
|
39
|
+
# connecting. The user part of the URL is not provided.
|
40
|
+
redis = Redic.new("redis://:bar@localhost:6380/2")
|
41
|
+
```
|
42
|
+
|
43
|
+
It is also possible to configure a timeout for the connection. The
|
44
|
+
default timeout is 10 seconds.
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
# Timeout expressed in microseconds.
|
48
|
+
redis = Redic.new(REDIS_URL, 2_000_000)
|
49
|
+
redis.timeout == 2_000_000 #=> true
|
50
|
+
```
|
51
|
+
A client can be re-configured, forcing the next connection to
|
52
|
+
be established with the new details:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
redis = Redic.new("redis://localhost:6379")
|
56
|
+
redis.configure("redis://localhost:6380")
|
57
|
+
```
|
58
|
+
|
59
|
+
Here's one final example using both a Redis URL and a timeout:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
# It's recommended to store the REDIS_URL as an environment
|
63
|
+
# variable. Use `fetch` to retrieve values that must be present,
|
64
|
+
# as it raises an error if the value is not found.
|
65
|
+
REDIS_URL = ENV.fetch("REDIS_URL")
|
66
|
+
REDIS_TIMEOUT = ENV.fetch("REDIS_TIMEOUT")
|
67
|
+
|
68
|
+
redis = Redic.new(REDIS_URL, REDIS_TIMEOUT)
|
69
|
+
```
|
70
|
+
|
71
|
+
Both the initializer and the `configure` method accept a `URL` and
|
72
|
+
a `timeout`.
|
73
|
+
|
74
|
+
In order to close the connection, call `quit`:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
redis = Redic.new("redis://localhost:6379")
|
78
|
+
redis.quit
|
79
|
+
```
|
80
|
+
|
81
|
+
With that command, `"QUIT"` is sent to Redis and the socket is closed.
|
82
|
+
|
83
|
+
## Differences with redis-rb
|
84
|
+
|
85
|
+
Redic uses [hiredis][hiredis] for the connection and for parsing
|
86
|
+
the replies. There are no alternative connection drivers. Unlike
|
87
|
+
[redis-rb][redis-rb] it doesn't define all the Redis commands, and
|
88
|
+
instead it acts as a transport layer. The lock provided is smaller
|
89
|
+
than that of redis-rb, as it only wraps the writing and reading from
|
90
|
+
the connection. So even if both clients are thread-safe by default,
|
91
|
+
the peformance of a smaller lock is marginally better.
|
92
|
+
|
93
|
+
[redigo]: https://github.com/garyburd/redigo
|
94
|
+
[hiredis]: https://github.com/pietern/hiredis-rb
|
95
|
+
[redis-rb]: https://github.com/redis/redis-rb
|
96
|
+
|
97
|
+
## Limitations
|
98
|
+
|
99
|
+
When a client enters a subscribed mode, further reads to retrieve the
|
100
|
+
messages are not thread safe. It is very important to take this into
|
101
|
+
account and to create a different client if you need to send different
|
102
|
+
operations while a client is subscribed to a channel.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
# Example of pub/sub usage.
|
106
|
+
c1 = Redic.new
|
107
|
+
c2 = Redic.new
|
108
|
+
|
109
|
+
# After this command, the client is no longer thread safe.
|
110
|
+
c1.call("SUBSCRIBE", "foo")
|
111
|
+
|
112
|
+
# That's why we need to publish from a different connection.
|
113
|
+
c2.call("PUBLISH", "foo")
|
114
|
+
|
115
|
+
# Note that this operation is not thread safe.
|
116
|
+
assert_equal ["message", "foo", "value1"], c1.client.read
|
117
|
+
```
|
118
|
+
|
119
|
+
You can wrap thread unsafe operations in a mutex:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
redis = Redic.new
|
123
|
+
|
124
|
+
mutex = Mutex.new
|
125
|
+
|
126
|
+
mutex.synchronize do
|
127
|
+
redis.call("MONITOR")
|
128
|
+
|
129
|
+
# Display every command sent to Redis.
|
130
|
+
loop do
|
131
|
+
puts redis.client.read
|
132
|
+
end
|
133
|
+
end
|
134
|
+
```
|
135
|
+
|
136
|
+
## Installation
|
137
|
+
|
138
|
+
You can install it using rubygems.
|
139
|
+
|
140
|
+
```
|
141
|
+
$ gem install redic
|
142
|
+
```
|
data/bin/console
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "redic"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
require "pry"
|
11
|
+
Pry.start
|
12
|
+
|
data/bin/setup
ADDED
data/lib/redic/client.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require_relative "connection"
|
2
|
+
require "uri"
|
3
|
+
require "redis"
|
4
|
+
|
5
|
+
class Redic
|
6
|
+
class Client
|
7
|
+
EMPTY = "".freeze
|
8
|
+
SLASH = "/".freeze
|
9
|
+
|
10
|
+
attr_accessor :timeout
|
11
|
+
|
12
|
+
def initialize(url, timeout)
|
13
|
+
@semaphore = Mutex.new
|
14
|
+
@connection = false
|
15
|
+
|
16
|
+
configure(url, timeout)
|
17
|
+
end
|
18
|
+
|
19
|
+
def configure(url, timeout)
|
20
|
+
disconnect!
|
21
|
+
|
22
|
+
@uri = URI.parse(url)
|
23
|
+
@timeout = timeout
|
24
|
+
end
|
25
|
+
|
26
|
+
def read
|
27
|
+
#@connection.read
|
28
|
+
@response
|
29
|
+
end
|
30
|
+
|
31
|
+
def write(command)
|
32
|
+
#@connection.write(command)
|
33
|
+
cmd_name = command.shift.to_s.downcase
|
34
|
+
block = command.find{ |cmd| cmd.is_a? Proc }
|
35
|
+
#binding.pry if cmd_name == "subscribe"
|
36
|
+
@response = if command.empty?
|
37
|
+
@connection.send(cmd_name)
|
38
|
+
else
|
39
|
+
@connection.send(cmd_name, *command, &block)
|
40
|
+
end
|
41
|
+
rescue => e
|
42
|
+
return raise e if e.message =~ /ERR invalid DB index/
|
43
|
+
return raise e if e.message =~ /ERR invalid password/
|
44
|
+
@response = RuntimeError.new(e.message)
|
45
|
+
end
|
46
|
+
|
47
|
+
def connect
|
48
|
+
establish_connection unless connected?
|
49
|
+
|
50
|
+
@semaphore.synchronize do
|
51
|
+
yield
|
52
|
+
end
|
53
|
+
rescue Errno::ECONNRESET
|
54
|
+
@connection = false
|
55
|
+
retry
|
56
|
+
end
|
57
|
+
|
58
|
+
def connected?
|
59
|
+
@connection && @connection.connected?
|
60
|
+
end
|
61
|
+
|
62
|
+
def disconnect!
|
63
|
+
if connected?
|
64
|
+
@connection.disconnect!
|
65
|
+
@connection = false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def quit
|
70
|
+
if connected?
|
71
|
+
assert_ok(call("QUIT"))
|
72
|
+
disconnect!
|
73
|
+
|
74
|
+
true
|
75
|
+
else
|
76
|
+
false
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def establish_connection
|
82
|
+
begin
|
83
|
+
#Redic::Connection.new(@uri, @timeout)
|
84
|
+
@connection = Redis.new(url: @uri)
|
85
|
+
raise StandardError if @connection.ping != "PONG"
|
86
|
+
rescue StandardError => err
|
87
|
+
raise err, "Can't connect to: #{@uri} because: #{err.message}"
|
88
|
+
end
|
89
|
+
|
90
|
+
if @uri.scheme != "unix"
|
91
|
+
if @uri.password
|
92
|
+
assert_ok(call("AUTH", @uri.password))
|
93
|
+
end
|
94
|
+
|
95
|
+
if @uri.path != EMPTY && @uri.path != SLASH
|
96
|
+
assert_ok(call("SELECT", @uri.path[1..-1]))
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def call(*args)
|
102
|
+
@semaphore.synchronize do
|
103
|
+
write(args)
|
104
|
+
read
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def assert(value, error)
|
109
|
+
raise error unless value
|
110
|
+
end
|
111
|
+
|
112
|
+
def assert_ok(reply)
|
113
|
+
assert(reply == "OK", reply)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#require "hiredis/connection"
|
2
|
+
#
|
3
|
+
#class Redic
|
4
|
+
# module Connection
|
5
|
+
# def self.new(uri, timeout)
|
6
|
+
# connection = Hiredis::Connection.new
|
7
|
+
#
|
8
|
+
# if uri.scheme == "unix"
|
9
|
+
# connection.connect_unix(uri.path, timeout)
|
10
|
+
# else
|
11
|
+
# connection.connect(uri.host, uri.port, timeout)
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# connection
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#end
|
data/lib/redic.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'redis'
|
2
|
+
require_relative "redic/client"
|
3
|
+
|
4
|
+
class Redic
|
5
|
+
attr :url
|
6
|
+
attr :client
|
7
|
+
|
8
|
+
def initialize(url = "redis://127.0.0.1:6379", timeout = 10_000_000)
|
9
|
+
@url = url
|
10
|
+
@client = Redic::Client.new(url, timeout)
|
11
|
+
@buffer = Hash.new { |h, k| h[k] = [] }
|
12
|
+
end
|
13
|
+
|
14
|
+
def buffer
|
15
|
+
@buffer[Thread.current.object_id]
|
16
|
+
end
|
17
|
+
|
18
|
+
def reset
|
19
|
+
@buffer.delete(Thread.current.object_id)
|
20
|
+
end
|
21
|
+
|
22
|
+
def clear
|
23
|
+
@buffer.clear
|
24
|
+
end
|
25
|
+
|
26
|
+
def configure(url, timeout = 10_000_000)
|
27
|
+
if @url != url
|
28
|
+
@url = url
|
29
|
+
@client.configure(url, timeout)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def call(*args)
|
34
|
+
@client.connect do
|
35
|
+
@client.write(args)
|
36
|
+
@client.read
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def call!(*args)
|
41
|
+
reply = call(*args)
|
42
|
+
|
43
|
+
if RuntimeError === reply
|
44
|
+
raise reply
|
45
|
+
end
|
46
|
+
|
47
|
+
return reply
|
48
|
+
end
|
49
|
+
|
50
|
+
def queue(*args)
|
51
|
+
buffer << args
|
52
|
+
end
|
53
|
+
|
54
|
+
def commit
|
55
|
+
@client.connect do
|
56
|
+
buffer.map do |args|
|
57
|
+
@client.write(args)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
ensure
|
61
|
+
reset
|
62
|
+
end
|
63
|
+
|
64
|
+
def timeout
|
65
|
+
@client.timeout
|
66
|
+
end
|
67
|
+
|
68
|
+
def timeout=(timeout)
|
69
|
+
@client.timeout = timeout
|
70
|
+
end
|
71
|
+
|
72
|
+
def quit
|
73
|
+
@client.quit
|
74
|
+
end
|
75
|
+
end
|
data/makefile
ADDED
data/redic-rb.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "redic-rb"
|
5
|
+
s.version = "1.6"
|
6
|
+
s.summary = "Lightweight Redis Client"
|
7
|
+
s.description = "Lightweight Redis Client"
|
8
|
+
s.authors = ["Michel Martens", "Cyril David", "Diego Gomez"]
|
9
|
+
s.email = ["michel@soveran.com", "cyx@cyx.is", "diego.f.gomez.pardo@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/degz/redic-rb"
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.license = "MIT"
|
13
|
+
|
14
|
+
s.add_dependency "redis", "~> 4.1"
|
15
|
+
|
16
|
+
s.add_development_dependency "spirit_hands", "~> 2.1"
|
17
|
+
s.add_development_dependency "cutest", "~> 1.2"
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "cutest"
|
2
|
+
require_relative "../lib/redic"
|
3
|
+
|
4
|
+
REDIS_URL = "redis://localhost:6379/"
|
5
|
+
|
6
|
+
prepare do
|
7
|
+
c = Redic.new(REDIS_URL)
|
8
|
+
|
9
|
+
begin
|
10
|
+
c.call("FLUSHDB")
|
11
|
+
rescue
|
12
|
+
c.call("AUTH", "foo")
|
13
|
+
c.call("FLUSHDB")
|
14
|
+
c.call("CONFIG", "SET", "requirepass", "")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
test "multiple threads" do
|
19
|
+
|
20
|
+
cs = Array.new
|
21
|
+
|
22
|
+
c = Redic.new(REDIS_URL)
|
23
|
+
|
24
|
+
c.queue("SET", "foo", "1")
|
25
|
+
|
26
|
+
t1 = Thread.new do
|
27
|
+
c.queue("SET", "bar", "2")
|
28
|
+
end
|
29
|
+
|
30
|
+
t2 = Thread.new do
|
31
|
+
c.queue("SET", "baz", "3")
|
32
|
+
c.commit
|
33
|
+
end
|
34
|
+
|
35
|
+
t1.join
|
36
|
+
t2.join
|
37
|
+
|
38
|
+
assert_equal nil, c.call("GET", "foo")
|
39
|
+
assert_equal nil, c.call("GET", "bar")
|
40
|
+
assert_equal "3", c.call("GET", "baz")
|
41
|
+
|
42
|
+
c.commit
|
43
|
+
|
44
|
+
# The buffer for `c` still exists
|
45
|
+
assert_equal "1", c.call("GET", "foo")
|
46
|
+
|
47
|
+
# Buffer for the thread that didn't commit is the only one left
|
48
|
+
assert_equal 1, c.instance_variable_get("@buffer").keys.size
|
49
|
+
|
50
|
+
c.clear
|
51
|
+
|
52
|
+
# All buffers are cleared
|
53
|
+
assert_equal 0, c.instance_variable_get("@buffer").keys.size
|
54
|
+
end
|
data/tests/redic_test.rb
ADDED
@@ -0,0 +1,232 @@
|
|
1
|
+
require "cutest"
|
2
|
+
require_relative "../lib/redic"
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
REDIS_URL = "redis://localhost:6379/0"
|
6
|
+
|
7
|
+
prepare do
|
8
|
+
c = Redic.new(REDIS_URL)
|
9
|
+
|
10
|
+
begin
|
11
|
+
c.call!("FLUSHDB").inspect
|
12
|
+
rescue
|
13
|
+
c.call!("AUTH", "foo")
|
14
|
+
c.call!("SELECT", "0")
|
15
|
+
c.call!("FLUSHDB")
|
16
|
+
c.call!("CONFIG", "SET", "requirepass", "")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
setup do
|
21
|
+
Redic.new(REDIS_URL)
|
22
|
+
end
|
23
|
+
|
24
|
+
test "url" do |c|
|
25
|
+
assert_equal "redis://localhost:6379/0", c.url
|
26
|
+
end
|
27
|
+
|
28
|
+
test "select db from url" do |c1|
|
29
|
+
|
30
|
+
# Connect to db 1
|
31
|
+
c2 = Redic.new("redis://localhost:6379/2")
|
32
|
+
|
33
|
+
c2.call("FLUSHDB")
|
34
|
+
|
35
|
+
# Set a value in db 0
|
36
|
+
c1.call("SET", "foo", "bar")
|
37
|
+
|
38
|
+
assert_equal(1, c1.call("DBSIZE"))
|
39
|
+
assert_equal(0, c2.call("DBSIZE"))
|
40
|
+
end
|
41
|
+
|
42
|
+
test "error when selecting db from url" do
|
43
|
+
c2 = Redic.new("redis://localhost:6379/foo")
|
44
|
+
|
45
|
+
assert_raise(RuntimeError) do
|
46
|
+
|
47
|
+
# Connection is lazy, send a command
|
48
|
+
c2.call("PING")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
test "error when authenticating from url" do |c1|
|
53
|
+
|
54
|
+
# Configure password as "foo"
|
55
|
+
c1.call("CONFIG", "SET", "requirepass", "foo")
|
56
|
+
|
57
|
+
# The password provided is wrong
|
58
|
+
c2 = Redic.new("redis://:bar@localhost:6379/")
|
59
|
+
|
60
|
+
assert_raise(RuntimeError) do
|
61
|
+
|
62
|
+
# Connection is lazy, send a command
|
63
|
+
c2.call("PING")
|
64
|
+
end
|
65
|
+
|
66
|
+
c3 = Redic.new("redis://:foo@localhost:6379/")
|
67
|
+
assert_equal "PONG", c3.call("PING")
|
68
|
+
|
69
|
+
# Remove password
|
70
|
+
c3.call("CONFIG", "SET", "requirepass", "")
|
71
|
+
end
|
72
|
+
|
73
|
+
#test "Can connect to sentinel" do
|
74
|
+
# c2 = Redic.new "redis://localhost:26379"
|
75
|
+
# c2.call "SENTINEL", "masters"
|
76
|
+
#end
|
77
|
+
|
78
|
+
test "timeout" do |c1|
|
79
|
+
|
80
|
+
# Default timeout is 10 seconds
|
81
|
+
assert_equal 10_000_000, c1.timeout
|
82
|
+
|
83
|
+
# Timeout configured to 200_000 microseconds
|
84
|
+
c2 = Redic.new(REDIS_URL, 200_000)
|
85
|
+
|
86
|
+
assert_equal 200_000, c2.timeout
|
87
|
+
|
88
|
+
# Change timeout to 5 seconds
|
89
|
+
c2.timeout = 5_000_000
|
90
|
+
|
91
|
+
assert_equal 5_000_000, c2.timeout
|
92
|
+
end
|
93
|
+
|
94
|
+
test "normal commands" do |c|
|
95
|
+
c.call("SET", "foo", "bar")
|
96
|
+
|
97
|
+
assert_equal "bar", c.call("GET", "foo")
|
98
|
+
end
|
99
|
+
|
100
|
+
test "pipelining" do |c|
|
101
|
+
c.queue("SET", "foo", "perro")
|
102
|
+
c.queue("GET", "foo")
|
103
|
+
|
104
|
+
assert_equal ["OK", "perro"], c.commit
|
105
|
+
end
|
106
|
+
|
107
|
+
test "multi/exec" do |c|
|
108
|
+
c.queue("MULTI")
|
109
|
+
c.queue("SET", "foo", "bar")
|
110
|
+
c.queue("EXEC")
|
111
|
+
|
112
|
+
assert_equal ["OK", "QUEUED", ["OK"]], c.commit
|
113
|
+
end
|
114
|
+
|
115
|
+
test "runtime errors" do |c|
|
116
|
+
res = c.call("KABLAMMO")
|
117
|
+
|
118
|
+
assert res.is_a?(RuntimeError)
|
119
|
+
end
|
120
|
+
|
121
|
+
test "encoding" do |c|
|
122
|
+
Encoding.default_external = "UTF-8"
|
123
|
+
|
124
|
+
c.call("SET", "foo", "שלום")
|
125
|
+
|
126
|
+
assert_equal "Shalom שלום", "Shalom " + c.call("GET", "foo")
|
127
|
+
|
128
|
+
end if defined?(Encoding)
|
129
|
+
|
130
|
+
test "errors in pipeline" do |c|
|
131
|
+
c.queue("SET", "foo", "bar")
|
132
|
+
c.queue("INCR", "foo")
|
133
|
+
c.queue("GET", "foo")
|
134
|
+
|
135
|
+
res = c.commit
|
136
|
+
|
137
|
+
assert "OK" == res[0]
|
138
|
+
assert RuntimeError === res[1]
|
139
|
+
assert "bar" == res[2]
|
140
|
+
end
|
141
|
+
|
142
|
+
test "thread safety" do |c|
|
143
|
+
c.call("SET", "foo", 1)
|
144
|
+
c.call("SET", "bar", 2)
|
145
|
+
|
146
|
+
foos, bars = nil, nil
|
147
|
+
|
148
|
+
t1 = Thread.new do
|
149
|
+
foos = Array.new(100) { c.call("GET", "foo") }
|
150
|
+
end
|
151
|
+
|
152
|
+
t2 = Thread.new do
|
153
|
+
bars = Array.new(100) { c.call("GET", "bar") }
|
154
|
+
end
|
155
|
+
|
156
|
+
t1.join
|
157
|
+
t2.join
|
158
|
+
|
159
|
+
assert_equal ["1"], foos.uniq
|
160
|
+
assert_equal ["2"], bars.uniq
|
161
|
+
end
|
162
|
+
|
163
|
+
test "blocking commands" do |c1|
|
164
|
+
c2 = Redic.new
|
165
|
+
r = nil
|
166
|
+
|
167
|
+
t1 = Thread.new do
|
168
|
+
r = c1.call("BLPOP", "foo", 5)
|
169
|
+
end
|
170
|
+
|
171
|
+
t2 = Thread.new do
|
172
|
+
c2.call("RPUSH", "foo", "value")
|
173
|
+
end
|
174
|
+
|
175
|
+
t1.join
|
176
|
+
t2.join
|
177
|
+
|
178
|
+
assert_equal ["foo", "value"], r
|
179
|
+
end
|
180
|
+
|
181
|
+
#test "pub/sub" do |c1|
|
182
|
+
# c2 = Redic.new
|
183
|
+
#
|
184
|
+
# binding.pry
|
185
|
+
# res = c1.call("SUBSCRIBE", "foo", Proc.new{})
|
186
|
+
# assert_equal ["subscribe", "foo", 1], res
|
187
|
+
#
|
188
|
+
# c2.call("PUBLISH", "foo", "value1")
|
189
|
+
# c2.call("PUBLISH", "foo", "value2")
|
190
|
+
#
|
191
|
+
# assert_equal ["message", "foo", "value1"], c1.client.read
|
192
|
+
# assert_equal ["message", "foo", "value2"], c1.client.read
|
193
|
+
#
|
194
|
+
# c1.call("UNSUBSCRIBE", "foo")
|
195
|
+
#
|
196
|
+
# assert_equal "PONG", c1.call("PING")
|
197
|
+
#end
|
198
|
+
|
199
|
+
test "reconnect" do |c1|
|
200
|
+
url = "redis://:foo@localhost:6379/"
|
201
|
+
|
202
|
+
assert url != c1.url
|
203
|
+
|
204
|
+
c1.call("CONFIG", "SET", "requirepass", "foo")
|
205
|
+
|
206
|
+
c1.configure(url)
|
207
|
+
|
208
|
+
assert url == c1.url
|
209
|
+
assert url.object_id == c1.url.object_id
|
210
|
+
|
211
|
+
# Reconfigure only if URLs differ
|
212
|
+
c1.configure(url.dup)
|
213
|
+
|
214
|
+
# No reconnection ocurred
|
215
|
+
assert url.object_id == c1.url.object_id
|
216
|
+
|
217
|
+
assert_equal "PONG", c1.call("PING")
|
218
|
+
end
|
219
|
+
|
220
|
+
test "disconnect" do |c1|
|
221
|
+
|
222
|
+
# Connection is lazy
|
223
|
+
assert_equal false, c1.client.connected?
|
224
|
+
assert_equal false, c1.quit
|
225
|
+
|
226
|
+
c1.call("PING")
|
227
|
+
|
228
|
+
assert_equal true, c1.client.connected?
|
229
|
+
assert_equal true, c1.quit
|
230
|
+
|
231
|
+
assert_equal false, c1.client.connected?
|
232
|
+
end
|
data/tests/unix_test.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require "cutest"
|
2
|
+
require_relative "../lib/redic"
|
3
|
+
|
4
|
+
setup do
|
5
|
+
Redic.new("unix:///tmp/redis.6379.sock")
|
6
|
+
end
|
7
|
+
|
8
|
+
test "normal commands" do |c|
|
9
|
+
c.call("SET", "foo", "bar")
|
10
|
+
|
11
|
+
assert_equal "bar", c.call("GET", "foo")
|
12
|
+
end
|
13
|
+
|
14
|
+
test "pipelining" do |c|
|
15
|
+
c.queue("SET", "foo", "bar")
|
16
|
+
c.queue("GET", "foo")
|
17
|
+
|
18
|
+
assert_equal ["OK", "bar"], c.commit
|
19
|
+
end
|
20
|
+
|
21
|
+
test "multi/exec" do |c|
|
22
|
+
c.queue("MULTI")
|
23
|
+
c.queue("SET", "foo", "bar")
|
24
|
+
c.queue("EXEC")
|
25
|
+
|
26
|
+
assert_equal ["OK", "QUEUED", ["OK"]], c.commit
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redic-rb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.6'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michel Martens
|
8
|
+
- Cyril David
|
9
|
+
- Diego Gomez
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2019-03-11 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: redis
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - "~>"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '4.1'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - "~>"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '4.1'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: spirit_hands
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "~>"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '2.1'
|
36
|
+
type: :development
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '2.1'
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: cutest
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '1.2'
|
50
|
+
type: :development
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - "~>"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '1.2'
|
57
|
+
description: Lightweight Redis Client
|
58
|
+
email:
|
59
|
+
- michel@soveran.com
|
60
|
+
- cyx@cyx.is
|
61
|
+
- diego.f.gomez.pardo@gmail.com
|
62
|
+
executables: []
|
63
|
+
extensions: []
|
64
|
+
extra_rdoc_files: []
|
65
|
+
files:
|
66
|
+
- ".gems"
|
67
|
+
- ".gitignore"
|
68
|
+
- CHANGELOG
|
69
|
+
- CONTRIBUTING
|
70
|
+
- Gemfile
|
71
|
+
- Gemfile.lock
|
72
|
+
- LICENSE
|
73
|
+
- README.md
|
74
|
+
- bin/console
|
75
|
+
- bin/setup
|
76
|
+
- lib/redic.rb
|
77
|
+
- lib/redic/client.rb
|
78
|
+
- lib/redic/connection.rb
|
79
|
+
- makefile
|
80
|
+
- redic-rb.gemspec
|
81
|
+
- tests/multithreaded_test.rb
|
82
|
+
- tests/redic_test.rb
|
83
|
+
- tests/unix_test.rb
|
84
|
+
homepage: https://github.com/degz/redic-rb
|
85
|
+
licenses:
|
86
|
+
- MIT
|
87
|
+
metadata: {}
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 2.7.3
|
105
|
+
signing_key:
|
106
|
+
specification_version: 4
|
107
|
+
summary: Lightweight Redis Client
|
108
|
+
test_files: []
|