beanstalk-client 0.9.0 → 0.10.0

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/History.txt CHANGED
@@ -1,3 +1,10 @@
1
+ == 0.10.0 2008-04-11
2
+
3
+ * Some optimizations to avoid sending unnecessary use and watch commands.
4
+ * Use newer protocol features to open only one connection per server address.
5
+ * More consistent and complete peek methods.
6
+ * Various bug fixes.
7
+
1
8
  == 0.9.0 2008-02-27
2
9
 
3
10
  * Skipping 0.8.x; beanstalkd 0.8 was a botched release.
data/Manifest.txt CHANGED
@@ -6,7 +6,6 @@ Rakefile
6
6
  config/hoe.rb
7
7
  config/requirements.rb
8
8
  lib/beanstalk-client.rb
9
- lib/beanstalk-client/bag.rb
10
9
  lib/beanstalk-client/connection.rb
11
10
  lib/beanstalk-client/errors.rb
12
11
  lib/beanstalk-client/job.rb
@@ -19,18 +19,19 @@ require 'socket'
19
19
  require 'fcntl'
20
20
  require 'yaml'
21
21
  require 'set'
22
- require 'beanstalk-client/bag'
23
22
  require 'beanstalk-client/errors'
24
23
  require 'beanstalk-client/job'
25
24
 
26
25
  module Beanstalk
27
- class RawConnection
26
+ class Connection
28
27
  attr_reader :addr
29
28
 
30
29
  def initialize(addr, jptr=self)
31
30
  @addr = addr
32
31
  @jptr = jptr
33
32
  connect
33
+ @last_used = 'default'
34
+ @watch_list = ['default']
34
35
  end
35
36
 
36
37
  def connect
@@ -47,98 +48,116 @@ module Beanstalk
47
48
  end
48
49
 
49
50
  def put(body, pri=65536, delay=0, ttr=120)
50
- @socket.write("put #{pri} #{delay} #{ttr} #{body.size}\r\n#{body}\r\n")
51
- check_resp('INSERTED', 'BURIED')[0].to_i
51
+ interact("put #{pri} #{delay} #{ttr} #{body.size}\r\n#{body}\r\n",
52
+ %w(INSERTED BURIED))[0].to_i
52
53
  end
53
54
 
54
55
  def yput(obj, pri=65536, delay=0, ttr=120)
55
56
  put(YAML.dump(obj), pri, delay, ttr)
56
57
  end
57
58
 
58
- def peek()
59
- @socket.write("peek\r\n")
60
- begin
61
- Job.new(@jptr, *read_job('FOUND'))
62
- rescue UnexpectedResponse
63
- nil
64
- end
59
+ def peek_job(id)
60
+ interact("peek #{id}\r\n", :job)
65
61
  end
66
62
 
67
- def peek_job(id)
68
- @socket.write("peek #{id}\r\n")
69
- Job.new(@jptr, *read_job('FOUND'))
63
+ def peek_ready()
64
+ interact("peek-ready\r\n", :job)
65
+ end
66
+
67
+ def peek_delayed()
68
+ interact("peek-delayed\r\n", :job)
69
+ end
70
+
71
+ def peek_buried()
72
+ interact("peek-buried\r\n", :job)
70
73
  end
71
74
 
72
75
  def reserve()
76
+ raise WaitingForJobError if @waiting
73
77
  @socket.write("reserve\r\n")
78
+
79
+ begin
80
+ @waiting = true
81
+ # Give the user a chance to select on multiple fds.
82
+ Beanstalk.select.call([@socket]) if Beanstalk.select
83
+ rescue WaitingForJobError
84
+ # just continue
85
+ ensure
86
+ @waiting = false
87
+ end
88
+
74
89
  Job.new(@jptr, *read_job('RESERVED'))
75
90
  end
76
91
 
77
92
  def delete(id)
78
- @socket.write("delete #{id}\r\n")
79
- check_resp('DELETED')
93
+ interact("delete #{id}\r\n", %w(DELETED))
80
94
  :ok
81
95
  end
82
96
 
