beaneater 0.3.1 → 1.1.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 454588f4369a9102f949a43c73d7fe8d06de5191
4
- data.tar.gz: e7655c12db30696d1feb41b1f3c6de9c4100115c
2
+ SHA256:
3
+ metadata.gz: 60e34aa7bfb6dc0a66ce540b609fa2ed112af52a7defcb3d7148efdbf9a8d213
4
+ data.tar.gz: 36c9c9cf9a1d2330243f4f8dbd664318bda4b702ecc1c160e9a288255b211764
5
5
  SHA512:
6
- metadata.gz: e523bcdfeb277ed363335be20de8d7114680fe7fa98a7b8a3f7e341547dd0b41290fba35fece3615bd6839ef9381ee8312aac003662e5b2afc4accfdbfa00fbd
7
- data.tar.gz: 0e0d48e4514d953d2acc4bce929a08201912c8f04dc14b0f70bd2ccaa7f7ca2dfbd91ac73156a8542c374fed1b963bd910abe65cfd561d7ff24b7323f1875ff6
6
+ metadata.gz: e447f52b2790b98867e613009d7a31b3cc2af0c116656fe209514000f0a023d662a850328a3242609590f1a2fdfd9f006936f24aaf9b04d86d44f2a7b40a2dd4
7
+ data.tar.gz: 90eca2d3c5db4c0c96a0bfccc774d894c2d942f9ea52979f15a0a5f6b0bb39990299896a994e56f7c60537e015fc152b7fa030293c980498b545e8f5c52cb411
data/.travis.yml ADDED
@@ -0,0 +1,15 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.5
4
+ - 2.6
5
+ - 2.7
6
+ - 3.0
7
+ before_install:
8
+ - curl -L https://github.com/kr/beanstalkd/archive/v1.9.tar.gz | tar xz -C /tmp
9
+ - cd /tmp/beanstalkd-1.9/
10
+ - make
11
+ - ./beanstalkd &
12
+ - cd $TRAVIS_BUILD_DIR
13
+ script:
14
+ - bundle install
15
+ - bundle exec rake test:full
data/CHANGELOG.md CHANGED
@@ -1,6 +1,35 @@
1
1
  # CHANGELOG for Beaneater
2
2
 
3
- ## 0.3.2 (Unreleased)
3
+ ## 1.1.1 (April 27th 2021)
4
+
5
+ * Support Ruby 3 keyword arguments (@yahonda)
6
+ * Add CI for Ruby 3 (@yahonda)
7
+
8
+ ## 1.1.0 (April 25th 2021)
9
+
10
+ * 'clear' behavior was failing unexpectedly, swallow issues during delete as well (@bfolkens)
11
+ * Fix assigned but unused variables (@utilum)
12
+ * Fix last_used tube not stored (@albb0920)
13
+ * Fix deprecation warning in ruby 2.7 (@albb0920)
14
+ * Fix watched tubes not restored after reconnect (@albb0920)
15
+ * Fix keyword arguemnt warning (@albb0920)
16
+
17
+ ## 1.0.0 (April 26th 2015)
18
+
19
+ * Beginning from version 1.0.0 the support for `Beaneater::Pool` has been dropped (@alup)
20
+ * `Jobs#find_all` method has been removed, since it is no longer necessary after removing pool (@alup)
21
+ * `Tubes` is now an enumerable allowing `tubes` to be handled as a collection (@Aethelflaed)
22
+
23
+ ## 0.3.3 (August 16th 2014)
24
+
25
+ * Fix failure when job is not defined and fix exception handling for jobs (@nicholasorenrawlings)
26
+ * Add reserve_timeout option to job processing (@nicholasorenrawlings)
27
+ * Add travis-ci badge (@tdg5)
28
+ * Fix tests to run more reliably (@tdg5)
29
+
30
+ ## 0.3.2 (Sept 15 2013)
31
+
32
+ * Fix #29 ExpectedCrlfError name and invocation (@tdg5)
4
33
 
5
34
  ## 0.3.1 (Jun 28 2013)
6
35
 
