em-beanstalk 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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,12 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+
3
+ require 'em-jack/job'
4
+ require 'em-jack/errors'
5
+ require 'em-jack/beanstalk_connection'
6
+ require 'em-jack/connection'
7
+
8
+ module EMJack
9
+ module VERSION
10
+ STRING = '0.0.3'
11
+ end
12
+ end
@@ -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
@@ -0,0 +1,7 @@
1
+ module EMJack
2
+ class Disconnected < RuntimeError
3
+ end
4
+
5
+ class InvalidCommand < RuntimeError
6
+ end
7
+ end
@@ -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
@@ -0,0 +1,7 @@
1
+ --colour
2
+ --format
3
+ specdoc
4
+ --loadby
5
+ mtime
6
+ --reverse
7
+ --backtrace
@@ -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