83
97
  def release(id, pri, delay)
84
- @socket.write("release #{id} #{pri} #{delay}\r\n")
85
- check_resp('RELEASED')
98
+ interact("release #{id} #{pri} #{delay}\r\n", %w(RELEASED))
86
99
  :ok
87
100
  end
88
101
 
89
102
  def bury(id, pri)
90
- @socket.write("bury #{id} #{pri}\r\n")
91
- check_resp('BURIED')
103
+ interact("bury #{id} #{pri}\r\n", %w(BURIED))
92
104
  :ok
93
105
  end
94
106
 
95
107
  def use(tube)
96
- @socket.write("use #{tube}\r\n")
97
- check_resp('USING')[0]
108
+ return tube if tube == @last_used
109
+ @last_used = interact("use #{tube}\r\n", %w(USING))[0]
98
110
  end
99
111
 
100
112
  def watch(tube)
101
- @socket.write("watch #{tube}\r\n")
102
- check_resp('WATCHING')[0]
113
+ return @watch_list.size if @watch_list.include?(tube)
114
+ r = interact("watch #{tube}\r\n", %w(WATCHING))[0].to_i
115
+ @watch_list += [tube]
116
+ return r
103
117
  end
104
118
 
105
119
  def ignore(tube)
106
- @socket.write("ignore #{tube}\r\n")
107
- check_resp('WATCHING')[0]
120
+ return @watch_list.size if !@watch_list.include?(tube)
121
+ r = interact("ignore #{tube}\r\n", %w(WATCHING))[0].to_i
122
+ @watch_list -= [tube]
123
+ return r
108
124
  end
109
125
 
110
126
  def stats()
111
- @socket.write("stats\r\n")
112
- read_yaml('OK')
127
+ interact("stats\r\n", :yaml)
113
128
  end
114
129
 
115
130
  def job_stats(id)
116
- @socket.write("stats-job #{id}\r\n")
117
- read_yaml('OK')
131
+ interact("stats-job #{id}\r\n", :yaml)
118
132
  end
119
133
 
120
134
  def stats_tube(tube)
121
- @socket.write("stats-tube #{tube}\r\n")
122
- read_yaml('OK')
135
+ interact("stats-tube #{tube}\r\n", :yaml)
123
136
  end
124
137
 
125
138
  def list_tubes()
126
- @socket.write("list-tubes\r\n")
127
- read_yaml('OK')
139
+ interact("list-tubes\r\n", :yaml)
128
140
  end
129
141
 
130
142
  def list_tube_used()
131
- @socket.write("list-tube-used\r\n")
132
- check_resp('USING')[0]
143
+ interact("list-tube-used\r\n", %w(USING))[0]
133
144
  end
134
145
 
135
- def list_tubes_watched()
136
- @socket.write("list-tubes-watched\r\n")
137
- read_yaml('OK')
146
+ def list_tubes_watched(cached=false)
147
+ return @watch_list if cached
148
+ @watch_list = interact("list-tubes-watched\r\n", :yaml)
138
149
  end
139
150
 
140
151
  private
141
152
 
153
+ def interact(cmd, rfmt)
154
+ raise WaitingForJobError if @waiting
155
+ @socket.write(cmd)
156
+ return read_yaml('OK') if rfmt == :yaml
157
+ return found_job if rfmt == :job
158
+ check_resp(*rfmt)
159
+ end
160
+
142
161
  def get_resp()
143
162
  r = @socket.gets("\r\n")
144
163
  raise EOFError if r == nil
@@ -149,15 +168,18 @@ module Beanstalk
149
168
  r = get_resp()
150
169
  rword, *vals = r.split(/\s+/)
151
170
  if (words.size > 0) and !words.include?(rword)
152
- raise UnexpectedResponse.new(r)
171
+ raise UnexpectedResponse.classify(rword, r)
153
172
  end
154
173
  vals
155
174
  end
156
175
 
