beanstalk-client 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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