em-jack 0.1.3 → 0.1.4

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.
@@ -0,0 +1,5 @@
1
+ doc
2
+ pkg
3
+ *.gem
4
+ Gemfile.lock
5
+ .rvmrc
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,11 @@
1
+ require 'bundler'
2
+ require 'bundler/setup'
3
+ require 'bundler/gem_helper'
4
+
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ require 'rspec/core/rake_task'
8
+
9
+ RSpec::Core::RakeTask.new('spec') do |t|
10
+ t.verbose = false
11
+ end
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift("lib")
4
+
5
+ require 'rubygems'
6
+ require 'eventmachine'
7
+ require 'em-jack'
8
+ require 'pp'
9
+
10
+ $stdout.sync = true
11
+
12
+ class KeyboardHandler < EM::Connection
13
+ include EM::Protocols::LineText2
14
+
15
+ def post_init
16
+ @jack = EMJack::Connection.new
17
+ @jack.on_error { |err| puts "ERROR: #{err}" }
18
+
19
+ print "> "
20
+ end
21
+
22
+ def receive_line(line)
23
+ line.chomp!
24
+ line.gsub!(/^\s+/, '')
25
+
26
+ df = case(line)
27
+ when /^\s*$/ then
28
+ # noop
29
+ nil
30
+
31
+ when /^use / then
32
+ tube = line.gsub(/use /, '')
33
+ @jack.use(tube) { |tube| puts "Using #{tube}" }
34
+
35
+ when /^watch / then
36
+ tube = line.gsub(/watch /, '')
37
+ @jack.watch(tube) { |tube| puts "Watching #{tube}" }
38
+
39
+ when /^ignore / then
40
+ tube = line.gsub(/ignore /, '')
41
+ @jack.ignore(tube) { |tube| puts "Ignoring #{tube}" }
42
+
43
+ when /^put / then
44
+ msg = line.gsub(/put /, '')
45
+ @jack.put(msg) { |id| puts "Inserted job #{id}" }
46
+
47
+ when /^delete / then
48
+ id = line.gsub(/delete /, '').to_i
49
+ job = EMJack::Job.new(@jack, id, "asdf")
50
+ job.delete { puts "Deleted" }
51
+
52
+ when 'reserve' then
53
+ @jack.reserve { |job| puts "Reserved #{job}" }
54
+
55
+ when 'list-tubes' then
56
+ @jack.list { |tubes| pp tubes }
57
+
58
+ when 'list-watched' then
59
+ @jack.list(:watched) { |tubes| pp tubes }
60
+
61
+ when 'list-used' then
62
+ @jack.list(:used) { |tube| puts "Using #{tube}" }
63
+
64
+ when /^peek\s+(\d+)\s*$/ then
65
+ @jack.peek($1) { |job| puts "Peeked: #{job}" }
66
+
67
+ when 'peek-ready' then
68
+ @jack.peek(:ready) { |job| puts "Peeked ready: #{job}"}
69
+
70
+ when 'peek-delayed' then
71
+ @jack.peek(:delayed) { |job| puts "Peeked delayed: #{job}" }
72
+
73
+ when 'peek-buried' then
74
+ @jack.peek(:buried) { |job| puts "Peeked buried: #{job}" }
75
+
76
+ when 'stats' then
77
+ @jack.stats { |stats| pp stats }
78
+
79
+ when /^stats-tube\s+(.*)$/ then
80
+ @jack.stats(:tube, $1) { |stats| pp stats }
81
+
82
+ when /^stats-job\s+(\d+)/ then
83
+ j = EMJack::Job.new(@jack, $1, "blah")
84
+ j.stats { |stats| pp stats }
85
+
86
+ when 'help' then
87
+ msg = "COMMANDS:\n"
88
+ msg << " put <msg> - put message onto beanstalk\n"
89
+ msg << " reserve - reserve a job on beanstalk\n"
90
+ msg << " delete <id> - delete message with ID <id>\n"
91
+ msg << "\n"
92
+ msg << " use <tube> - use tube for messages\n"
93
+ msg << " watch <tube> - add <tube to watch list for messages\n"
94
+ msg << "\n"
95
+ msg << " stats - display beanstalk stats\n"
96
+ msg << " stats-tube <tube> - display tube stats\n"
97
+ msg << " stats-job <id> - display job stats\n"
98
+ msg << "\n"
99
+ msg << " list-tubes - display beanstalk tubes\n"
100
+ msg << " list-used - display the currently used tube\n"
101
+ msg << " list-watched - display the currently watched tubes\n"
102
+ msg << "\n"
103
+ msg << " peek <id> - peek at job with ID <id>\n"
104
+ msg << " peek-ready - peek at the first job in the ready queue\n"
105
+ msg << " peek-delayed - peek at the delayed job with shortest delay\n"
106
+ msg << " peek-buried - peek at the next buried job\n"
107
+ msg << "\n"
108
+ msg << " help - this help text\n"
109
+ msg << " quit - quit application\n"
110
+
111
+ puts msg
112
+ nil
113
+
114
+ when 'quit' then
115
+ EM.stop_event_loop
116
+ nil
117
+ end
118
+
119
+ unless df.nil?
120
+ df.callback { print "> " }
121
+ df.errback { print "> " }
122
+ end
123
+
124
+ print "> "
125
+ end
126
+ end
127
+
128
+ EM.run do
129
+ EM.open_keyboard(KeyboardHandler)
130
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $:.unshift(lib) unless $:.include?(lib)
4
+
5
+ require 'em-jack/version'
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = 'em-jack'
9
+ s.version = EMJack::VERSION
10
+ s.authors = ['Dan Sinclair']
11
+ s.email = ['dj2@everburning.com']
12
+ s.homepage = 'https://github.com/dj2/em-jack/'
13
+ s.summary = 'An evented Beanstalk client'
14
+ s.description = 'An evented Beanstalk client'
15
+
16
+ s.required_rubygems_version = '>= 1.3.6'
17
+
18
+ s.add_dependency 'eventmachine', ['>= 0.12.10']
19
+
20
+ s.add_development_dependency 'bundler', ['~> 1.0.13']
21
+ s.add_development_dependency 'rake', ['>= 0.8.7']
22
+ s.add_development_dependency 'rspec', ['~> 2.6']
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
26
+ s.require_path = ['lib']
27
+ end
@@ -4,13 +4,11 @@ require 'em-jack/job'
4
4
  require 'em-jack/errors'