157
- def read_job(word)
158
- # Give the user a chance to select on multiple fds.
159
- Beanstalk.select.call([@socket]) if Beanstalk.select
176
+ def found_job()
177
+ Job.new(@jptr, *read_job('FOUND'))
178
+ rescue NotFoundError
179
+ nil
180
+ end
160
181
 
182
+ def read_job(word)
161
183
  id, bytes = check_resp(word).map{|s| s.to_i}
162
184
  body = read_bytes(bytes)
163
185
  raise 'bad trailer' if read_bytes(2) != "\r\n"
@@ -179,47 +201,6 @@ module Beanstalk
179
201
  end
180
202
  end
181
203
 
182
- # Same interface as RawConnection.
183
- # With this you can reserve more than one job at a time.
184
- class Connection
185
- attr_reader :addr
186
-
187
- def initialize(addr, jptr=self)
188
- @addr = addr
189
- @misc = RawConnection.new(addr, jptr)
190
- @free = Bag.new{RawConnection.new(addr, jptr)}
191
- @used = {}
192
- end
193
-
194
- def reserve()
195
- c = @free.take()
196
- j = c.reserve()
197
- @used[j.id] = c
198
- j
199
- ensure
200
- @free.give(c) if c and not j
201
- end
202
-
203
- def delete(id)
204
- @used[id].delete(id)
205
- @free.give(@used.delete(id))
206
- end
207
-
208
- def release(id, pri, delay)
209
- @used[id].release(id, pri, delay)
210
- @free.give(@used.delete(id))
211
- end
212
-
213
- def bury(id, pri)
214
- @used[id].bury(id, pri)
215
- @free.give(@used.delete(id))
216
- end
217
-
218
- def method_missing(selector, *args, &block)
219
- @misc.send(selector, *args, &block)
220
- end
221
- end
222
-
223
204
  class CleanupWrapper
224
205
  def initialize(addr, multi)
225
206
  @conn = Connection.new(addr, self)
@@ -266,14 +247,6 @@ module Beanstalk
266
247
  @last_conn.addr
267
248
  end
268
249
 
269
- def send_to_rand_conn(sel, *args)
270
- wrap(pick_connection, sel, *args)
271
- end
272
-
273
- def send_to_all_conns(sel, *args)
274
- make_hash(@connections.map{|a, c| [a, wrap(c, sel, *args)]})
275
- end
276
-
277
250
  def put(body, pri=65536, delay=0, ttr=120)
278
251
  send_to_rand_conn(:put, body, pri, delay, ttr)
279
252
  end
@@ -332,20 +305,46 @@ module Beanstalk
332
305
 
333
306
  def close
334
307
  while @connections.size > 0
335
- addr, conn = @connections.pop
308
+ addr = @connections.keys.last
309
+ conn = @connections[addr]
310
+ @connections.delete(addr)
336
311
  conn.close
337
312
  end
338
313
  end
339
314
 
340
- def peek()
315
+ def peek_ready()
316
+ send_to_each_conn_first_res(:peek_ready)
317
+ end
318
+
319
+ def peek_delayed()
320
+ send_to_each_conn_first_res(:peek_delayed)
321
+ end
322
+
323
+ def peek_buried()
324
+ send_to_each_conn_first_res(:peek_buried)
325
+ end
326
+
327
+ def peek_job(id)
328
+ make_hash(send_to_all_conns(:peek_job, id))
329
+ end
330
+
331
+ private
332
+
333
+ def send_to_each_conn_first_res(sel, *args)
341
334
  open_connections.each do |c|
342
- job = c.peek
343
- return job if job
335
+ x = wrap(c, sel, *args)
336
+ return x if x
344
337
  end
345
338
  nil
346
339
  end
347
340
 
348
- private
341
+ def send_to_rand_conn(sel, *args)
342
+ wrap(pick_connection, sel, *args)
343
+ end
344
+
345
+ def send_to_all_conns(sel, *args)
346
+ compact_hash(make_hash(@connections.map{|a, c| [a, wrap(c, sel, *args)]}))
347
+ end
349
348
 
350
349
  def pick_connection()
351
350
  open_connections[rand(open_connections.size)] or raise NotConnected
