beanstalk_farmer 0.1.2 → 0.2.0

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