Permalink
Browse files

Initial commit.

  • Loading branch information...
0 parents commit e4f9cb35aa8721353f2a1f5b5b260df909ae7266 @fnando committed Nov 9, 2012
4 .gitignore
@@ -0,0 +1,4 @@
+.DS_Store
+*.sqlite3
+*.rdb
+pkg
1 .rspec
@@ -0,0 +1 @@
+--color --format documentation --order random
2 Gemfile
@@ -0,0 +1,2 @@
+source :rubygems
+gemspec
105 Gemfile.lock
@@ -0,0 +1,105 @@
+PATH
+ remote: .
+ specs:
+ qe (0.1.0)
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ activemodel (3.2.8)
+ activesupport (= 3.2.8)
+ builder (~> 3.0.0)
+ activerecord (3.2.8)
+ activemodel (= 3.2.8)
+ activesupport (= 3.2.8)
+ arel (~> 3.0.2)
+ tzinfo (~> 0.3.29)
+ activesupport (3.2.8)
+ i18n (~> 0.6)
+ multi_json (~> 1.0)
+ arel (3.0.2)
+ awesome_print (1.1.0)
+ backburner (0.2.0)
+ beaneater (~> 0.1.2)
+ dante (~> 0.1.5)
+ beaneater (0.1.2)
+ builder (3.0.4)
+ celluloid (0.12.3)
+ facter (>= 1.6.12)
+ timers (>= 1.0.0)
+ coderay (1.0.8)
+ connection_pool (0.9.2)
+ dante (0.1.5)
+ delayed_job (3.0.3)
+ activesupport (~> 3.0)
+ delayed_job_active_record (0.3.3)
+ activerecord (>= 2.1.0, < 4)
+ delayed_job (~> 3.0)
+ diff-lcs (1.1.3)
+ facter (1.6.14)
+ i18n (0.6.1)
+ method_source (0.8.1)
+ multi_json (1.3.7)
+ pry (0.9.10)
+ coderay (~> 1.0.5)
+ method_source (~> 0.8)
+ slop (~> 3.3.1)
+ qu (0.2.0)
+ multi_json
+ qu-redis (0.2.0)
+ qu (= 0.2.0)
+ redis-namespace
+ simple_uuid
+ rack (1.4.1)
+ rack-protection (1.2.0)
+ rack
+ redis (3.0.2)
+ redis-namespace (1.2.1)
+ redis (~> 3.0.0)
+ resque (1.23.0)
+ multi_json (~> 1.0)
+ redis-namespace (~> 1.0)
+ sinatra (>= 0.9.2)
+ vegas (~> 0.1.2)
+ rspec (2.11.0)
+ rspec-core (~> 2.11.0)
+ rspec-expectations (~> 2.11.0)
+ rspec-mocks (~> 2.11.0)
+ rspec-core (2.11.1)
+ rspec-expectations (2.11.3)
+ diff-lcs (~> 1.1.3)
+ rspec-mocks (2.11.3)
+ sidekiq (2.5.2)
+ celluloid (~> 0.12.0)
+ connection_pool (~> 0.9.2)
+ multi_json (~> 1)
+ redis (~> 3)
+ redis-namespace
+ simple_uuid (0.2.0)
+ sinatra (1.3.3)
+ rack (~> 1.3, >= 1.3.6)
+ rack-protection (~> 1.2)
+ tilt (~> 1.3, >= 1.3.3)
+ slop (3.3.3)
+ sqlite3 (1.3.6)
+ tilt (1.3.3)
+ timers (1.0.1)
+ tzinfo (0.3.35)
+ vegas (0.1.11)
+ rack (>= 1.0.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ activerecord
+ awesome_print
+ backburner
+ delayed_job_active_record
+ pry
+ qe!
+ qu-redis
+ resque
+ rspec
+ sidekiq
+ sqlite3
118 README.md
@@ -0,0 +1,118 @@
+# Qe
+
+
+A simple interface over several background job libraries like Resque, Sidekiq and DelayedJob.
+
+## Usage
+
+In this wild world where a new asynchronous job processing
+library is released every once in a while, Qe tries to keep a unified
+interface that works with the most famous libraries:
+
+* Sidekiq
+* Resque
+* DelayedJob
+* Qu
+* Beanstalk/Backburner
+
+See an example:
+
+You can choose an adapter:
+
+``` ruby
+Qe.adapter = Qe::Sidekiq
+# Qe.adapter = Qe::Resque
+# Qe.adapter = Qe::Qu
+# Qe.adapter = Qe::DelayedJob
+# Qe.adapter = Qe::Beanstalk
+```
+
+Create our worker that will send e-mails through `ActionMailer`.
+
+``` ruby
+class MailerWorker
+ include Qe::Worker
+
+ def perform
+ Mailer.public_send(options[:mail], options).deliver
+ end
+end
+```
+
+Define our `Mailer` class.
+
+``` ruby
+class Mailer < ActionMailer::Base
+ def welcome(options)
+ @options = options
+ mail :to => options[:email]
+ end
+end
+```
+
+Enqueue a job to be processed asynchronously.
+
+``` ruby
+MailerWorker.enqueue({
+ :mail => :welcome,
+ :email => "john@example.org",
+ :name => "John Doe"
+})
+```
+
+## Testing support
+
+Qe comes with testing support. Just require the `qe/testing.rb` file
+and a fake queuing adapter will be used. All enqueued jobs will be stored
+at `Qe.jobs`. Note that this method is only available on testing mode.
+
+``` ruby
+require "qe/testing"
+Qe.adapter = Qe::Testing
+```
+
+If you"re using RSpec, you can require the `qe/testing/rspec.rb` file
+instead. This will reset `Qe.jobs` before every spec and will add a
+`enqueue` matcher.
+
+``` ruby
+require "qe/testing/rspec"
+
+describe "Enqueuing a job" do
+ it "enqueues job" do
+ expect {
+ # do something
+ }.to enqueue(MailerWorker).with(:email => "john@example.org")
+ end
+end
+```
+
+
+Maintainer
+----------
+
+* Nando Vieira (<http://nandovieira.com.br>)
+
+License:
+--------
+
+(The MIT License)
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
5 Rakefile
@@ -0,0 +1,5 @@
+require "bundler"
+Bundler::GemHelper.install_tasks
+
+require "rspec/core/rake_task"
+RSpec::Core::RakeTask.new
3 examples/.env
@@ -0,0 +1,3 @@
+QUEUES="mail,default"
+QUEUE="mail,default"
+BEANSTALK_URL="beanstalk://localhost/"
14 examples/Gemfile
@@ -0,0 +1,14 @@
+source :rubygems
+
+gem "sidekiq"
+gem "backburner"
+gem "beanstalkd_view"
+gem "rake"
+gem "thin"
+gem "resque"
+gem "activerecord"
+gem "delayed_job_active_record"
+gem "sqlite3"
+gem "daemons"
+gem "qu-redis"
+gem "slim"
119 examples/Gemfile.lock
@@ -0,0 +1,119 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ activemodel (3.2.8)
+ activesupport (= 3.2.8)
+ builder (~> 3.0.0)
+ activerecord (3.2.8)
+ activemodel (= 3.2.8)
+ activesupport (= 3.2.8)
+ arel (~> 3.0.2)
+ tzinfo (~> 0.3.29)
+ activesupport (3.2.8)
+ i18n (~> 0.6)
+ multi_json (~> 1.0)
+ arel (3.0.2)
+ backburner (0.2.0)
+ beaneater (~> 0.1.2)
+ dante (~> 0.1.5)
+ backports (2.6.5)
+ beaneater (0.1.2)
+ beanstalkd_view (1.1.0)
+ beaneater (~> 0.1.0)
+ json
+ sinatra (>= 1.3.0)
+ sinatra-assetpack (>= 0.0.11)
+ sinatra-contrib (>= 1.3.0)
+ vegas (~> 0.1.2)
+ builder (3.0.4)
+ celluloid (0.12.3)
+ facter (>= 1.6.12)
+ timers (>= 1.0.0)
+ connection_pool (0.9.2)
+ daemons (1.1.9)
+ dante (0.1.5)
+ delayed_job (3.0.3)
+ activesupport (~> 3.0)
+ delayed_job_active_record (0.3.3)
+ activerecord (>= 2.1.0, < 4)
+ delayed_job (~> 3.0)
+ eventmachine (1.0.0)
+ facter (1.6.14)
+ i18n (0.6.1)
+ jsmin (1.0.1)
+ json (1.7.5)
+ multi_json (1.3.7)
+ qu (0.2.0)
+ multi_json
+ qu-redis (0.2.0)
+ qu (= 0.2.0)
+ redis-namespace
+ simple_uuid
+ rack (1.4.1)
+ rack-protection (1.2.0)
+ rack
+ rack-test (0.6.2)
+ rack (>= 1.0)
+ rake (0.9.2.2)
+ redis (3.0.2)
+ redis-namespace (1.2.1)
+ redis (~> 3.0.0)
+ resque (1.23.0)
+ multi_json (~> 1.0)
+ redis-namespace (~> 1.0)
+ sinatra (>= 0.9.2)
+ vegas (~> 0.1.2)
+ sidekiq (2.5.2)
+ celluloid (~> 0.12.0)
+ connection_pool (~> 0.9.2)
+ multi_json (~> 1)
+ redis (~> 3)
+ redis-namespace
+ simple_uuid (0.2.0)
+ sinatra (1.3.3)
+ rack (~> 1.3, >= 1.3.6)
+ rack-protection (~> 1.2)
+ tilt (~> 1.3, >= 1.3.3)
+ sinatra-assetpack (0.0.11)
+ jsmin
+ rack-test
+ sinatra
+ tilt (>= 1.3.0)
+ sinatra-contrib (1.3.2)
+ backports (>= 2.0)
+ eventmachine
+ rack-protection
+ rack-test
+ sinatra (~> 1.3.0)
+ tilt (~> 1.3)
+ slim (1.3.3)
+ temple (~> 0.5.5)
+ tilt (~> 1.3.3)
+ sqlite3 (1.3.6)
+ temple (0.5.5)
+ thin (1.5.0)
+ daemons (>= 1.0.9)
+ eventmachine (>= 0.12.6)
+ rack (>= 1.0.0)
+ tilt (1.3.3)
+ timers (1.0.1)
+ tzinfo (0.3.35)
+ vegas (0.1.11)
+ rack (>= 1.0.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ activerecord
+ backburner
+ beanstalkd_view
+ daemons
+ delayed_job_active_record
+ qu-redis
+ rake
+ resque
+ sidekiq
+ slim
+ sqlite3
+ thin
20 examples/Procfile
@@ -0,0 +1,20 @@
+redis: redis-server redis.conf
+# beanstalkd: beanstalkd
+
+# Uncomment the following lines to enable Sidekiq
+sidekiq: bundle exec sidekiq -r ./workers.rb -q default -q mail
+sidekiq_web: bundle exec thin -R sidekiq.ru -p 9292 -V start
+
+# Uncomment the following lines to enable Beanstalk/Backburner
+# backburner: bundle exec rake backburner:work
+# beanstalkd_view: bundle exec thin -R beanstalkd.ru -p 9292 -V start
+
+# Uncomment the following line to enable DelayedJob
+# delayed_job: bundle exec rake jobs:work
+
+# Uncomment the following line to enable Qu
+# qu: bundle exec rake qu:work
+
+# Uncomment the following lines to enable Resque
+# resque: bundle exec rake resque:work
+# resque_web: bundle exec thin -R resque.ru -p 9292 -V start
9 examples/README.md
@@ -0,0 +1,9 @@
+# Running examples
+
+1. First, start processes with `$ foreman start`
+2. Then execute `$ ruby sample.rb`
+3. Go back to the Foreman's terminal and watch the output
+
+Tweak the `Procfile` to enable different libraries and web panels.
+
+You'll need to install Redis and/or Beanstalkd.
14 examples/Rakefile
@@ -0,0 +1,14 @@
+require "backburner/tasks"
+require "resque/tasks"
+require "delayed/tasks"
+require "qu/tasks"
+
+task :environment do #=> required by Backburner
+ require "./workers"
+
+ Backburner.configure do |config|
+ config.logger = Logger.new(STDOUT)
+ end
+end
+
+task "resque:setup" => :environment
5 examples/beanstalkd.ru
@@ -0,0 +1,5 @@
+require "bundler"
+Bundler.setup(:default)
+Bundler.require(:default)
+
+run BeanstalkdView::Server
16 examples/migrate.rb
@@ -0,0 +1,16 @@
+require "./workers"
+
+ActiveRecord::Schema.define(:version => 0) do
+ create_table :delayed_jobs, :force => true do |table|
+ table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue
+ table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually.
+ table.text :handler # YAML-encoded string of the object that will do work
+ table.text :last_error # reason for last failure (See Note below)
+ table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.
+ table.datetime :locked_at # Set when a client is working on this object
+ table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
+ table.string :locked_by # Who is working on this object (if locked)
+ table.string :queue # The name of the queue this job is in
+ table.timestamps
+ end
+end
1 examples/redis.conf
@@ -0,0 +1 @@
+loglevel notice
6 examples/resque.ru
@@ -0,0 +1,6 @@
+require "bundler"
+Bundler.setup(:default)
+Bundler.require(:default)
+
+require "resque/server"
+run Resque::Server
11 examples/sample.rb
@@ -0,0 +1,11 @@
+require "./workers"
+
+loop do
+ puts "=> Enqueuing jobs [#{Time.now}]"
+
+ MailerWorker.enqueue(:name => "John Doe", :email => "john@example.org")
+ ClockWorker.enqueue
+
+ sleep 3
+end
+
10 examples/sidekiq.ru
@@ -0,0 +1,10 @@
+require "bundler"
+Bundler.setup(:default)
+Bundler.require(:default)
+
+Sidekiq.configure_client do |config|
+ config.redis = { :size => 1 }
+end
+
+require "sidekiq/web"
+run Sidekiq::Web
43 examples/workers.rb
@@ -0,0 +1,43 @@
+require "bundler"
+Bundler.setup(:default)
+Bundler.require
+
+$:.unshift File.expand_path("../../lib", __FILE__)
+require "qe"
+
+$stdout.sync = true
+
+Qe.adapter = Qe::Sidekiq
+# Qe.adapter = Qe::Beanstalk
+# Qe.adapter = Qe::Resque
+# Qe.adapter = Qe::DelayedJob
+# Qe.adapter = Qe::Qu
+# Qe.adapter = Qe::Testing
+
+ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "jobs.sqlite3")
+
+class ClockWorker
+ include Qe::Worker
+
+ def perform
+ puts "=> Time: #{Time.now}"
+ end
+end
+
+class MailerWorker
+ include Qe::Worker
+ queue :mail
+
+ def before
+ puts "=> Running before"
+ end
+
+ def perform
+ puts "=> Performing"
+ puts "=> Options: #{options.inspect}"
+ end
+
+ def after
+ puts "=> Running after"
+ end
+end
83 lib/qe.rb
@@ -0,0 +1,83 @@
+require "qe/beanstalk"
+require "qe/delayed_job"
+require "qe/qu"
+require "qe/resque"
+require "qe/sidekiq"
+require "qe/version"
+require "qe/worker"
+
+# In this wild world where a new asynchronous job processing
+# library is released every once in a while, Qe tries to keep a unified
+# interface that works with most famous libraries:
+#
+# # Sidekiq
+# # Resque
+# # DelayedJob
+# # Qu
+# # Beanstalk/Backburner
+#
+# See an example:
+#
+# You can choose an adapter:
+#
+# Qe.adapter = Qe::Sidekiq
+# Qe.adapter = Qe::Resque
+# Qe.adapter = Qe::Qu
+# Qe.adapter = Qe::DelayedJob
+# Qe.adapter = Qe::Beanstalk
+#
+# Create our worker that will send e-mails through +ActionMailer+.
+#
+# class MailerWorker
+# include Qe::Worker
+#
+# def perform
+# Mailer.public_send(options[:mail], options).deliver
+# end
+# end
+#
+# Define our +Mailer+ class.
+#
+# class Mailer < ActionMailer::Base
+# def welcome(options)
+# @options = options
+# mail :to => options[:email]
+# end
+# end
+#
+# Enqueue a job to be processed asynchronously.
+#
+# MailerWorker.enqueue({
+# :mail => :welcome,
+# :email => "john@example.org",
+# :name => "John Doe"
+# })
+#
+# == Testing support
+#
+# Qe comes with testing support. Just require the <tt>qe/testing.rb</tt> file
+# and a fake queuing adapter will be used. All enqueued jobs will be stored
+# at <tt>Qe.jobs</tt>. Note that this method is only available on testing mode.
+#
+# require "qe/testing"
+# Qe.adapter = Qe::Testing
+#
+# If you're using RSpec, you can require the <tt>qe/testing/rspec.rb</tt> file
+# instead. This will reset <tt>Qe.jobs</tt> before every spec and will add a
+# +enqueue+ matcher.
+#
+# require "qe/testing/rspec"
+#
+# describe "Enqueuing a job" do
+# it "enqueues job" do
+# expect {
+# # do something
+# }.to enqueue(MailerWorker).with(:email => "john@example.org")
+# end
+# end
+#
+module Qe
+ class << self
+ attr_accessor :adapter
+ end
+end
16 lib/qe/beanstalk.rb
@@ -0,0 +1,16 @@
+module Qe
+ class Beanstalk
+ class Worker
+ include Backburner::Queue
+
+ def self.perform(*args)
+ Qe::Worker.perform(*args)
+ end
+ end
+
+ def self.enqueue(worker, options = {})
+ Worker.queue worker.queue
+ Backburner.enqueue Worker, worker.name, options
+ end
+ end
+end
13 lib/qe/delayed_job.rb
@@ -0,0 +1,13 @@
+module Qe
+ class DelayedJob
+ class Worker < Struct.new(:worker_name, :options)
+ def perform
+ Qe::Worker.perform(worker_name, options)
+ end
+ end
+
+ def self.enqueue(worker, options = {})
+ Delayed::Job.enqueue Worker.new(worker.name, options), :queue => worker.queue
+ end
+ end
+end
16 lib/qe/qu.rb
@@ -0,0 +1,16 @@
+module Qe
+ class Qu
+ class Worker
+ include ::Sidekiq::Worker
+
+ def self.perform(*args)
+ Qe::Worker.perform(*args)
+ end
+ end
+
+ def self.enqueue(worker, options = {})
+ Worker.instance_variable_set("@queue", worker.queue)
+ ::Qu.enqueue Worker, worker.name, options
+ end
+ end
+end
14 lib/qe/resque.rb
@@ -0,0 +1,14 @@
+module Qe
+ class Resque
+ class Worker
+ def self.perform(*args)
+ Qe::Worker.perform(*args)
+ end
+ end
+
+ def self.enqueue(worker, options = {})
+ Worker.instance_variable_set "@queue", worker.queue
+ ::Resque.enqueue Worker, worker.name, options
+ end
+ end
+end
16 lib/qe/sidekiq.rb
@@ -0,0 +1,16 @@
+module Qe
+ class Sidekiq
+ class Worker
+ include ::Sidekiq::Worker
+
+ def perform(*args)
+ Qe::Worker.perform(*args)
+ end
+ end
+
+ def self.enqueue(worker, options = {})
+ Worker.sidekiq_options :queue => worker.queue
+ Worker.perform_async(worker.name, options)
+ end
+ end
+end
11 lib/qe/testing.rb
@@ -0,0 +1,11 @@
+module Qe
+ def self.jobs
+ @jobs ||= []
+ end
+
+ class Testing
+ def self.enqueue(worker, options = {})
+ Qe.jobs << {:worker => worker, :options => options}
+ end
+ end
+end
59 lib/qe/testing/rspec.rb
@@ -0,0 +1,59 @@
+require "qe/testing"
+
+module Qe
+ module EnqueueMatcher
+ class Matcher
+ attr_reader :worker, :options
+
+ def initialize(worker)
+ @worker = worker
+ end
+
+ def with(options)
+ @options = options
+ self
+ end
+
+ def matches?(block)
+ block.call
+
+ Qe.jobs.find do |job|
+ condition = job[:worker] == worker
+ condition = condition && job[:options] == options if options
+ condition
+ end != nil
+ end
+
+ def description
+ "enqueue job for #{worker.inspect} worker"
+ end
+
+ def failure_message_for_should
+ build_message "expect #{worker.inspect} to be enqueued"
+ end
+
+ def failure_message_for_should_not
+ build_message "expect #{worker.inspect} not to be enqueued"
+ end
+
+ def build_message(base)
+ base << (options.empty? ? "" : " with #{options.inspect}")
+ end
+ end
+
+ #
+ # expect {}.to enqueue(MailerWorker).with(options)
+ #
+ def enqueue(worker)
+ Matcher.new(worker)
+ end
+ end
+end
+
+RSpec.configure do |config|
+ config.include Qe::EnqueueMatcher
+ config.before(:each) do
+ Qe.adapter = Qe::Testing
+ Qe.jobs.clear
+ end
+end
3 lib/qe/version.rb
@@ -0,0 +1,3 @@
+module Qe
+ VERSION = "0.1.0"
+end
71 lib/qe/worker.rb
@@ -0,0 +1,71 @@
+module Qe
+ module Worker
+ def self.included(base)
+ base.class_eval do
+ include InstanceMethods
+ extend ClassMethods
+ end
+ end
+
+ module InstanceMethods
+ def initialize(options)
+ @options = options
+ end
+
+ # Return options that were provided when
+ # adding job to the queue.
+ def options
+ @options
+ end
+
+ # Set before hook.
+ def before
+ end
+
+ # Set after hook.
+ def after
+ end
+
+ # Set the error hook.
+ def error(error)
+ raise error
+ end
+ end
+
+ module ClassMethods
+ # Enqueue job on given worker class.
+ def enqueue(options = {})
+ Qe.adapter.enqueue(self, options)
+ end
+
+ # Set the queue name when receiving on argument.
+ # Return queue name otherwise.
+ def queue(*args)
+ @queue = args.first unless args.empty?
+ (@queue || :default).to_s
+ end
+ end
+
+ # Find a worker by its name.
+ # If worker constant is not found, raises a +NameError+
+ # exception.
+ def self.find(name)
+ name.split("::").reduce(Object) do |const, name|
+ const.const_get(name)
+ end
+ end
+
+ # Perform the specified worker if given options.
+ def self.perform(worker_name, options)
+ find(worker_name).new(options).tap do |job|
+ begin
+ job.before
+ job.perform
+ job.after
+ rescue Exception => error
+ job.error(error)
+ end
+ end
+ end
+ end
+end
30 qe.gemspec
@@ -0,0 +1,30 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+require "qe/version"
+
+Gem::Specification.new do |s|
+ s.name = "qe"
+ s.version = Qe::VERSION
+ s.platform = Gem::Platform::RUBY
+ s.authors = ["Nando Vieira"]
+ s.email = ["fnando.vieira@gmail.com"]
+ s.homepage = "http://rubygems.org/gems/qe"
+ s.summary = "A simple interface over several background job libraries like Resque, Sidekiq and DelayedJob."
+ s.description = s.summary
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ s.require_paths = ["lib"]
+
+ s.add_development_dependency "activerecord"
+ s.add_development_dependency "awesome_print"
+ s.add_development_dependency "backburner"
+ s.add_development_dependency "delayed_job_active_record"
+ s.add_development_dependency "pry"
+ s.add_development_dependency "qu-redis"
+ s.add_development_dependency "resque"
+ s.add_development_dependency "rspec"
+ s.add_development_dependency "sidekiq"
+ s.add_development_dependency "sqlite3"
+end
39 spec/qe/beanstalk_spec.rb
@@ -0,0 +1,39 @@
+require "spec_helper"
+
+describe Qe::Beanstalk do
+ context "worker" do
+ it "performs job" do
+ Qe::Worker
+ .should_receive(:perform)
+ .with(:a, :b, :c)
+
+ Qe::Beanstalk::Worker.perform(:a, :b, :c)
+ end
+ end
+
+ context "enqueuing" do
+ let(:worker) {
+ mock("worker", :queue => "some_queue", :name => "SomeWorker")
+ }
+
+ before do
+ Backburner.stub :enqueue
+ end
+
+ it "sets queue name" do
+ Qe::Beanstalk::Worker
+ .should_receive(:queue)
+ .with("some_queue")
+
+ Qe::Beanstalk.enqueue(worker)
+ end
+
+ it "enqueues job" do
+ ::Backburner
+ .should_receive(:enqueue)
+ .with(Qe::Beanstalk::Worker, "SomeWorker", :a => 1)
+
+ Qe::Beanstalk.enqueue(worker, :a => 1)
+ end
+ end
+end
52 spec/qe/delayed_job_spec.rb
@@ -0,0 +1,52 @@
+require "spec_helper"
+
+describe Qe::DelayedJob do
+ context "worker" do
+ it "performs job" do
+ job = Qe::DelayedJob::Worker.new("SomeWorker", :a => 1)
+
+ Qe::Worker
+ .should_receive(:perform)
+ .with("SomeWorker", :a => 1)
+
+ job.perform
+ end
+ end
+
+ context "enqueuing" do
+ let(:worker) {
+ mock("worker", :queue => "some_queue", :name => "SomeWorker")
+ }
+
+ before do
+ Delayed::Job.stub :enqueue
+ end
+
+ it "sets queue name" do
+ Delayed::Job
+ .should_receive(:enqueue)
+ .with(anything, :queue => "some_queue")
+
+ Qe::DelayedJob.enqueue(worker)
+ end
+
+ it "instantiates worker" do
+ Qe::DelayedJob::Worker
+ .should_receive(:new)
+ .with("SomeWorker", :a => 1)
+
+ Qe::DelayedJob.enqueue(worker, :a => 1)
+ end
+
+ it "enqueues job" do
+ job = mock("job")
+ Qe::DelayedJob::Worker.stub :new => job
+
+ Delayed::Job
+ .should_receive(:enqueue)
+ .with(job, kind_of(Hash))
+
+ Qe::DelayedJob.enqueue(worker, :a => 1)
+ end
+ end
+end
33 spec/qe/enqueue_matcher_spec.rb
@@ -0,0 +1,33 @@
+require "spec_helper"
+
+describe Qe::EnqueueMatcher do
+ HelloWorker = Class.new { include Qe::Worker }
+
+ it "sets adapter" do
+ expect(Qe.adapter).to eql(Qe::Testing)
+ end
+
+ it "enqueues job with options" do
+ expect {
+ HelloWorker.enqueue(:message => "hello")
+ }.to enqueue(HelloWorker).with(:message => "hello")
+ end
+
+ it "enqueues job without options" do
+ expect {
+ HelloWorker.enqueue
+ }.to enqueue(HelloWorker)
+ end
+
+ it "doesn't enqueue job with options" do
+ expect {
+ HelloWorker.enqueue
+ }.not_to enqueue(HelloWorker).with(:a => 1)
+ end
+
+ it "enqueues job with options but matches it without options" do
+ expect {
+ HelloWorker.enqueue(:message => "hello")
+ }.to enqueue(HelloWorker)
+ end
+end
36 spec/qe/qu_spec.rb
@@ -0,0 +1,36 @@
+require "spec_helper"
+
+describe Qe::Qu do
+ context "worker" do
+ it "performs job" do
+ Qe::Worker
+ .should_receive(:perform)
+ .with(:a, :b, :c)
+
+ Qe::Qu::Worker.perform(:a, :b, :c)
+ end
+ end
+
+ context "enqueuing" do
+ let(:worker) {
+ mock("worker", :queue => "some_queue", :name => "SomeWorker")
+ }
+
+ before do
+ Qu.stub :enqueue
+ end
+
+ it "sets queue name" do
+ Qe::Qu.enqueue(worker)
+ expect(Qe::Qu::Worker.instance_variable_get("@queue")).to eql("some_queue")
+ end
+
+ it "enqueues job" do
+ ::Qu
+ .should_receive(:enqueue)
+ .with(Qe::Qu::Worker, "SomeWorker", :a => 1)
+
+ Qe::Qu.enqueue(worker, :a => 1)
+ end
+ end
+end
36 spec/qe/resque_spec.rb
@@ -0,0 +1,36 @@
+require "spec_helper"
+
+describe Qe::Resque do
+ context "worker" do
+ it "performs job" do
+ Qe::Worker
+ .should_receive(:perform)
+ .with(:a, :b, :c)
+
+ Qe::Resque::Worker.perform(:a, :b, :c)
+ end
+ end
+
+ context "enqueuing" do
+ let(:worker) {
+ mock("worker", :queue => "some_queue", :name => "SomeWorker")
+ }
+
+ before do
+ Resque.stub :enqueue
+ end
+
+ it "sets queue name" do
+ Qe::Resque.enqueue(worker)
+ expect(Qe::Resque::Worker.instance_variable_get("@queue")).to eql("some_queue")
+ end
+
+ it "enqueues job" do
+ ::Resque
+ .should_receive(:enqueue)
+ .with(Qe::Resque::Worker, "SomeWorker", :a => 1)
+
+ Qe::Resque.enqueue(worker, :a => 1)
+ end
+ end
+end
43 spec/qe/sidekiq_spec.rb
@@ -0,0 +1,43 @@
+require "spec_helper"
+
+describe Qe::Sidekiq do
+ context "worker" do
+ it "includes Sidekiq::Worker" do
+ expect(Qe::Sidekiq::Worker.included_modules).to include(Sidekiq::Worker)
+ end
+
+ it "performs job" do
+ Qe::Worker
+ .should_receive(:perform)
+ .with(:a, :b, :c)
+
+ Qe::Sidekiq::Worker.new.perform(:a, :b, :c)
+ end
+ end
+
+ context "enqueuing" do
+ let(:worker) {
+ mock("worker", :queue => "some_queue", :name => "SomeWorker")
+ }
+
+ before do
+ Qe::Sidekiq::Worker.stub :perform_async
+ end
+
+ it "sets queue name" do
+ Qe::Sidekiq::Worker
+ .should_receive(:sidekiq_options)
+ .with(:queue => "some_queue")
+
+ Qe::Sidekiq.enqueue(worker)
+ end
+
+ it "enqueues job" do
+ Qe::Sidekiq::Worker
+ .should_receive(:perform_async)
+ .with("SomeWorker", :a => 1)
+
+ Qe::Sidekiq.enqueue(worker, :a => 1)
+ end
+ end
+end
82 spec/qe/worker_spec.rb
@@ -0,0 +1,82 @@
+require "spec_helper"
+
+describe Qe::Worker do
+ HelloWorker = Class.new do
+ include Qe::Worker
+
+ def perform; end
+ end
+
+ it "sets queue name" do
+ HelloWorker.queue "hello"
+ expect(HelloWorker.queue).to eql("hello")
+ end
+
+ it "returns options" do
+ hello = HelloWorker.new(:a => 1)
+ expect(hello.options).to eql(:a => 1)
+ end
+
+ it "delegates #enqueue to adapter" do
+ adapter = mock("adapter")
+ adapter
+ .should_receive(:enqueue)
+ .with(HelloWorker, :a => 1)
+
+ Qe.adapter = adapter
+
+ HelloWorker.enqueue(:a => 1)
+ end
+
+ it "finds worker by its name" do
+ worker = mock("worker")
+ stub_const "Some::Weird::Worker", worker
+
+ expect(Qe::Worker.find("Some::Weird::Worker")).to eql(worker)
+ end
+
+ describe "#perform" do
+ it "finds worker by its name" do
+ Qe::Worker
+ .should_receive(:find)
+ .with("HelloWorker")
+ .and_return(HelloWorker)
+
+ Qe::Worker.perform("HelloWorker", {})
+ end
+
+ it "initializes worker with provided options" do
+ HelloWorker
+ .should_receive(:new)
+ .with(:a => 1)
+ .and_return(mock.as_null_object)
+
+ Qe::Worker.perform("HelloWorker", :a => 1)
+ end
+
+ it "performs job" do
+ worker = HelloWorker.new({})
+ HelloWorker.stub :new => worker
+ worker.should_receive(:before).ordered
+ worker.should_receive(:perform).ordered
+ worker.should_receive(:after).ordered
+
+ Qe::Worker.perform("HelloWorker", {})
+ end
+
+ it "triggers default error handler" do
+ HelloWorker.any_instance.stub(:perform).and_raise("ZOMG!")
+
+ expect {
+ Qe::Worker.perform("HelloWorker", {})
+ }.to raise_error("ZOMG!")
+ end
+
+ it "passes error object to error handler" do
+ HelloWorker.any_instance.should_receive(:error).with(kind_of(StandardError))
+ HelloWorker.any_instance.stub(:perform).and_raise("ZOMG!")
+
+ Qe::Worker.perform("HelloWorker", {})
+ end
+ end
+end
6 spec/spec_helper.rb
@@ -0,0 +1,6 @@
+require "bundler"
+Bundler.setup(:development)
+Bundler.require(:development)
+
+require "qe"
+require "qe/testing/rspec"

0 comments on commit e4f9cb3

Please sign in to comment.