afterparty 0.1.1 → 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.
- checksums.yaml +6 -6
- data/.gitignore +1 -0
- data/Gemfile +4 -1
- data/README.md +2 -0
- data/config/routes.rb +0 -4
- data/lib/afterparty.rb +1 -25
- data/lib/afterparty/afterparty_job.rb +1 -1
- data/lib/afterparty/job_container.rb +0 -6
- data/lib/afterparty/jobs.rb +21 -25
- data/lib/afterparty/queue.rb +8 -26
- data/lib/afterparty/queue_helpers.rb +1 -27
- data/lib/afterparty/version.rb +1 -1
- data/lib/afterparty/worker.rb +1 -2
- data/lib/generators/afterparty_generator.rb +1 -0
- data/lib/generators/templates/initializer.rb +1 -1
- data/spec/afterparty_spec.rb +34 -0
- data/spec/generators/afterparty_generator_spec.rb +5 -0
- data/spec/helper_jobs_spec.rb +57 -0
- data/spec/job_container_spec.rb +22 -0
- data/spec/queue_helpers_spec.rb +35 -0
- data/spec/queue_spec.rb +12 -0
- data/spec/spec_helper.rb +13 -1
- data/spec/worker_spec.rb +48 -0
- metadata +19 -14
- data/afterparty_test.sqlite3 +0 -0
- data/lib/afterparty/threaded_queue_consumer.rb +0 -58
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
5
|
-
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f2a6ac0e585c1d786ce00d1cd91863f5bead9af4
|
4
|
+
data.tar.gz: c276f1d73253a5ab5189689ce72cc39e8e1db8fa
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 24e8c2dbe66ebee04fab02fdfff9e9430588b1df9d23b71ec1e76e6c6b9002ae80afb5679f3f5ad91252c1a3710758a070b9afaa669866fcc964529c828f4a0a
|
7
|
+
data.tar.gz: 1c7cff779dd6172e921ccfd8f7015bc84e6a720709356e3bbc363089112f3116f659c7b670cc43f858b70b719d16d3675872b37c57103c1743c2957b0f40690d
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# Afterparty
|
2
2
|
|
3
3
|
[](https://travis-ci.org/hstove/afterparty)
|
4
|
+
[](https://codeclimate.com/github/hstove/afterparty)
|
5
|
+
[](https://coveralls.io/r/hstove/afterparty)
|
4
6
|
|
5
7
|
A Rails 3 & 4 compatible queue with support for executing jobs in the future and persistence with ActiveRecord.
|
6
8
|
|
data/config/routes.rb
CHANGED
@@ -2,8 +2,4 @@ Afterparty::Engine.routes.draw do
|
|
2
2
|
get "/" => "afterparty/dashboard#index", as: :dashboard
|
3
3
|
get "/run" => "afterparty/dashboard#run", as: :run_job
|
4
4
|
get "/delete" => "afterparty/dashboard#destroy", as: :destroy_job
|
5
|
-
end
|
6
|
-
|
7
|
-
Rails.application.routes.draw do
|
8
|
-
mount Afterparty::Engine, at: "afterparty", as: "afterparty_engine"
|
9
5
|
end
|
data/lib/afterparty.rb
CHANGED
@@ -5,21 +5,6 @@ Dir[File.expand_path('../afterparty/*', __FILE__)].each { |f| require f }
|
|
5
5
|
|
6
6
|
|
7
7
|
module Afterparty
|
8
|
-
def self.clear namespace=:default
|
9
|
-
redis_call namespace, :del
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.redis_call namespace, command, *args
|
13
|
-
@@redis.send(command, redis_queue_name(namespace), *args)
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.redis_queue_name namespace=:default
|
17
|
-
"afterparty_#{namespace}_queue"
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.queues
|
21
|
-
# @@redis.smembers "afterparty_queues"
|
22
|
-
end
|
23
8
|
|
24
9
|
# return timestamp of :execute_at or current time
|
25
10
|
def self.queue_time job
|
@@ -30,30 +15,21 @@ module Afterparty
|
|
30
15
|
def self.job_valid? job
|
31
16
|
job.respond_to?(:execute_at) && !job.execute_at.nil?
|
32
17
|
end
|
33
|
-
|
18
|
+
|
34
19
|
def self.load(raw)
|
35
20
|
begin
|
36
|
-
# postgres converts it to utf-8
|
37
|
-
# raw.encode!("ascii")
|
38
21
|
begin
|
39
|
-
# job = Marshal.load(raw)
|
40
|
-
# job = Marshal.load(job) if String === job
|
41
22
|
return YAML.load(raw)
|
42
23
|
rescue ArgumentError => e
|
43
24
|
# lots of yaml load errors are because something that hasn't been
|
44
25
|
# required. recursively require on these errors
|
45
26
|
# Invoke the autoloader and try again if object's class is undefined
|
46
27
|
if e.message =~ /undefined class\/module (.*)$/
|
47
|
-
# puts "autoloading #{$1}"
|
48
28
|
$1.constantize rescue return nil
|
49
29
|
end
|
50
30
|
return load(raw)
|
51
31
|
end
|
52
32
|
rescue Exception => e
|
53
|
-
puts e
|
54
|
-
puts "Exception while unmarshaling a job:"
|
55
|
-
puts e.message
|
56
|
-
puts e.backtrace
|
57
33
|
return nil
|
58
34
|
end
|
59
35
|
end
|
@@ -5,7 +5,7 @@ class AfterpartyJob < ::ActiveRecord::Base
|
|
5
5
|
|
6
6
|
validates_presence_of :job_dump, :execute_at, :queue
|
7
7
|
|
8
|
-
scope :namespaced, lambda { |name| where(queue: name) }
|
8
|
+
scope :namespaced, lambda { |name| where(queue: name) if name }
|
9
9
|
scope :incomplete, -> { where(completed: false).order("execute_at") }
|
10
10
|
scope :valid, -> { incomplete.where(execute_at: 10.years.ago..DateTime.now) }
|
11
11
|
scope :completed, -> { where(completed: true).order("execute_at desc") }
|
@@ -13,7 +13,6 @@ module Afterparty
|
|
13
13
|
@job_id = job.afterparty_job_id if @job.respond_to? :afterparty_job_id
|
14
14
|
@queue_name = job.afterparty_queue if @job.respond_to? :afterparty_queue
|
15
15
|
rescue Exception => e
|
16
|
-
ap "Error during load: #{e.message}"
|
17
16
|
@job = nil
|
18
17
|
end
|
19
18
|
@raw = _raw
|
@@ -27,10 +26,5 @@ module Afterparty
|
|
27
26
|
nil
|
28
27
|
end
|
29
28
|
end
|
30
|
-
|
31
|
-
def raw_string
|
32
|
-
ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
|
33
|
-
ic.iconv(@raw.dup + ' ')[0..-2]
|
34
|
-
end
|
35
29
|
end
|
36
30
|
end
|
data/lib/afterparty/jobs.rb
CHANGED
@@ -1,41 +1,37 @@
|
|
1
1
|
module Afterparty
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
|
3
|
+
module JobDescribers
|
4
|
+
attr_accessor :object, :method, :args, :execute_at
|
5
|
+
|
6
|
+
def initialize object, method, *args
|
7
|
+
@object = object
|
7
8
|
@method = method
|
8
9
|
@args = args
|
9
10
|
end
|
10
|
-
|
11
|
-
def run
|
12
|
-
@mail = @clazz.send @method, *@args
|
13
|
-
@mail.deliver
|
14
|
-
end
|
15
11
|
|
16
12
|
def description
|
17
|
-
desc = "
|
18
|
-
desc << "Method: #{(@method || nil)}."
|
19
|
-
desc << "Args: #{(@args || nil)}"
|
13
|
+
desc = "Object: #{(@object || "nil")}."
|
14
|
+
desc << "Method: #{(@method || "nil")}."
|
15
|
+
desc << "Args: #{(@args || "nil")}"
|
20
16
|
end
|
17
|
+
alias_method :inspect, :description
|
21
18
|
end
|
22
19
|
|
23
|
-
class
|
24
|
-
|
25
|
-
def initialize object, method, *args
|
26
|
-
@object = object
|
27
|
-
@method = method
|
28
|
-
@args = args
|
29
|
-
end
|
20
|
+
class MailerJob
|
21
|
+
include JobDescribers
|
30
22
|
|
31
23
|
def run
|
32
|
-
@object.send
|
24
|
+
@mail = @object.send @method, *@args
|
25
|
+
@mail.deliver
|
33
26
|
end
|
27
|
+
end
|
34
28
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
29
|
+
class BasicJob
|
30
|
+
include JobDescribers
|
31
|
+
|
32
|
+
def run
|
33
|
+
@object.send(@method, *@args)
|
39
34
|
end
|
40
35
|
end
|
36
|
+
|
41
37
|
end
|
data/lib/afterparty/queue.rb
CHANGED
@@ -4,38 +4,20 @@ module Afterparty
|
|
4
4
|
include Afterparty::QueueHelpers
|
5
5
|
|
6
6
|
def push job
|
7
|
-
|
8
|
-
|
9
|
-
queue_name = @temp_namespace || @options[:namespace]
|
10
|
-
AfterpartyJob.make_with_job job, queue_name
|
11
|
-
# end
|
7
|
+
return nil if job.nil?
|
8
|
+
AfterpartyJob.make_with_job job, @options[:namespace]
|
12
9
|
end
|
13
10
|
alias :<< :push
|
14
11
|
alias :eng :push
|
15
12
|
|
16
13
|
def pop
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
return _job
|
22
|
-
end
|
23
|
-
sleep(@options[:sleep])
|
14
|
+
while true do
|
15
|
+
unless (_job = next_valid_job).nil?
|
16
|
+
_job.save
|
17
|
+
return _job
|
24
18
|
end
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
class TestQueue < Queue
|
30
|
-
attr_accessor :completed_jobs
|
31
|
-
|
32
|
-
def initialize opts={}
|
33
|
-
super
|
34
|
-
@completed_jobs = []
|
35
|
-
@exceptions = []
|
36
|
-
end
|
37
|
-
def handle_exception job, exception
|
38
|
-
@exceptions << [job, exception]
|
19
|
+
sleep(@options[:sleep])
|
20
|
+
end
|
39
21
|
end
|
40
22
|
end
|
41
23
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Afterparty
|
2
2
|
module QueueHelpers
|
3
|
+
attr_reader :options
|
3
4
|
|
4
5
|
def initialize options = {}
|
5
6
|
@options = options
|
@@ -9,12 +10,7 @@ module Afterparty
|
|
9
10
|
self
|
10
11
|
end
|
11
12
|
|
12
|
-
def [] namespace
|
13
|
-
@temp_namespace = namespace
|
14
|
-
end
|
15
|
-
|
16
13
|
def clear
|
17
|
-
# redis_call :del
|
18
14
|
AfterpartyJob.namespaced(@options[:namespace]).destroy_all
|
19
15
|
end
|
20
16
|
|
@@ -22,10 +18,6 @@ module Afterparty
|
|
22
18
|
AfterpartyJob.namespaced(@options[:namespace]).incomplete
|
23
19
|
end
|
24
20
|
|
25
|
-
def jobs_with_scores
|
26
|
-
hash_from_scores(redis_call(:zrange, 0, -1, {withscores: true}))
|
27
|
-
end
|
28
|
-
|
29
21
|
def valid_jobs
|
30
22
|
AfterpartyJob.namespaced(@options[:namespace]).valid
|
31
23
|
end
|
@@ -50,11 +42,6 @@ module Afterparty
|
|
50
42
|
AfterpartyJob.namespaced(@options[:namespace]).completed
|
51
43
|
end
|
52
44
|
|
53
|
-
def completed_with_scores
|
54
|
-
@temp_namespace = "completed"
|
55
|
-
hash_from_scores(redis_call(:zrange, -20, -1, withscores: true)).reverse
|
56
|
-
end
|
57
|
-
|
58
45
|
def run(job)
|
59
46
|
real_job = job.reify
|
60
47
|
if real_job
|
@@ -92,18 +79,5 @@ module Afterparty
|
|
92
79
|
raise 'Must set queue.config_login to use dashboard' if @login_block.nil?
|
93
80
|
@login_block.call(username, password)
|
94
81
|
end
|
95
|
-
|
96
|
-
|
97
|
-
private
|
98
|
-
|
99
|
-
# returns true if job has an :execute_at value
|
100
|
-
def job_valid? job
|
101
|
-
job.respond_to?(:execute_at) && !job.execute_at.nil?
|
102
|
-
end
|
103
|
-
|
104
|
-
# return timestamp of :execute_at or current time
|
105
|
-
def queue_time job
|
106
|
-
time = job_valid?(job) ? job.execute_at.to_i : Time.now.to_i
|
107
|
-
end
|
108
82
|
end
|
109
83
|
end
|
data/lib/afterparty/version.rb
CHANGED
data/lib/afterparty/worker.rb
CHANGED
@@ -4,7 +4,6 @@ module Afterparty
|
|
4
4
|
|
5
5
|
def consume
|
6
6
|
@stopped = false
|
7
|
-
# puts "starting worker with namespace [#{@options[:namespace]}]."
|
8
7
|
@thread = Thread.new {
|
9
8
|
consume_sync
|
10
9
|
}
|
@@ -21,7 +20,7 @@ module Afterparty
|
|
21
20
|
while !@stopped
|
22
21
|
job = next_valid_job
|
23
22
|
if job
|
24
|
-
puts "Executing job: #{job.id}"
|
23
|
+
puts "Executing job: #{job.id}" if job.respond_to? :id
|
25
24
|
run job
|
26
25
|
else
|
27
26
|
sleep(@options[:sleep])
|
@@ -5,5 +5,6 @@ class AfterpartyGenerator < Rails::Generators::Base
|
|
5
5
|
def install
|
6
6
|
copy_file "jobs_migration.rb", "db/migrate/#{Time.now.strftime('%Y%m%d%H%M%S')}_create_afterparty_jobs.rb"
|
7
7
|
copy_file "initializer.rb", "config/initializers/afterparty.rb"
|
8
|
+
route "mount Afterparty::Engine, at: \"afterparty\", as: \"afterparty_engine\""
|
8
9
|
end
|
9
10
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Afterparty do
|
4
|
+
describe "#load" do
|
5
|
+
it "should constantize any missing Classes while loading" do
|
6
|
+
obj = TestJob.new
|
7
|
+
dump = YAML.dump(obj)
|
8
|
+
YAML.stub(:load) do
|
9
|
+
YAML.unstub(:load)
|
10
|
+
raise ArgumentError, "undefined class/module TestJob"
|
11
|
+
end
|
12
|
+
String.any_instance.should_receive(:constantize).once
|
13
|
+
Afterparty.load(dump).should_not be_nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it "returns nil when an exception is raised in YAML.load" do
|
17
|
+
YAML.stub(:load){ raise Exception }
|
18
|
+
Afterparty.load("anything").should be_nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#job_valid?" do
|
23
|
+
it "returns false unless job responds to #execute_at" do
|
24
|
+
job = {}
|
25
|
+
Afterparty.job_valid?(job).should be_false
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns false if #execute_at returns nil" do
|
29
|
+
job = TestJob.new
|
30
|
+
job.execute_at = nil
|
31
|
+
Afterparty.job_valid?(job).should be_false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,10 +1,15 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
describe :afterparty do
|
3
|
+
within_source_root do
|
4
|
+
FileUtils.mkdir_p "config"
|
5
|
+
FileUtils.touch "config/routes.rb"
|
6
|
+
end
|
3
7
|
it "works" do
|
4
8
|
subject.should generate(:copy_file, "jobs_migration.rb")
|
5
9
|
subject.should generate("config/initializers/afterparty.rb") do |content|
|
6
10
|
content.should include("Afterparty::Queue.new")
|
7
11
|
content.should include("queue.config_login do |username, password|")
|
8
12
|
end
|
13
|
+
subject.should generate(:route, "mount Afterparty::Engine, at: \"afterparty\", as: \"afterparty_engine\"")
|
9
14
|
end
|
10
15
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Afterparty::MailerJob do
|
4
|
+
class Mailer
|
5
|
+
def self.mail
|
6
|
+
self.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def deliver
|
10
|
+
true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:job){ Afterparty::MailerJob.new Mailer, :mail }
|
15
|
+
|
16
|
+
it "initializes and sets the right attributes" do
|
17
|
+
job.args.should eq([])
|
18
|
+
job.object.should eq(Mailer)
|
19
|
+
job.method.should eq(:mail)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "description" do
|
23
|
+
it "describes correctly" do
|
24
|
+
description = "Object: Mailer.Method: mail.Args: []"
|
25
|
+
job.description.should eq(description)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "calls #delivers on the mailer in when ran" do
|
30
|
+
Mailer.any_instance.should_receive(:deliver).once
|
31
|
+
job.run
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
describe Afterparty::BasicJob do
|
37
|
+
class Person
|
38
|
+
def say_hello name
|
39
|
+
"hello #{name}!"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
ran = false
|
44
|
+
let(:job){ Afterparty::BasicJob.new(Person.new, :say_hello, "hank") }
|
45
|
+
|
46
|
+
it "initializes and sets the right attributes" do
|
47
|
+
job.args.should eq(["hank"])
|
48
|
+
job.object.should be_a(Person)
|
49
|
+
job.method.should eq(:say_hello)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "sends the given method to @object with @args" do
|
53
|
+
Person.any_instance.should_receive(:say_hello).with("hank").once
|
54
|
+
job.run
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Afterparty::JobContainer do
|
4
|
+
it "initializes correctly" do
|
5
|
+
job = TestJob.new
|
6
|
+
raw = YAML.dump(job)
|
7
|
+
container = Afterparty::JobContainer.new raw, Time.now.to_i
|
8
|
+
container.job.should be_a(TestJob)
|
9
|
+
|
10
|
+
container.job_class.should eq(TestJob)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "sets job to nil if an error is thrown in YAML.load" do
|
14
|
+
Afterparty.stub(:load){ raise }
|
15
|
+
job = TestJob.new
|
16
|
+
raw = YAML.dump(job)
|
17
|
+
container = Afterparty::JobContainer.new raw, Time.now.to_i
|
18
|
+
container.job.should be_nil
|
19
|
+
|
20
|
+
container.job_class.should be_nil
|
21
|
+
end
|
22
|
+
end
|
data/spec/queue_helpers_spec.rb
CHANGED
@@ -53,4 +53,39 @@ describe Afterparty::QueueHelpers do
|
|
53
53
|
@q.authenticate("userbad","pass").should == false
|
54
54
|
@q.authenticate("user","passbad").should == false
|
55
55
|
end
|
56
|
+
|
57
|
+
it "saves errors if a job can't reify" do
|
58
|
+
job = AfterpartyJob.make_with_job test_job
|
59
|
+
job.stub(:reify) { nil }
|
60
|
+
@q.run job
|
61
|
+
job.has_error.should be_true
|
62
|
+
job.error_message.should eq("Error marshaling job.")
|
63
|
+
end
|
64
|
+
|
65
|
+
it "handles exceptions when running a job" do
|
66
|
+
job = AfterpartyJob.make_with_job test_job
|
67
|
+
job.stub(:reify).and_raise(Exception, "message")
|
68
|
+
@q.should_receive(:handle_exception)
|
69
|
+
@q.run job
|
70
|
+
end
|
71
|
+
|
72
|
+
it "saves error data about a job when handling exceptions" do
|
73
|
+
job = AfterpartyJob.make_with_job test_job
|
74
|
+
job.stub(:reify).and_raise(Exception, "message")
|
75
|
+
@q.options[:logger].should_receive(:error)
|
76
|
+
@q.run job
|
77
|
+
job.completed.should be_true
|
78
|
+
job.completed_at.should be < DateTime.now
|
79
|
+
job.has_error.should be_true
|
80
|
+
job.error_message.should eq("message")
|
81
|
+
job.error_backtrace.should_not be_nil
|
82
|
+
end
|
83
|
+
|
84
|
+
it "returns the last completed job" do
|
85
|
+
job = AfterpartyJob.make_with_job test_job
|
86
|
+
job.completed = true
|
87
|
+
job.save
|
88
|
+
@q.last_completed.should eq(job)
|
89
|
+
@q.completed.should eq([job])
|
90
|
+
end
|
56
91
|
end
|
data/spec/queue_spec.rb
CHANGED
@@ -46,4 +46,16 @@ describe Afterparty::Queue do
|
|
46
46
|
(inner = wrapper.reify).name.should == "testy"
|
47
47
|
wrapper.queue.should == "tester"
|
48
48
|
end
|
49
|
+
|
50
|
+
it "should sleep if no jobs are valid on #pop" do
|
51
|
+
sleep_called = false
|
52
|
+
job = nil
|
53
|
+
@q.clear
|
54
|
+
@q.stub(:sleep) {
|
55
|
+
sleep_called = true
|
56
|
+
job = AfterpartyJob.make_with_job test_job
|
57
|
+
}
|
58
|
+
@q.pop
|
59
|
+
sleep_called.should be_true
|
60
|
+
end
|
49
61
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require 'coveralls'
|
3
|
+
|
4
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
5
|
+
SimpleCov::Formatter::HTMLFormatter,
|
6
|
+
Coveralls::SimpleCov::Formatter
|
7
|
+
]
|
8
|
+
SimpleCov.start do
|
9
|
+
add_filter "/spec/"
|
10
|
+
end
|
11
|
+
|
1
12
|
require 'rubygems'
|
2
13
|
require 'bundler/setup'
|
3
14
|
require 'awesome_print'
|
@@ -5,6 +16,7 @@ require 'redis'
|
|
5
16
|
require 'afterparty' # and any other gems you need
|
6
17
|
require 'helpers'
|
7
18
|
require 'genspec'
|
19
|
+
require 'fileutils'
|
8
20
|
|
9
21
|
RSpec.configure do |config|
|
10
22
|
# some (optional) config here
|
@@ -22,4 +34,4 @@ def clean_database!
|
|
22
34
|
ActiveRecord::Base.connection.execute "DELETE FROM afterparty_jobs"
|
23
35
|
end
|
24
36
|
|
25
|
-
clean_database!
|
37
|
+
clean_database!
|
data/spec/worker_spec.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Afterparty::Worker do
|
4
|
+
let(:worker) { Afterparty::Worker.new({sleep: 0.25}) }
|
5
|
+
after do
|
6
|
+
worker.stop
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "consume" do
|
10
|
+
it "calls a new thread" do
|
11
|
+
Thread.should_receive(:new).once
|
12
|
+
worker.consume
|
13
|
+
end
|
14
|
+
|
15
|
+
it "calls consume_sync on itself" do
|
16
|
+
worker.should_receive(:consume_sync).once
|
17
|
+
worker.consume
|
18
|
+
sleep 0.25
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "consume_sync" do
|
23
|
+
it "runs the next_valid_job" do
|
24
|
+
worker.instance_variable_set "@stopped", false
|
25
|
+
job = TestJob.new
|
26
|
+
worker.stub(:next_valid_job) {
|
27
|
+
worker.unstub(:next_valid_job)
|
28
|
+
worker.instance_variable_set "@stopped", true
|
29
|
+
job
|
30
|
+
}
|
31
|
+
worker.should_receive(:run).with(job)
|
32
|
+
worker.consume_sync
|
33
|
+
worker.stop
|
34
|
+
end
|
35
|
+
|
36
|
+
it "sleeps if there are no valid jobs" do
|
37
|
+
worker.instance_variable_set "@stopped", false
|
38
|
+
worker.stub(:next_valid_job) {
|
39
|
+
worker.unstub(:next_valid_job)
|
40
|
+
worker.instance_variable_set "@stopped", true
|
41
|
+
nil
|
42
|
+
}
|
43
|
+
worker.should_receive(:sleep).with(0.25)
|
44
|
+
worker.consume_sync
|
45
|
+
worker.stop
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
metadata
CHANGED
@@ -1,41 +1,41 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: afterparty
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hank Stoever
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: iconv
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - '>='
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - '>='
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
@@ -56,14 +56,14 @@ dependencies:
|
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - '>='
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
description: Rails 4 compatible queue with support for executing jobs later.
|
@@ -82,7 +82,6 @@ files:
|
|
82
82
|
- README.md
|
83
83
|
- Rakefile
|
84
84
|
- afterparty.gemspec
|
85
|
-
- afterparty_test.sqlite3
|
86
85
|
- app/assets/javascripts/afterparty.js.coffee
|
87
86
|
- app/assets/stylesheets/afterparty.css.sass
|
88
87
|
- app/controllers/afterparty/dashboard_controller.rb
|
@@ -97,7 +96,6 @@ files:
|
|
97
96
|
- lib/afterparty/jobs.rb
|
98
97
|
- lib/afterparty/queue.rb
|
99
98
|
- lib/afterparty/queue_helpers.rb
|
100
|
-
- lib/afterparty/threaded_queue_consumer.rb
|
101
99
|
- lib/afterparty/version.rb
|
102
100
|
- lib/afterparty/worker.rb
|
103
101
|
- lib/generators/afterparty_generator.rb
|
@@ -105,14 +103,18 @@ files:
|
|
105
103
|
- lib/generators/templates/jobs_migration.rb
|
106
104
|
- lib/tasks/tasks.rake
|
107
105
|
- spec/afterparty_job_spec.rb
|
106
|
+
- spec/afterparty_spec.rb
|
108
107
|
- spec/database.yml
|
109
108
|
- spec/generators/afterparty_generator_spec.rb
|
109
|
+
- spec/helper_jobs_spec.rb
|
110
110
|
- spec/helpers.rb
|
111
|
+
- spec/job_container_spec.rb
|
111
112
|
- spec/queue_functional_spec.rb
|
112
113
|
- spec/queue_helpers_spec.rb
|
113
114
|
- spec/queue_spec.rb
|
114
115
|
- spec/schema.rb
|
115
116
|
- spec/spec_helper.rb
|
117
|
+
- spec/worker_spec.rb
|
116
118
|
homepage: ''
|
117
119
|
licenses:
|
118
120
|
- MIT
|
@@ -123,28 +125,31 @@ require_paths:
|
|
123
125
|
- lib
|
124
126
|
required_ruby_version: !ruby/object:Gem::Requirement
|
125
127
|
requirements:
|
126
|
-
- -
|
128
|
+
- - '>='
|
127
129
|
- !ruby/object:Gem::Version
|
128
130
|
version: '0'
|
129
131
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
132
|
requirements:
|
131
|
-
- -
|
133
|
+
- - '>='
|
132
134
|
- !ruby/object:Gem::Version
|
133
135
|
version: '0'
|
134
136
|
requirements: []
|
135
137
|
rubyforge_project:
|
136
|
-
rubygems_version: 2.0.
|
138
|
+
rubygems_version: 2.0.6
|
137
139
|
signing_key:
|
138
140
|
specification_version: 4
|
139
141
|
summary: Rails 4 compatible queue with support for executing jobs later.
|
140
142
|
test_files:
|
141
143
|
- spec/afterparty_job_spec.rb
|
144
|
+
- spec/afterparty_spec.rb
|
142
145
|
- spec/database.yml
|
143
146
|
- spec/generators/afterparty_generator_spec.rb
|
147
|
+
- spec/helper_jobs_spec.rb
|
144
148
|
- spec/helpers.rb
|
149
|
+
- spec/job_container_spec.rb
|
145
150
|
- spec/queue_functional_spec.rb
|
146
151
|
- spec/queue_helpers_spec.rb
|
147
152
|
- spec/queue_spec.rb
|
148
153
|
- spec/schema.rb
|
149
154
|
- spec/spec_helper.rb
|
150
|
-
|
155
|
+
- spec/worker_spec.rb
|
data/afterparty_test.sqlite3
DELETED
Binary file
|
@@ -1,58 +0,0 @@
|
|
1
|
-
module Afterparty
|
2
|
-
|
3
|
-
# inspired by the rails 4 implementation:
|
4
|
-
# https://github.com/rails/rails/blob/jobs/activesupport/lib/active_support/queueing.rb
|
5
|
-
|
6
|
-
# The threaded consumer will run jobs in a background thread in
|
7
|
-
# development mode or in a VM where running jobs on a thread in
|
8
|
-
# production mode makes sense.
|
9
|
-
#
|
10
|
-
# When the process exits, the consumer pushes a nil onto the
|
11
|
-
# queue and joins the thread, which will ensure that all jobs
|
12
|
-
# are executed before the process finally dies.
|
13
|
-
class ThreadedQueueConsumer
|
14
|
-
attr_accessor :logger, :thread
|
15
|
-
|
16
|
-
def initialize(queue, options = {})
|
17
|
-
@queue = queue
|
18
|
-
@logger = options[:logger]
|
19
|
-
@fallback_logger = Logger.new($stderr)
|
20
|
-
end
|
21
|
-
|
22
|
-
def start
|
23
|
-
@thread = Thread.new { consume }
|
24
|
-
self
|
25
|
-
end
|
26
|
-
|
27
|
-
def shutdown
|
28
|
-
@queue.push nil
|
29
|
-
@thread.join
|
30
|
-
end
|
31
|
-
|
32
|
-
def drain
|
33
|
-
while job = @queue.pop(true)
|
34
|
-
job.run
|
35
|
-
end
|
36
|
-
rescue ThreadError
|
37
|
-
end
|
38
|
-
|
39
|
-
def consume
|
40
|
-
while job = @queue.pop
|
41
|
-
if @queue.respond_to? :completed_jobs
|
42
|
-
@queue.completed_jobs << job
|
43
|
-
end
|
44
|
-
run job
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def run(job)
|
49
|
-
job.run
|
50
|
-
rescue Exception => exception
|
51
|
-
handle_exception job, exception
|
52
|
-
end
|
53
|
-
|
54
|
-
def handle_exception(job, exception)
|
55
|
-
(logger || @fallback_logger).error "Job Error: #{job.inspect}\n#{exception.message}\n#{exception.backtrace.join("\n")}"
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|