em-beanstalk 0.0.3
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/COPYING +21 -0
- data/README.rdoc +73 -0
- data/Rakefile +48 -0
- data/VERSION +1 -0
- data/lib/em-jack.rb +12 -0
- data/lib/em-jack/beanstalk_connection.rb +30 -0
- data/lib/em-jack/connection.rb +281 -0
- data/lib/em-jack/errors.rb +7 -0
- data/lib/em-jack/job.rb +30 -0
- data/spec/connection_spec.rb +374 -0
- data/spec/integration_spec.rb +365 -0
- data/spec/job_spec.rb +39 -0
- data/spec/spec.opts +7 -0
- data/spec/spec_helper.rb +14 -0
- metadata +80 -0
data/COPYING
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2009, dan sinclair. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
4
|
+
modification, are permitted provided that the following conditions are
|
5
|
+
met:
|
6
|
+
* Redistributions of source code must retain the above copyright
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
8
|
+
* Redistributions in binary form must reproduce the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
10
|
+
documentation and/or other materials provided with the distribution.
|
11
|
+
|
12
|
+
THIS SOFTWARE IS PROVIDED BY dan sinclair ''AS IS'' AND ANY
|
13
|
+
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
14
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
15
|
+
DISCLAIMED. IN NO EVENT SHALL dan sinclair BE LIABLE FOR ANY
|
16
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
17
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
18
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
19
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
20
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
21
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
= EMJack
|
2
|
+
An attempt to wrap portions of the Beanstalk protocol with EventMachine. Every command
|
3
|
+
will return a deferrable object. That object will succeed or fail depending on the
|
4
|
+
reply returned from Beanstalk.
|
5
|
+
|
6
|
+
The current, default, errback is to print the error message received.
|
7
|
+
|
8
|
+
One thing to keep in mind. The Beanstalk protocol executes all commands serially.
|
9
|
+
So, if you send a reserve command and there are no jobs Beanstalk _won't_ process
|
10
|
+
any of the commands that come after the reserve until the reserve completes.
|
11
|
+
|
12
|
+
This is a bit of a gotcha when sending a series of reserve, delete, etc and there
|
13
|
+
are no available jobs.
|
14
|
+
|
15
|
+
= Dependencies
|
16
|
+
- EventMachine
|
17
|
+
- RSpec (to run the tests)
|
18
|
+
- EM::Spec
|
19
|
+
|
20
|
+
= Examples
|
21
|
+
EM.run {
|
22
|
+
jack = EMJack::Connection.new
|
23
|
+
|
24
|
+
jack.use('mytube') { |tube| puts "Using #{tube}" }
|
25
|
+
|
26
|
+
jack.reserve do |job|
|
27
|
+
puts job.id
|
28
|
+
process(job)
|
29
|
+
|
30
|
+
jack.delete(job) { puts "Successfully deleted" }
|
31
|
+
end
|
32
|
+
|
33
|
+
jack.put("my message", :ttr => 300) { |id| puts "put successful #{id}" }
|
34
|
+
|
35
|
+
jack.stats { |stats| puts "Server up for #{stats['uptime']} seconds" "}
|
36
|
+
|
37
|
+
jack.stats(:tube, "mytube") { |stats| puts "Total jobs #{stats['total-jobs']}" }
|
38
|
+
|
39
|
+
jack.list(:tubes) { |tubes| puts "There are #{tubes.length} tubes defined" }
|
40
|
+
}
|
41
|
+
|
42
|
+
|
43
|
+
EMJack#each_job is useful for scenarios where the client is a job worker
|
44
|
+
and intended to process jobs continuously as they become available. Once
|
45
|
+
the queue is empty, the client will block and wait for a new job.
|
46
|
+
|
47
|
+
If multiple workers connect to the queue Beanstalkd will round-robin between
|
48
|
+
the workers.
|
49
|
+
EM.run {
|
50
|
+
jack = EMJack::Connection.new
|
51
|
+
|
52
|
+
jack.each_job do |job|
|
53
|
+
puts "Got job ##{job.id}: #{job}"
|
54
|
+
|
55
|
+
if process(job)
|
56
|
+
jack.delete(job) { puts "*Deleted #{job}*" }
|
57
|
+
else
|
58
|
+
# something went horribly wrong!
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def process(job)
|
63
|
+
# Some kind of job processing
|
64
|
+
end
|
65
|
+
}
|
66
|
+
|
67
|
+
|
68
|
+
= Contact
|
69
|
+
If you've got any questions, comments or bugs, please let me know. You can
|
70
|
+
contact me by email at dj2 at everburning dot com.
|
71
|
+
|
72
|
+
= Contributors
|
73
|
+
Peter Kieltyka (EMJack#each_job)
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
libdir = File.expand_path("lib")
|
2
|
+
$:.unshift(libdir) unless $:.include?(libdir)
|
3
|
+
|
4
|
+
require 'em-jack'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'jeweler'
|
8
|
+
Jeweler::Tasks.new do |s|
|
9
|
+
s.name = "em-beanstalk"
|
10
|
+
s.description = s.summary = "EventMachine client for Beanstalkd"
|
11
|
+
s.email = "dan@postrank.com"
|
12
|
+
s.homepage = "http://github.com/joshbuddy/em-jack"
|
13
|
+
s.authors = ["Dan"]
|
14
|
+
s.files = FileList["[A-Z]*", "{lib,spec}/**/*"]
|
15
|
+
s.add_dependency 'eventmachine'
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'spec'
|
23
|
+
require 'spec/rake/spectask'
|
24
|
+
task :spec => 'spec:all'
|
25
|
+
namespace(:spec) do
|
26
|
+
Spec::Rake::SpecTask.new(:all) do |t|
|
27
|
+
t.spec_opts ||= []
|
28
|
+
t.spec_opts << "-rubygems"
|
29
|
+
t.spec_opts << "--options" << "spec/spec.opts"
|
30
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Run all examples with RCov"
|
36
|
+
Spec::Rake::SpecTask.new('spec_with_rcov') do |t|
|
37
|
+
t.spec_files = FileList['spec/**/*.rb']
|
38
|
+
t.rcov = true
|
39
|
+
t.rcov_opts = ['--exclude', 'spec']
|
40
|
+
end
|
41
|
+
|
42
|
+
require 'rake/rdoctask'
|
43
|
+
desc "Generate documentation"
|
44
|
+
Rake::RDocTask.new do |rd|
|
45
|
+
rd.main = "README.rdoc"
|
46
|
+
rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
|
47
|
+
rd.rdoc_dir = 'rdoc'
|
48
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.3
|
data/lib/em-jack.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
module EMJack
|
4
|
+
class BeanstalkConnection < EM::Connection
|
5
|
+
attr_accessor :client
|
6
|
+
|
7
|
+
def connection_completed
|
8
|
+
@client.connected
|
9
|
+
end
|
10
|
+
|
11
|
+
def receive_data(data)
|
12
|
+
@client.received(data)
|
13
|
+
end
|
14
|
+
|
15
|
+
def send(command, *args)
|
16
|
+
cmd = command.to_s
|
17
|
+
cmd << " #{args.join(" ")}" unless args.length == 0
|
18
|
+
cmd << "\r\n"
|
19
|
+
send_data(cmd)
|
20
|
+
end
|
21
|
+
|
22
|
+
def send_with_data(command, data, *args)
|
23
|
+
send_data("#{command.to_s} #{args.join(" ")}\r\n#{data}\r\n")
|
24
|
+
end
|
25
|
+
|
26
|
+
def unbind
|
27
|
+
@client.disconnected
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,281 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module EMJack
|
5
|
+
class Connection
|
6
|
+
|
7
|
+
attr_accessor :host, :port
|
8
|
+
attr_reader :default_priority, :default_delay, :default_ttr
|
9
|
+
|
10
|
+
def initialize(opts = nil)
|
11
|
+
@host = opts && opts[:host] || 'localhost'
|
12
|
+
@port = opts && opts[:port] || 11300
|
13
|
+
@tube = opts && opts[:tube]
|
14
|
+
@retry_count = opts && opts[:retry_count] || 5
|
15
|
+
@default_priority = opts && opts[:default_priority] || 65536
|
16
|
+
@default_delay = opts && opts[:default_delay] || 0
|
17
|
+
@default_ttr = opts && opts[:default_ttr] || 300
|
18
|
+
@default_timeout = opts && opts[:timeout] || 5
|
19
|
+
|
20
|
+
@used_tube = 'default'
|
21
|
+
@watched_tubes = [@used_tube]
|
22
|
+
|
23
|
+
@data = ""
|
24
|
+
@retries = 0
|
25
|
+
@in_reserve = false
|
26
|
+
@deferrables = []
|
27
|
+
|
28
|
+
@conn = EM::connect(host, port, EMJack::BeanstalkConnection) do |conn|
|
29
|
+
conn.client = self
|
30
|
+
conn.comm_inactivity_timeout = 0
|
31
|
+
conn.pending_connect_timeout = @default_timeout
|
32
|
+
end
|
33
|
+
|
34
|
+
unless @tube.nil?
|
35
|
+
use(@tube)
|
36
|
+
watch(@tube)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def close
|
41
|
+
@disconnect_manually = true
|
42
|
+
@conn.close_connection
|
43
|
+
end
|
44
|
+
|
45
|
+
def drain!(&block)
|
46
|
+
stats do |stats|
|
47
|
+
stats['current-jobs-ready'].zero? ?
|
48
|
+
EM.next_tick(&block) :
|
49
|
+
reserve{|job| job.delete{ drain!(&block) }}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def use(tube, &block)
|
54
|
+
return if @used_tube == tube
|
55
|
+
@used_tube = tube
|
56
|
+
@conn.send(:use, tube)
|
57
|
+
add_deferrable(&block)
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
def watch(tube, &block)
|
62
|
+
return if @watched_tubes.include?(tube)
|
63
|
+
@watched_tubes.push(tube)
|
64
|
+
@conn.send(:watch, tube)
|
65
|
+
add_deferrable(&block)
|
66
|
+
end
|
67
|
+
|
68
|
+
def ignore(tube, &block)
|
69
|
+
return if not @watched_tubes.include?(tube)
|
70
|
+
@watched_tubes.delete(tube)
|
71
|
+
@conn.send(:ignore, tube)
|
72
|
+
add_deferrable(&block)
|
73
|
+
end
|
74
|
+
|
75
|
+
def reserve(timeout = nil, &block)
|
76
|
+
if timeout
|
77
|
+
@conn.send(:'reserve-with-timeout', timeout)
|
78
|
+
else
|
79
|
+
@conn.send(:reserve)
|
80
|
+
end
|
81
|
+
add_deferrable(&block)
|
82
|
+
end
|
83
|
+
|
84
|
+
def each_job(timeout = nil, &block)
|
85
|
+
work = Proc.new do
|
86
|
+
r = reserve(timeout)
|
87
|
+
r.callback do |job|
|
88
|
+
block.call(job)
|
89
|
+
EM.next_tick { work.call }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
work.call
|
93
|
+
end
|
94
|
+
|
95
|
+
def stats(type = nil, val = nil, &block)
|
96
|
+
case(type)
|
97
|
+
when nil then @conn.send(:stats)
|
98
|
+
when :tube then @conn.send(:'stats-tube', val)
|
99
|
+
when :job then @conn.send(:'stats-job', job_id(val))
|
100
|
+
else raise EMJack::InvalidCommand.new
|
101
|
+
end
|
102
|
+
add_deferrable(&block)
|
103
|
+
end
|
104
|
+
|
105
|
+
def job_id(val)
|
106
|
+
case val
|
107
|
+
when Job
|
108
|
+
val.id
|
109
|
+
else
|
110
|
+
val
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def list(type = nil, &block)
|
115
|
+
case(type)
|
116
|
+
when nil then @conn.send(:'list-tubes')
|
117
|
+
when :use, :used then @conn.send(:'list-tube-used')
|
118
|
+
when :watch, :watched then @conn.send(:'list-tubes-watched')
|
119
|
+
else raise EMJack::InvalidCommand.new
|
120
|
+
end
|
121
|
+
add_deferrable(&block)
|
122
|
+
end
|
123
|
+
|
124
|
+
def delete(val, &block)
|
125
|
+
return unless val
|
126
|
+
@conn.send(:delete, job_id(val))
|
127
|
+
add_deferrable(&block)
|
128
|
+
end
|
129
|
+
|
130
|
+
def put(msg, opts = nil, &block)
|
131
|
+
case msg
|
132
|
+
when Job
|
133
|
+
priority = opts && opts[:priority] || msg.priority
|
134
|
+
delay = opts && opts[:delay] || msg.delay
|
135
|
+
ttr = opts && opts[:ttr] || msg.ttr
|
136
|
+
body = msg.body
|
137
|
+
else
|
138
|
+
priority = opts && opts[:priority] || default_priority
|
139
|
+
delay = opts && opts[:delay] || default_delay
|
140
|
+
ttr = opts && opts[:ttr] || default_ttr
|
141
|
+
body = msg.to_s
|
142
|
+
end
|
143
|
+
|
144
|
+
priority = default_priority if priority < 0
|
145
|
+
priority = 2 ** 32 if priority > (2 ** 32)
|
146
|
+
delay = default_delay if delay < 0
|
147
|
+
ttr = default_ttr if ttr < 0
|
148
|
+
|
149
|
+
@conn.send_with_data(:put, body, priority, delay, ttr, body.size)
|
150
|
+
add_deferrable(&block)
|
151
|
+
end
|
152
|
+
|
153
|
+
def release(job, &block)
|
154
|
+
return if job.nil?
|
155
|
+
@conn.send(:release, job.jobid, 0, 0)
|
156
|
+
add_deferrable(&block)
|
157
|
+
end
|
158
|
+
|
159
|
+
def connected
|
160
|
+
@retries = 0
|
161
|
+
end
|
162
|
+
|
163
|
+
def disconnected
|
164
|
+
@deferrables.each {|d| d.fail }
|
165
|
+
unless @disconnect_manually
|
166
|
+
raise EMJack::Disconnected if @retries >= @retry_count
|
167
|
+
@retries += 1
|
168
|
+
EM.add_timer(1) { reconnect }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def reconnect
|
173
|
+
@disconnect_manually = false
|
174
|
+
@conn.reconnect(host, port)
|
175
|
+
end
|
176
|
+
|
177
|
+
def add_deferrable(&block)
|
178
|
+
df = EM::DefaultDeferrable.new
|
179
|
+
df.errback do
|
180
|
+
if @error_callback
|
181
|
+
@error_callback.call
|
182
|
+
else
|
183
|
+
puts "ERROR"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
@deferrables.push(df)
|
188
|
+
df.callback(&block) if block
|
189
|
+
df
|
190
|
+
end
|
191
|
+
|
192
|
+
def on_error(&block)
|
193
|
+
@error_callback = block
|
194
|
+
end
|
195
|
+
|
196
|
+
def received(data)
|
197
|
+
@data << data
|
198
|
+
|
199
|
+
until @data.empty?
|
200
|
+
idx = @data.index(/(.*?\r\n)/)
|
201
|
+
break if idx.nil?
|
202
|
+
|
203
|
+
first = $1
|
204
|
+
|
205
|
+
handled = false
|
206
|
+
%w(OUT_OF_MEMORY INTERNAL_ERROR DRAINING BAD_FORMAT
|
207
|
+
UNKNOWN_COMMAND EXPECTED_CRLF JOB_TOO_BIG DEADLINE_SOON
|
208
|
+
TIMED_OUT NOT_FOUND).each do |cmd|
|
209
|
+
next unless first =~ /^#{cmd}\r\n/i
|
210
|
+
df = @deferrables.shift
|
211
|
+
df.fail(cmd.downcase.to_sym)
|
212
|
+
|
213
|
+
@data = @data[(cmd.length + 2)..-1]
|
214
|
+
handled = true
|
215
|
+
break
|
216
|
+
end
|
217
|
+
next if handled
|
218
|
+
|
219
|
+
case (first)
|
220
|
+
when /^DELETED\r\n/ then
|
221
|
+
df = @deferrables.shift
|
222
|
+
df.succeed
|
223
|
+
|
224
|
+
when /^INSERTED\s+(\d+)\r\n/ then
|
225
|
+
df = @deferrables.shift
|
226
|
+
df.succeed($1.to_i)
|
227
|
+
|
228
|
+
when /^RELEASED\r\n/ then
|
229
|
+
df = @deferrables.shift
|
230
|
+
df.succeed
|
231
|
+
|
232
|
+
when /^BURIED\s+(\d+)\r\n/ then
|
233
|
+
df = @deferrables.shift
|
234
|
+
df.fail(:buried, $1.to_i)
|
235
|
+
|
236
|
+
when /^USING\s+(.*)\r\n/ then
|
237
|
+
df = @deferrables.shift
|
238
|
+
df.succeed($1)
|
239
|
+
|
240
|
+
when /^WATCHING\s+(\d+)\r\n/ then
|
241
|
+
df = @deferrables.shift
|
242
|
+
df.succeed($1.to_i)
|
243
|
+
|
244
|
+
when /^OK\s+(\d+)\r\n/ then
|
245
|
+
bytes = $1.to_i
|
246
|
+
|
247
|
+
body, @data = extract_body(bytes, @data)
|
248
|
+
break if body.nil?
|
249
|
+
|
250
|
+
df = @deferrables.shift
|
251
|
+
df.succeed(YAML.load(body))
|
252
|
+
next
|
253
|
+
|
254
|
+
when /^RESERVED\s+(\d+)\s+(\d+)\r\n/ then
|
255
|
+
id = $1.to_i
|
256
|
+
bytes = $2.to_i
|
257
|
+
|
258
|
+
body, @data = extract_body(bytes, @data)
|
259
|
+
break if body.nil?
|
260
|
+
|
261
|
+
df = @deferrables.shift
|
262
|
+
job = EMJack::Job.new(self, id, body)
|
263
|
+
df.succeed(job)
|
264
|
+
next
|
265
|
+
else
|
266
|
+
break
|
267
|
+
end
|
268
|
+
@data.slice!(0, first.size)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def extract_body(bytes, data)
|
273
|
+
rem = data[(data.index(/\r\n/) + 2)..-1]
|
274
|
+
return [nil, data] if rem.length < bytes
|
275
|
+
body = rem[0..(bytes - 1)]
|
276
|
+
data = rem[(bytes + 2)..-1]
|
277
|
+
data = "" if data.nil?
|
278
|
+
[body, data]
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
data/lib/em-jack/job.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module EMJack
|
2
|
+
class Job
|
3
|
+
|
4
|
+
attr_reader :id, :conn
|
5
|
+
attr_accessor :body, :ttr, :priority, :delay
|
6
|
+
|
7
|
+
def initialize(conn, id, body)
|
8
|
+
@conn = conn
|
9
|
+
@id = id && Integer(id)
|
10
|
+
@body = body
|
11
|
+
@priority = conn && conn.default_priority
|
12
|
+
@delay = conn && conn.default_delay
|
13
|
+
@ttr = conn && conn.default_ttr
|
14
|
+
end
|
15
|
+
|
16
|
+
alias_method :jobid, :id
|
17
|
+
|
18
|
+
def delete(&block)
|
19
|
+
conn.delete(self, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def stats(&block)
|
23
|
+
conn.stats(:job, self, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
"#{id} -- #{body.inspect}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,374 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe EMJack::Connection do
|
4
|
+
before(:each) do
|
5
|
+
@connection_mock = mock(:conn)
|
6
|
+
EM.should_receive(:connect).and_return(@connection_mock)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should use a default host of "localhost"' do
|
10
|
+
conn = EMJack::Connection.new
|
11
|
+
conn.host.should == 'localhost'
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should use a default port of 11300' do
|
15
|
+
conn = EMJack::Connection.new
|
16
|
+
conn.port.should == 11300
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should watch and use a provided tube on connect' do
|
20
|
+
@connection_mock.should_receive(:send).once.with(:use, "mytube")
|
21
|
+
@connection_mock.should_receive(:send).once.with(:watch, "mytube")
|
22
|
+
conn = EMJack::Connection.new(:tube => "mytube")
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should send the "use" command' do
|
26
|
+
@connection_mock.should_receive(:send).once.with(:use, "mytube")
|
27
|
+
conn = EMJack::Connection.new
|
28
|
+
conn.use("mytube")
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should not send the use command to the currently used tube' do
|
32
|
+
@connection_mock.should_receive(:send).once.with(:use, "mytube")
|
33
|
+
conn = EMJack::Connection.new
|
34
|
+
conn.use("mytube")
|
35
|
+
conn.use("mytube")
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should send the "watch" command' do
|
39
|
+
@connection_mock.should_receive(:send).once.with(:watch, "mytube")
|
40
|
+
conn = EMJack::Connection.new
|
41
|
+
conn.watch("mytube")
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should not send the watch command for a tube currently watched' do
|
45
|
+
@connection_mock.should_receive(:send).once.with(:watch, "mytube")
|
46
|
+
conn = EMJack::Connection.new
|
47
|
+
conn.watch("mytube")
|
48
|
+
conn.watch("mytube")
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should send the "put" command' do
|
52
|
+
msg = "my message"
|
53
|
+
@connection_mock.should_receive(:send_with_data).once.
|
54
|
+
with(:put, msg, anything, anything, anything, msg.length)
|
55
|
+
conn = EMJack::Connection.new
|
56
|
+
conn.put(msg)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should default the delay, priority and ttr settings' do
|
60
|
+
@connection_mock.should_receive(:send_with_data).once.
|
61
|
+
with(:put, anything, 65536, 0, 300, anything)
|
62
|
+
conn = EMJack::Connection.new
|
63
|
+
conn.put("msg")
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should accept a delay setting' do
|
67
|
+
@connection_mock.should_receive(:send_with_data).once.
|
68
|
+
with(:put, anything, anything, 42, anything, anything)
|
69
|
+
conn = EMJack::Connection.new
|
70
|
+
conn.put("msg", :delay => 42)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should accept a ttr setting' do
|
74
|
+
@connection_mock.should_receive(:send_with_data).once.
|
75
|
+
with(:put, anything, anything, anything, 999, anything)
|
76
|
+
conn = EMJack::Connection.new
|
77
|
+
conn.put("msg", :ttr => 999)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should accept a priority setting' do
|
81
|
+
@connection_mock.should_receive(:send_with_data).once.
|
82
|
+
with(:put, anything, 233, anything, anything, anything)
|
83
|
+
conn = EMJack::Connection.new
|
84
|
+
conn.put("msg", :priority => 233)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'shoudl accept a priority, delay and ttr setting' do
|
88
|
+
@connection_mock.should_receive(:send_with_data).once.
|
89
|
+
with(:put, anything, 99, 42, 2000, anything)
|
90
|
+
conn = EMJack::Connection.new
|
91
|
+
conn.put("msg", :priority => 99, :delay => 42, :ttr => 2000)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should force delay to be >= 0' do
|
95
|
+
@connection_mock.should_receive(:send_with_data).once.
|
96
|
+
with(:put, anything, anything, 0, anything, anything)
|
97
|
+
conn = EMJack::Connection.new
|
98
|
+
conn.put("msg", :delay => -42)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should force ttr to be >= 0' do
|
102
|
+
@connection_mock.should_receive(:send_with_data).once.
|
103
|
+
with(:put, anything, anything, anything, 300, anything)
|
104
|
+
conn = EMJack::Connection.new
|
105
|
+
conn.put("msg", :ttr => -42)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'should force priority to be >= 0' do
|
109
|
+
@connection_mock.should_receive(:send_with_data).once.
|
110
|
+
with(:put, anything, 65536, anything, anything, anything)
|
111
|
+
conn = EMJack::Connection.new
|
112
|
+
conn.put("msg", :priority => -42)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'should force priority to be < 2**32' do
|
116
|
+
@connection_mock.should_receive(:send_with_data).once.
|
117
|
+
with(:put, anything, (2 ** 32), anything, anything, anything)
|
118
|
+
conn = EMJack::Connection.new
|
119
|
+
conn.put("msg", :priority => (2 ** 32 + 1))
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should handle a non-string provided as the put message' do
|
123
|
+
msg = 22
|
124
|
+
@connection_mock.should_receive(:send_with_data).once.
|
125
|
+
with(:put, msg.to_s, anything, anything, anything, msg.to_s.length)
|
126
|
+
conn = EMJack::Connection.new
|
127
|
+
conn.put(msg)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should send the "delete" command' do
|
131
|
+
@connection_mock.should_receive(:send).once.with(:delete, 1)
|
132
|
+
job = EMJack::Job.new(nil, 1, "body")
|
133
|
+
conn = EMJack::Connection.new
|
134
|
+
conn.delete(job)
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'should handle a nil job sent to the "delete" command' do
|
138
|
+
@connection_mock.should_not_receive(:send).with(:delete, nil)
|
139
|
+
conn = EMJack::Connection.new
|
140
|
+
conn.delete(nil)
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'should send the "reserve" command' do
|
144
|
+
@connection_mock.should_receive(:send).with(:reserve)
|
145
|
+
conn = EMJack::Connection.new
|
146
|
+
conn.reserve
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'should raise exception if reconnect fails more then RETRY_COUNT times' do
|
150
|
+
EM.should_receive(:add_timer).exactly(5).times
|
151
|
+
|
152
|
+
conn = EMJack::Connection.new
|
153
|
+
5.times { conn.disconnected }
|
154
|
+
lambda { conn.disconnected }.should raise_error(EMJack::Disconnected)
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should reset the retry count on connection' do
|
158
|
+
EM.should_receive(:add_timer).at_least(1).times
|
159
|
+
|
160
|
+
conn = EMJack::Connection.new
|
161
|
+
5.times { conn.disconnected }
|
162
|
+
conn.connected
|
163
|
+
lambda { conn.disconnected }.should_not raise_error(EMJack::Disconnected)
|
164
|
+
end
|
165
|
+
|
166
|
+
%w(OUT_OF_MEMORY INTERNAL_ERROR DRAINING BAD_FORMAT
|
167
|
+
UNKNOWN_COMMAND EXPECTED_CRLF JOB_TOO_BIG DEADLINE_SOON
|
168
|
+
TIMED_OUT NOT_FOUND).each do |cmd|
|
169
|
+
it "should handle #{cmd} messages" do
|
170
|
+
conn = EMJack::Connection.new
|
171
|
+
|
172
|
+
df = conn.add_deferrable
|
173
|
+
df.should_receive(:fail).with(cmd.downcase.to_sym)
|
174
|
+
|
175
|
+
conn.received("#{cmd}\r\n")
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should handle deleted messages' do
|
180
|
+
conn = EMJack::Connection.new
|
181
|
+
|
182
|
+
df = conn.add_deferrable
|
183
|
+
df.should_receive(:succeed)
|
184
|
+
|
185
|
+
conn.received("DELETED\r\n")
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'should handle inserted messages' do
|
189
|
+
conn = EMJack::Connection.new
|
190
|
+
|
191
|
+
df = conn.add_deferrable
|
192
|
+
df.should_receive(:succeed).with(40)
|
193
|
+
|
194
|
+
conn.received("INSERTED 40\r\n")
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'should handle buried messages' do
|
198
|
+
conn = EMJack::Connection.new
|
199
|
+
|
200
|
+
df = conn.add_deferrable
|
201
|
+
df.should_receive(:fail).with(:buried, 40)
|
202
|
+
|
203
|
+
conn.received("BURIED 40\r\n")
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'should handle using messages' do
|
207
|
+
conn = EMJack::Connection.new
|
208
|
+
|
209
|
+
df = conn.add_deferrable
|
210
|
+
df.should_receive(:succeed).with("mytube")
|
211
|
+
|
212
|
+
conn.received("USING mytube\r\n")
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'should handle watching messages' do
|
216
|
+
conn = EMJack::Connection.new
|
217
|
+
|
218
|
+
df = conn.add_deferrable
|
219
|
+
df.should_receive(:succeed).with(24)
|
220
|
+
|
221
|
+
conn.received("WATCHING 24\r\n")
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'should handle reserved messages' do
|
225
|
+
conn = EMJack::Connection.new
|
226
|
+
|
227
|
+
msg = "This is my message"
|
228
|
+
|
229
|
+
df = conn.add_deferrable
|
230
|
+
df.should_receive(:succeed).with do |job|
|
231
|
+
job.class.should == EMJack::Job
|
232
|
+
job.jobid.should == 42
|
233
|
+
job.body.should == msg
|
234
|
+
end
|
235
|
+
|
236
|
+
conn.received("RESERVED 42 #{msg.length}\r\n#{msg}\r\n")
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'should handle receiving multiple replies in one packet' do
|
240
|
+
conn = EMJack::Connection.new
|
241
|
+
|
242
|
+
df = conn.add_deferrable
|
243
|
+
df.should_receive(:succeed).with(24)
|
244
|
+
|
245
|
+
df2 = conn.add_deferrable
|
246
|
+
df2.should_receive(:succeed).with("mytube")
|
247
|
+
|
248
|
+
conn.received("WATCHING 24\r\nUSING mytube\r\n")
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'should handle receiving data in chunks' do
|
252
|
+
conn = EMJack::Connection.new
|
253
|
+
|
254
|
+
msg1 = "First half of the message\r\n"
|
255
|
+
msg2 = "Last half of the message"
|
256
|
+
|
257
|
+
df = conn.add_deferrable
|
258
|
+
df.should_receive(:succeed).with do |job|
|
259
|
+
job.body.should == "#{msg1}#{msg2}"
|
260
|
+
end
|
261
|
+
|
262
|
+
conn.received("RESERVED 9 #{(msg1 + msg2).length}\r\n#{msg1}")
|
263
|
+
conn.received("#{msg2}\r\n")
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'should send the stat command' do
|
267
|
+
@connection_mock.should_receive(:send).once.with(:stats)
|
268
|
+
conn = EMJack::Connection.new
|
269
|
+
conn.stats
|
270
|
+
end
|
271
|
+
|
272
|
+
it 'should handle receiving the OK command' do
|
273
|
+
conn = EMJack::Connection.new
|
274
|
+
|
275
|
+
msg =<<-HERE
|
276
|
+
---
|
277
|
+
current-jobs-urgent: 42
|
278
|
+
current-jobs-ready: 92
|
279
|
+
current-jobs-reserved: 18
|
280
|
+
current-jobs-delayed: 7
|
281
|
+
current-jobs-buried: 0
|
282
|
+
pid: 416
|
283
|
+
version: dev
|
284
|
+
HERE
|
285
|
+
|
286
|
+
df = conn.add_deferrable
|
287
|
+
df.should_receive(:succeed).with do |stats|
|
288
|
+
stats['current-jobs-urgent'].should == 42
|
289
|
+
stats['current-jobs-ready'].should == 92
|
290
|
+
stats['current-jobs-reserved'].should == 18
|
291
|
+
stats['current-jobs-delayed'].should == 7
|
292
|
+
stats['current-jobs-buried'].should == 0
|
293
|
+
stats['pid'].should == 416
|
294
|
+
stats['version'].should == 'dev'
|
295
|
+
end
|
296
|
+
|
297
|
+
conn.received("OK #{msg.length}\r\n#{msg}\r\n")
|
298
|
+
end
|
299
|
+
|
300
|
+
it 'should support job stats' do
|
301
|
+
job = EMJack::Job.new(nil, 42, "blah")
|
302
|
+
|
303
|
+
@connection_mock.should_receive(:send).once.with(:'stats-job', 42)
|
304
|
+
conn = EMJack::Connection.new
|
305
|
+
conn.stats(:job, job)
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'should support tube stats' do
|
309
|
+
@connection_mock.should_receive(:send).once.with(:'stats-tube', "mytube")
|
310
|
+
conn = EMJack::Connection.new
|
311
|
+
conn.stats(:tube, "mytube")
|
312
|
+
end
|
313
|
+
|
314
|
+
it 'should throw exception on invalid stats command' do
|
315
|
+
@connection_mock.should_not_receive(:send)
|
316
|
+
conn = EMJack::Connection.new
|
317
|
+
lambda { conn.stats(:blah) }.should raise_error(EMJack::InvalidCommand)
|
318
|
+
end
|
319
|
+
|
320
|
+
it 'should support listing tubes' do
|
321
|
+
@connection_mock.should_receive(:send).once.with(:'list-tubes')
|
322
|
+
conn = EMJack::Connection.new
|
323
|
+
conn.list
|
324
|
+
end
|
325
|
+
|
326
|
+
it 'should support listing tube used' do
|
327
|
+
@connection_mock.should_receive(:send).once.with(:'list-tube-used')
|
328
|
+
conn = EMJack::Connection.new
|
329
|
+
conn.list(:used)
|
330
|
+
end
|
331
|
+
|
332
|
+
it 'should support listing tubes watched' do
|
333
|
+
@connection_mock.should_receive(:send).once.with(:'list-tubes-watched')
|
334
|
+
conn = EMJack::Connection.new
|
335
|
+
conn.list(:watched)
|
336
|
+
end
|
337
|
+
|
338
|
+
it 'should throw exception on invalid list command' do
|
339
|
+
@connection_mock.should_not_receive(:send)
|
340
|
+
conn = EMJack::Connection.new
|
341
|
+
lambda { conn.list(:blah) }.should raise_error(EMJack::InvalidCommand)
|
342
|
+
end
|
343
|
+
|
344
|
+
it 'should accept a response broken over multiple packets' do
|
345
|
+
conn = EMJack::Connection.new
|
346
|
+
|
347
|
+
msg1 = "First half of the message\r\n"
|
348
|
+
msg2 = "Last half of the message"
|
349
|
+
|
350
|
+
df = conn.add_deferrable
|
351
|
+
df.should_receive(:succeed).with do |job|
|
352
|
+
job.body.should == "#{msg1}#{msg2}"
|
353
|
+
end
|
354
|
+
|
355
|
+
conn.received("RESERVED 9 ")
|
356
|
+
conn.received("#{(msg1 + msg2).length}")
|
357
|
+
conn.received("\r\n#{msg1}#{msg2}\r\n")
|
358
|
+
end
|
359
|
+
|
360
|
+
it 'should accept a response broken over multiple packets' do
|
361
|
+
conn = EMJack::Connection.new
|
362
|
+
|
363
|
+
msg1 = "First half of the message\r\n"
|
364
|
+
msg2 = "Last half of the message"
|
365
|
+
|
366
|
+
df = conn.add_deferrable
|
367
|
+
df.should_receive(:succeed).with do |job|
|
368
|
+
job.body.should == "#{msg1}#{msg2}"
|
369
|
+
end
|
370
|
+
|
371
|
+
conn.received("RESERVED 9 #{(msg1 + msg2).length}\r\n#{msg1}#{msg2}")
|
372
|
+
conn.received("\r\n")
|
373
|
+
end
|
374
|
+
end
|
@@ -0,0 +1,365 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe EMJack::Connection, "integration" do
|
4
|
+
include EM::Spec
|
5
|
+
|
6
|
+
it 'should use a default host of "localhost" and port of 11300' do
|
7
|
+
conn = EMJack::Connection.new
|
8
|
+
conn.host.should == 'localhost'
|
9
|
+
conn.port.should == 11300
|
10
|
+
done
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should watch and use a provided tube on connect' do
|
14
|
+
conn = EMJack::Connection.new
|
15
|
+
conn.watch('my-lovely-tube') do
|
16
|
+
conn.list(:watched) do |watched|
|
17
|
+
watched.should include("my-lovely-tube")
|
18
|
+
done
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should send the "use" command' do
|
24
|
+
conn = EMJack::Connection.new
|
25
|
+
conn.use('my-lovely-tube') do
|
26
|
+
conn.list(:used) do |used|
|
27
|
+
used.should include("my-lovely-tube")
|
28
|
+
done
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should put a job" do
|
34
|
+
conn = EMJack::Connection.new
|
35
|
+
conn.put('myjob') do
|
36
|
+
conn.reserve do |job|
|
37
|
+
job.body.should == 'myjob'
|
38
|
+
job.delete { done }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should drain the queue" do
|
44
|
+
conn = EMJack::Connection.new
|
45
|
+
conn.put('myjob')
|
46
|
+
conn.put('myjob')
|
47
|
+
conn.put('myjob')
|
48
|
+
conn.put('myjob')
|
49
|
+
conn.put('myjob')
|
50
|
+
conn.drain!{
|
51
|
+
conn.stats do |stats|
|
52
|
+
stats['current-jobs-ready'].should == 0
|
53
|
+
done
|
54
|
+
end
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should default the delay, priority and ttr settings' do
|
59
|
+
conn = EMJack::Connection.new
|
60
|
+
conn.put('myjob') do
|
61
|
+
conn.reserve do |job|
|
62
|
+
job.delay.should == conn.default_delay
|
63
|
+
job.ttr.should == conn.default_ttr
|
64
|
+
job.priority.should == conn.default_priority
|
65
|
+
job.delete { done }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should accept a delay setting' do
|
71
|
+
conn = EMJack::Connection.new
|
72
|
+
start = Time.new.to_f
|
73
|
+
conn.put('mydelayedjob', :delay => 3) do |id|
|
74
|
+
conn.reserve do |job|
|
75
|
+
(Time.new.to_f - start).should be_close(3, 0.1)
|
76
|
+
job.id.should == id
|
77
|
+
job.delete { done }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should accept a ttr setting' do
|
83
|
+
conn = EMJack::Connection.new
|
84
|
+
conn.put('mydelayedjob', :ttr => 1) do |id|
|
85
|
+
|
86
|
+
conn.reserve do |job|
|
87
|
+
job.id.should == id
|
88
|
+
EM.add_timer(3) do
|
89
|
+
conn2 = EMJack::Connection.new
|
90
|
+
conn2.reserve do |job2|
|
91
|
+
job2.id.should == id
|
92
|
+
job2.delete { done }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should accept a priority setting' do
|
100
|
+
conn = EMJack::Connection.new
|
101
|
+
conn.put('myhighpriorityjob', :priority => 800) do |low_id|
|
102
|
+
conn.put('myhighpriorityjob', :priority => 300) do |high_id|
|
103
|
+
conn.reserve do |job|
|
104
|
+
job.id.should == high_id
|
105
|
+
job.delete { conn.delete(low_id){done}}
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should handle a non-string provided as the put message' do
|
112
|
+
conn = EMJack::Connection.new
|
113
|
+
conn.put(22) do |id|
|
114
|
+
conn.reserve do |job|
|
115
|
+
job.body.should == '22'
|
116
|
+
job.delete {done}
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
#
|
121
|
+
#it 'should send the "delete" command' do
|
122
|
+
# @connection_mock.should_receive(:send).once.with(:delete, 1)
|
123
|
+
# job = EMJack::Job.new(nil, 1, "body")
|
124
|
+
# conn = EMJack::Connection.new
|
125
|
+
# conn.delete(job)
|
126
|
+
#end
|
127
|
+
#
|
128
|
+
#it 'should handle a nil job sent to the "delete" command' do
|
129
|
+
# @connection_mock.should_not_receive(:send).with(:delete, nil)
|
130
|
+
# conn = EMJack::Connection.new
|
131
|
+
# conn.delete(nil)
|
132
|
+
#end
|
133
|
+
#
|
134
|
+
#it 'should send the "reserve" command' do
|
135
|
+
# @connection_mock.should_receive(:send).with(:reserve)
|
136
|
+
# conn = EMJack::Connection.new
|
137
|
+
# conn.reserve
|
138
|
+
#end
|
139
|
+
#
|
140
|
+
#it 'should raise exception if reconnect fails more then RETRY_COUNT times' do
|
141
|
+
# EM.should_receive(:add_timer).exactly(5).times
|
142
|
+
#
|
143
|
+
# conn = EMJack::Connection.new
|
144
|
+
# 5.times { conn.disconnected }
|
145
|
+
# lambda { conn.disconnected }.should raise_error(EMJack::Disconnected)
|
146
|
+
#end
|
147
|
+
#
|
148
|
+
#it 'should reset the retry count on connection' do
|
149
|
+
# EM.should_receive(:add_timer).at_least(1).times
|
150
|
+
#
|
151
|
+
# conn = EMJack::Connection.new
|
152
|
+
# 5.times { conn.disconnected }
|
153
|
+
# conn.connected
|
154
|
+
# lambda { conn.disconnected }.should_not raise_error(EMJack::Disconnected)
|
155
|
+
#end
|
156
|
+
#
|
157
|
+
#%w(OUT_OF_MEMORY INTERNAL_ERROR DRAINING BAD_FORMAT
|
158
|
+
# UNKNOWN_COMMAND EXPECTED_CRLF JOB_TOO_BIG DEADLINE_SOON
|
159
|
+
# TIMED_OUT NOT_FOUND).each do |cmd|
|
160
|
+
# it "should handle #{cmd} messages" do
|
161
|
+
# conn = EMJack::Connection.new
|
162
|
+
#
|
163
|
+
# df = conn.add_deferrable
|
164
|
+
# df.should_receive(:fail).with(cmd.downcase.to_sym)
|
165
|
+
#
|
166
|
+
# conn.received("#{cmd}\r\n")
|
167
|
+
# end
|
168
|
+
#end
|
169
|
+
#
|
170
|
+
#it 'should handle deleted messages' do
|
171
|
+
# conn = EMJack::Connection.new
|
172
|
+
#
|
173
|
+
# df = conn.add_deferrable
|
174
|
+
# df.should_receive(:succeed)
|
175
|
+
#
|
176
|
+
# conn.received("DELETED\r\n")
|
177
|
+
#end
|
178
|
+
#
|
179
|
+
#it 'should handle inserted messages' do
|
180
|
+
# conn = EMJack::Connection.new
|
181
|
+
#
|
182
|
+
# df = conn.add_deferrable
|
183
|
+
# df.should_receive(:succeed).with(40)
|
184
|
+
#
|
185
|
+
# conn.received("INSERTED 40\r\n")
|
186
|
+
#end
|
187
|
+
#
|
188
|
+
#it 'should handle buried messages' do
|
189
|
+
# conn = EMJack::Connection.new
|
190
|
+
#
|
191
|
+
# df = conn.add_deferrable
|
192
|
+
# df.should_receive(:fail).with(:buried, 40)
|
193
|
+
#
|
194
|
+
# conn.received("BURIED 40\r\n")
|
195
|
+
#end
|
196
|
+
#
|
197
|
+
#it 'should handle using messages' do
|
198
|
+
# conn = EMJack::Connection.new
|
199
|
+
#
|
200
|
+
# df = conn.add_deferrable
|
201
|
+
# df.should_receive(:succeed).with("mytube")
|
202
|
+
#
|
203
|
+
# conn.received("USING mytube\r\n")
|
204
|
+
#end
|
205
|
+
#
|
206
|
+
#it 'should handle watching messages' do
|
207
|
+
# conn = EMJack::Connection.new
|
208
|
+
#
|
209
|
+
# df = conn.add_deferrable
|
210
|
+
# df.should_receive(:succeed).with(24)
|
211
|
+
#
|
212
|
+
# conn.received("WATCHING 24\r\n")
|
213
|
+
#end
|
214
|
+
#
|
215
|
+
#it 'should handle reserved messages' do
|
216
|
+
# conn = EMJack::Connection.new
|
217
|
+
#
|
218
|
+
# msg = "This is my message"
|
219
|
+
#
|
220
|
+
# df = conn.add_deferrable
|
221
|
+
# df.should_receive(:succeed).with do |job|
|
222
|
+
# job.class.should == EMJack::Job
|
223
|
+
# job.id.should == 42
|
224
|
+
# job.body.should == msg
|
225
|
+
# end
|
226
|
+
#
|
227
|
+
# conn.received("RESERVED 42 #{msg.length}\r\n#{msg}\r\n")
|
228
|
+
#end
|
229
|
+
#
|
230
|
+
#it 'should handle receiving multiple replies in one packet' do
|
231
|
+
# conn = EMJack::Connection.new
|
232
|
+
#
|
233
|
+
# df = conn.add_deferrable
|
234
|
+
# df.should_receive(:succeed).with(24)
|
235
|
+
#
|
236
|
+
# df2 = conn.add_deferrable
|
237
|
+
# df2.should_receive(:succeed).with("mytube")
|
238
|
+
#
|
239
|
+
# conn.received("WATCHING 24\r\nUSING mytube\r\n")
|
240
|
+
#end
|
241
|
+
#
|
242
|
+
#it 'should handle receiving data in chunks' do
|
243
|
+
# conn = EMJack::Connection.new
|
244
|
+
#
|
245
|
+
# msg1 = "First half of the message\r\n"
|
246
|
+
# msg2 = "Last half of the message"
|
247
|
+
#
|
248
|
+
# df = conn.add_deferrable
|
249
|
+
# df.should_receive(:succeed).with do |job|
|
250
|
+
# job.body.should == "#{msg1}#{msg2}"
|
251
|
+
# end
|
252
|
+
#
|
253
|
+
# conn.received("RESERVED 9 #{(msg1 + msg2).length}\r\n#{msg1}")
|
254
|
+
# conn.received("#{msg2}\r\n")
|
255
|
+
#end
|
256
|
+
#
|
257
|
+
#it 'should send the stat command' do
|
258
|
+
# @connection_mock.should_receive(:send).once.with(:stats)
|
259
|
+
# conn = EMJack::Connection.new
|
260
|
+
# conn.stats
|
261
|
+
#end
|
262
|
+
#
|
263
|
+
#it 'should handle receiving the OK command' do
|
264
|
+
# conn = EMJack::Connection.new
|
265
|
+
#
|
266
|
+
# msg =<<-HERE
|
267
|
+
#---
|
268
|
+
#current-jobs-urgent: 42
|
269
|
+
#current-jobs-ready: 92
|
270
|
+
#current-jobs-reserved: 18
|
271
|
+
#current-jobs-delayed: 7
|
272
|
+
#current-jobs-buried: 0
|
273
|
+
#pid: 416
|
274
|
+
#version: dev
|
275
|
+
#HERE
|
276
|
+
#
|
277
|
+
# df = conn.add_deferrable
|
278
|
+
# df.should_receive(:succeed).with do |stats|
|
279
|
+
# stats['current-jobs-urgent'].should == 42
|
280
|
+
# stats['current-jobs-ready'].should == 92
|
281
|
+
# stats['current-jobs-reserved'].should == 18
|
282
|
+
# stats['current-jobs-delayed'].should == 7
|
283
|
+
# stats['current-jobs-buried'].should == 0
|
284
|
+
# stats['pid'].should == 416
|
285
|
+
# stats['version'].should == 'dev'
|
286
|
+
# end
|
287
|
+
#
|
288
|
+
# conn.received("OK #{msg.length}\r\n#{msg}\r\n")
|
289
|
+
#end
|
290
|
+
#
|
291
|
+
#it 'should support job stats' do
|
292
|
+
# job = EMJack::Job.new(nil, 42, "blah")
|
293
|
+
#
|
294
|
+
# @connection_mock.should_receive(:send).once.with(:'stats-job', 42)
|
295
|
+
# conn = EMJack::Connection.new
|
296
|
+
# conn.stats(:job, job)
|
297
|
+
#end
|
298
|
+
#
|
299
|
+
#it 'should support tube stats' do
|
300
|
+
# @connection_mock.should_receive(:send).once.with(:'stats-tube', "mytube")
|
301
|
+
# conn = EMJack::Connection.new
|
302
|
+
# conn.stats(:tube, "mytube")
|
303
|
+
#end
|
304
|
+
#
|
305
|
+
#it 'should throw exception on invalid stats command' do
|
306
|
+
# @connection_mock.should_not_receive(:send)
|
307
|
+
# conn = EMJack::Connection.new
|
308
|
+
# lambda { conn.stats(:blah) }.should raise_error(EMJack::InvalidCommand)
|
309
|
+
#end
|
310
|
+
#
|
311
|
+
#it 'should support listing tubes' do
|
312
|
+
# @connection_mock.should_receive(:send).once.with(:'list-tubes')
|
313
|
+
# conn = EMJack::Connection.new
|
314
|
+
# conn.list
|
315
|
+
#end
|
316
|
+
#
|
317
|
+
#it 'should support listing tube used' do
|
318
|
+
# @connection_mock.should_receive(:send).once.with(:'list-tube-used')
|
319
|
+
# conn = EMJack::Connection.new
|
320
|
+
# conn.list(:used)
|
321
|
+
#end
|
322
|
+
#
|
323
|
+
#it 'should support listing tubes watched' do
|
324
|
+
# @connection_mock.should_receive(:send).once.with(:'list-tubes-watched')
|
325
|
+
# conn = EMJack::Connection.new
|
326
|
+
# conn.list(:watched)
|
327
|
+
#end
|
328
|
+
#
|
329
|
+
#it 'should throw exception on invalid list command' do
|
330
|
+
# @connection_mock.should_not_receive(:send)
|
331
|
+
# conn = EMJack::Connection.new
|
332
|
+
# lambda { conn.list(:blah) }.should raise_error(EMJack::InvalidCommand)
|
333
|
+
#end
|
334
|
+
#
|
335
|
+
#it 'should accept a response broken over multiple packets' do
|
336
|
+
# conn = EMJack::Connection.new
|
337
|
+
#
|
338
|
+
# msg1 = "First half of the message\r\n"
|
339
|
+
# msg2 = "Last half of the message"
|
340
|
+
#
|
341
|
+
# df = conn.add_deferrable
|
342
|
+
# df.should_receive(:succeed).with do |job|
|
343
|
+
# job.body.should == "#{msg1}#{msg2}"
|
344
|
+
# end
|
345
|
+
#
|
346
|
+
# conn.received("RESERVED 9 ")
|
347
|
+
# conn.received("#{(msg1 + msg2).length}")
|
348
|
+
# conn.received("\r\n#{msg1}#{msg2}\r\n")
|
349
|
+
#end
|
350
|
+
#
|
351
|
+
#it 'should accept a response broken over multiple packets' do
|
352
|
+
# conn = EMJack::Connection.new
|
353
|
+
#
|
354
|
+
# msg1 = "First half of the message\r\n"
|
355
|
+
# msg2 = "Last half of the message"
|
356
|
+
#
|
357
|
+
# df = conn.add_deferrable
|
358
|
+
# df.should_receive(:succeed).with do |job|
|
359
|
+
# job.body.should == "#{msg1}#{msg2}"
|
360
|
+
# end
|
361
|
+
#
|
362
|
+
# conn.received("RESERVED 9 #{(msg1 + msg2).length}\r\n#{msg1}#{msg2}")
|
363
|
+
# conn.received("\r\n")
|
364
|
+
#end
|
365
|
+
end
|
data/spec/job_spec.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe EMJack::Job do
|
4
|
+
it 'should convert id to an integer' do
|
5
|
+
j = EMJack::Job.new(nil, "123", "body")
|
6
|
+
j.id.should == 123
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should fail if id is not an integer' do
|
10
|
+
proc {
|
11
|
+
j = EMJack::Job.new(nil, "asd", "body")
|
12
|
+
}.should raise_error
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should send a delete command to the connection' do
|
16
|
+
conn = mock(:conn)
|
17
|
+
conn.should_receive(:default_priority)
|
18
|
+
conn.should_receive(:default_delay)
|
19
|
+
conn.should_receive(:default_ttr)
|
20
|
+
|
21
|
+
j = EMJack::Job.new(conn, 1, "body")
|
22
|
+
|
23
|
+
conn.should_receive(:delete).with(j)
|
24
|
+
|
25
|
+
j.delete
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should send a stats command to the connection' do
|
29
|
+
conn = mock(:conn)
|
30
|
+
conn.should_receive(:default_priority)
|
31
|
+
conn.should_receive(:default_delay)
|
32
|
+
conn.should_receive(:default_ttr)
|
33
|
+
|
34
|
+
j = EMJack::Job.new(conn, 2, 'body')
|
35
|
+
conn.should_receive(:stats).with(:job, j)
|
36
|
+
|
37
|
+
j.stats
|
38
|
+
end
|
39
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
libdir = File.expand_path("lib")
|
2
|
+
$:.unshift(libdir) unless $:.include?(libdir)
|
3
|
+
|
4
|
+
require 'em-jack'
|
5
|
+
#require 'rubygems'
|
6
|
+
require "em-spec/rspec"
|
7
|
+
|
8
|
+
#def with_connection(*args)
|
9
|
+
# EM.run do
|
10
|
+
# yield EMJack::Connection.new(*args)
|
11
|
+
# EM.stop
|
12
|
+
# end
|
13
|
+
|
14
|
+
#end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: em-beanstalk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-20 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: eventmachine
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: EventMachine client for Beanstalkd
|
26
|
+
email: dan@postrank.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README.rdoc
|
33
|
+
files:
|
34
|
+
- COPYING
|
35
|
+
- README.rdoc
|
36
|
+
- Rakefile
|
37
|
+
- VERSION
|
38
|
+
- lib/em-jack.rb
|
39
|
+
- lib/em-jack/beanstalk_connection.rb
|
40
|
+
- lib/em-jack/connection.rb
|
41
|
+
- lib/em-jack/errors.rb
|
42
|
+
- lib/em-jack/job.rb
|
43
|
+
- spec/connection_spec.rb
|
44
|
+
- spec/integration_spec.rb
|
45
|
+
- spec/job_spec.rb
|
46
|
+
- spec/spec.opts
|
47
|
+
- spec/spec_helper.rb
|
48
|
+
has_rdoc: true
|
49
|
+
homepage: http://github.com/joshbuddy/em-jack
|
50
|
+
licenses: []
|
51
|
+
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options:
|
54
|
+
- --charset=UTF-8
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
version:
|
69
|
+
requirements: []
|
70
|
+
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 1.3.5
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: EventMachine client for Beanstalkd
|
76
|
+
test_files:
|
77
|
+
- spec/connection_spec.rb
|
78
|
+
- spec/integration_spec.rb
|
79
|
+
- spec/job_spec.rb
|
80
|
+
- spec/spec_helper.rb
|