beanstalk_farmer 0.1.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 ADDED
@@ -0,0 +1,4 @@
1
+ Gemfile.lock
2
+ doc/
3
+ .yardoc/
4
+ pkg/*.gem
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rvmrc ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bash
2
+
3
+ #
4
+ # The following is cribbed directly from the rvm-site project. It's a more
5
+ # convenient way of ensuring we're using the correct gemset, or initializing it
6
+ # if need be.
7
+ #
8
+
9
+ ruby_name="ruby-1.9.2"
10
+ gemset_name="farmer"
11
+ environment_id="$ruby_name@$gemset_name"
12
+
13
+ #
14
+ # First we attempt to load the desired environment directly from the environment
15
+ # file, this is very fast and efficient compared to running through the entire
16
+ # CLI and selector. If you want feedback on which environment was used then
17
+ # insert the word 'use' after --create as this triggers verbose mode.
18
+ #
19
+
20
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
21
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]] ; then
22
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
23
+ else
24
+ # If the environment file has not yet been created, use the RVM CLI to select.
25
+ rvm --create use "$environment_id"
26
+ fi
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ --no-private
2
+ --title "Farmer: a Beanstalk Job Queue Gem"
3
+ lib/**/*.rb -
4
+ README.md
5
+ LICENSE
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+ gemspec
3
+
4
+ group :documentation do
5
+ gem "yard", "~> 0.6.8"
6
+ gem "bluecloth"
7
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2011 James F. Herdman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Beanstalk Farmer
2
+
3
+ Farmer is a simple library that helps you manage your Beanstalk job queue. Its
4
+ API is heavily inspired by [Stalker](https://github.com/han/stalker), to help
5
+ you get done as quickly as possible, but whilst allowing you to use plain old
6
+ Ruby classes to ensure testability.
7
+
8
+ ## Setting up Jobs
9
+
10
+ Jobs are handled by any object whose instances respond to a `#call` method, à
11
+ la Rack (e.g. `Proc`s, `procs`s). The payload of the job will be passed into
12
+ the arguments of this method. Bear in mind that the payload for a Beanstalk job
13
+ is de-serialized JSON.
14
+
15
+ ### Example
16
+
17
+ BeanstalkFarmer::Runner.register_handlers do
18
+ # You can use objects
19
+ tube 'email.welcome_message', WelcomeMessageHandler
20
+
21
+ # You can use Procs
22
+ tube 'push.message', Proc.new { |args| PushService.send_message(args) }
23
+ end
24
+
25
+ BeanstalkFarmer::Runner.run!
26
+
27
+ ## Configuration
28
+
29
+ Easy as pie! See `BeanstalkFarmer::Config` for all available options.
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'bundler'
2
+ require 'bundler/setup'
3
+ require 'bundler/gem_helper'
4
+
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ require 'rspec/core/rake_task'
8
+
9
+ RSpec::Core::RakeTask.new(:spec) do |t|
10
+ t.rspec_opts = %w[--color]
11
+ t.verbose = false
12
+ end
13
+
14
+ require 'yard'
15
+ require 'yard/rake/yardoc_task'
16
+
17
+ YARD::Rake::YardocTask.new do |t|
18
+ # Options managed by .yardopts
19
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $:.unshift(lib) unless $:.include?(lib)
4
+
5
+ require 'beanstalk_farmer/version'
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = 'beanstalk_farmer'
9
+ s.version = BeanstalkFarmer::VERSION
10
+ s.platform = Gem::Platform::RUBY
11
+ s.authors = ['James Herdamn']
12
+ s.email = ['james.herdman@me.com']
13
+ s.homepage = 'https://github.com/jherdman/beanstalk_farmer'
14
+ s.summary = 'A nice little kit to manage a Beanstalk job queue'
15
+ s.description = 'Farmer is a nice little kit to manage a Beanstalk job queue'
16
+
17
+ s.required_rubygems_version = '>= 1.3.6'
18
+ s.rubyforge_project = 'farmer'
19
+
20
+ s.add_dependency 'beanstalk-client', ['~> 1.1.0']
21
+ s.add_dependency 'multi_json', ['~> 1.0.2']
22
+
23
+ s.add_development_dependency 'rake', ['~> 0.8.7']
24
+ s.add_development_dependency 'rspec', ['~> 2.6']
25
+
26
+ s.files = `git ls-files`.split("\n")
27
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
28
+ s.require_path = ['lib']
29
+ end
data/example/echo.rb ADDED
@@ -0,0 +1,18 @@
1
+ ##
2
+ # This is a simple echo worker example. To use this, you must have the Farmer
3
+ # gem installed, or built.
4
+
5
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
6
+ require 'farmer'
7
+
8
+ BeanstalkFarmer::Runner.register_handlers do
9
+ 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}" }
11
+ end
12
+
13
+ trap 'INT' do
14
+ puts "\rExiting"
15
+ exit
16
+ end
17
+
18
+ BeanstalkFarmer::Runner.run!
@@ -0,0 +1,53 @@
1
+ module BeanstalkFarmer
2
+ # Manages configuration settings and defaults.
3
+ #
4
+ # @example
5
+ # BeanstalkFarmer::Config.logger = Rails.logger
6
+ # BeanstalkFarmer::Config.json_engine = :yajl
7
+ module Config
8
+ extend self
9
+
10
+ DEFAULT_HOST = '0.0.0.0'
11
+ DEFAULT_PORT = 11300
12
+
13
+ attr_accessor :settings
14
+ @settings = {}
15
+
16
+ # Define a configuration option with a default.
17
+ #
18
+ # @example Define the option.
19
+ # Config.option(:persist_in_safe_mode, :default => false)
20
+ #
21
+ # @param [Symbol] name The name of the configuration option.
22
+ # @param [Hash] options Extras for the option.
23
+ #
24
+ # @option options [Object] :default The default value.
25
+ #
26
+ # @note Copied from Mongoid. Thank you!
27
+ #
28
+ # @private
29
+ def option(name, options = {})
30
+ define_method(name) do
31
+ settings.has_key?(name) ? settings[name] : options[:default]
32
+ end
33
+ define_method("#{name}=") { |value| settings[name] = value }
34
+ define_method("#{name}?") { send(name) }
35
+ end
36
+
37
+ option :host, default: DEFAULT_HOST
38
+
39
+ option :port, default: DEFAULT_PORT
40
+
41
+ option :logger, default: ::Logger.new($stdout)
42
+
43
+ # @see MultiJson.default_engine
44
+ def json_engine
45
+ MultiJson.engine
46
+ end
47
+
48
+ # @see MultiJson.engine=
49
+ def json_engine=(json_engine)
50
+ MultiJson.engine = json_engine
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,14 @@
1
+ module BeanstalkFarmer
2
+ # @private Provides a clean slate object that we can safely instance eval on.
3
+ class DSL
4
+ def tube(tube_name, job_handler)
5
+ Job.handler_pool[tube_name] = job_handler
6
+ end
7
+
8
+ def self.execute!(&block)
9
+ new.tap do |dsl|
10
+ dsl.instance_eval(&block)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,50 @@
1
+ require 'multi_json'
2
+ require 'timeout'
3
+
4
+ module BeanstalkFarmer
5
+ class Job
6
+ # Raised when a job cannot complete in time
7
+ class OutOfTimeError < Timeout::Error; end
8
+
9
+ attr_accessor :name, :args, :job
10
+
11
+ # @param [Beanstalk::Job] job A Beanstalk job that has been reserved to be
12
+ # worked upon
13
+ def initialize(job)
14
+ self.job = job
15
+ set_name_and_arguments
16
+ end
17
+
18
+ # Performs work for this job
19
+ def work
20
+ # Stalker, the inspiration for this project, subtracted 1 from the job's
21
+ # TTR value. I'm not sure why at this point in time. Maybe to compensate
22
+ # for Job setup time.
23
+ Timeout.timeout(job.ttr) do
24
+ handler = self.class.handler_pool[name]
25
+ handler.call(args)
26
+ end
27
+ rescue Timeout::Error
28
+ raise OutOfTimeError, "#{name} could not finish in #{job.ttr} seconds"
29
+ ensure
30
+ job.delete
31
+ end
32
+
33
+ # A pool of job handlers that can work on jobs in our queue
34
+ #
35
+ # @param [Boolean] reset (false) When true, our collection of job handlers
36
+ # will be purged from memory
37
+ #
38
+ # @return [Hash] the collection of job handlers
39
+ def self.handler_pool(reset=false)
40
+ @pool = nil if reset
41
+ @pool ||= Hash.new
42
+ end
43
+
44
+ private
45
+
46
+ def set_name_and_arguments
47
+ self.name, self.args = MultiJson.decode(job.body)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,51 @@
1
+ require 'singleton'
2
+
3
+ module BeanstalkFarmer
4
+ # Maps tube names to job handlers, and manages the run loop
5
+ class Runner
6
+ include Singleton
7
+
8
+ # @see Farmer::Job.handler_pool
9
+ def handler_pool
10
+ Job.handler_pool
11
+ end
12
+
13
+ # @return [Farmer::BeanstalkService] a connection to the Beanstalk queue
14
+ def service
15
+ @service ||= Service.new
16
+ end
17
+
18
+ # Prepares tubes for watching
19
+ def prep_tubes
20
+ service.prep(handler_pool.keys)
21
+ end
22
+
23
+ # Reserves a job, and works it
24
+ def reserve_and_work_job
25
+ job = service.reserve
26
+ job.work
27
+ end
28
+
29
+ # Runs a loop looking for jobs to reserve and run
30
+ def work_jobs
31
+ loop { reserve_and_work_job }
32
+ end
33
+
34
+ # @yield block A DSL to define jobs for your queue
35
+ #
36
+ # @example
37
+ # Farmer::Runner.register_handlers do
38
+ # tube 'sms', proc { |args| puts args }
39
+ # tube 'mine', Proc.new { |args| puts args }
40
+ # end
41
+ def self.register_handlers(&block)
42
+ DSL.execute!(&block)
43
+ end
44
+
45
+ # Looks for jobs to reserve, and applies handlers to them
46
+ def self.run!
47
+ instance.prep_tubes
48
+ instance.work_jobs
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,83 @@
1
+ require 'beanstalk-client'
2
+ require 'multi_json'
3
+
4
+ module BeanstalkFarmer
5
+ ##
6
+ # Provides an abstraction against our Beanstalk client to buffer us against
7
+ # changes in their API.
8
+ #
9
+ # @todo Abstract all 3rd party errors
10
+ # @todo Catch all 3rd party errors, and re-raise our own
11
+ #
12
+ # @private
13
+ class Service
14
+ DEFAULT_DELAY = 0
15
+ DEFAULT_PRIORITY = 65536
16
+ DEFAULT_TTR = 120
17
+
18
+ # Raised when we cannot connect to the Beanstalk queue
19
+ class NotConnectedError < Beanstalk::NotConnected; end
20
+
21
+ attr_accessor :uri
22
+
23
+ # Sets the URI of your Beanstalk queue.
24
+ #
25
+ # @param [String] host (DEFAULT_HOST) the host name to of your Beanstalk queue
26
+ #
27
+ # @param [String] port (DEFAULT_PORT) the port that your Beanstalk queue is on
28
+ def initialize(host=Config.host, port=Config.port)
29
+ self.uri = build_uri(host, port)
30
+ end
31
+
32
+ # @return [Beanstalk::Pool] a connection to Beanstalk
33
+ def connection
34
+ @connection ||= Beanstalk::Pool.new([uri])
35
+ end
36
+
37
+ # Closes the Beanstalk connection
38
+ #
39
+ # @return [nil] Nothing. Absolutely nothing.
40
+ def close
41
+ @connection.close
42
+ @connection = nil
43
+ end
44
+
45
+ # @param [Array<String>] tube_names The tube names to be watched
46
+ def prep(tube_names)
47
+ watch_tubes(tube_names)
48
+ ignore_unwatched_tubes(tube_names)
49
+ end
50
+
51
+ # @param [Array<String>] tube_names The tube names to be watched
52
+ def watch_tubes(tube_names)
53
+ tube_names.each { |tube_name| connection.watch(tube_name) }
54
+ end
55
+
56
+ # Ignores any tubes that aren't of interest, excluding the default tube.
57
+ def ignore_unwatched_tubes(watched_tube_names)
58
+ connection.list_tubes_watched.values do |tube_name|
59
+ connection.ignore(tube_name) unless watched_tube_names.include?(tube_name)
60
+ end
61
+ end
62
+
63
+ # Helper method. Used when testing to see if our queue works
64
+ def enqueue(tube_name, args={})
65
+ connection.use(tube_name)
66
+ connection.put MultiJson.encode([tube_name, args]), DEFAULT_PRIORITY, DEFAULT_DELAY, DEFAULT_TTR
67
+ end
68
+
69
+ # @param [Integer, nil] timeout When nil, a job is reserved indefinitely,
70
+ # otherwise for the number of seconds provided
71
+ #
72
+ # @return [Farmer::Job] a Farmer Job to work
73
+ def reserve(timeout=nil)
74
+ Job.new(connection.reserve(timeout))
75
+ end
76
+
77
+ private
78
+
79
+ def build_uri(host, port)
80
+ [host, port].join(':')
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,3 @@
1
+ module BeanstalkFarmer
2
+ VERSION = "0.1.0"
3
+ end
data/lib/farmer.rb ADDED
@@ -0,0 +1,33 @@
1
+ require 'logger'
2
+
3
+ ##
4
+ # Automatically loads classes as needed, provides logger, etc.
5
+ module BeanstalkFarmer
6
+ autoload :Config, 'beanstalk_farmer/config'
7
+ autoload :Service, 'beanstalk_farmer/service'
8
+ autoload :DSL, 'beanstalk_farmer/dsl'
9
+ autoload :Job, 'beanstalk_farmer/job'
10
+ autoload :Runner, 'beanstalk_farmer/runner'
11
+
12
+ # @param [String] host The host name where your beanstalkd connection is
13
+ # located. Defaults to `DEFAULT_PORT`.
14
+ #
15
+ # @param [#to_s] port The port number where your beanstalkd connection may
16
+ # be accessed. Defaults to `DEFAULT_PORT`.
17
+ def self.connection(host=Config.host, port=Config.port)
18
+ @service ||= Service.new(host, port)
19
+ @service.connection
20
+ end
21
+
22
+ # Close Beanstalk connection
23
+ def self.close_connection
24
+ @service.close
25
+ @service = nil
26
+ end
27
+
28
+ # @return [Logger] the logger, defaulting to a STDOUT logger
29
+ def self.logger
30
+ @logger ||= Config.logger
31
+ @logger
32
+ end
33
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe BeanstalkFarmer::Config do
4
+ describe ".option" do
5
+ before(:all) do
6
+ described_class.option(:test_setting, default: true)
7
+ end
8
+
9
+ it "creates a getter for an option" do
10
+ described_class.should respond_to(:test_setting)
11
+ end
12
+
13
+ it "creates a setter for the option" do
14
+ described_class.should respond_to(:test_setting=)
15
+ end
16
+
17
+ it "creates a conditional for the option" do
18
+ described_class.should respond_to(:test_setting?)
19
+ end
20
+
21
+ it "allows the setting of a default value" do
22
+ described_class.test_setting.should == true
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe BeanstalkFarmer::DSL do
4
+ describe '#tube' do
5
+ let(:tube_name) { 'my_tube' }
6
+ let(:handler) { Proc.new { |args| args } }
7
+
8
+ after(:each) do
9
+ BeanstalkFarmer::Job.handler_pool(true)
10
+ end
11
+
12
+ it 'correctly adds job handlers for some tube' do
13
+ subject.tube(tube_name, handler)
14
+ BeanstalkFarmer::Job.handler_pool[tube_name].should == handler
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe BeanstalkFarmer::Job do
4
+ let(:job) {
5
+ double('job', body: "[\"bacon\",{\"msg\":\"Hello\"}]", name: 'bacon', args: { 'msg' => 'Hello' }, ttr: 56, delete: true)
6
+ }
7
+
8
+ describe '.new' do
9
+ it 'sets the job' do
10
+ described_class.new(job).job.should == job
11
+ end
12
+
13
+ it 'decodes the name of the job' do
14
+ described_class.new(job).name.should == job.name
15
+ end
16
+
17
+ it 'decodes the arguments for the job' do
18
+ described_class.new(job).args.should == job.args
19
+ end
20
+ end
21
+
22
+ describe '.handler_pool' do
23
+ it 'is a Hash of job handlers' do
24
+ described_class.handler_pool.should be_a_kind_of(Hash)
25
+ end
26
+
27
+ it 'can reset the Hash of job hanlders' do
28
+ described_class.handler_pool['bacon'] = lambda { |args| args }
29
+ described_class.handler_pool(true)
30
+ described_class.handler_pool.should be_empty
31
+ end
32
+ end
33
+
34
+ describe '#work' do
35
+ let(:handler) { proc { |args| args } }
36
+
37
+ subject { described_class.new(job) }
38
+
39
+ before(:each) do
40
+ described_class.handler_pool[job.name] = handler
41
+ end
42
+
43
+ it 'performs some work for the handler' do
44
+ subject.work.should == job.args
45
+ end
46
+
47
+ it 'times out if the job has run out of time' do
48
+ Timeout.should_receive(:timeout).with(job.ttr) { raise Timeout::Error }
49
+ expect { subject.work }.to raise_error(BeanstalkFarmer::Job::OutOfTimeError)
50
+ end
51
+
52
+ it 'deletes the job' do
53
+ subject.job.should_receive(:delete)
54
+ subject.work
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe BeanstalkFarmer::Runner do
4
+ subject { described_class.instance }
5
+
6
+ it 'is a singleton' do
7
+ described_class.included_modules.should include(Singleton)
8
+ end
9
+
10
+ its(:service) { should be_a_kind_of(BeanstalkFarmer::Service) }
11
+
12
+ describe '#handler_pool' do
13
+ it 'is the job handler pool' do
14
+ BeanstalkFarmer::Job.should_receive(:handler_pool)
15
+ subject.handler_pool
16
+ end
17
+ end
18
+
19
+ describe '#prep_tubes' do
20
+ it 'prepares tubes for watching' do
21
+ subject.service.should_receive(:prep).with(subject.handler_pool.keys)
22
+ subject.prep_tubes
23
+ end
24
+ end
25
+
26
+ describe '#reserve_and_work_job' do
27
+ let(:job) { double('Farmer::Job', work: true) }
28
+
29
+ before(:each) do
30
+ subject.stub_chain(:service, :reserve) { job }
31
+ end
32
+
33
+ it 'reserves a job' do
34
+ subject.service.should_receive(:reserve) { job }
35
+ subject.reserve_and_work_job
36
+ end
37
+
38
+ it 'works the job' do
39
+ job.should_receive(:work)
40
+ subject.reserve_and_work_job
41
+ end
42
+ end
43
+
44
+ describe '.run!' do
45
+ before(:each) do
46
+ subject.stub(:prep_tubes)
47
+ subject.stub(:work_jobs)
48
+ end
49
+
50
+ it 'prepares tubes for watching' do
51
+ subject.should_receive(:prep_tubes)
52
+ described_class.run!
53
+ end
54
+
55
+ it 'works jobs' do
56
+ subject.should_receive(:work_jobs)
57
+ described_class.run!
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
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/
7
+ end
8
+ end
9
+
10
+ describe '#connection' do
11
+ after(:each) do
12
+ subject.close
13
+ end
14
+
15
+ it 'establishes a connection to Beanstalk' do
16
+ expect {
17
+ subject.connection.put 'Hello'
18
+ }.to_not raise_error(BeanstalkFarmer::Service::NotConnectedError)
19
+ end
20
+ end
21
+
22
+ describe '#close' do
23
+ it 'closes the Beanstalk connection' do
24
+ connection = subject.connection # ensure we're connected
25
+ subject.close
26
+ connection.open_connections.should be_empty
27
+ end
28
+ end
29
+
30
+ describe '#watch_tubes' do
31
+ it 'adds a tube to the list of watched tubes' do
32
+ subject.watch_tubes(%w[bacon])
33
+ subject.connection.list_tubes_watched.should have_tube_named('bacon')
34
+ end
35
+ end
36
+
37
+ describe '#ignore_unwatched_tubes' do
38
+ it 'ignores tubes that are not being watched' do
39
+ pending 'a creative way to test this problem'
40
+ end
41
+ end
42
+
43
+ describe '#reserve' do
44
+ let(:job) {
45
+ double('job', body: "[\"bacon\",{\"msg\":\"Hello\"}]", name: 'bacon', args: { 'msg' => 'Hello' }, ttr: 56)
46
+ }
47
+
48
+ before(:each) do
49
+ subject.connection.stub(:reserve) { job }
50
+ end
51
+
52
+ it 'builds a Farmer::Job' do
53
+ subject.reserve.should be_a_kind_of(BeanstalkFarmer::Job)
54
+ end
55
+
56
+ it 'reserves a job indefinitely' do
57
+ subject.connection.should_receive(:reserve).with(nil) { job }
58
+ subject.reserve
59
+ end
60
+
61
+ it 'reserves a job for the specified number of seconds' do
62
+ subject.connection.should_receive(:reserve).with(30) { job }
63
+ subject.reserve(30)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe BeanstalkFarmer do
4
+ describe '.connection' do
5
+ it 'initializes a new Beanstalk connection' do
6
+ described_class::Service.any_instance.should_receive(:connection)
7
+ described_class.connection
8
+ end
9
+ end
10
+
11
+ describe '.close_connection' do
12
+ before(:each) do
13
+ described_class::Service.any_instance.stub(:connection)
14
+ described_class::Service.any_instance.stub(:close)
15
+
16
+ described_class.connection # sets up a connection
17
+ end
18
+
19
+ it 'closes all connections to Beanstalk' do
20
+ described_class::Service.any_instance.should_receive(:close)
21
+ described_class.close_connection
22
+ end
23
+ end
24
+
25
+ its(:logger) { should be_a_kind_of(Logger) }
26
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'farmer'
4
+
5
+ Dir['./spec/support/**/*.rb'].each { |support_file| require support_file }
6
+
7
+ RSpec.configure do |config|
8
+ end
@@ -0,0 +1,7 @@
1
+ RSpec::Matchers.define :have_tube_named do |expected_tube_name|
2
+ match do |watched_tubes_hash|
3
+ watched_tubes_hash.values.flatten.include?(expected_tube_name)
4
+ end
5
+
6
+ diffable
7
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: beanstalk_farmer
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - James Herdamn
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-05-16 00:00:00 -04:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: beanstalk-client
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 1.1.0
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: multi_json
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: 1.0.2
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: rake
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.8.7
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: rspec
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: "2.6"
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ description: Farmer is a nice little kit to manage a Beanstalk job queue
61
+ email:
62
+ - james.herdman@me.com
63
+ executables: []
64
+
65
+ extensions: []
66
+
67
+ extra_rdoc_files: []
68
+
69
+ files:
70
+ - .gitignore
71
+ - .rspec
72
+ - .rvmrc
73
+ - .yardopts
74
+ - Gemfile
75
+ - LICENSE
76
+ - README.md
77
+ - Rakefile
78
+ - beanstalk_farmer.gemspec
79
+ - example/echo.rb
80
+ - lib/beanstalk_farmer/config.rb
81
+ - lib/beanstalk_farmer/dsl.rb
82
+ - lib/beanstalk_farmer/job.rb
83
+ - lib/beanstalk_farmer/runner.rb
84
+ - lib/beanstalk_farmer/service.rb
85
+ - lib/beanstalk_farmer/version.rb
86
+ - lib/farmer.rb
87
+ - spec/beanstalk_farmer/config_spec.rb
88
+ - spec/beanstalk_farmer/dsl_spec.rb
89
+ - spec/beanstalk_farmer/job_spec.rb
90
+ - spec/beanstalk_farmer/runner_spec.rb
91
+ - spec/beanstalk_farmer/service_spec.rb
92
+ - spec/beastalk_farmer_spec.rb
93
+ - spec/spec_helper.rb
94
+ - spec/support/have_tube_named.rb
95
+ has_rdoc: true
96
+ homepage: https://github.com/jherdman/beanstalk_farmer
97
+ licenses: []
98
+
99
+ post_install_message:
100
+ rdoc_options: []
101
+
102
+ require_paths:
103
+ - - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ hash: -1506406479877648838
110
+ segments:
111
+ - 0
112
+ version: "0"
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: 1.3.6
119
+ requirements: []
120
+
121
+ rubyforge_project: farmer
122
+ rubygems_version: 1.6.2
123
+ signing_key:
124
+ specification_version: 3
125
+ summary: A nice little kit to manage a Beanstalk job queue
126
+ test_files: []
127
+