qe 0.1.3
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 +4 -0
- data/.rspec +1 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +100 -0
- data/README.md +144 -0
- data/Rakefile +5 -0
- data/examples/.env +3 -0
- data/examples/Gemfile +14 -0
- data/examples/Gemfile.lock +119 -0
- data/examples/Procfile +20 -0
- data/examples/README.md +9 -0
- data/examples/Rakefile +14 -0
- data/examples/beanstalkd.ru +5 -0
- data/examples/migrate.rb +16 -0
- data/examples/redis.conf +1 -0
- data/examples/resque.ru +6 -0
- data/examples/sample.rb +11 -0
- data/examples/sidekiq.ru +10 -0
- data/examples/workers.rb +43 -0
- data/lib/qe.rb +79 -0
- data/lib/qe/beanstalk.rb +21 -0
- data/lib/qe/delayed_job.rb +18 -0
- data/lib/qe/immediate.rb +7 -0
- data/lib/qe/qu.rb +21 -0
- data/lib/qe/resque.rb +19 -0
- data/lib/qe/sidekiq.rb +21 -0
- data/lib/qe/testing.rb +11 -0
- data/lib/qe/testing/rspec.rb +59 -0
- data/lib/qe/version.rb +3 -0
- data/lib/qe/worker.rb +71 -0
- data/qe.gemspec +31 -0
- data/spec/qe/beanstalk_spec.rb +45 -0
- data/spec/qe/delayed_job_spec.rb +59 -0
- data/spec/qe/enqueue_matcher_spec.rb +33 -0
- data/spec/qe/immediate_spec.rb +26 -0
- data/spec/qe/qu_spec.rb +42 -0
- data/spec/qe/resque_spec.rb +42 -0
- data/spec/qe/sidekiq_spec.rb +49 -0
- data/spec/qe/worker_spec.rb +82 -0
- data/spec/spec_helper.rb +12 -0
- metadata +247 -0
data/examples/migrate.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "./workers"
|
2
|
+
|
3
|
+
ActiveRecord::Schema.define(:version => 0) do
|
4
|
+
create_table :delayed_jobs, :force => true do |table|
|
5
|
+
table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue
|
6
|
+
table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually.
|
7
|
+
table.text :handler # YAML-encoded string of the object that will do work
|
8
|
+
table.text :last_error # reason for last failure (See Note below)
|
9
|
+
table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.
|
10
|
+
table.datetime :locked_at # Set when a client is working on this object
|
11
|
+
table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
|
12
|
+
table.string :locked_by # Who is working on this object (if locked)
|
13
|
+
table.string :queue # The name of the queue this job is in
|
14
|
+
table.timestamps
|
15
|
+
end
|
16
|
+
end
|
data/examples/redis.conf
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
loglevel notice
|
data/examples/resque.ru
ADDED
data/examples/sample.rb
ADDED
data/examples/sidekiq.ru
ADDED
data/examples/workers.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require "bundler"
|
2
|
+
Bundler.setup(:default)
|
3
|
+
Bundler.require
|
4
|
+
|
5
|
+
$:.unshift File.expand_path("../../lib", __FILE__)
|
6
|
+
require "qe"
|
7
|
+
|
8
|
+
$stdout.sync = true
|
9
|
+
|
10
|
+
Qe.adapter = Qe::Sidekiq
|
11
|
+
# Qe.adapter = Qe::Beanstalk
|
12
|
+
# Qe.adapter = Qe::Resque
|
13
|
+
# Qe.adapter = Qe::DelayedJob
|
14
|
+
# Qe.adapter = Qe::Qu
|
15
|
+
# Qe.adapter = Qe::Testing
|
16
|
+
|
17
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "jobs.sqlite3")
|
18
|
+
|
19
|
+
class ClockWorker
|
20
|
+
include Qe::Worker
|
21
|
+
|
22
|
+
def perform
|
23
|
+
puts "=> Time: #{Time.now}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class MailerWorker
|
28
|
+
include Qe::Worker
|
29
|
+
queue :mail
|
30
|
+
|
31
|
+
def before
|
32
|
+
puts "=> Running before"
|
33
|
+
end
|
34
|
+
|
35
|
+
def perform
|
36
|
+
puts "=> Performing"
|
37
|
+
puts "=> Options: #{options.inspect}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def after
|
41
|
+
puts "=> Running after"
|
42
|
+
end
|
43
|
+
end
|
data/lib/qe.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require "qe/immediate"
|
2
|
+
require "qe/version"
|
3
|
+
require "qe/worker"
|
4
|
+
|
5
|
+
# In this wild world where a new asynchronous job processing
|
6
|
+
# library is released every once in a while, Qe tries to keep a unified
|
7
|
+
# interface that works with most famous libraries:
|
8
|
+
#
|
9
|
+
# # Sidekiq
|
10
|
+
# # Resque
|
11
|
+
# # DelayedJob
|
12
|
+
# # Qu
|
13
|
+
# # Beanstalk/Backburner
|
14
|
+
#
|
15
|
+
# See an example:
|
16
|
+
#
|
17
|
+
# You can choose an adapter:
|
18
|
+
#
|
19
|
+
# Qe.adapter = Qe::Sidekiq
|
20
|
+
# Qe.adapter = Qe::Resque
|
21
|
+
# Qe.adapter = Qe::Qu
|
22
|
+
# Qe.adapter = Qe::DelayedJob
|
23
|
+
# Qe.adapter = Qe::Beanstalk
|
24
|
+
#
|
25
|
+
# Create our worker that will send e-mails through +ActionMailer+.
|
26
|
+
#
|
27
|
+
# class MailerWorker
|
28
|
+
# include Qe::Worker
|
29
|
+
#
|
30
|
+
# def perform
|
31
|
+
# Mailer.public_send(options[:mail], options).deliver
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# Define our +Mailer+ class.
|
36
|
+
#
|
37
|
+
# class Mailer < ActionMailer::Base
|
38
|
+
# def welcome(options)
|
39
|
+
# @options = options
|
40
|
+
# mail :to => options[:email]
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# Enqueue a job to be processed asynchronously.
|
45
|
+
#
|
46
|
+
# MailerWorker.enqueue({
|
47
|
+
# :mail => :welcome,
|
48
|
+
# :email => "john@example.org",
|
49
|
+
# :name => "John Doe"
|
50
|
+
# })
|
51
|
+
#
|
52
|
+
# == Testing support
|
53
|
+
#
|
54
|
+
# Qe comes with testing support. Just require the <tt>qe/testing.rb</tt> file
|
55
|
+
# and a fake queuing adapter will be used. All enqueued jobs will be stored
|
56
|
+
# at <tt>Qe.jobs</tt>. Note that this method is only available on testing mode.
|
57
|
+
#
|
58
|
+
# require "qe/testing"
|
59
|
+
# Qe.adapter = Qe::Testing
|
60
|
+
#
|
61
|
+
# If you're using RSpec, you can require the <tt>qe/testing/rspec.rb</tt> file
|
62
|
+
# instead. This will reset <tt>Qe.jobs</tt> before every spec and will add a
|
63
|
+
# +enqueue+ matcher.
|
64
|
+
#
|
65
|
+
# require "qe/testing/rspec"
|
66
|
+
#
|
67
|
+
# describe "Enqueuing a job" do
|
68
|
+
# it "enqueues job" do
|
69
|
+
# expect {
|
70
|
+
# # do something
|
71
|
+
# }.to enqueue(MailerWorker).with(:email => "john@example.org")
|
72
|
+
# end
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
module Qe
|
76
|
+
class << self
|
77
|
+
attr_accessor :adapter
|
78
|
+
end
|
79
|
+
end
|
data/lib/qe/beanstalk.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "qe"
|
2
|
+
require "backburner"
|
3
|
+
|
4
|
+
module Qe
|
5
|
+
class Beanstalk
|
6
|
+
class Worker
|
7
|
+
include Backburner::Queue
|
8
|
+
|
9
|
+
def self.perform(*args)
|
10
|
+
Qe::Worker.perform(*args)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.enqueue(worker, options = {})
|
15
|
+
Worker.queue worker.queue
|
16
|
+
Backburner.enqueue Worker, worker.name, options
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
self.adapter = Beanstalk
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "qe"
|
2
|
+
require "delayed_job"
|
3
|
+
|
4
|
+
module Qe
|
5
|
+
class DelayedJob
|
6
|
+
class Worker < Struct.new(:worker_name, :options)
|
7
|
+
def perform
|
8
|
+
Qe::Worker.perform(worker_name, options)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.enqueue(worker, options = {})
|
13
|
+
Delayed::Job.enqueue Worker.new(worker.name, options), :queue => worker.queue
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
self.adapter = DelayedJob
|
18
|
+
end
|
data/lib/qe/immediate.rb
ADDED
data/lib/qe/qu.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "qe"
|
2
|
+
require "qu"
|
3
|
+
|
4
|
+
module Qe
|
5
|
+
class Qu
|
6
|
+
class Worker
|
7
|
+
include ::Sidekiq::Worker
|
8
|
+
|
9
|
+
def self.perform(*args)
|
10
|
+
Qe::Worker.perform(*args)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.enqueue(worker, options = {})
|
15
|
+
Worker.instance_variable_set("@queue", worker.queue)
|
16
|
+
::Qu.enqueue Worker, worker.name, options
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
self.adapter = Qu
|
21
|
+
end
|
data/lib/qe/resque.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "qe"
|
2
|
+
require "resque"
|
3
|
+
|
4
|
+
module Qe
|
5
|
+
class Resque
|
6
|
+
class Worker
|
7
|
+
def self.perform(*args)
|
8
|
+
Qe::Worker.perform(*args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.enqueue(worker, options = {})
|
13
|
+
Worker.instance_variable_set "@queue", worker.queue
|
14
|
+
::Resque.enqueue Worker, worker.name, options
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
self.adapter = Resque
|
19
|
+
end
|
data/lib/qe/sidekiq.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "qe"
|
2
|
+
require "sidekiq"
|
3
|
+
|
4
|
+
module Qe
|
5
|
+
class Sidekiq
|
6
|
+
class Worker
|
7
|
+
include ::Sidekiq::Worker
|
8
|
+
|
9
|
+
def perform(*args)
|
10
|
+
Qe::Worker.perform(*args)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.enqueue(worker, options = {})
|
15
|
+
Worker.sidekiq_options :queue => worker.queue
|
16
|
+
Worker.perform_async(worker.name, options)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
self.adapter = Sidekiq
|
21
|
+
end
|
data/lib/qe/testing.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require "qe/testing"
|
2
|
+
|
3
|
+
module Qe
|
4
|
+
module EnqueueMatcher
|
5
|
+
class Matcher
|
6
|
+
attr_reader :worker, :options
|
7
|
+
|
8
|
+
def initialize(worker)
|
9
|
+
@worker = worker
|
10
|
+
end
|
11
|
+
|
12
|
+
def with(options)
|
13
|
+
@options = options
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def matches?(block)
|
18
|
+
block.call
|
19
|
+
|
20
|
+
Qe.jobs.find do |job|
|
21
|
+
condition = job[:worker] == worker
|
22
|
+
condition = condition && job[:options] == options if options
|
23
|
+
condition
|
24
|
+
end != nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def description
|
28
|
+
"enqueue job for #{worker.inspect} worker"
|
29
|
+
end
|
30
|
+
|
31
|
+
def failure_message_for_should
|
32
|
+
build_message "expect #{worker.inspect} to be enqueued"
|
33
|
+
end
|
34
|
+
|
35
|
+
def failure_message_for_should_not
|
36
|
+
build_message "expect #{worker.inspect} not to be enqueued"
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_message(base)
|
40
|
+
base << (options.empty? ? "" : " with #{options.inspect}")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# expect {}.to enqueue(MailerWorker).with(options)
|
46
|
+
#
|
47
|
+
def enqueue(worker)
|
48
|
+
Matcher.new(worker)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
RSpec.configure do |config|
|
54
|
+
config.include Qe::EnqueueMatcher
|
55
|
+
config.before(:each) do
|
56
|
+
Qe.adapter = Qe::Testing
|
57
|
+
Qe.jobs.clear
|
58
|
+
end
|
59
|
+
end
|
data/lib/qe/version.rb
ADDED
data/lib/qe/worker.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module Qe
|
2
|
+
module Worker
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
include InstanceMethods
|
6
|
+
extend ClassMethods
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
def initialize(options)
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return options that were provided when
|
16
|
+
# adding job to the queue.
|
17
|
+
def options
|
18
|
+
@options
|
19
|
+
end
|
20
|
+
|
21
|
+
# Set before hook.
|
22
|
+
def before
|
23
|
+
end
|
24
|
+
|
25
|
+
# Set after hook.
|
26
|
+
def after
|
27
|
+
end
|
28
|
+
|
29
|
+
# Set the error hook.
|
30
|
+
def error(error)
|
31
|
+
raise error
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module ClassMethods
|
36
|
+
# Enqueue job on given worker class.
|
37
|
+
def enqueue(options = {})
|
38
|
+
Qe.adapter.enqueue(self, options)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Set the queue name when receiving on argument.
|
42
|
+
# Return queue name otherwise.
|
43
|
+
def queue(*args)
|
44
|
+
@queue = args.first unless args.empty?
|
45
|
+
(@queue || :default).to_s
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Find a worker by its name.
|
50
|
+
# If worker constant is not found, raises a +NameError+
|
51
|
+
# exception.
|
52
|
+
def self.find(name)
|
53
|
+
name.split("::").reduce(Object) do |const, name|
|
54
|
+
const.const_get(name)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Perform the specified worker if given options.
|
59
|
+
def self.perform(worker_name, options)
|
60
|
+
find(worker_name).new(options).tap do |job|
|
61
|
+
begin
|
62
|
+
job.before
|
63
|
+
job.perform
|
64
|
+
job.after
|
65
|
+
rescue Exception => error
|
66
|
+
job.error(error)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|