disque 0.0.1.alpha → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/AUTHORS +2 -0
- data/README.md +28 -7
- data/disque.gemspec +5 -5
- data/lib/disque.rb +195 -2
- data/makefile +27 -0
- data/tests/disque_test.rb +237 -0
- metadata +12 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a89227c0d69455fc6897e54b94b06239d0e0ad56
|
4
|
+
data.tar.gz: a913c4b33ba3bee489f1a87a442675cb0b8d7128
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a64e05ec0ffe98863874e148b7ccad5ab34587b5edcf10a5f8dcef1149f67f0a595d96b2f25b61f472e50d53783c80eb66bdc29806280a07b53ac9f42e55a512
|
7
|
+
data.tar.gz: ff5b61bd64b033056a42d5f5d162fa7fb8c8e4a57e484f1aa7f91a344ad4a74941a0b8484618b4a8a39d4a47c6875b89c674e46e07667bb6799d5fe62ae8ba87
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
/tmp
|
data/AUTHORS
ADDED
data/README.md
CHANGED
@@ -3,18 +3,39 @@ Disque.rb
|
|
3
3
|
|
4
4
|
Client for Disque, an in-memory, distributed job queue.
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
Usage
|
7
|
+
-----
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
Create a new Disque client by passing a list of nodes:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
client = Disque.new(["127.0.0.1:7711", "127.0.0.1:7712", "127.0.0.1:7713"])
|
13
|
+
```
|
14
|
+
|
15
|
+
Now you can add jobs:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
client.push("foo", "bar", 100)
|
19
|
+
```
|
20
|
+
|
21
|
+
It will push the job "bar" to the queue "foo" with a timeout of 100
|
22
|
+
ms, and return the id of the job if it was received and replicated
|
23
|
+
in time.
|
24
|
+
|
25
|
+
Then, your workers will do something like this:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
loop do
|
29
|
+
client.fetch(from: ["foo"]) do |job|
|
30
|
+
# Do something with `job`
|
31
|
+
end
|
32
|
+
end
|
33
|
+
```
|
13
34
|
|
14
35
|
Installation
|
15
36
|
------------
|
16
37
|
|
17
|
-
You
|
38
|
+
You can install it using rubygems.
|
18
39
|
|
19
40
|
```
|
20
41
|
$ gem install disque
|
data/disque.gemspec
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "disque"
|
5
|
-
s.version = "0.0.
|
5
|
+
s.version = "0.0.2"
|
6
6
|
s.summary = "Client for Disque"
|
7
|
-
s.description = "
|
8
|
-
s.authors = ["Michel Martens"]
|
9
|
-
s.email = ["michel@soveran.com"]
|
10
|
-
s.homepage = "https://github.com/soveran/disque
|
7
|
+
s.description = "Disque for Ruby"
|
8
|
+
s.authors = ["Michel Martens", "Damian Janowski"]
|
9
|
+
s.email = ["michel@soveran.com", "damian.janowski@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/soveran/disque-rb"
|
11
11
|
s.files = `git ls-files`.split("\n")
|
12
12
|
s.license = "MIT"
|
13
13
|
|
data/lib/disque.rb
CHANGED
@@ -1,7 +1,200 @@
|
|
1
1
|
require "redic"
|
2
2
|
|
3
3
|
class Disque
|
4
|
-
|
5
|
-
|
4
|
+
ECONN = [
|
5
|
+
Errno::ECONNREFUSED,
|
6
|
+
Errno::EINVAL,
|
7
|
+
]
|
8
|
+
|
9
|
+
attr :stats
|
10
|
+
attr :nodes
|
11
|
+
attr :prefix
|
12
|
+
|
13
|
+
# Create a new Disque client by passing a list of nodes.
|
14
|
+
#
|
15
|
+
# Disque.new(["127.0.0.1:7711", "127.0.0.1:7712", "127.0.0.1:7713"])
|
16
|
+
#
|
17
|
+
# For each operation, a counter is updated to signal which node was
|
18
|
+
# the originator of the message. Based on that information, after
|
19
|
+
# a full cycle (1000 operations, but configurable on initialization)
|
20
|
+
# the stats are checked to see what is the most convenient node
|
21
|
+
# to connect to in order to avoid extra jumps.
|
22
|
+
#
|
23
|
+
# TODO Account for authentication
|
24
|
+
# TODO Account for timeout
|
25
|
+
def initialize(hosts, cycle: 1000)
|
26
|
+
|
27
|
+
# Cycle length
|
28
|
+
@cycle = cycle
|
29
|
+
|
30
|
+
# Operations counter
|
31
|
+
@count = 0
|
32
|
+
|
33
|
+
# Known nodes
|
34
|
+
@nodes = Hash.new
|
35
|
+
|
36
|
+
# Connection stats
|
37
|
+
@stats = Hash.new(0)
|
38
|
+
|
39
|
+
# Main client
|
40
|
+
@client = Redic.new
|
41
|
+
|
42
|
+
# Scout client
|
43
|
+
@scout = Redic.new
|
44
|
+
|
45
|
+
# Preferred client prefix
|
46
|
+
@prefix = nil
|
47
|
+
|
48
|
+
explore!(hosts)
|
49
|
+
end
|
50
|
+
|
51
|
+
def url(host)
|
52
|
+
sprintf("disque://%s", host)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Collect the list of nodes by means of `CLUSTER NODES` and
|
56
|
+
# keep a connection to the node that provided that information.
|
57
|
+
def explore!(hosts)
|
58
|
+
|
59
|
+
# Reset nodes
|
60
|
+
@nodes.clear
|
61
|
+
|
62
|
+
hosts.each do |host|
|
63
|
+
begin
|
64
|
+
@scout.configure(url(host))
|
65
|
+
|
66
|
+
@scout.call("CLUSTER", "NODES").lines do |line|
|
67
|
+
id, host, flag = line.split
|
68
|
+
|
69
|
+
prefix = id[0,8]
|
70
|
+
|
71
|
+
if flag == "myself"
|
72
|
+
|
73
|
+
# Configure main client
|
74
|
+
@client.configure(@scout.url)
|
75
|
+
|
76
|
+
# Keep track of selected node
|
77
|
+
@prefix = prefix
|
78
|
+
end
|
79
|
+
|
80
|
+
@nodes[prefix] = host
|
81
|
+
end
|
82
|
+
|
83
|
+
@scout.quit
|
84
|
+
|
85
|
+
break
|
86
|
+
|
87
|
+
rescue *ECONN
|
88
|
+
$stderr.puts($!.inspect)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
if @nodes.empty?
|
93
|
+
raise ArgumentError, "nodes unavailable"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def pick_client!
|
98
|
+
if @count == @cycle
|
99
|
+
@count = 0
|
100
|
+
prefix, _ = @stats.max { |a, b| a[1] <=> b[1] }
|
101
|
+
|
102
|
+
if prefix != @prefix
|
103
|
+
host = @nodes[prefix]
|
104
|
+
|
105
|
+
if host
|
106
|
+
|
107
|
+
# Reconfigure main client
|
108
|
+
@client.configure(url(host))
|
109
|
+
@prefix = prefix
|
110
|
+
|
111
|
+
# Reset stats for this new connection
|
112
|
+
@stats.clear
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Run commands on the active connection. If the
|
119
|
+
# connection is lost, new connections are tried
|
120
|
+
# until all nodes become unavailable.
|
121
|
+
def call(*args)
|
122
|
+
@client.call(*args)
|
123
|
+
rescue *ECONN
|
124
|
+
explore!(@nodes.values)
|
125
|
+
retry
|
126
|
+
end
|
127
|
+
|
128
|
+
# Disque's ADDJOB signature is as follows:
|
129
|
+
#
|
130
|
+
# ADDJOB queue_name job <ms-timeout>
|
131
|
+
# [REPLICATE <count>]
|
132
|
+
# [DELAY <sec>]
|
133
|
+
# [RETRY <sec>]
|
134
|
+
# [TTL <sec>]
|
135
|
+
# [MAXLEN <count>]
|
136
|
+
# [ASYNC]
|
137
|
+
#
|
138
|
+
# You can pass any optional arguments as a hash,
|
139
|
+
# for example:
|
140
|
+
#
|
141
|
+
# disque.push("foo", "myjob", 1000, ttl: 1, async: true)
|
142
|
+
#
|
143
|
+
# Note that `async` is a special case because it's just a
|
144
|
+
# flag. That's why `true` must be passed as its value.
|
145
|
+
def push(queue_name, job, ms_timeout, options = {})
|
146
|
+
command = ["ADDJOB", queue_name, job, ms_timeout]
|
147
|
+
command += options_to_arguments(options)
|
148
|
+
|
149
|
+
call(*command)
|
150
|
+
end
|
151
|
+
|
152
|
+
def fetch(from: [], count: 1, timeout: 0)
|
153
|
+
pick_client!
|
154
|
+
|
155
|
+
jobs = call(
|
156
|
+
"GETJOB",
|
157
|
+
"TIMEOUT", timeout,
|
158
|
+
"COUNT", count,
|
159
|
+
"FROM", *from)
|
160
|
+
|
161
|
+
if jobs then
|
162
|
+
@count += 1
|
163
|
+
|
164
|
+
jobs.each do |queue, msgid, job|
|
165
|
+
|
166
|
+
# Update stats
|
167
|
+
@stats[msgid[2,8]] += 1
|
168
|
+
|
169
|
+
if block_given?
|
170
|
+
|
171
|
+
# Process job
|
172
|
+
yield(job, queue)
|
173
|
+
|
174
|
+
# Remove job
|
175
|
+
call("ACKJOB", msgid)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
return jobs
|
181
|
+
end
|
182
|
+
|
183
|
+
def options_to_arguments(options)
|
184
|
+
arguments = []
|
185
|
+
|
186
|
+
options.each do |key, value|
|
187
|
+
if value == true
|
188
|
+
arguments.push(key)
|
189
|
+
else
|
190
|
+
arguments.push(key, value)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
return arguments
|
195
|
+
end
|
196
|
+
|
197
|
+
def quit
|
198
|
+
@client.quit
|
6
199
|
end
|
7
200
|
end
|
data/makefile
CHANGED
@@ -1,2 +1,29 @@
|
|
1
|
+
DIR?=./tmp
|
2
|
+
|
3
|
+
all: start test stop
|
4
|
+
|
1
5
|
test:
|
2
6
|
RUBYLIB=./lib cutest tests/*.rb
|
7
|
+
|
8
|
+
default:
|
9
|
+
@echo make \<port\>
|
10
|
+
@echo make \[start\|meet\|stop\|list\]
|
11
|
+
|
12
|
+
start: 7711 7712 7713
|
13
|
+
@disque -p 7712 CLUSTER MEET 127.0.0.1 7711 > /dev/null
|
14
|
+
@disque -p 7713 CLUSTER MEET 127.0.0.1 7712 > /dev/null
|
15
|
+
|
16
|
+
stop:
|
17
|
+
@kill `cat $(DIR)/disque.*.pid`
|
18
|
+
|
19
|
+
%:
|
20
|
+
@disque-server \
|
21
|
+
--port $@ \
|
22
|
+
--dir $(DIR) \
|
23
|
+
--daemonize yes \
|
24
|
+
--bind 127.0.0.1 \
|
25
|
+
--loglevel notice \
|
26
|
+
--pidfile disque.$@.pid \
|
27
|
+
--appendfilename disque.$@.aof \
|
28
|
+
--cluster-config-file disque.$@.nodes \
|
29
|
+
--logfile disque.$@.log
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require_relative "../lib/disque"
|
2
|
+
require "stringio"
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
module Silencer
|
6
|
+
@output = nil
|
7
|
+
|
8
|
+
def self.start
|
9
|
+
$olderr = $stderr
|
10
|
+
$stderr = StringIO.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.stop
|
14
|
+
@output = $stderr.string
|
15
|
+
$stderr = $olderr
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.output
|
19
|
+
@output
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
DISQUE_NODES = [
|
24
|
+
"127.0.0.1:7710",
|
25
|
+
"127.0.0.1:7711",
|
26
|
+
"127.0.0.1:7712",
|
27
|
+
"127.0.0.1:7713",
|
28
|
+
]
|
29
|
+
|
30
|
+
DISQUE_BAD_NODES = DISQUE_NODES[0,1]
|
31
|
+
DISQUE_GOOD_NODES = DISQUE_NODES[1,3]
|
32
|
+
|
33
|
+
test "raise if connection is not possible" do
|
34
|
+
Silencer.start
|
35
|
+
assert_raise(ArgumentError) do
|
36
|
+
c = Disque.new(DISQUE_BAD_NODES)
|
37
|
+
end
|
38
|
+
Silencer.stop
|
39
|
+
|
40
|
+
assert_equal "#<Errno::ECONNREFUSED: Can't connect to: disque://127.0.0.1:7710>\n", Silencer.output
|
41
|
+
end
|
42
|
+
|
43
|
+
test "retry until a connection is reached" do
|
44
|
+
Silencer.start
|
45
|
+
c = Disque.new(DISQUE_NODES)
|
46
|
+
Silencer.stop
|
47
|
+
|
48
|
+
assert_equal "#<Errno::ECONNREFUSED: Can't connect to: disque://127.0.0.1:7710>\n", Silencer.output
|
49
|
+
assert_equal "PONG", c.call("PING")
|
50
|
+
end
|
51
|
+
|
52
|
+
test "lack of jobs" do
|
53
|
+
c = Disque.new(DISQUE_GOOD_NODES)
|
54
|
+
reached = false
|
55
|
+
|
56
|
+
c.fetch(from: ["foo"], timeout: 1) do |job|
|
57
|
+
reached = true
|
58
|
+
end
|
59
|
+
|
60
|
+
assert_equal false, reached
|
61
|
+
end
|
62
|
+
|
63
|
+
test "one job" do
|
64
|
+
c = Disque.new(DISQUE_GOOD_NODES)
|
65
|
+
|
66
|
+
c.push("foo", "bar", 1000)
|
67
|
+
|
68
|
+
c.fetch(from: ["foo"], count: 10) do |job, queue|
|
69
|
+
assert_equal "bar", job
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
test "multiple jobs" do
|
74
|
+
c = Disque.new(DISQUE_GOOD_NODES)
|
75
|
+
|
76
|
+
c.push("foo", "bar", 1000)
|
77
|
+
c.push("foo", "baz", 1000)
|
78
|
+
|
79
|
+
jobs = ["baz", "bar"]
|
80
|
+
|
81
|
+
c.fetch(from: ["foo"], count: 10) do |job, queue|
|
82
|
+
assert_equal jobs.pop, job
|
83
|
+
assert_equal "foo", queue
|
84
|
+
end
|
85
|
+
|
86
|
+
assert jobs.empty?
|
87
|
+
end
|
88
|
+
|
89
|
+
test "multiple queues" do
|
90
|
+
c = Disque.new(DISQUE_GOOD_NODES)
|
91
|
+
|
92
|
+
c.push("foo", "bar", 1000)
|
93
|
+
c.push("qux", "baz", 1000)
|
94
|
+
|
95
|
+
queues = ["qux", "foo"]
|
96
|
+
jobs = ["baz", "bar"]
|
97
|
+
|
98
|
+
result = c.fetch(from: ["foo", "qux"], count: 10) do |job, queue|
|
99
|
+
assert_equal jobs.pop, job
|
100
|
+
assert_equal queues.pop, queue
|
101
|
+
end
|
102
|
+
|
103
|
+
assert jobs.empty?
|
104
|
+
assert queues.empty?
|
105
|
+
end
|
106
|
+
|
107
|
+
test "add jobs with other parameters" do
|
108
|
+
c = Disque.new(DISQUE_GOOD_NODES)
|
109
|
+
|
110
|
+
c.push("foo", "bar", 1000, async: true, ttl: 0)
|
111
|
+
|
112
|
+
sleep 0.1
|
113
|
+
|
114
|
+
queues = ["foo"]
|
115
|
+
jobs = ["bar"]
|
116
|
+
|
117
|
+
result = c.fetch(from: ["foo"], count: 10, timeout: 1) do |job, queue|
|
118
|
+
assert_equal jobs.pop, job
|
119
|
+
assert_equal queues.pop, queue
|
120
|
+
end
|
121
|
+
|
122
|
+
assert_equal ["bar"], jobs
|
123
|
+
assert_equal ["foo"], queues
|
124
|
+
end
|
125
|
+
|
126
|
+
test "connect to the best node" do
|
127
|
+
c1 = Disque.new([DISQUE_GOOD_NODES[0]], cycle: 2)
|
128
|
+
c2 = Disque.new([DISQUE_GOOD_NODES[1]], cycle: 2)
|
129
|
+
|
130
|
+
assert c1.prefix != c2.prefix
|
131
|
+
|
132
|
+
# Tamper stats to trigger a reconnection
|
133
|
+
c1.stats[c2.prefix] = 10
|
134
|
+
|
135
|
+
c1.push("q1", "j1", 1000)
|
136
|
+
c1.push("q1", "j2", 1000)
|
137
|
+
|
138
|
+
c2.push("q1", "j3", 1000)
|
139
|
+
|
140
|
+
c1.fetch(from: ["q1"])
|
141
|
+
c1.fetch(from: ["q1"])
|
142
|
+
c1.fetch(from: ["q1"])
|
143
|
+
|
144
|
+
# Client should have reconnected
|
145
|
+
assert c1.prefix == c2.prefix
|
146
|
+
end
|
147
|
+
|
148
|
+
test "connect to the best node, part 2" do
|
149
|
+
c1 = Disque.new([DISQUE_GOOD_NODES[0]], cycle: 2)
|
150
|
+
c2 = Disque.new([DISQUE_GOOD_NODES[1]], cycle: 2)
|
151
|
+
|
152
|
+
assert c1.prefix != c2.prefix
|
153
|
+
|
154
|
+
c1.push("q1", "j1", 0)
|
155
|
+
c1.push("q1", "j2", 0)
|
156
|
+
c1.push("q1", "j3", 0)
|
157
|
+
|
158
|
+
c2.fetch(from: ["q1"])
|
159
|
+
c2.fetch(from: ["q1"])
|
160
|
+
c2.fetch(from: ["q1"])
|
161
|
+
|
162
|
+
# Client should have reconnected
|
163
|
+
assert c1.prefix == c2.prefix
|
164
|
+
end
|
165
|
+
|
166
|
+
test "recover after node disconnection" do
|
167
|
+
c1 = Disque.new([DISQUE_GOOD_NODES[0]], cycle: 2)
|
168
|
+
|
169
|
+
prefix = c1.prefix
|
170
|
+
|
171
|
+
# Tamper stats to trigger a reconnection to a bad node
|
172
|
+
c1.stats["fake"] = 10
|
173
|
+
c1.nodes["fake"] = DISQUE_BAD_NODES[0]
|
174
|
+
|
175
|
+
# Delete the other nodes just in case
|
176
|
+
c1.nodes.delete_if do |key, val|
|
177
|
+
key != prefix &&
|
178
|
+
key != "fake"
|
179
|
+
end
|
180
|
+
|
181
|
+
c1.push("q1", "j1", 1000)
|
182
|
+
c1.push("q1", "j2", 1000)
|
183
|
+
c1.push("q1", "j3", 1000)
|
184
|
+
|
185
|
+
c1.fetch(from: ["q1"])
|
186
|
+
c1.fetch(from: ["q1"])
|
187
|
+
c1.fetch(from: ["q1"])
|
188
|
+
|
189
|
+
# Prefix should stay the same
|
190
|
+
assert prefix == c1.prefix
|
191
|
+
end
|
192
|
+
|
193
|
+
test "federation" do
|
194
|
+
c1 = Disque.new([DISQUE_GOOD_NODES[0]], cycle: 2)
|
195
|
+
c2 = Disque.new([DISQUE_GOOD_NODES[1]], cycle: 2)
|
196
|
+
|
197
|
+
c1.push("q1", "j1", 0)
|
198
|
+
|
199
|
+
c2.fetch(from: ["q1"], count: 10) do |job, queue|
|
200
|
+
assert_equal "j1", job
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
test "ack jobs when block is given" do
|
205
|
+
c = Disque.new(DISQUE_GOOD_NODES)
|
206
|
+
|
207
|
+
c.push("q1", "j1", 1000)
|
208
|
+
|
209
|
+
id = nil
|
210
|
+
|
211
|
+
_, id, _ = c.fetch(from: ["q1"]) { |*a| }[0]
|
212
|
+
|
213
|
+
assert id
|
214
|
+
|
215
|
+
info = Hash[*c.call("SHOW", id)]
|
216
|
+
|
217
|
+
if info.any?
|
218
|
+
|
219
|
+
# If the test runs too fast, we may get the job
|
220
|
+
# with the status set to "acked"
|
221
|
+
assert_equal "acked", info.fetch("state")
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
test "don't ack jobs when no block is given" do
|
226
|
+
c = Disque.new(DISQUE_GOOD_NODES)
|
227
|
+
|
228
|
+
c.push("q1", "j1", 1000)
|
229
|
+
|
230
|
+
_, id, _ = c.fetch(from: ["q1"])[0]
|
231
|
+
|
232
|
+
assert id
|
233
|
+
|
234
|
+
info = Hash[*c.call("SHOW", id)]
|
235
|
+
|
236
|
+
assert_equal info.fetch("state"), "active"
|
237
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: disque
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michel Martens
|
8
|
+
- Damian Janowski
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2015-
|
12
|
+
date: 2015-04-27 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: redic
|
@@ -24,20 +25,24 @@ dependencies:
|
|
24
25
|
- - '>='
|
25
26
|
- !ruby/object:Gem::Version
|
26
27
|
version: '0'
|
27
|
-
description:
|
28
|
+
description: Disque for Ruby
|
28
29
|
email:
|
29
30
|
- michel@soveran.com
|
31
|
+
- damian.janowski@gmail.com
|
30
32
|
executables: []
|
31
33
|
extensions: []
|
32
34
|
extra_rdoc_files: []
|
33
35
|
files:
|
34
36
|
- .gems
|
37
|
+
- .gitignore
|
38
|
+
- AUTHORS
|
35
39
|
- LICENSE
|
36
40
|
- README.md
|
37
41
|
- disque.gemspec
|
38
42
|
- lib/disque.rb
|
39
43
|
- makefile
|
40
|
-
|
44
|
+
- tests/disque_test.rb
|
45
|
+
homepage: https://github.com/soveran/disque-rb
|
41
46
|
licenses:
|
42
47
|
- MIT
|
43
48
|
metadata: {}
|
@@ -52,12 +57,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
52
57
|
version: '0'
|
53
58
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
59
|
requirements:
|
55
|
-
- - '
|
60
|
+
- - '>='
|
56
61
|
- !ruby/object:Gem::Version
|
57
|
-
version:
|
62
|
+
version: '0'
|
58
63
|
requirements: []
|
59
64
|
rubyforge_project:
|
60
|
-
rubygems_version: 2.0.
|
65
|
+
rubygems_version: 2.0.3
|
61
66
|
signing_key:
|
62
67
|
specification_version: 4
|
63
68
|
summary: Client for Disque
|