beanstalk_farmer 0.1.2 → 0.2.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/.gitignore CHANGED
@@ -2,3 +2,5 @@ Gemfile.lock
2
2
  doc/
3
3
  .yardoc/
4
4
  pkg/*.gem
5
+ coverage/
6
+ log/
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ rvm:
2
+ - 1.9.2
3
+ script: bundle exec rspec --tag "~beanstalk_required" -f d spec
data/.yardopts CHANGED
@@ -1,5 +1,4 @@
1
1
  --no-private
2
- --title "Farmer: a Beanstalk Job Queue Gem"
2
+ --title "Beanstalk Farmer: a Beanstalk Job Queue Gem"
3
3
  lib/**/*.rb -
4
- README.md
5
4
  LICENSE
data/README.md CHANGED
@@ -12,18 +12,12 @@ la Rack (e.g. `Proc`s, `procs`s). The payload of the job will be passed into
12
12
  the arguments of this method. Bear in mind that the payload for a Beanstalk job
13
13
  is de-serialized JSON.
14
14
 
15
- ### Example
15
+ For usage examples, see the examples directory in the source code.
16
16
 
17
- BeanstalkFarmer::Runner.register_handlers do
18
- # You can use objects
19
- tube 'email.welcome_message', WelcomeMessageHandler
17
+ ## Development
20
18
 