5
5
  require 'em-jack/beanstalk_connection'
6
6
  require 'em-jack/connection'
7
+ require 'em-jack/version'
7
8
 
8
9
  Dir["#{File.dirname(__FILE__)}/em-jack/handlers/*.rb"].each do |file|
9
10
  require file
10
11
  end
11
12
 
12
13
  module EMJack
13
- module VERSION
14
- STRING = '0.1.3'
15
- end
16
14
  end
@@ -1,5 +1,6 @@
1
1
  require 'eventmachine'
2
2
  require 'yaml'
3
+ require 'uri'
3
4
 
4
5
  module EMJack
5
6
  class Connection
@@ -21,15 +22,24 @@ module EMJack
21
22
  end
22
23
 
23
24
  def initialize(opts = {})
24
- @host = opts[:host] || 'localhost'
25
- @port = opts[:port] || 11300
26
- @tube = opts[:tube]
25
+ case opts
26
+ when Hash
27
+ @host = opts[:host] || 'localhost'
28
+ @port = opts[:port] || 11300
29
+ @tube = opts[:tube]
30
+ when String
31
+ uri = URI.parse(opts)
32
+ @host = uri.host || 'localhost'
33
+ @port = uri.port || 11300
34
+ @tube = uri.path.gsub(/^\//, '') # Kill the leading /
35
+ end
27
36
 
28
37
  reset_tube_state
29
38
 
30
39
  @data = ""
31
40
  @retries = 0
32
41
  @in_reserve = false
42
+ @fiberized = false
33
43
 
34
44
  @conn = EM::connect(host, port, EMJack::BeanstalkConnection) do |conn|
35
45
  conn.client = self
@@ -42,7 +52,7 @@ module EMJack
42
52
  end
43
53
 
44
54
  def reset_tube_state
45
- prev_used = @used_tube
55
+ prev_used = @used_tube
46
56
  prev_watched = @watched_tubes.dup if @watched_tubes
47
57
 
48
58
  @used_tube = 'default'
@@ -53,16 +63,15 @@ module EMJack
53
63
  end
54
64
 
55
65
  def fiber!
56
- eigen = (class << self
57
- self
58
- end)
66
+ @fiberized = true
67
+
68
+ eigen = (class << self; self; end)
59
69
  eigen.instance_eval do
60
70
  %w(use reserve ignore watch peek stats list delete touch bury kick pause release put).each do |meth|
61
71
  alias_method :"a#{meth}", meth.to_sym
62
72
  define_method(meth.to_sym) do |*args|
63
73
  fib = Fiber.current
64
74
  ameth = :"a#{meth}"
65
- p [ameth, *args]
66
75
  proc = lambda { |*result| fib.resume(*result) }
67
76
  send(ameth, *args, &proc)
68
77
  Fiber.yield
@@ -219,16 +228,26 @@ module EMJack
219
228
  end
220
229
 
221
230
  def each_job(timeout = nil, &blk)
222
- work = Proc.new do
223
- r = reserve(timeout)
224
- r.callback do |job|
225
- blk.call(job)
226
- EM.next_tick { work.call }
231
+ if (@fiberized)
232
+ work = Proc.new do
233
+ Fiber.new do
234
+ job = reserve(timeout)
235
+ blk.call(job)
236
+ end.resume
237
+ EM.next_tick { work.call }
227
238
  end
228
- r.errback do
229
- EM.next_tick { work.call }
239
+ else
240
+ work = Proc.new do
241
+ r = reserve(timeout)
242
+ r.callback do |job|
243
+ blk.call(job)
244
+ EM.next_tick { work.call }
245
+ end
246
+ r.errback do
247
+ EM.next_tick { work.call }
248
+ end
230
249
  end
231
- end
250
+ end
232
251
  work.call
233
252
  end
234
253
 
@@ -236,9 +255,12 @@ module EMJack
236
255
  @reconnect_proc = nil
237
256
  @retries = 0
238
257
  succeed
258
+ @connected = true
259
+ @connected_callback.call if @connected_callback
239
260
  end
240
261
 
241
262
  def disconnected
263
+ @connected = false
242
264
  d = @deferrables.dup
243
265
 
244
266
  ## if reconnecting, need to fail ourself to remove any callbacks
@@ -256,18 +278,26 @@ module EMJack
256
278
  end
257
279
 
258
280
  prev_used, prev_watched = reset_tube_state
259
- @reconnect_proc = Proc.new { reconnect(prev_used, prev_watched) } unless @reconnect_proc
281
+ unless @reconnect_proc
282
+ recon = Proc.new { reconnect(prev_used, prev_watched) }
283
+ if @fiberized
284
+ @reconnect_proc = Proc.new { Fiber.new { recon.call }.resume }
285
+ else
286
+ @reconnect_proc = recon
287
+ end
288
+ end
260
289
 
261
290
  @retries += 1
262
291
  EM.add_timer(5) { @reconnect_proc.call }
263
292
  end
264
-
293
+
265
294
  def reconnect(prev_used, prev_watched)
266
295
  @conn.reconnect(@host, @port)
267
296
 
268
297
  use(prev_used) if prev_used
269
- [ prev_watched ].flatten.compact.each do |tube|
270
- watch(tube)
298
+
299
+ [prev_watched].flatten.compact.each do |tube|
300
+ @fiberized ? awatch(tube) : watch(tube)
271
301
  end
272
302
  end
273
303
 
@@ -275,7 +305,6 @@ module EMJack
275
305
  @retries = 0
276
306
 
277
307
  prev_used, prev_watched = reset_tube_state
278
-
279
308
  EM.next_tick { reconnect(prev_used, prev_watched) }
280
309
  end
281
310
 
@@ -294,11 +323,19 @@ module EMJack
294
323
  def on_error(&blk)
295
324
  @error_callback = blk
296
325
  end
297
-
326
+
298
327
  def on_disconnect(&blk)
299
328
  @disconnected_callback = blk
300
329
  end
301
330
 
331
+ def on_connect(&blk)
332
+ @connected_callback = blk
333
+ end
334
+
335
+ def connected?
336
+ @connected
337
+ end
338
+
302
339
  def received(data)
303
340
  @data << data
304
341
 
@@ -0,0 +1,3 @@
1
+ module EMJack
2
+ VERSION = '0.1.4'
3
+ end
@@ -0,0 +1,589 @@
1
+ require 'spec_helper'
2
+
3
+ describe EMJack::Connection do
4
+ let(:conn) do
5
+ conn = EMJack::Connection.new
6
+ conn.connected
7
+ conn
8
+ end
9
+
10
+ let(:connection_mock) do
11
+ connection_mock = mock(:conn)
12
+ connection_mock
13
+ end
14
+
15
+ before(:each) do
16
+ EM.stub!(:connect).and_return(connection_mock)
17
+ end
18
+
19
+ describe "uri connection string" do
20
+ let(:conn) do
21
+ EMJack::Connection.new('beanstalk://leet:1337/leetube')
22
+ end
23
+
24
+ it "should parse host" do
25
+ conn.host.should eql('leet')
26
+ end
27
+
28
+ it "should parse tube" do
29
+ connection_mock.should_receive(:send).once.with(:use, "leetube")
30
+ connection_mock.should_receive(:send).once.with(:watch, "leetube")
31
+ conn.connected
32
+ end
33
+
34
+ it "should parse port" do
35
+ conn.port.should eql(1337)
36
+ end
37
+ end
38
+
39
+ describe 'defaults' do
40
+ it 'host of "localhost"' do
41
+ conn.host.should == 'localhost'
42
+ end
43
+
44
+ it 'port of 11300' do
45
+ conn.port.should == 11300
46
+ end
47
+
48
+ it 'watch and use to provided tube on connect' do
49
+ connection_mock.should_receive(:send).once.with(:use, "mytube")
50
+ connection_mock.should_receive(:send).once.with(:watch, "mytube")
51
+ conn = EMJack::Connection.new(:tube => "mytube")
52
+ conn.connected
53
+ end
54
+
55
+ end
56
+
57
+ describe 'sending commands' do
58
+ it "doesn't send the command until we've connected" do
59
+ conn = EMJack::Connection.new
60
+ conn.should_not_receive(:send)
61
+ conn.use("mytube")
62
+ end
63
+
64
+ it "sends commands after we've connected" do
65
+ connected = false
66
+
67
+ connection_mock.should_receive(:send).twice do
68
+ connected.should be_true
69
+ end
70
+
71
+ conn = EMJack::Connection.new
72
+ conn.use('mytube')
73
+ conn.watch('mytube')
74
+
75
+ connected = true
76
+ conn.connected
77
+ end
78
+
79
+ it 'the "use" command' do
80
+ connection_mock.should_receive(:send).once.with(:use, "mytube")
81
+ conn.use("mytube")
82
+ end
83
+
84
+ it "doesn't send the use command to the currently used tube" do
85
+ connection_mock.should_receive(:send).once.with(:use, "mytube")
86
+ conn.use("mytube")
87
+ conn.use("mytube")
88
+ end
89
+
90
+ it 'the "watch" command' do
91
+ connection_mock.should_receive(:send).once.with(:watch, "mytube")
92
+ conn.watch("mytube")
93
+ end
94
+
95
+ it "doesn't send the watch command for a tube currently watched" do
96
+ connection_mock.should_not_receive(:send)
97
+ conn.instance_variable_get("@watched_tubes").push("mytube")
98
+ conn.watch("mytube")
99
+ end
100
+
101
+ describe "'put' command" do
102
+ it 'basic' do
103
+ msg = "my message"
104
+ connection_mock.should_receive(:send_with_data).once.
105
+ with(:put, msg, anything, anything, anything, msg.length)
106
+ conn.put(msg)
107
+ end
108
+
109
+ it 'defaults the delay, priority and ttr settings' do
110
+ connection_mock.should_receive(:send_with_data).once.
111
+ with(:put, anything, 65536, 0, 300, anything)
112
+ conn.put("msg")
113
+ end
114
+
115
+ it 'accepts a delay setting' do
116
+ connection_mock.should_receive(:send_with_data).once.
117
+ with(:put, anything, anything, 42, anything, anything)
118
+ conn.put("msg", :delay => 42)
119
+ end
120
+
121
+ it 'accepts a ttr setting' do
122
+ connection_mock.should_receive(:send_with_data).once.
123
+ with(:put, anything, anything, anything, 999, anything)
124
+ conn.put("msg", :ttr => 999)
125
+ end
126
+
127
+ it 'accepts a priority setting' do
128
+ connection_mock.should_receive(:send_with_data).once.
129
+ with(:put, anything, 233, anything, anything, anything)
130
+ conn.put("msg", :priority => 233)
131
+ end
132
+
133
+ it 'accepts a priority, delay and ttr setting' do
134
+ connection_mock.should_receive(:send_with_data).once.
135
+ with(:put, anything, 99, 42, 2000, anything)
136
+ conn.put("msg", :priority => 99, :delay => 42, :ttr => 2000)
137
+ end
138
+
139
+ it 'forces delay to be >= 0' do
140
+ connection_mock.should_receive(:send_with_data).once.
141
+ with(:put, anything, anything, 0, anything, anything)
142
+ conn.put("msg", :delay => -42)
143
+ end
144
+
145
+ it 'forces ttr to be >= 0' do
146
+ connection_mock.should_receive(:send_with_data).once.
147
+ with(:put, anything, anything, anything, 300, anything)
148
+ conn.put("msg", :ttr => -42)
149
+ end
150
+
151
+ it 'forces priority to be >= 0' do
152
+ connection_mock.should_receive(:send_with_data).once.
153
+ with(:put, anything, 65536, anything, anything, anything)
154
+ conn.put("msg", :priority => -42)
155
+ end
156
+
157
+ it 'forces priority to be < 2**32' do
158
+ connection_mock.should_receive(:send_with_data).once.
159
+ with(:put, anything, (2 ** 32), anything, anything, anything)
160
+ conn.put("msg", :priority => (2 ** 32 + 1))
161
+ end
162
+
163
+ it 'handles a non-string provided as the put message' do
164
+ msg = 22
165
+ connection_mock.should_receive(:send_with_data).once.
166
+ with(:put, msg.to_s, anything, anything, anything, msg.to_s.length)
167
+ conn.put(msg)
168
+ end
169
+ end
170
+
171
+ it 'the "delete" command' do
172
+ connection_mock.should_receive(:send).once.with(:delete, 1)
173
+ job = EMJack::Job.new(nil, 1, "body")
174
+ conn.delete(job)
175
+ end
176
+
177
+ it 'the "touch" command' do
178
+ connection_mock.should_receive(:send).once.with(:touch, 1)
179
+ job = EMJack::Job.new(nil, 1, "body")
180
+ conn.touch(job)
181
+ end
182
+
183
+ it 'the "kick" command' do
184
+ connection_mock.should_receive(:send).once.with(:kick, 15)
185
+ conn.kick(15)
186
+ end
187
+
188
+ it 'the "kick" command defaults to 1' do
189
+ connection_mock.should_receive(:send).once.with(:kick, 1)
190
+ conn.kick
191
+ end
192
+
193
+ it 'the "pause-tube" command' do
194
+ connection_mock.should_receive(:send).once.with(:'pause-tube', 60)
195
+ conn.pause('mytube', 60)
196
+ end
197
+
198
+ it 'the "bury" command' do
199
+ connection_mock.should_receive(:send).once.with(:'bury', 1, 1234)
200
+ job = EMJack::Job.new(nil, 1, "body")
201
+ conn.bury(job, 1234)
202
+ end
203
+
204
+ it 'handles a nil job sent to the "delete" command' do
205
+ connection_mock.should_not_receive(:send).with(:delete, nil)
206
+ conn.delete(nil)
207
+ end
208
+
209
+ it 'the "reserve" command' do
210
+ connection_mock.should_receive(:send).with(:reserve)
211
+ conn.reserve
212
+ end
213
+
214
+ describe 'stats' do
215
+ it 'default' do
216
+ connection_mock.should_receive(:send).once.with(:stats)
217
+ conn.stats
218
+ end
219
+
220
+ it 'job' do
221
+ job = EMJack::Job.new(nil, 42, "blah")
222
+
223
+ connection_mock.should_receive(:send).once.with(:'stats-job', 42)
224
+ conn.stats(:job, job)
225
+ end
226
+
227
+ it 'tube' do
228
+ connection_mock.should_receive(:send).once.with(:'stats-tube', "mytube")
229
+ conn.stats(:tube, "mytube")
230
+ end
231
+
232
+ it 'throws exception on invalid param' do
233
+ connection_mock.should_not_receive(:send)
234
+ lambda { conn.stats(:blah) }.should raise_error(EMJack::InvalidCommand)
235
+ end
236
+ end
237
+
238
+ describe 'list' do
239
+ it 'default' do
240
+ connection_mock.should_receive(:send).once.with(:'list-tubes')
241
+ conn.list
242
+ end
243
+
244
+ it 'tube used' do
245
+ connection_mock.should_receive(:send).once.with(:'list-tube-used')
246
+ conn.list(:used)
247
+ end
248
+
249
+ it 'tubes watched' do
250
+ connection_mock.should_receive(:send).once.with(:'list-tubes-watched')
251
+ conn.list(:watched)
252
+ end
253
+
254
+ it 'throws exception on invalid param' do
255
+ connection_mock.should_not_receive(:send)
256
+ lambda { conn.list(:blah) }.should raise_error(EMJack::InvalidCommand)
257
+ end
258
+ end
259
+
260
+ describe 'peek' do
261
+ it 'with a job id' do
262
+ connection_mock.should_receive(:send).once.with(:'peek', 42)
263
+ conn.peek(42)
264
+ end
265
+
266
+ it 'ready' do
267
+ connection_mock.should_receive(:send).once.with(:'peek-ready')
268
+ conn.peek(:ready)
269
+ end
270
+
271
+ it 'delayed' do
272
+ connection_mock.should_receive(:send).once.with(:'peek-delayed')
273
+ conn.peek(:delayed)
274
+ end
275
+
276
+ it 'buried' do
277
+ connection_mock.should_receive(:send).once.with(:'peek-buried')
278
+ conn.peek(:buried)
279
+ end
280
+
281
+ it 'throws exception on invalid param' do
282
+ connection_mock.should_not_receive(:send)
283
+ lambda { conn.peek(:blah) }.should raise_error(EMJack::InvalidCommand)
284
+ end
285
+ end
286
+ end
287
+
288
+ describe 'reconnect' do
289
+ let (:blk) do
290
+ Proc.new { "my proc" }
291
+ end
292
+
293
+ context 'on disconnect' do
294
+ before(:each) do
295
+ EM.should_receive(:add_timer).exactly(1).times.and_yield
296
+ connection_mock.as_null_object
297
+ end
298
+
299
+ it 'reuses a used tube' do
300
+ conn.should_receive(:use).with('used')
301
+ conn.instance_variable_set(:@used_tube, 'used')
302
+ conn.disconnected
303
+ end
304
+
305
+ it 'reuses a used tube' do
306
+ conn.should_receive(:use).with('default')
307
+ conn.disconnected
308
+ end
309
+
310
+ it 'rewatches a watched tube' do
311
+ conn.should_receive(:watch).with('watched')
312
+ conn.instance_variable_set(:@watched_tubes, ['watched'])
313
+ conn.disconnected
314
+ end
315
+
316
+ it 'rewatches multiple watched tubes' do
317
+ tubes = ['watched0', 'watched1']
318
+
319
+ conn.should_receive(:watch).twice do |tube|
320
+ tubes.delete(tube).should be_true
321
+ end
322
+
323
+ conn.instance_variable_set(:@watched_tubes, tubes)
324
+ conn.disconnected
325
+ end
326
+
327
+ it 'rewatches and reuses previous tubes on disconnect' do
328
+ conn.should_receive(:use).with('used')
329
+ conn.should_receive(:watch).with('watched')
330
+
331
+ conn.instance_variable_set(:@used_tube, 'used')
332
+ conn.instance_variable_set(:@watched_tubes, ['watched'])
333
+ conn.disconnected
334
+ end
335
+ end
336
+
337
+ it 'watches and uses previous tubes on disconnect' do
338
+ conn.should_receive(:watch).with('other')
339
+ connection_mock.as_null_object
340
+
341
+ EM.should_receive(:add_timer).exactly(1).times.and_yield
342
+ conn.instance_variable_set(:@watched_tubes, ['other'])
343
+ conn.disconnected
344
+ end
345
+
346
+ it 'raises exception if it fails more then RETRY_COUNT times' do
347
+ EM.should_receive(:add_timer).exactly(5).times
348
+
349
+ 5.times { conn.disconnected }
350
+ lambda { conn.disconnected }.should raise_error(EMJack::Disconnected)
351
+ end
352
+
353
+ it 'resets the retry count on connection' do
354
+ EM.should_receive(:add_timer).at_least(1).times
355
+
356
+ 5.times { conn.disconnected }
357
+ conn.connected
358
+ lambda { conn.disconnected }.should_not raise_error(EMJack::Disconnected)
359
+ end
360
+
361
+ it 'handles deferrables added during the fail phase' do
362
+ EM.stub!(:add_timer)
363
+
364
+ count = 0
365
+ blk = Proc.new do
366
+ count += 1
367
+ if count < 2
368
+ df = conn.add_deferrable
369
+ df.errback { blk.call }
370
+ end
371
+ end
372
+
373
+ df = conn.add_deferrable
374
+ df.errback { blk.call }
375
+
376
+ conn.disconnected
377
+ count.should == 1
378
+ end
379
+ end
380
+
381
+ describe 'beanstalk responses' do
382
+ let(:df) do
383
+ conn.add_deferrable
384
+ end
385
+
386
+ %w(OUT_OF_MEMORY INTERNAL_ERROR DRAINING BAD_FORMAT
387
+ UNKNOWN_COMMAND EXPECTED_CRLF JOB_TOO_BIG DEADLINE_SOON
388
+ TIMED_OUT NOT_FOUND).each do |cmd|
389
+ it "#{cmd} messages" do
390
+ df.should_receive(:fail).with(cmd.downcase.to_sym)
391
+ conn.received("#{cmd}\r\n")
392
+ end
393
+ end
394
+
395
+ ['buried', 'paused', 'touched', 'deleted'].each do |type|
396
+ it "#{type} messages" do
397
+ df.should_receive(:succeed)
398
+ conn.received("#{type.upcase}\r\n")
399
+ end
400
+ end
401
+
402
+ it 'inserted messages' do
403
+ df.should_receive(:succeed).with(40)
404
+ conn.received("INSERTED 40\r\n")
405
+ end
406
+
407
+ it 'buried messages' do
408
+ df.should_receive(:fail).with(:buried, 40)
409
+ conn.received("BURIED 40\r\n")
410
+ end
411
+
412
+ it 'kicked messages' do
413
+ df.should_receive(:succeed).with(15)
414
+ conn.received("KICKED 15\r\n")
415
+ end
416
+
417
+ it 'using messages' do
418
+ df.should_receive(:succeed).with("mytube")
419
+ conn.received("USING mytube\r\n")
420
+ end
421
+
422
+ it 'watching messages' do
423
+ df.should_receive(:succeed).with(24)
424
+ conn.received("WATCHING 24\r\n")
425
+ end
426
+
427
+ ['reserved', 'found'].each do |type|
428
+ it "#{type} messages" do
429
+ msg = "This is my message"
430
+
431
+ df.should_receive(:succeed).with do |job|
432
+ job.class.should == EMJack::Job
433
+ job.jobid.should == 42
434
+ job.body.should == msg
435
+ end
436
+
437
+ conn.received("#{type.upcase} 42 #{msg.length}\r\n#{msg}\r\n")
438
+ end
439
+ end
440
+
441
+ it 'OK messages' do
442
+ msg =<<-HERE
443
+ ---
444
+ current-jobs-urgent: 42
445
+ current-jobs-ready: 92
446
+ current-jobs-reserved: 18
447
+ current-jobs-delayed: 7
448
+ current-jobs-buried: 0
449
+ pid: 416
450
+ version: dev
451
+ HERE
452
+
453
+ df.should_receive(:succeed).with do |stats|
454
+ stats['current-jobs-urgent'].should == 42
455
+ stats['current-jobs-ready'].should == 92
456
+ stats['current-jobs-reserved'].should == 18
457
+ stats['current-jobs-delayed'].should == 7
458
+ stats['current-jobs-buried'].should == 0
459
+ stats['pid'].should == 416
460
+ stats['version'].should == 'dev'
461
+ end
462
+
463
+ conn.received("OK #{msg.length}\r\n#{msg}\r\n")
464
+ end
465
+
466
+ it 'receiving multiple replies in one packet' do
467
+ df.should_receive(:succeed).with(24)
468
+
469
+ df2 = conn.add_deferrable
470
+ df2.should_receive(:succeed).with("mytube")
471
+
472
+ conn.received("WATCHING 24\r\nUSING mytube\r\n")
473
+ end
474
+
475
+ it 'recieving multiple replies in one packet where first is STATS and second is DEADLINE_SOON (anti regression)' do
476
+ df.should_receive(:succeed).with do |stats|
477
+ stats['id'].should == 2
478
+ end
479
+
480
+ df2 = conn.add_deferrable
481
+ df2.should_receive(:fail).with(:deadline_soon)
482
+
483
+ conn.received("OK 142\r\n---\nid: 2\ntube: default\nstate: reserved\npri: 65536\nage: 2\ndelay: 0\nttr: 3\ntime-left: 0\nreserves: 1\ntimeouts: 0\nreleases: 0\nburies: 0\nkicks: 0\n\r\nDEADLINE_SOON\r\n")
484
+ end
485
+
486
+ it 'receiving data in chunks' do
487
+ msg1 = "First half of the message\r\n"
488
+ msg2 = "Last half of the message"
489
+
490
+ df.should_receive(:succeed).with do |job|
491
+ job.body.should == "#{msg1}#{msg2}"
492
+ end
493
+
494
+ conn.received("RESERVED 9 #{(msg1 + msg2).length}\r\n#{msg1}")
495
+ conn.received("#{msg2}\r\n")
496
+ end
497
+
498
+ it 'receiving a response broken over multiple packets' do
499
+ msg1 = "First half of the message\r\n"
500
+ msg2 = "Last half of the message"
501
+
502
+ df.should_receive(:succeed).with do |job|
503
+ job.body.should == "#{msg1}#{msg2}"
504
+ end
505
+
506
+ conn.received("RESERVED 9 ")
507
+ conn.received("#{(msg1 + msg2).length}")
508
+ conn.received("\r\n#{msg1}#{msg2}\r\n")
509
+ end
510
+
511
+ it 'the \r\n in a separate packet' do
512
+ msg1 = "First half of the message\r\n"
513
+ msg2 = "Last half of the message"
514
+
515
+ df.should_receive(:succeed).with do |job|
516
+ job.body.should == "#{msg1}#{msg2}"
517
+ end
518
+
519
+ conn.received("RESERVED 9 #{(msg1 + msg2).length}\r\n#{msg1}#{msg2}")
520
+ conn.received("\r\n")
521
+ end
522
+ end
523
+
524
+ context 'passed blocks' do
525
+ def callbacks(df)
526
+ df.instance_variable_get("@callbacks")
527
+ end
528
+
529
+ let (:blk) do
530
+ Proc.new { "my proc" }
531
+ end
532
+
533
+ describe 'uses #send' do
534
+ before(:each) do
535
+ connection_mock.should_receive(:send)
536
+ end
537
+
538
+ it 'use should set the callback when provided a block' do
539
+ df = conn.use('test', &blk)
540
+ callbacks(df).include?(blk).should be_true
541
+ end
542
+
543
+ it 'watch should set the callback when provided a block' do
544
+ df = conn.watch('blarg', &blk)
545
+ callbacks(df).include?(blk).should be_true
546
+ end
547
+
548
+ it 'ignore should set the callback when provided a block' do
549
+ conn.instance_variable_get("@watched_tubes").push('blarg')
550
+ df = conn.ignore('blarg', &blk)
551
+ callbacks(df).include?(blk).should be_true
552
+ end
553
+
554
+ it 'reserve should set the callback when provided a block' do
555
+ df = conn.reserve(&blk)
556
+ callbacks(df).include?(blk).should be_true
557
+ end
558
+
559
+ it 'stats should set the callback when provided a block' do
560
+ df = conn.stats(&blk)
561
+ callbacks(df).include?(blk).should be_true
562
+ end
563
+
564
+ it 'list should set the callback when provided a block' do
565
+ df = conn.list(&blk)
566
+ callbacks(df).include?(blk).should be_true
567
+ end
568
+
569
+ it 'delete should set the callback when provided a block' do
570
+ job = EMJack::Job.new(nil, 1, "body")
571
+ df = conn.delete(job, &blk)
572
+ callbacks(df).include?(blk).should be_true
573
+ end
574
+
575
+ it 'release should set the callback when provided a block' do
576
+ job = EMJack::Job.new(nil, 1, "body")
577
+ df = conn.release(job, &blk)
578
+ callbacks(df).include?(blk).should be_true
579
+ end
580
+ end
581
+
582
+ it 'put should set the callback when provided a block' do
583
+ connection_mock.should_receive(:send_with_data)
584
+
585
+ df = conn.put("asdf", nil, &blk)
586
+ callbacks(df).include?(blk).should be_true
587
+ end
588
+ end
589
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ if RUBY_VERSION > '1.9'
4
+ require 'fiber'
5
+
6
+ describe EMJack::Connection do
7
+ it "should process live messages" do
8
+ EM.run do
9
+ EM.add_timer(10) { EM.stop }
10
+
11
+ Fiber.new do
12
+ bean = EMJack::Connection.new
13
+ bean.fiber!
14
+
15
+ bean.put("hello!")
16
+ job = bean.reserve
17
+ job.body.should == "hello!"
18
+ job.delete
19
+
20
+ EM.stop
21
+ end.resume
22
+ end
23
+ end
24
+ it "should process each job" do
25
+ EM.run do
26
+ EM.add_timer(10) { EM.stop }
27
+
28
+ job_body = ''
29
+
30
+ f = Fiber.new do
31
+ bean = EMJack::Connection.new
32
+ bean.fiber!
33
+
34
+ bean.put("hello!")
35
+ bean.put("bonjour!")
36
+
37
+ mock = double()
38
+ mock.should_receive(:foo).with("hello!")
39
+ mock.should_receive(:foo).with("bonjour!")
40
+
41
+ bean.each_job(0) do |job|
42
+ mock.foo(job.body)
43
+ job_body = job.body
44
+ job.delete
45
+ end
46
+
47
+ end
48
+
49
+ f.resume
50
+
51
+ EM.add_timer(1) { EM.stop unless f.alive?; job_body.should eq "bonjour!" unless f.alive? }
52
+
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe EMJack::Job do
4
+ let (:conn ) { mock(:conn) }
5
+
6
+ it 'converts jobid to an integer' do
7
+ j = EMJack::Job.new(nil, "1", "body")
8
+ j.jobid.class.should == Fixnum
9
+ j.jobid.should == 1
10
+ end
11
+
12
+ it 'sends a delete command to the connection' do
13
+ j = EMJack::Job.new(conn, 1, "body")
14
+ conn.should_receive(:delete).with(j)
15
+
16
+ j.delete
17
+ end
18
+
19
+ it 'sends a stats command to the connection' do
20
+ j = EMJack::Job.new(conn, 2, 'body')
21
+ conn.should_receive(:stats).with(:job, j)
22
+
23
+ j.stats
24
+ end
25
+
26
+ it 'sends a release command to the connection' do
27
+ blk = Proc.new { x = 1 }
28
+
29
+ j = EMJack::Job.new(conn, 2, 'body')
30
+ conn.should_receive(:release).with(j, {:foo => :bar}, &blk)
31
+
32
+ j.release({:foo => :bar}, &blk)
33
+ end
34
+
35
+ it 'sends a touch command to the connection' do
36
+ j = EMJack::Job.new(conn, 2, 'body')
37
+ conn.should_receive(:touch).with(j)
38
+
39
+ j.touch
40
+ end
41
+
42
+ it 'sends a bury command to the connection' do
43
+ j = EMJack::Job.new(conn, 2, 'body')
44
+ conn.should_receive(:bury).with(j, 1234)
45
+
46
+ j.bury(1234)
47
+ end
48
+ end
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'em-jack'
4
+
5
+ RSpec.configure do |config|
6
+ end
metadata CHANGED
@@ -1,103 +1,123 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: em-jack
3
- version: !ruby/object:Gem::Version
4
- hash: 29
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 1
9
- - 3
10
- version: 0.1.3
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ prerelease:
11
6
  platform: ruby
12
- authors:
13
- - dan sinclair
7
+ authors:
8
+ - Dan Sinclair
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2010-12-01 00:00:00 -05:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
12
+ date: 2012-02-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
22
15
  name: eventmachine
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &70242395661740 !ruby/object:Gem::Requirement
25
17
  none: false
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- hash: 3
30
- segments:
31
- - 0
32
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.12.10
33
22
  type: :runtime
34
- version_requirements: *id001
35
- description: An evented Beanstalk client.
36
- email: dj2@everburning.com
23
+ prerelease: false
24
+ version_requirements: *70242395661740
25
+ - !ruby/object:Gem::Dependency
26
+ name: bundler
27
+ requirement: &70242395661260 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.13
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70242395661260
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &70242395660620 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 0.8.7
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70242395660620
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: &70242395660100 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.6'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70242395660100
58
+ description: An evented Beanstalk client
59
+ email:
60
+ - dj2@everburning.com
37
61
  executables: []
38
-
39
62
  extensions: []
40
-
41
- extra_rdoc_files:
42
- - README.rdoc
43
- files:
44
- - README.rdoc
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - .rspec
45
67
  - COPYING
68
+ - Gemfile
69
+ - README.rdoc
70
+ - Rakefile
71
+ - bin/shell
72
+ - em-jack.gemspec
46
73
  - lib/em-jack.rb
47
74
  - lib/em-jack/beanstalk_connection.rb
48
75
  - lib/em-jack/connection.rb
49
76
  - lib/em-jack/errors.rb
50
- - lib/em-jack/job.rb
51
77
  - lib/em-jack/handlers/buried.rb
78
+ - lib/em-jack/handlers/deleted.rb
79
+ - lib/em-jack/handlers/errors.rb
52
80
  - lib/em-jack/handlers/inserted.rb
81
+ - lib/em-jack/handlers/kicked.rb
53
82
  - lib/em-jack/handlers/not_ignored.rb
54
83
  - lib/em-jack/handlers/ok.rb
84
+ - lib/em-jack/handlers/paused.rb
55
85
  - lib/em-jack/handlers/released.rb
56
86
  - lib/em-jack/handlers/reserved.rb
87
+ - lib/em-jack/handlers/touched.rb
57
88
  - lib/em-jack/handlers/using.rb
58
89
  - lib/em-jack/handlers/watching.rb
59
- - lib/em-jack/handlers/deleted.rb
60
- - lib/em-jack/handlers/errors.rb
61
- - lib/em-jack/handlers/paused.rb
62
- - lib/em-jack/handlers/touched.rb
63
- - lib/em-jack/handlers/kicked.rb
64
- has_rdoc: true
65
- homepage: http://github.com/dj2/em-jack/
90
+ - lib/em-jack/job.rb
91
+ - lib/em-jack/version.rb
92
+ - spec/em-jack/connection_spec.rb
93
+ - spec/em-jack/fiber_spec.rb
94
+ - spec/em-jack/job_spec.rb
95
+ - spec/spec_helper.rb
96
+ homepage: https://github.com/dj2/em-jack/
66
97
  licenses: []
67
-
68
98
  post_install_message:
69
- rdoc_options:
70
- - --title
71
- - EM-Jack Documentation
72
- - --main
73
- - README.rdoc
74
- - --line-numbers
75
- require_paths:
76
- - lib
77
- required_ruby_version: !ruby/object:Gem::Requirement
99
+ rdoc_options: []
100
+ require_paths:
101
+ - - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
78
103
  none: false
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- hash: 3
83
- segments:
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ segments:
84
109
  - 0
85
- version: "0"
86
- required_rubygems_version: !ruby/object:Gem::Requirement
110
+ hash: -3983606337964967417
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
112
  none: false
88
- requirements:
89
- - - ">="
90
- - !ruby/object:Gem::Version
91
- hash: 3
92
- segments:
93
- - 0
94
- version: "0"
113
+ requirements:
114
+ - - ! '>='
115
+ - !ruby/object:Gem::Version
116
+ version: 1.3.6
95
117
  requirements: []
96
-
97
118
  rubyforge_project:
98
- rubygems_version: 1.3.7
119
+ rubygems_version: 1.8.10
99
120
  signing_key:
100
121
  specification_version: 3
101
- summary: An evented Beanstalk client.
122
+ summary: An evented Beanstalk client
102
123
  test_files: []
103
-