data/Gemfile CHANGED
@@ -7,4 +7,8 @@ group :development do
7
7
  gem 'redcarpet', '~> 1'
8
8
  gem 'github-markup'
9
9
  gem 'yard'
10
- end
10
+ end
11
+
12
+ group :development, :test do
13
+ gem 'coveralls', :require => false
14
+ end
data/README.md CHANGED
@@ -1,11 +1,14 @@
1
1
  # Beaneater
2
+ [![Build Status](https://secure.travis-ci.org/beanstalkd/beaneater.png)](http://travis-ci.org/beanstalkd/beaneater)
3
+ [![Coverage Status](https://coveralls.io/repos/beanstalkd/beaneater/badge.png?branch=master)](https://coveralls.io/r/beanstalkd/beaneater)
2
4
 
3
5
  Beaneater is the best way to interact with beanstalkd from within Ruby.
4
6
  [Beanstalkd](http://kr.github.com/beanstalkd/) is a simple, fast work queue. Its interface is generic, but was
5
7
  originally designed for reducing the latency of page views in high-volume web applications by
6
8
  running time-consuming tasks asynchronously. Read the [yardocs](http://rdoc.info/github/beanstalkd/beaneater) and/or the
7
- [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.md) for more details.
9
+ [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.txt) for more details.
8
10
 
11
+ **Important Note**: This README is **for branch 1.0.x which is under development**. Please switch to latest `0.x` branch for stable version.
9
12
 
10
13
  ## Why Beanstalk?
11
14
 
@@ -81,13 +84,25 @@ gem 'beaneater'
81
84
 
82
85
  and run `bundle install` to install the dependency.
83
86
 
87
+ ## Breaking Changes since 1.0!
88
+
89
+ Starting in 1.0, we removed the concept of the `Beaneater::Pool` which introduced considerable complexity into this gem.
90
+
91
+ * Beginning from version 1.0.0 the support for `Beaneater::Pool` has been dropped.
92
+ The specific feature may be supported again in the next versions as separate module
93
+ or through a separate gem. If you want to use the pool feature you should switch to
94
+ 0.x stable branches instead.
95
+ * `Jobs#find_all` method has been removed, since it is no longer necessary.
96
+
97
+ To manage a pool of beanstalkd instances, we'd prefer to leave the handling to the developer or other higher-level libraries.
98
+
84
99
  ## Quick Overview:
85
100
 
86
101
  The concise summary of how to use beaneater:
87
102
 
88
103
  ```ruby
89
104
  # Connect to pool
90
- @beanstalk = Beaneater::Pool.new(['localhost:11300'])
105
+ @beanstalk = Beaneater.new('localhost:11300')
91
106
  # Enqueue jobs to tube
92
107
  @tube = @beanstalk.tubes["my-tube"]
93
108
  @tube.put '{ "key" : "foo" }', :pri => 5
@@ -95,7 +110,7 @@ The concise summary of how to use beaneater:
95
110
  # Process jobs from tube
96
111
  while @tube.peek(:ready)
97
112
  job = @tube.reserve
98
- puts "job value is #{job.body["key"]}!"
113
+ puts "job value is #{JSON.parse(job.body)["key"]}!"
99
114
  job.delete
100
115
  end
101
116
  # Disconnect the pool
@@ -116,7 +131,8 @@ Beaneater.configure do |config|
116
131
  # config.default_put_pri = 65536
117
132
  # config.default_put_ttr = 120
118
133
  # config.job_parser = lambda { |body| body }
119
- # config.beanstalkd_url = ['localhost:11300']
134
+ # config.job_serializer = lambda { |body| body }
135
+ # config.beanstalkd_url = 'localhost:11300'
120
136
  end
121
137
  ```
122
138
 
@@ -124,18 +140,17 @@ The above options are all defaults, so only include a configuration block if you
124
140
 
125
141
  ### Connection
126
142
 
127
- To interact with a beanstalk queue, first establish a connection by providing a set of addresses:
143
+ To interact with a beanstalk queue, first establish a connection by providing an address:
128
144
 
129
145
  ```ruby
130
- @beanstalk = Beaneater::Pool.new(['10.0.1.5:11300'])
146
+ @beanstalk = Beaneater.new('10.0.1.5:11300')
131
147
 
132
- # Or if ENV['BEANSTALKD_URL] == 'localhost:11300,127.0.0.1:11300'
133
- @beanstalk = Beaneater::Pool.new
134
- @beanstalk.connections.first # => localhost:11300
135
- @beanstalk.connections.last # => 127.0.0.1:11300
148
+ # Or if ENV['BEANSTALKD_URL'] == '127.0.0.1:11300'
149
+ @beanstalk = Beaneater.new
150
+ @beanstalk.connection # => localhost:11300
136
151
  ```
137
152
 
138
- You can conversely close and dispose of a pool at any time with:
153
+ You can conversely close and dispose of a connection at any time with:
139
154
 
140
155
  ```ruby
141
156
  @beanstalk.close
@@ -246,6 +261,16 @@ Beanstalkd can only stores strings as job bodies, but you can easily encode your
246
261
  @tube.put({:foo => 'bar'}.to_json)
247
262
  ```
248
263
 
264
+ Moreover, you can provide a default job serializer by setting the corresponding configuration
265
+ option (`job_serializer`), in order to apply the encoding on each job body which
266
+ is going to be send using the `put` command. For example, to encode a ruby object to JSON format:
267
+
268
+ ```ruby
269
+ Beaneater.configure do |config|
270
+ config.job_serializer = lambda { |body| JSON.dump(body) }
271
+ end
272
+ ```
273
+
249
274
  Each job has various metadata associated such as `priority`, `delay`, and `ttr` which can be
250
275
  specified as part of the `put` command:
251
276
 
@@ -264,7 +289,7 @@ In order to process jobs, the client should first specify the intended tubes to
264
289
  this will default to watching just the `default` tube.
265
290
 
266
291
  ```ruby
267
- @beanstalk = Beaneater::Pool.new(['10.0.1.5:11300'])
292
+ @beanstalk = Beaneater.new('10.0.1.5:11300')
268
293
  @beanstalk.tubes.watch!('tube-name', 'other-tube')
269
294
  ```
270
295
 
@@ -328,13 +353,6 @@ inspected using the 'peek' commands. To find and peek at a particular job based
328
353
  # => <Beaneater::Job id=123 body="foo">
329
354
  ```
330
355
 
331
- You can also `find_all` jobs across all connections:
332
-
333
- ```ruby
334
- @beanstalk.jobs.find_all(123)
335
- # => [<Beaneater::Job id=123 body="foo">, <Beaneater::Job id=123 body="bar">]
336
- ```
337
-
338
356
  or you can peek at jobs within a tube:
339
357
 
340
358
  ```ruby
@@ -411,7 +429,7 @@ are listed below:
411
429
  There are other exceptions that are less common such as `OutOfMemoryError`, `DrainingError`,
412
430
  `DeadlineSoonError`, `InternalError`, `BadFormatError`, `UnknownCommandError`,
413
431
  `ExpectedCRLFError`, `JobTooBigError`, `NotIgnoredError`. Be sure to check the
414
- [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.md) for more information.
432
+ [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.txt) for more information.
415
433
 
416
434
 
417
435
  ### Stats
@@ -422,8 +440,9 @@ beanstalk overall:
422
440
  ```ruby
423
441
  # Get overall stats about the job processing that has occurred
424
442
  print @beanstalk.stats
425
- # => { 'current_connections': 1, 'current_jobs_buried': 0, ... }
426
- print @beanstalk.stats.current_connections
443
+ # => #<Beaneater::StatStruct current_jobs_urgent=0, current_jobs_ready=0, current_jobs_reserved=0, current_jobs_delayed=0, current_jobs_buried=0, ...
444
+
445
+ print @beanstalk.stats.current_tubes
427
446
  # => 1
428
447
  ```
429
448
 
@@ -443,7 +462,7 @@ print @beanstalk.jobs[some_job_id].stats
443
462
  # => {'age': 0, 'id': 2, 'state': 'reserved', 'tube': 'default', ... }
444
463
  ```
445
464
 
446
- Be sure to check the [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.md) for
465
+ Be sure to check the [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.txt) for
447
466
  more details about the stats commands.
448
467
 
449
468
  ## Resources
@@ -454,8 +473,9 @@ There are other resources helpful when learning about beanstalk:
454
473
  * [Beaneater on Rubygems](https://rubygems.org/gems/beaneater)
455
474
  * [Beanstalkd homepage](http://kr.github.com/beanstalkd/)
456
475
  * [beanstalk on github](https://github.com/kr/beanstalkd)
457
- * [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.md)
476
+ * [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.txt)
458
477
  * [Backburner](https://github.com/nesquena/backburner) - Ruby job queue for Rails/Sinatra
478
+ * [BeanCounter](https://github.com/gemeraldbeanstalk/bean_counter) - TestUnit/MiniTest assertions and RSpec matchers for testing code that relies on Beaneater
459
479
 
460
480
  ## Contributors
461
481
 
@@ -463,3 +483,4 @@ There are other resources helpful when learning about beanstalk:
463
483
  - [Nathan Esquenazi](https://github.com/nesquena) - Contributor and co-maintainer
464
484
  - [Keith Rarick](https://github.com/kr) - Much code inspired and adapted from beanstalk-client
465
485
  - [Vidar Hokstad](https://github.com/vidarh) - Replaced telnet with correct TCP socket handling
486
+ - [Andreas Loupasakis](https://github.com/alup) - Improve test coverage, improve job configuration
data/Rakefile CHANGED
@@ -28,4 +28,6 @@ end
28
28
  YARD::Rake::YardocTask.new do |t|
29
29
  t.files = ['lib/beaneater/**/*.rb']
30
30
  t.options = []
31
- end
31
+ end
32
+
33
+ task :default => 'test:full'
data/beaneater.gemspec CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |gem|
11
11
  gem.description = %q{Simple beanstalkd client for ruby}
12
12
  gem.summary = %q{Simple beanstalkd client for ruby.}
13
13
  gem.homepage = ""
14
+ gem.license = 'MIT'
14
15
 
15
16
  gem.files = `git ls-files`.split($/)
16
17
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
data/examples/demo.rb CHANGED
@@ -6,8 +6,7 @@ require 'beaneater'
6
6
 
7
7
  # Establish a pool of beanstalks
8
8
  puts step("Connecting to Beanstalk")
9
- bc = Beaneater::Pool.new('localhost')
10
- # bc = Beaneater::Pool.new(['localhost', 'localhost:11301', 'localhost:11302'])
9
+ bc = Beaneater.new('localhost')
11
10
  puts bc
12
11
 
13
12
  # Print out key stats
data/lib/beaneater.rb CHANGED
@@ -1,11 +1,64 @@
1
1
  require 'thread' unless defined?(Mutex)
2
2
 
3
- %w(version configuration errors pool_command pool connection stats tube job).each do |f|
3
+ %w(version configuration errors connection tube job stats).each do |f|
4
4
  require "beaneater/#{f}"
5
5
  end
6
6
 
7
- module Beaneater
8
- # Simple ruby client for beanstalkd.
7
+ class Beaneater
8
+
9
+ # @!attribute connection
10
+ # @return <Beaneater::Connection> returns the associated connection object
11
+ attr_reader :connection
12
+
13
+ # Initialize new instance of Beaneater
14
+ #
15
+ # @param [String] address in the form "host:port"
16
+ # @example
17
+ # Beaneater.new('127.0.0.1:11300')
18
+ #
19
+ # ENV['BEANSTALKD_URL'] = '127.0.0.1:11300'
20
+ # @b = Beaneater.new
21
+ # @b.connection.host # => '127.0.0.1'
22
+ # @b.connection.port # => '11300'
23
+ #
24
+ def initialize(address=nil)
25
+ @connection = Connection.new(address)
26
+ end
27
+
28
+ # Returns Beaneater::Tubes object for accessing tube related functions.
29
+ #
30
+ # @return [Beaneater::Tubes] tubes object
31
+ # @api public
32
+ def tubes
33
+ @tubes ||= Beaneater::Tubes.new(self)
34
+ end
35
+
36
+ # Returns Beaneater::Jobs object for accessing job related functions.
37
+ #
38
+ # @return [Beaneater::Jobs] jobs object
39
+ # @api public
40
+ def jobs
41
+ @jobs ||= Beaneater::Jobs.new(self)
42
+ end
43
+
44
+ # Returns Beaneater::Stats object for accessing beanstalk stats.
45
+ #
46
+ # @return [Beaneater::Stats] stats object
47
+ # @api public
48
+ def stats
49
+ @stats ||= Stats.new(self)
50
+ end
51
+
52
+ # Closes the related connection
53
+ #
54
+ # @example
55
+ # @beaneater_instance.close
56
+ #
57
+ def close
58
+ connection.close if connection
59
+ end
60
+
61
+ protected
9
62
 
10
63
  class << self
11
64
  # Yields a configuration block
@@ -29,4 +82,4 @@ module Beaneater
29
82
  @_configuration ||= Configuration.new
30
83
  end
31
84
  end
32
- end # Beaneater
85
+ end # Beaneater
@@ -1,9 +1,10 @@
1
- module Beaneater
1
+ class Beaneater
2
2
  class Configuration
3
3
  attr_accessor :default_put_delay # default delay value to put a job
4
4
  attr_accessor :default_put_pri # default priority value to put a job
5
5
  attr_accessor :default_put_ttr # default ttr value to put a job
6
6
  attr_accessor :job_parser # default job_parser to parse job body
7
+ attr_accessor :job_serializer # default serializer for job body
7
8
  attr_accessor :beanstalkd_url # default beanstalkd url
8
9
 
9
10
  def initialize
@@ -11,6 +12,7 @@ module Beaneater
11
12
  @default_put_pri = 65536
12
13
  @default_put_ttr = 120
13
14
  @job_parser = lambda { |body| body }
15
+ @job_serializer = lambda { |body| body }
14
16
  @beanstalkd_url = nil
15
17
  end
16
18
  end # Configuration
@@ -1,10 +1,16 @@
1
1
  require 'yaml'
2
2
  require 'socket'
3
3
 
4
- module Beaneater
4
+ class Beaneater
5
5
  # Represents a connection to a beanstalkd instance.
6
6
  class Connection
7
7
 
8
+ # Default number of retries to send a command to a connection
9
+ MAX_RETRIES = 3
10
+
11
+ # Default retry interval
12
+ DEFAULT_RETRY_INTERVAL = 1
13
+
8
14
  # @!attribute address
9
15
  # @return [String] returns Beanstalkd server address
10
16
  # @example
@@ -21,6 +27,12 @@ module Beaneater
21
27
  # @return [Net::TCPSocket] returns connection object
22
28
  attr_reader :address, :host, :port, :connection
23
29
 
30
+ # @!attribute tubes_watched
31
+ # @returns [Array<String>] returns currently watched tube names
32
+ # @!attribute tube_used
33
+ # @returns [String] returns currently used tube name
34
+ attr_accessor :tubes_watched, :tube_used
35
+
24
36
  # Default port value for beanstalk connection
25
37
  DEFAULT_PORT = 11300
26
38
 
@@ -28,13 +40,23 @@ module Beaneater
28
40
  #
29
41
  # @param [String] address beanstalkd instance address.
30
42
  # @example
31
- # Beaneater::Connection.new('localhost')
32
- # Beaneater::Connection.new('localhost:11300')
43
+ # Beaneater::Connection.new('127.0.0.1')
44
+ # Beaneater::Connection.new('127.0.0.1:11300')
45
+ #
46
+ # ENV['BEANSTALKD_URL'] = '127.0.0.1:11300'
47
+ # @b = Beaneater.new
48
+ # @b.connection.host # => '127.0.0.1'
49
+ # @b.connection.port # => '11300'
33
50
  #
34
51
  def initialize(address)
35
- @address = address
36
- @connection = establish_connection
52
+ @address = address || _host_from_env || Beaneater.configuration.beanstalkd_url
37
53
  @mutex = Mutex.new
54
+ @tube_used = 'default'
55
+ @tubes_watched = ['default']
56
+
57
+ establish_connection
58
+ rescue
59
+ _raise_not_connected!
38
60
  end
39
61
 
40
62
  # Send commands to beanstalkd server via connection.
@@ -43,18 +65,19 @@ module Beaneater
43
65
  # @param [String] command Beanstalkd command
44
66
  # @return [Array<Hash{String => String, Number}>] Beanstalkd command response
45
67
  # @example
68
+ # @conn = Beaneater::Connection.new
46
69
  # @conn.transmit('bury 123')
70
+ # @conn.transmit('stats')
47
71
  #
48
- def transmit(command, options={})
49
- @mutex.synchronize do
50
- if connection
72
+ def transmit(command, **options)
73
+ _with_retry(**options.slice(:retry_interval, :init)) do
74
+ @mutex.synchronize do
75
+ _raise_not_connected! unless connection
76
+
51
77
  command = command.force_encoding('ASCII-8BIT') if command.respond_to?(:force_encoding)
52
78
  connection.write(command.to_s + "\r\n")
53
- res = connection.gets
54
- raise_not_connected! unless res
79
+ res = connection.readline
55
80
  parse_response(command, res)
56
- else # no connection
57
- raise_not_connected!
58
81
  end
59
82
  end
60
83
  end
@@ -65,8 +88,10 @@ module Beaneater
65
88
  # @conn.close
66
89
  #
67
90
  def close
68
- @connection.close
69
- @connection = nil
91
+ if @connection
92
+ @connection.close
93
+ @connection = nil
94
+ end
70
95
  end
71
96
 
72
97
  # Returns string representation of job.
@@ -79,23 +104,30 @@ module Beaneater
79
104
  end
80
105
  alias :inspect :to_s
81
106
 
107
+ def add_to_watched(tube_name)
108
+ @tubes_watched << tube_name
109
+ @tubes_watched.uniq
110
+ end
111
+
112
+ def remove_from_watched(tube_name)
113
+ @tubes_watched.delete(tube_name)
114
+ end
115
+
82
116
  protected
83
117
 
84
118
  # Establish a connection based on beanstalk address.
85
119
  #
86
120
  # @return [Net::TCPSocket] connection for specified address.
87
- # @raise [Beanstalk::NotConnected] Could not connect to specified beanstalkd instance.
121
+ # @raise [Beaneater::NotConnected] Could not connect to specified beanstalkd instance.
88
122
  # @example
89
123
  # establish_connection('localhost:3005')
90
124
  #
91
125
  def establish_connection
92
- @match = address.split(':')
93
- @host, @port = @match[0], Integer(@match[1] || DEFAULT_PORT)
94
- TCPSocket.new @host, @port
95
- rescue Errno::ECONNREFUSED => e
96
- raise NotConnected, "Could not connect to '#{@host}:#{@port}'"
97
- rescue Exception => ex
98
- raise NotConnected, "#{ex.class}: #{ex}"
126
+ @address = address.first if address.is_a?(Array)
127
+ match = address.split(':')
128
+ @host, @port = match[0], Integer(match[1] || DEFAULT_PORT)
129
+
130
+ @connection = TCPSocket.new @host, @port
99
131
  end
100
132
 
101
133
  # Parses the response and returns the useful beanstalk response.
@@ -103,11 +135,11 @@ module Beaneater
103
135
  #
104
136
  # @param [String] cmd Beanstalk command transmitted
105
137
  # @param [String] res Telnet command response
106
- # @return [Array<Hash{String => String, Number}>] Beanstalk response with `status`, `id`, `body`, and `connection`
138
+ # @return [Array<Hash{String => String, Number}>] Beanstalk response with `status`, `id`, `body`
107
139
  # @raise [Beaneater::UnexpectedResponse] Response from beanstalk command was an error status
108
140
  # @example
109
141
  # parse_response("delete 56", "DELETED 56\nFOO")
110
- # # => { :body => "FOO", :status => "DELETED", :id => 56, :connection => <Connection> }
142
+ # # => { :body => "FOO", :status => "DELETED", :id => 56 }
111
143
  #
112
144
  def parse_response(cmd, res)
113
145
  status = res.chomp
@@ -120,12 +152,12 @@ module Beaneater
120
152
  raw_body = connection.read(bytes_size)
121
153
  body = status == 'OK' ? YAML.load(raw_body) : config.job_parser.call(raw_body)
122
154
  crlf = connection.read(2) # \r\n
123
- raise ExpectedCRLFError if crlf != "\r\n"
155
+ raise ExpectedCrlfError.new('EXPECTED_CRLF', cmd) if crlf != "\r\n"
124
156
  end
125
157
  id = body_values[1]
126
- response = { :status => status, :body => body }
158
+ response = { :status => status }
127
159
  response[:id] = id if id
128
- response[:connection] = self
160
+ response[:body] = body if body
129
161
  response
130
162
  end
131
163
 
@@ -136,10 +168,77 @@ module Beaneater
136
168
  Beaneater.configuration
137
169
  end
138
170
 
171
+ private
172
+
173
+ def _initialize_tubes
174
+ if @tubes_watched != ['default']
175
+ tubes_watched.each do |t|
176
+ transmit("watch #{t}", init: false)
177
+ end
178
+
179
+ transmit("ignore default", init: false)
180
+ end
181
+
182
+ transmit("use #{tube_used}", init: false) if @tube_used != 'default'
183
+ end
184
+
185
+ # Wrapper method for capturing certain failures and retry the payload block
186
+ #
187
+ # @param [Proc] block The command to execute.
188
+ # @param [Integer] retry_interval The time to wait before the next retry
189
+ # @param [Integer] tries The maximum number of tries in draining mode
190
+ # @return [Object] Result of the block passed
191
+ #
192
+ def _with_retry(retry_interval: DEFAULT_RETRY_INTERVAL, init: true, tries: MAX_RETRIES, &block)
193
+ yield
194
+ rescue EOFError, Errno::ECONNRESET, Errno::EPIPE,
195
+ Errno::ECONNREFUSED => ex
196
+ _reconnect(ex, retry_interval)
197
+ _initialize_tubes if init
198
+ retry
199
+ rescue Beaneater::DrainingError
200
+ tries -= 1
201
+ if tries.zero?
202
+ close
203
+ raise
204
+ end
205
+ sleep(retry_interval)
206
+ retry
207
+ end
208
+
209
+ # Tries to re-establish connection to the beanstalkd
210
+ #
211
+ # @param [Exception] original_exception The exception caused the retry
212
+ # @param [Integer] retry_interval The time to wait before the next reconnect
213
+ # @param [Integer] tries The maximum number of attempts to reconnect
214
+ def _reconnect(original_exception, retry_interval, tries=MAX_RETRIES)
215
+ close
216
+ establish_connection
217
+ rescue Errno::ECONNREFUSED
218
+ tries -= 1
219
+ if tries.zero?
220
+ _raise_not_connected!
221
+ end
222
+ sleep(retry_interval || DEFAULT_RETRY_INTERVAL)
223
+ retry
224
+ end
225
+
226
+ # The host provided by BEANSTALKD_URL environment variable, if available.
227
+ #
228
+ # @return [String] A beanstalkd host address
229
+ # @example
230
+ # ENV['BEANSTALKD_URL'] = "localhost:1212"
231
+ # # => 'localhost:1212'
232
+ #
233
+ def _host_from_env
234
+ ENV['BEANSTALKD_URL'].respond_to?(:length) && ENV['BEANSTALKD_URL'].length > 0 && ENV['BEANSTALKD_URL'].strip
235
+ end
236
+
139
237
  # Raises an error to be triggered when the connection has failed
140
238
  # @raise [Beaneater::NotConnected] Beanstalkd is no longer connected
141
- def raise_not_connected!
142
- raise NotConnected, "Connection to beanstalk '#{@host}:#{@port}' is closed!"
239
+ def _raise_not_connected!
240
+ raise Beaneater::NotConnected, "Connection to beanstalk '#{@host}:#{@port}' is closed!"
143
241
  end
242
+
144
243
  end # Connection
145
244
  end # Beaneater