redic-rb 1.6
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/.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: []
|