famoseagle-sweat_shop 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -0
- data/LICENSE +20 -0
- data/README.markdown +65 -0
- data/Rakefile +41 -0
- data/VERSION.yml +4 -0
- data/config/defaults.yml +4 -0
- data/config/sweatshop.yml +12 -0
- data/lib/kestrel.rb +29 -0
- data/lib/sweat_shop.rb +107 -0
- data/lib/sweat_shop/metaid.rb +5 -0
- data/lib/sweat_shop/sweatd.rb +67 -0
- data/lib/sweat_shop/worker.rb +130 -0
- data/test/hello_worker.rb +16 -0
- data/test/test_functional_worker.rb +35 -0
- data/test/test_helper.rb +14 -0
- data/test/test_sweatshop.rb +67 -0
- metadata +70 -0
data/History.txt
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Amos Elliston
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# SweatShop
|
2
|
+
|
3
|
+
SweatShop provides an api to background resource intensive tasks. Much of the api design was copied from Workling, with a few tweaks.
|
4
|
+
Currently, it runs kestrel, but it can support any number of queues.
|
5
|
+
|
6
|
+
## Installing
|
7
|
+
|
8
|
+
gem install sweat_shop
|
9
|
+
freeze in your rails directory
|
10
|
+
cd vendor/gems/sweat_shop
|
11
|
+
rake setup
|
12
|
+
|
13
|
+
## Writing workers
|
14
|
+
|
15
|
+
Put `email_worker.rb` into app/workers and sublcass `SweatShop::Worker`:
|
16
|
+
|
17
|
+
class EmailWorker
|
18
|
+
def send_mail(to)
|
19
|
+
user = User.find_by_id(to)
|
20
|
+
Mailer.deliver_welcome(to)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Then, anywhere in your app you can execute:
|
25
|
+
|
26
|
+
EmailWorker.async_send_mail(1)
|
27
|
+
|
28
|
+
The `async` signifies that this task will be placed on a queue to be serviced by the EmailWorker possibly on another machine. You can also
|
29
|
+
call:
|
30
|
+
|
31
|
+
EmailWorker.send_mail(1)
|
32
|
+
|
33
|
+
That will do the work immediately, without placing the task on the queue. You can also define a `queue_group` at the top of the file
|
34
|
+
which will allow you to split workers out into logical groups. This is important if you have various machines serving different
|
35
|
+
queues.
|
36
|
+
|
37
|
+
## Running the queue
|
38
|
+
|
39
|
+
SweatShop has been tested with Kestrel, but it will also work with Starling. You can install and start kestrel following the instructions here:
|
40
|
+
|
41
|
+
http://github.com/robey/kestrel/tree/master
|
42
|
+
|
43
|
+
config/sweatshop.yml specifies the machine address of the queue (default localhost:22133).
|
44
|
+
|
45
|
+
## Running the workers
|
46
|
+
|
47
|
+
Assuming you ran `rake setup` in Rails, you can type:
|
48
|
+
|
49
|
+
script/sweatshop
|
50
|
+
|
51
|
+
By default, the script will run all workers defined in the app/workers dir. Every task will be processed on each queue using a round-robin algorithm. You can also add the `-d` flag which will put the worker in daemon mode. The daemon also takes other params. Add a `-h` for more details.
|
52
|
+
|
53
|
+
script/sweatshop -d
|
54
|
+
script/sweatshop -d stop
|
55
|
+
|
56
|
+
If you would like to run SweatShop as a daemon on a linux machine, use the initd.sh script provided in the sweat_shop/script dir.
|
57
|
+
|
58
|
+
# REQUIREMENTS
|
59
|
+
|
60
|
+
i_can_daemonize
|
61
|
+
memcache (for kestrel)
|
62
|
+
|
63
|
+
# LICENSE
|
64
|
+
|
65
|
+
Copyright (c) 2009 Amos Elliston, Geni.com; Published under The MIT License, see License
|
data/Rakefile
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'rcov/rcovtask'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'jeweler'
|
8
|
+
Jeweler::Tasks.new do |s|
|
9
|
+
s.name = "sweat_shop"
|
10
|
+
s.summary = %Q{SweatShop is a simple asynchronous worker queue build on top of rabbitmq/ampq}
|
11
|
+
s.email = "amos@geni.com"
|
12
|
+
s.homepage = "http://github.com/famoseagle/sweat-shop"
|
13
|
+
s.description = "TODO"
|
14
|
+
s.authors = ["Amos Elliston"]
|
15
|
+
s.files = FileList["[A-Z]*", "{lib,test,config}/**/*"]
|
16
|
+
end
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
19
|
+
end
|
20
|
+
|
21
|
+
Rake::TestTask.new
|
22
|
+
|
23
|
+
Rake::RDocTask.new do |rdoc|
|
24
|
+
rdoc.rdoc_dir = 'rdoc'
|
25
|
+
rdoc.title = 'new_project'
|
26
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
27
|
+
rdoc.rdoc_files.include('README*')
|
28
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
29
|
+
end
|
30
|
+
|
31
|
+
Rcov::RcovTask.new do |t|
|
32
|
+
t.libs << 'test'
|
33
|
+
t.test_files = FileList['test/**/*_test.rb']
|
34
|
+
t.verbose = true
|
35
|
+
end
|
36
|
+
|
37
|
+
task :default => :test
|
38
|
+
|
39
|
+
task :setup do
|
40
|
+
require File.dirname(__FILE__) + '/install'
|
41
|
+
end
|
data/VERSION.yml
ADDED
data/config/defaults.yml
ADDED
data/lib/kestrel.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
class Kestrel
|
2
|
+
attr_reader :client, :servers
|
3
|
+
|
4
|
+
def initialize(opts)
|
5
|
+
@servers = opts[:servers]
|
6
|
+
@client = opts[:client] || MemCache.new(@servers)
|
7
|
+
end
|
8
|
+
|
9
|
+
def queue_size(queue)
|
10
|
+
size = 0
|
11
|
+
stats = client.stats
|
12
|
+
servers.each do |server|
|
13
|
+
size += stats[server]["queue_#{queue}_items"].to_i
|
14
|
+
end
|
15
|
+
size
|
16
|
+
end
|
17
|
+
|
18
|
+
def enqueue(queue, data)
|
19
|
+
client.set(queue, data)
|
20
|
+
end
|
21
|
+
|
22
|
+
def dequeue(queue)
|
23
|
+
client.get("#{queue}/open")
|
24
|
+
end
|
25
|
+
|
26
|
+
def confirm(queue)
|
27
|
+
client.get("#{queue}/close")
|
28
|
+
end
|
29
|
+
end
|
data/lib/sweat_shop.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'digest'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
$:.unshift(File.dirname(__FILE__))
|
6
|
+
require 'kestrel'
|
7
|
+
require 'sweat_shop/worker'
|
8
|
+
|
9
|
+
module SweatShop
|
10
|
+
extend self
|
11
|
+
|
12
|
+
def workers
|
13
|
+
@workers ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
def workers=(workers)
|
17
|
+
@workers = workers
|
18
|
+
end
|
19
|
+
|
20
|
+
def workers_in_group(groups)
|
21
|
+
groups = [groups] unless groups.is_a?(Array)
|
22
|
+
if groups.include?(:all)
|
23
|
+
workers
|
24
|
+
else
|
25
|
+
workers.select do |worker|
|
26
|
+
groups.include?(worker.queue_group)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def do_tasks(workers)
|
32
|
+
loop do
|
33
|
+
wait = true
|
34
|
+
workers.each do |worker|
|
35
|
+
if task = worker.dequeue
|
36
|
+
worker.do_task(task)
|
37
|
+
wait = false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
exit if stop?
|
41
|
+
sleep 1 if wait
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def do_all_tasks
|
46
|
+
do_tasks(
|
47
|
+
workers_in_group(:all)
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def do_default_tasks
|
52
|
+
do_tasks(
|
53
|
+
workers_in_group(:default)
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
def stop
|
58
|
+
@stop = true
|
59
|
+
end
|
60
|
+
|
61
|
+
def stop?
|
62
|
+
@stop
|
63
|
+
end
|
64
|
+
|
65
|
+
def config
|
66
|
+
@config ||= begin
|
67
|
+
defaults = YAML.load_file(File.dirname(__FILE__) + '/../config/defaults.yml')
|
68
|
+
if defined?(RAILS_ROOT)
|
69
|
+
file = RAILS_ROOT + '/config/sweatshop.yml'
|
70
|
+
if File.exist?(file)
|
71
|
+
YAML.load_file(file)[RAILS_ENV || 'development']
|
72
|
+
else
|
73
|
+
defaults['enable'] = false
|
74
|
+
defaults
|
75
|
+
end
|
76
|
+
else
|
77
|
+
defaults
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def queue_sizes
|
83
|
+
workers.inject([]) do |all, worker|
|
84
|
+
all << [worker, worker.queue_size]
|
85
|
+
all
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def pp_sizes
|
90
|
+
max_width = workers.collect{|w| w.to_s.size}.max
|
91
|
+
puts '-' * (max_width + 10)
|
92
|
+
puts queue_sizes.collect{|p| sprintf("%-#{max_width}s %2s", p.first, p.last)}.join("\n")
|
93
|
+
puts '-' * (max_width + 10)
|
94
|
+
end
|
95
|
+
|
96
|
+
def queue
|
97
|
+
@queue ||= Kestrel.new(:servers => config['servers'])
|
98
|
+
end
|
99
|
+
|
100
|
+
def queue=(queue)
|
101
|
+
@queue = queue
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
if defined?(RAILS_ROOT)
|
106
|
+
Dir.glob(RAILS_ROOT + '/app/workers/*.rb').each{|worker| require worker }
|
107
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../sweat_shop'
|
2
|
+
require 'i_can_daemonize'
|
3
|
+
|
4
|
+
module SweatShop
|
5
|
+
class Sweatd
|
6
|
+
include ICanDaemonize
|
7
|
+
queues = []
|
8
|
+
groups = []
|
9
|
+
rails_root = nil
|
10
|
+
|
11
|
+
arg '--workers=Worker,Worker', 'Workers to service (Default is all)' do |value|
|
12
|
+
queues = value.split(',')
|
13
|
+
end
|
14
|
+
|
15
|
+
arg '--groups=GROUP,GROUP', 'Groups of queues to service' do |value|
|
16
|
+
groups = value.split(',').collect{|g| g.to_sym}
|
17
|
+
end
|
18
|
+
|
19
|
+
arg '--worker-file=WORKERFILE', 'Worker file to load' do |value|
|
20
|
+
require value
|
21
|
+
end
|
22
|
+
|
23
|
+
arg '--worker-dir=WORKERDIR', 'Directory containing workers' do |value|
|
24
|
+
Dir.glob(value + '*.rb').each{|worker| require worker}
|
25
|
+
end
|
26
|
+
|
27
|
+
arg '--rails=DIR', 'Pass in RAILS_ROOT to run this daemon in a rails environment' do |value|
|
28
|
+
rails_root = value
|
29
|
+
end
|
30
|
+
|
31
|
+
sig(:term, :int) do
|
32
|
+
puts "Shutting down sweatd..."
|
33
|
+
SweatShop.stop
|
34
|
+
end
|
35
|
+
|
36
|
+
before do
|
37
|
+
if rails_root
|
38
|
+
puts "Loading Rails..."
|
39
|
+
require rails_root + '/config/environment'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
daemonize(:kill_timeout => 20) do
|
44
|
+
workers = []
|
45
|
+
|
46
|
+
if groups.any?
|
47
|
+
workers += SweatShop.workers_in_group(groups)
|
48
|
+
end
|
49
|
+
|
50
|
+
if queues.any?
|
51
|
+
workers += queues.collect{|q| Object.module_eval(q)}
|
52
|
+
end
|
53
|
+
|
54
|
+
if workers.any?
|
55
|
+
worker_str = workers.join(',')
|
56
|
+
puts "Starting #{worker_str}..."
|
57
|
+
$0 = "Sweatd: #{worker_str}"
|
58
|
+
SweatShop.do_tasks(workers)
|
59
|
+
else
|
60
|
+
puts "Starting all workers..."
|
61
|
+
$0 = 'Sweatd: all'
|
62
|
+
SweatShop.do_all_tasks
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/metaid'
|
2
|
+
|
3
|
+
module SweatShop
|
4
|
+
class Worker
|
5
|
+
@@logger = nil
|
6
|
+
|
7
|
+
def self.inherited(subclass)
|
8
|
+
self.workers << subclass
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.method_missing(method, *args, &block)
|
12
|
+
if method.to_s =~ /^async_(.*)/ and config['enable']
|
13
|
+
method = $1
|
14
|
+
expected_args = instance.method(method).arity
|
15
|
+
if expected_args != args.size
|
16
|
+
raise ArgumentError.new("#{method} expects #{expected_args} arguments")
|
17
|
+
end
|
18
|
+
|
19
|
+
uid = ::Digest::MD5.hexdigest("#{name}:#{method}:#{args}:#{Time.now.to_f}")
|
20
|
+
task = {:args => args, :method => method, :uid => uid, :queued_at => Time.now.to_i}
|
21
|
+
|
22
|
+
log("Putting #{uid} on #{queue_name}")
|
23
|
+
enqueue(task)
|
24
|
+
|
25
|
+
uid
|
26
|
+
elsif instance.respond_to?(method)
|
27
|
+
instance.send(method, *args)
|
28
|
+
else
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.instance
|
34
|
+
@instance ||= new
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.config
|
38
|
+
SweatShop.config
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.queue_name
|
42
|
+
@queue_name ||= self.to_s
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.queue_size
|
46
|
+
queue.queue_size(queue_name)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.enqueue(task)
|
50
|
+
queue.enqueue(queue_name, task)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.dequeue
|
54
|
+
queue.dequeue(queue_name)
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.confirm
|
58
|
+
queue.confirm(queue_name)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.do_tasks
|
62
|
+
while task = dequeue
|
63
|
+
do_task(task)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.do_task(task)
|
68
|
+
call_before_task(task)
|
69
|
+
|
70
|
+
queued_at = task[:queued_at] ? "(queued #{Time.at(task[:queued_at]).strftime('%Y/%m/%d %H:%M:%S')})" : ''
|
71
|
+
log("Dequeuing #{queue_name}::#{task[:method]} #{queued_at}")
|
72
|
+
task[:result] = instance.send(task[:method], *task[:args])
|
73
|
+
|
74
|
+
call_after_task(task)
|
75
|
+
confirm
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.call_before_task(task)
|
79
|
+
superclass.call_before_task(task) if superclass.respond_to?(:call_before_task)
|
80
|
+
before_task.call(task) if before_task
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.call_after_task(task)
|
84
|
+
superclass.call_after_task(task) if superclass.respond_to?(:call_after_task)
|
85
|
+
after_task.call(task) if after_task
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.workers
|
89
|
+
SweatShop.workers
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.log(msg)
|
93
|
+
return if logger == :silent
|
94
|
+
logger ? logger.debug(msg) : puts(msg)
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.logger
|
98
|
+
@@logger
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.logger=(logger)
|
102
|
+
@@logger = logger
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.before_task(&block)
|
106
|
+
if block
|
107
|
+
@before_task = block
|
108
|
+
else
|
109
|
+
@before_task
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.after_task(&block)
|
114
|
+
if block
|
115
|
+
@after_task = block
|
116
|
+
else
|
117
|
+
@after_task
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.queue
|
122
|
+
SweatShop.queue
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.queue_group(group=nil)
|
126
|
+
group ? meta_def(:_queue_group){ group } : _queue_group
|
127
|
+
end
|
128
|
+
queue_group :default
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# hack for functional tests
|
2
|
+
require File.dirname(__FILE__) + '/../../../memcache/lib/memcache_extended'
|
3
|
+
require File.dirname(__FILE__) + '/../../../memcache/lib/memcache_util'
|
4
|
+
|
5
|
+
class HelloWorker < SweatShop::Worker
|
6
|
+
TEST_FILE = File.dirname(__FILE__) + '/test.txt' unless defined?(TEST_FILE)
|
7
|
+
|
8
|
+
def hello(name)
|
9
|
+
puts name
|
10
|
+
"Hi, #{name}"
|
11
|
+
end
|
12
|
+
|
13
|
+
after_task do |task|
|
14
|
+
File.open(TEST_FILE, 'w'){|f| f << task[:result]}
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/sweat_shop'
|
2
|
+
require File.dirname(__FILE__) + '/test_helper'
|
3
|
+
require File.dirname(__FILE__) + '/hello_worker'
|
4
|
+
|
5
|
+
class WorkerTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
File.delete(HelloWorker::TEST_FILE) if File.exist?(HelloWorker::TEST_FILE)
|
9
|
+
end
|
10
|
+
|
11
|
+
def teardown
|
12
|
+
File.delete(HelloWorker::TEST_FILE) if File.exist?(HelloWorker::TEST_FILE)
|
13
|
+
end
|
14
|
+
|
15
|
+
# remove 'x' and start kestrel to run
|
16
|
+
test "daemon" do
|
17
|
+
begin
|
18
|
+
SweatShop.queue = nil
|
19
|
+
SweatShop::Worker.logger = :silent
|
20
|
+
|
21
|
+
worker = File.expand_path(File.dirname(__FILE__) + '/hello_worker')
|
22
|
+
sweatd = "#{File.dirname(__FILE__)}/../lib/sweat_shop/sweatd.rb"
|
23
|
+
uid = HelloWorker.async_hello('Amos')
|
24
|
+
|
25
|
+
`ruby #{sweatd} --worker-file #{worker} start`
|
26
|
+
`ruby #{sweatd} stop`
|
27
|
+
|
28
|
+
File.delete('sweatd.log') if File.exist?('sweatd.log')
|
29
|
+
assert_equal 'Hi, Amos', File.read(HelloWorker::TEST_FILE)
|
30
|
+
rescue MemCache::MemCacheError => e
|
31
|
+
puts "\n\n*** Start kestrel on localhost to run all functional tests. ***\n\n"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
class << Test::Unit::TestCase
|
4
|
+
def test(name, &block)
|
5
|
+
test_name = "test_#{name.gsub(/[\s\W]/,'_')}"
|
6
|
+
raise ArgumentError, "#{test_name} is already defined" if self.instance_methods.include? test_name
|
7
|
+
define_method test_name, &block
|
8
|
+
end
|
9
|
+
|
10
|
+
def xtest(name, &block)
|
11
|
+
# no-op, an empty test method is defined to prevent "no tests in testcase" errors when all tests are disabled
|
12
|
+
define_method(:test_disabled) { assert true }
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../memcache/lib/memcache_mock'
|
2
|
+
require File.dirname(__FILE__) + '/test_helper'
|
3
|
+
require File.dirname(__FILE__) + '/../lib/sweat_shop'
|
4
|
+
|
5
|
+
class SweatShopTest < Test::Unit::TestCase
|
6
|
+
SweatShop.workers = []
|
7
|
+
SweatShop.queue = Kestrel.new(:client => MemCacheMock.new)
|
8
|
+
|
9
|
+
class HelloWorker < SweatShop::Worker
|
10
|
+
def hello(name)
|
11
|
+
"Hi, #{name}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class GroupedWorker < SweatShop::Worker
|
16
|
+
queue_group :foo
|
17
|
+
end
|
18
|
+
|
19
|
+
test "group workers" do
|
20
|
+
assert_equal [HelloWorker, GroupedWorker], SweatShop.workers_in_group(:all)
|
21
|
+
assert_equal [HelloWorker], SweatShop.workers_in_group(:default)
|
22
|
+
assert_equal [GroupedWorker], SweatShop.workers_in_group(:foo)
|
23
|
+
end
|
24
|
+
|
25
|
+
test "synch call" do
|
26
|
+
worker = HelloWorker.new
|
27
|
+
assert_equal "Hi, Amos", worker.hello('Amos')
|
28
|
+
end
|
29
|
+
|
30
|
+
test "uid" do
|
31
|
+
SweatShop::Worker.logger = :silent
|
32
|
+
uid = HelloWorker.async_hello('Amos')
|
33
|
+
assert_not_nil uid
|
34
|
+
end
|
35
|
+
|
36
|
+
test "before task" do
|
37
|
+
HelloWorker.before_task do
|
38
|
+
"hello"
|
39
|
+
end
|
40
|
+
assert_equal "hello", HelloWorker.before_task.call
|
41
|
+
end
|
42
|
+
|
43
|
+
test "after task" do
|
44
|
+
HelloWorker.after_task do
|
45
|
+
"goodbye"
|
46
|
+
end
|
47
|
+
assert_equal "goodbye", HelloWorker.after_task.call
|
48
|
+
end
|
49
|
+
|
50
|
+
test "chainable before tasks" do
|
51
|
+
MESSAGES = []
|
52
|
+
class BaseWorker < SweatShop::Worker
|
53
|
+
before_task do |task|
|
54
|
+
MESSAGES << 'base'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
class SubWorker < BaseWorker
|
58
|
+
before_task do |task|
|
59
|
+
MESSAGES << 'sub'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
SubWorker.call_before_task('foo')
|
63
|
+
assert_equal ['base', 'sub'], MESSAGES
|
64
|
+
SweatShop.workers.delete(BaseWorker)
|
65
|
+
SweatShop.workers.delete(SubWorker)
|
66
|
+
end
|
67
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: famoseagle-sweat_shop
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Amos Elliston
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-18 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: TODO
|
17
|
+
email: amos@geni.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- History.txt
|
26
|
+
- LICENSE
|
27
|
+
- Rakefile
|
28
|
+
- README.markdown
|
29
|
+
- VERSION.yml
|
30
|
+
- lib/kestrel.rb
|
31
|
+
- lib/sweat_shop
|
32
|
+
- lib/sweat_shop/metaid.rb
|
33
|
+
- lib/sweat_shop/sweatd.rb
|
34
|
+
- lib/sweat_shop/worker.rb
|
35
|
+
- lib/sweat_shop.rb
|
36
|
+
- test/hello_worker.rb
|
37
|
+
- test/test_functional_worker.rb
|
38
|
+
- test/test_helper.rb
|
39
|
+
- test/test_sweatshop.rb
|
40
|
+
- config/defaults.yml
|
41
|
+
- config/sweatshop.yml
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://github.com/famoseagle/sweat-shop
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options:
|
46
|
+
- --inline-source
|
47
|
+
- --charset=UTF-8
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
version:
|
62
|
+
requirements: []
|
63
|
+
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.2.0
|
66
|
+
signing_key:
|
67
|
+
specification_version: 2
|
68
|
+
summary: SweatShop is a simple asynchronous worker queue build on top of rabbitmq/ampq
|
69
|
+
test_files: []
|
70
|
+
|