@@ -365,6 +364,10 @@ module Beanstalk
365
364
  Hash[*pairs.inject([]){|a,b|a+b}]
366
365
  end
367
366
 
367
+ def compact_hash(hash)
368
+ hash.reject{|k,v| v == nil}
369
+ end
370
+
368
371
  def sum_hashes(hs)
369
372
  hs.inject({}){|a,b| a.merge(b) {|k,o,n| combine_stats(k, o, n)}}
370
373
  end
@@ -20,14 +20,30 @@ module Beanstalk
20
20
  end
21
21
 
22
22
  class UnexpectedResponse < RuntimeError
23
- def self.new(word)
24
- if self == UnexpectedResponse and word == 'DRAINING'
25
- return DrainingError.new(nil)
23
+ def self.subclasses
24
+ @classes ||= []
25
+ end
26
+
27
+ def self.inherited(subclass)
28
+ subclasses << subclass
29
+ end
30
+
31
+ def self.classify(word, message)
32
+ for clas in subclasses
33
+ return clas.new(message) if clas::WORD == word
26
34
  end
27
- super(word)
35
+ return new(message)
28
36
  end
29
37
  end
30
38
 
31
39
  class DrainingError < UnexpectedResponse
40
+ WORD = 'DRAINING'
41
+ end
42
+
43
+ class NotFoundError < UnexpectedResponse
44
+ WORD = 'NOT_FOUND'
45
+ end
46
+
47
+ class WaitingForJobError < RuntimeError
32
48
  end
33
49
  end
@@ -1,5 +1,5 @@
1
1
  module Beanstalk #:nodoc:
2
2
  module VERSION #:nodoc:
3
- STRING = '0.9.0'
3
+ STRING = '0.10.0'
4
4
  end
5
5
  end
data/website/index.html CHANGED
@@ -33,7 +33,7 @@
33
33
  <h1>Beanstalk Client</h1>
34
34
  <div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/beanstalk"; return false'>
35
35
  <p>Get Version</p>
36
- <a href="http://rubyforge.org/projects/beanstalk" class="numbers">0.9.0</a>
36
+ <a href="http://rubyforge.org/projects/beanstalk" class="numbers">0.10.0</a>
37
37
  </div>
38
38
  <h1>&#x2192; &#8216;beanstalk-client&#8217;</h1>
39
39
 
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: beanstalk-client
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.9.0
7
- date: 2008-02-27 00:00:00 -08:00
6
+ version: 0.10.0
7
+ date: 2008-04-11 00:00:00 -07:00
8
8
  summary: Ruby client library for the Beanstalk protocol
9
9
  require_paths:
10
10
  - lib
@@ -37,7 +37,6 @@ files:
37
37
  - config/hoe.rb
38
38
  - config/requirements.rb
39
39
  - lib/beanstalk-client.rb
40
- - lib/beanstalk-client/bag.rb
41
40
  - lib/beanstalk-client/connection.rb
42
41
  - lib/beanstalk-client/errors.rb
43
42
  - lib/beanstalk-client/job.rb
@@ -1,36 +0,0 @@
1
- # beanstalk-client/bag.rb - client library for beanstalk
2
-
3
- # Copyright (C) 2007 Philotic Inc.
4
-
5
- # This program is free software: you can redistribute it and/or modify
6
- # it under the terms of the GNU General Public License as published by
7
- # the Free Software Foundation, either version 3 of the License, or
8
- # (at your option) any later version.
9
-
10
- # This program is distributed in the hope that it will be useful,
11
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
- # GNU General Public License for more details.
14
-
15
- # You should have received a copy of the GNU General Public License
16
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
-
18
- class Beanstalk::Bag
19
- def initialize(initial_size=0, &default)
20
- @default = default
21
- @items = []
22
- initial_size.times{give(default.call())}
23
- end
24
-
25
- def give(x)
26
- (@items << x)[-1]
27
- end
28
-
29
- def take()
30
- @items.pop or @default.call()
31
- end
32
-
33
- def size()
34
- @items.size
35
- end
36
- end