21
- # You can use Procs
22
- tube 'push.message', Proc.new { |args| PushService.send_message(args) }
23
- end
19
+ * [Continuous integration](http://travis-ci.org/#!/jherdman/beanstalk_farmer)
24
20
 
25
- BeanstalkFarmer::Runner.run!
21
+ ## Shout Outs
26
22
 
27
- ## Configuration
28
-
29
- Easy as pie! See `BeanstalkFarmer::Config` for all available options.
23
+ Many thanks to [nulayer](http://nulayer.com/).
data/Rakefile CHANGED
@@ -6,11 +6,13 @@ Bundler::GemHelper.install_tasks
6
6
 
7
7
  require 'rspec/core/rake_task'
8
8
 
9
- RSpec::Core::RakeTask.new(:spec) do |t|
10
- t.rspec_opts = %w[--color]
9
+ RSpec::Core::RakeTask.new("spec") do |t|
11
10
  t.verbose = false
11
+ t.rspec_opts = "--tag ~beanstalk_required -f d"
12
12
  end
13
13
 
14
+ task default: "spec"
15
+
14
16
  require 'yard'
15
17
  require 'yard/rake/yardoc_task'
16
18
 
@@ -20,8 +20,9 @@ Gem::Specification.new do |s|
20
20
  s.add_dependency 'beanstalk-client', ['~> 1.1.0']
21
21
  s.add_dependency 'multi_json', ['~> 1.0.2']
22
22
 
23
- s.add_development_dependency 'rake', ['~> 0.8.7']
24
- s.add_development_dependency 'rspec', ['~> 2.6']
23
+ s.add_development_dependency 'rake', ['~> 0.8.7']
24
+ s.add_development_dependency 'rspec', ['~> 2.6']
25
+ s.add_development_dependency 'simplecov', ['~> 0.4.2']
25
26
 
26
27
  s.files = `git ls-files`.split("\n")
27
28
  s.test_files = `git ls-files -- {spec}/*`.split("\n")
data/example/echo.rb CHANGED
@@ -5,9 +5,27 @@
5
5
  $:.unshift(File.dirname(__FILE__) + '/../lib')
6
6
  require 'beanstalk_farmer'
7
7
 
8
+ class ObjectHandler
9
+ attr_accessor :args
10
+
11
+ def initialize(args)
12
+ self.args = args
13
+ end
14
+
15
+ def call
16
+ BeanstalkFarmer.logger.info "Object handler says #{args.inspect}"
17
+ end
18
+
19
+ def self.call(args)
20
+ new(args).call
21
+ end
22
+ end
23
+
8
24
  BeanstalkFarmer::Runner.register_handlers do
25
+ tube 'echo.object', ObjectHandler
9
26
  tube 'echo.small_proc', proc { |args| BeanstalkFarmer.logger.info "Small proc says: #{args.inspect}" }
10
- tube 'echo.big_proc', Proc.new { |args| BeanstalkFarmer.logger.info "Big Proc says: #{args.inspect}" }
27
+ tube 'echo.big_proc', Proc.new { |args| BeanstalkFarmer.logger.info "Big Proc says: #{args.inspect}" }
28
+ tube 'echo.block', do |args| BeanstalkFarmer.logger.info "Block handler says: #{args.inspect}"; end
11
29
  end
12
30
 
13
31
  trap 'INT' do
@@ -0,0 +1,15 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+ require 'beanstalk_farmer'
3
+
4
+ loop do
5
+ handler_types = %w[object small_proc big_proc block]
6
+ handler_type = handler_types[rand(handler_types.length).round]
7
+ BeanstalkFarmer::Service.instance.enqueue "echo.#{handler_type}", "Hello, world!"
8
+ sleep 10
9
+ end
10
+
11
+ trap 'INT' do
12
+ puts "\rExiting"
13
+ BeanstalkFarmer::Runner.close_connection
14
+ exit
15
+ end
@@ -1,7 +1,9 @@
1
1
  module BeanstalkFarmer
2
2
  # @private Provides a clean slate object that we can safely instance eval on.
3
3
  class DSL
4
- def tube(tube_name, job_handler)
4
+ def tube(tube_name, job_handler=nil, &block)
5
+ job_handler = block.to_proc if block_given?
6
+
5
7
  Job.handler_pool[tube_name] = job_handler
6
8
  end
7
9
 
@@ -3,6 +3,8 @@ require 'timeout'
3
3
 
4
4
  module BeanstalkFarmer
5
5
  class Job
6
+ include BeanstalkFarmer
7
+
6
8
  # Raised when a job cannot complete in time
7
9
  class OutOfTimeError < Timeout::Error; end
8
10
 
@@ -13,6 +15,7 @@ module BeanstalkFarmer
13
15
  def initialize(job)
14
16
  self.job = job
15
17
  set_name_and_arguments
18
+ logger.info "JOB (#{name}) reserved"
16
19
  end
17
20
 
18
21
  # Performs work for this job
@@ -21,13 +24,16 @@ module BeanstalkFarmer
21
24
  # TTR value. I'm not sure why at this point in time. Maybe to compensate
22
25
  # for Job setup time.
23
26
  Timeout.timeout(job.ttr) do
27
+ logger.info "JOB (#{name}) working"
24
28
  handler = self.class.handler_pool[name]
25
29
  handler.call(args)
26
30
  end
27
31
  rescue Timeout::Error
32
+ logger.error "JOB (#{name}) out of time"
28
33
  raise OutOfTimeError, "#{name} could not finish in #{job.ttr} seconds"
29
34
  ensure
30
35
  job.delete
36
+ logger.info "JOB (#{name}) done"
31
37
  end
32
38
 
33
39
  # A pool of job handlers that can work on jobs in our queue
@@ -12,7 +12,7 @@ module BeanstalkFarmer
12
12
 
13
13
  # @return [Farmer::BeanstalkService] a connection to the Beanstalk queue
14
14
  def service
15
- @service ||= Service.new
15
+ @service ||= Service.instance
16
16
  end
17
17
 
18
18
  # Prepares tubes for watching
@@ -1,5 +1,6 @@
1
1
  require 'beanstalk-client'
2
2
  require 'multi_json'
3
+ require 'singleton'
3
4
 
4
5
  module BeanstalkFarmer
5
6
  ##
@@ -11,35 +12,44 @@ module BeanstalkFarmer
11
12
  #
12
13
  # @private
13
14
  class Service
15
+ include BeanstalkFarmer
16
+ include Singleton
17
+
14
18
  DEFAULT_DELAY = 0
15
19
  DEFAULT_PRIORITY = 65536
16
20
  DEFAULT_TTR = 120
17
21
 
18
- # Raised when we cannot connect to the Beanstalk queue
19
- class NotConnectedError < Beanstalk::NotConnected; end
20
-
21
- attr_accessor :uri
22
+ attr_accessor :host, :port
22
23
 
23
- # Sets the URI of your Beanstalk queue.
24
+ # Sets the host and port of your Beanstalk queue.
24
25
  #
25
26
  # @param [String] host (DEFAULT_HOST) the host name to of your Beanstalk queue
26
27
  #
27
28
  # @param [String] port (DEFAULT_PORT) the port that your Beanstalk queue is on
28
29
  def initialize(host=Config.host, port=Config.port)
29
- self.uri = build_uri(host, port)
30
+ self.host = host
31
+ self.port = port
32
+ end
33
+
34
+ # @return [String] a formatted URI for a Beanstalk queue
35
+ def uri
36
+ build_uri(host, port)
30
37
  end
31
38
 
32
39
  # @return [Beanstalk::Pool] a connection to Beanstalk
33
40
  def connection
34
41
  @connection ||= Beanstalk::Pool.new([uri])
42
+ rescue Beanstalk::NotConnected
43
+ raise NotConnectedError
35
44
  end
36
45
 
37
46
  # Closes the Beanstalk connection
38
47
  #
39
48
  # @return [nil] Nothing. Absolutely nothing.
40
49
  def close
41
- @connection.close
42
- @connection = nil
50
+ logger.warn "Closing connection to Beanstalk"
51
+ connection.close
52
+ delete_connection
43
53
  end
44
54
 
45
55
  # @param [Array<String>] tube_names The tube names to be watched
@@ -50,13 +60,17 @@ module BeanstalkFarmer
50
60
 
51
61
  # @param [Array<String>] tube_names The tube names to be watched
52
62
  def watch_tubes(tube_names)
63
+ logger.info "Watching tubes: #{tube_names.inspect}"
53
64
  tube_names.each { |tube_name| connection.watch(tube_name) }
54
65
  end
55
66
 
56
67
  # Ignores any tubes that aren't of interest, excluding the default tube.
57
68
  def ignore_unwatched_tubes(watched_tube_names)
58
69
  connection.list_tubes_watched.values do |tube_name|
59
- connection.ignore(tube_name) unless watched_tube_names.include?(tube_name)
70
+ unless watched_tube_names.include?(tube_name)
71
+ logger.info "Ignoring tube: #{tube_name}"
72
+ connection.ignore(tube_name)
73
+ end
60
74
  end
61
75
  end
62
76
 
@@ -79,5 +93,14 @@ module BeanstalkFarmer
79
93
  def build_uri(host, port)
80
94
  [host, port].join(':')
81
95
  end
96
+
97
+ def delete_connection
98
+ @connection = nil
99
+ end
82
100
  end
101
+
102
+ ## Errors
103
+
104
+ # Raised when a connection cannot be made
105
+ class NotConnectedError < Beanstalk::NotConnected; end
83
106
  end
@@ -1,3 +1,3 @@
1
1
  module BeanstalkFarmer
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -9,9 +9,23 @@ module BeanstalkFarmer
9
9
  autoload :Job, 'beanstalk_farmer/job'
10
10
  autoload :Runner, 'beanstalk_farmer/runner'
11
11
 
12
+ # @private
13
+ def logger
14
+ Config.logger
15
+ end
16
+
12
17
  # @return [Logger] the logger, defaulting to a STDOUT logger
13
18
  def self.logger
14
- @logger ||= Config.logger
15
- @logger
19
+ Config.logger
20
+ end
21
+
22
+ # @yield [BeanstalkFarmer::Config] a block that allows for convenient configuration
23
+ #
24
+ # @example Usage
25
+ # BeanstalkFarmer.config do |c|
26
+ # c.logger = Rails.logger
27
+ # end
28
+ def self.config
29
+ yield Config
16
30
  end
17
31
  end
data/log/.gitkeep ADDED
File without changes
@@ -1,6 +1,14 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe BeanstalkFarmer::Config do
4
+ its(:host) { should == described_class::DEFAULT_HOST }
5
+
6
+ its(:port) { should == described_class::DEFAULT_PORT }
7
+
8
+ its(:logger) { should be_a(Logger) }
9
+
10
+ its(:json_engine) { should == MultiJson.engine }
11
+
4
12
  describe ".option" do
5
13
  before(:all) do
6
14
  described_class.option(:test_setting, default: true)
@@ -13,5 +13,13 @@ describe BeanstalkFarmer::DSL do
13
13
  subject.tube(tube_name, handler)
14
14
  BeanstalkFarmer::Job.handler_pool[tube_name].should == handler
15
15
  end
16
+
17
+ it 'can add a block as a handler' do
18
+ subject.tube 'block', do |args|
19
+ args
20
+ end
21
+
22
+ BeanstalkFarmer::Job.handler_pool['block'].should be_a(Proc)
23
+ end
16
24
  end
17
25
  end
@@ -1,9 +1,15 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe BeanstalkFarmer::Service do
4
- describe '.new' do
5
- it 'sets the `uri` attribute' do
6
- described_class.new.uri.should =~ /\A#{BeanstalkFarmer::Config.host}:#{BeanstalkFarmer::Config.port}\z/
3
+ describe BeanstalkFarmer::Service, :beanstalk_required do
4
+ subject { described_class.instance }
5
+
6
+ its(:host) { should == BeanstalkFarmer::Config.host }
7
+
8
+ its(:port) { should == BeanstalkFarmer::Config.port }
9
+
10
+ describe '#uri' do
11
+ it 'is a formatted URI for a Beanstalk queue' do
12
+ subject.uri.should =~ /\A#{subject.host}:#{subject.port}\z/
7
13
  end
8
14
  end
9
15
 
@@ -14,8 +20,8 @@ describe BeanstalkFarmer::Service do
14
20
 
15
21
  it 'establishes a connection to Beanstalk' do
16
22
  expect {
17
- subject.connection.put 'Hello'
18
- }.to_not raise_error(BeanstalkFarmer::Service::NotConnectedError)
23
+ subject.connection.use 'fake.tube'
24
+ }.to_not raise_error(BeanstalkFarmer::NotConnectedError)
19
25
  end
20
26
  end
21
27
 
@@ -28,6 +34,10 @@ describe BeanstalkFarmer::Service do
28
34
  end
29
35
 
30
36
  describe '#watch_tubes' do
37
+ after(:each) do
38
+ subject.close
39
+ end
40
+
31
41
  it 'adds a tube to the list of watched tubes' do
32
42
  subject.watch_tubes(%w[bacon])
33
43
  subject.connection.list_tubes_watched.should have_tube_named('bacon')
@@ -35,6 +45,10 @@ describe BeanstalkFarmer::Service do
35
45
  end
36
46
 
37
47
  describe '#ignore_unwatched_tubes' do
48
+ after(:each) do
49
+ subject.close
50
+ end
51
+
38
52
  it 'ignores tubes that are not being watched' do
39
53
  pending 'a creative way to test this problem'
40
54
  end
@@ -49,6 +63,10 @@ describe BeanstalkFarmer::Service do
49
63
  subject.connection.stub(:reserve) { job }
50
64
  end
51
65
 
66
+ after(:each) do
67
+ subject.close
68
+ end
69
+
52
70
  it 'builds a Farmer::Job' do
53
71
  subject.reserve.should be_a_kind_of(BeanstalkFarmer::Job)
54
72
  end
@@ -2,4 +2,17 @@ require 'spec_helper'
2
2
 
3
3
  describe BeanstalkFarmer do
4
4
  its(:logger) { should be_a_kind_of(Logger) }
5
+
6
+ describe '.config' do
7
+ after(:each) do
8
+ BeanstalkFarmer::Config.port = BeanstalkFarmer::Config::DEFAULT_PORT
9
+ end
10
+
11
+ it 'yields a Config module' do
12
+ described_class.config do |c|
13
+ c.port = 3000
14
+ c.port.should == 3000
15
+ end
16
+ end
17
+ end
5
18
  end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ class ClassWorker
4
+ def self.call(args)
5
+ $stderr << args
6
+ end
7
+ end unless defined?(ClassWorker)
8
+
9
+ describe 'Workers do work', :beanstalk_required do
10
+ before(:each) do
11
+ @old_stderr = $stderr
12
+ @capture = StringIO.new
13
+ $stderr = @capture
14
+
15
+ BeanstalkFarmer::Runner.register_handlers do
16
+ tube 'echo.small_proc', proc { |args| $stderr << args }
17
+ tube 'echo.big_proc', Proc.new { |args| $stderr << args }
18
+ tube 'echo.class', ClassWorker
19
+ tube 'echo.block', do |args| $stderr << args; end
20
+ end
21
+
22
+ BeanstalkFarmer::Runner.instance.prep_tubes
23
+ end
24
+
25
+ after(:each) do
26
+ $stderr = @old_stderr
27
+ @capture = nil
28
+ BeanstalkFarmer::Service.instance.close
29
+ end
30
+
31
+ it 'performs enqueued work with small procs' do
32
+ message = 'Hello, proc'
33
+
34
+ BeanstalkFarmer::Service.instance.enqueue 'echo.small_proc', message
35
+ BeanstalkFarmer::Runner.instance.reserve_and_work_job
36
+
37
+ @capture.string.should == message
38
+ end
39
+
40
+ it 'performs enqueued work with blocks' do
41
+ message = 'Hello, block'
42
+
43
+ BeanstalkFarmer::Service.instance.enqueue 'echo.block', message
44
+ BeanstalkFarmer::Runner.instance.reserve_and_work_job
45
+
46
+ @capture.string.should == message
47
+ end
48
+
49
+ it 'performs enqueued work with big procs' do
50
+ message = 'Hello, big Proc'
51
+
52
+ BeanstalkFarmer::Service.instance.enqueue 'echo.big_proc', message
53
+ BeanstalkFarmer::Runner.instance.reserve_and_work_job
54
+
55
+ @capture.string.should == message
56
+ end
57
+
58
+ it 'performs enqueued work with class' do
59
+ message = 'Hello, Class'
60
+
61
+ BeanstalkFarmer::Service.instance.enqueue 'echo.class', message
62
+ BeanstalkFarmer::Runner.instance.reserve_and_work_job
63
+
64
+ @capture.string.should == message
65
+ end
66
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,8 +1,19 @@
1
1
  require 'rubygems'
2
2
  require 'bundler/setup'
3
+
4
+ # Must be loaded *before* the application starts
5
+ require 'simplecov'
6
+
7
+ SimpleCov.start do
8
+ add_filter '/spec/'
9
+ end
10
+
3
11
  require 'beanstalk_farmer'
4
12
 
5
13
  Dir['./spec/support/**/*.rb'].each { |support_file| require support_file }
6
14
 
15
+ BeanstalkFarmer::Config.logger = Logger.new('./log/test.log')
16
+
7
17
  RSpec.configure do |config|
18
+ config.treat_symbols_as_metadata_keys_with_true_values = true
8
19
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: beanstalk_farmer
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.2
5
+ version: 0.2.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - James Herdamn
@@ -10,8 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-05-16 00:00:00 -04:00
14
- default_executable:
13
+ date: 2011-05-20 00:00:00 Z
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
16
  name: beanstalk-client
@@ -57,6 +56,17 @@ dependencies:
57
56
  type: :development
58
57
  prerelease: false
59
58
  version_requirements: *id004
59
+ - !ruby/object:Gem::Dependency
60
+ name: simplecov
61
+ requirement: &id005 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ~>
65
+ - !ruby/object:Gem::Version
66
+ version: 0.4.2
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *id005
60
70
  description: Farmer is a nice little kit to manage a Beanstalk job queue
61
71
  email:
62
72
  - james.herdman@me.com
@@ -70,6 +80,7 @@ files:
70
80
  - .gitignore
71
81
  - .rspec
72
82
  - .rvmrc
83
+ - .travis.yml
73
84
  - .yardopts
74
85
  - Gemfile
75
86
  - LICENSE
@@ -77,6 +88,7 @@ files:
77
88
  - Rakefile
78
89
  - beanstalk_farmer.gemspec
79
90
  - example/echo.rb
91
+ - example/echo_enqueuer.rb
80
92
  - lib/beanstalk_farmer.rb
81
93
  - lib/beanstalk_farmer/config.rb
82
94
  - lib/beanstalk_farmer/dsl.rb
@@ -84,15 +96,16 @@ files:
84
96
  - lib/beanstalk_farmer/runner.rb
85
97
  - lib/beanstalk_farmer/service.rb
86
98
  - lib/beanstalk_farmer/version.rb
99
+ - log/.gitkeep
87
100
  - spec/beanstalk_farmer/config_spec.rb
88
101
  - spec/beanstalk_farmer/dsl_spec.rb
89
102
  - spec/beanstalk_farmer/job_spec.rb
90
103
  - spec/beanstalk_farmer/runner_spec.rb
91
104
  - spec/beanstalk_farmer/service_spec.rb
92
105
  - spec/beastalk_farmer_spec.rb
106
+ - spec/integration/working_spec.rb
93
107
  - spec/spec_helper.rb
94
108
  - spec/support/have_tube_named.rb
95
- has_rdoc: true
96
109
  homepage: https://github.com/jherdman/beanstalk_farmer
97
110
  licenses: []
98
111
 
@@ -106,7 +119,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
106
119
  requirements:
107
120
  - - ">="
108
121
  - !ruby/object:Gem::Version
109
- hash: 3918122291663500270
122
+ hash: 3832172194492424674
110
123
  segments:
111
124
  - 0
112
125
  version: "0"
@@ -119,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
132
  requirements: []
120
133
 
121
134
  rubyforge_project: farmer
122
- rubygems_version: 1.6.2
135
+ rubygems_version: 1.8.2
123
136
  signing_key:
124
137
  specification_version: 3
125
138
  summary: A nice little kit to manage a Beanstalk job queue