netinlet-sweat_shop 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ def metaclass; class << self; self; end; end
2
+ def meta_eval(&blk); metaclass.instance_eval(&blk); end
3
+ def meta_def(name, &blk)
4
+ meta_eval { define_method name, &blk }
5
+ end
@@ -0,0 +1,76 @@
1
+ require File.dirname(__FILE__) + '/daemoned'
2
+
3
+ module SweatShop
4
+ class Sweatd
5
+ include Daemoned
6
+ queues = []
7
+ groups = []
8
+ rails_root = nil
9
+ start_cmd = "ruby #{__FILE__} start #{ARGV.reject{|a| a == 'start'}.join(' ')}"
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
+ sig(:hup) do
37
+ puts "Received HUP"
38
+ SweatShop.stop
39
+ remove_pid!
40
+ puts "Restarting sweatd with #{start_cmd}..."
41
+ `#{start_cmd}`
42
+ end
43
+
44
+ before do
45
+ if rails_root
46
+ puts "Loading Rails..."
47
+ require rails_root + '/config/environment'
48
+ end
49
+ require File.dirname(__FILE__) + '/../sweat_shop'
50
+ end
51
+
52
+ daemonize(:kill_timeout => 20) do
53
+ workers = []
54
+
55
+ if groups.any?
56
+ workers += SweatShop.workers_in_group(groups)
57
+ end
58
+
59
+ if queues.any?
60
+ workers += queues.collect{|q| Object.module_eval(q)}
61
+ end
62
+
63
+ if workers.any?
64
+ worker_str = workers.join(',')
65
+ puts "Starting #{worker_str}..."
66
+ $0 = "Sweatd: #{worker_str}"
67
+ SweatShop.do_tasks(workers)
68
+ else
69
+ puts "Starting all workers..."
70
+ $0 = 'Sweatd: all'
71
+ SweatShop.do_all_tasks
72
+ end
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,152 @@
1
+ require File.dirname(__FILE__) + '/metaid'
2
+ require File.dirname(__FILE__) + '/../serializers/serializer'
3
+
4
+ module SweatShop
5
+ class Worker
6
+ def self.inherited(subclass)
7
+ self.workers << subclass
8
+ end
9
+
10
+ def self.method_missing(method, *args, &block)
11
+ if method.to_s =~ /^async_(.*)/
12
+ method = $1
13
+ expected_args = instance.method(method).arity
14
+ if expected_args != args.size
15
+ raise ArgumentError.new("#{method} expects #{expected_args} arguments")
16
+ end
17
+ #return instance.send(method, *args) unless true == config['enable']
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) if config['enable']
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.serialize_with(serializer_name)
34
+ @serializer = SweatShop::Serializer.serializers[serializer_name]
35
+ raise RuntimeError.new("No Such Serializer ['#{serializer_name}']") if @serializer.nil?
36
+ end
37
+
38
+ def self.serializer
39
+ @serializer ||= SweatShop::Serializer.default
40
+ end
41
+
42
+ def self.instance
43
+ @instance ||= new
44
+ end
45
+
46
+ def self.config
47
+ SweatShop.config
48
+ end
49
+
50
+ def self.queue_name
51
+ @queue_name ||= self.to_s
52
+ end
53
+
54
+ def self.delete_queue
55
+ queue.delete(queue_name)
56
+ end
57
+
58
+ def self.queue_size
59
+ queue.queue_size(queue_name)
60
+ end
61
+
62
+ def self.enqueue(task)
63
+ queue.enqueue(queue_name, task, serializer)
64
+ end
65
+
66
+ def self.dequeue
67
+ queue.dequeue(queue_name, serializer)
68
+ end
69
+
70
+ def self.confirm
71
+ queue.confirm(queue_name)
72
+ end
73
+
74
+ def self.subscribe
75
+ queue.subscribe(queue_name, serializer) do |task|
76
+ do_task(task)
77
+ end
78
+ end
79
+
80
+ def self.do_tasks
81
+ while task = dequeue
82
+ do_task(task)
83
+ end
84
+ end
85
+
86
+ def self.do_task(task)
87
+ call_before_task(task)
88
+
89
+ queued_at = task[:queued_at] ? "(queued #{Time.at(task[:queued_at]).strftime('%Y/%m/%d %H:%M:%S')})" : ''
90
+ log("Dequeuing #{queue_name}::#{task[:method]} #{queued_at}")
91
+ task[:result] = instance.send(task[:method], *task[:args])
92
+
93
+ call_after_task(task)
94
+ confirm
95
+ end
96
+
97
+ def self.call_before_task(task)
98
+ superclass.call_before_task(task) if superclass.respond_to?(:call_before_task)
99
+ before_task.call(task) if before_task
100
+ end
101
+
102
+ def self.call_after_task(task)
103
+ superclass.call_after_task(task) if superclass.respond_to?(:call_after_task)
104
+ after_task.call(task) if after_task
105
+ end
106
+
107
+ def self.queue
108
+ SweatShop.queue(queue_group.to_s)
109
+ end
110
+
111
+ def self.workers
112
+ SweatShop.workers
113
+ end
114
+
115
+ def self.config
116
+ SweatShop.config
117
+ end
118
+
119
+ def self.log(msg)
120
+ SweatShop.log(msg)
121
+ end
122
+
123
+ def self.before_task(&block)
124
+ if block
125
+ @before_task = block
126
+ else
127
+ @before_task
128
+ end
129
+ end
130
+
131
+ def self.after_task(&block)
132
+ if block
133
+ @after_task = block
134
+ else
135
+ @after_task
136
+ end
137
+ end
138
+
139
+ def self.stop
140
+ instance.stop
141
+ end
142
+
143
+ # called before we exit -- subclass can implement this method
144
+ def stop; end;
145
+
146
+
147
+ def self.queue_group(group=nil)
148
+ group ? meta_def(:_queue_group){ group } : _queue_group
149
+ end
150
+ queue_group :default
151
+ end
152
+ end
@@ -0,0 +1,13 @@
1
+ require File.dirname(__FILE__) + '/../lib/sweat_shop'
2
+ class HelloWorker < SweatShop::Worker
3
+ TEST_FILE = File.dirname(__FILE__) + '/test.txt' unless defined?(TEST_FILE)
4
+
5
+ def hello(name)
6
+ puts name
7
+ "Hi, #{name}"
8
+ end
9
+
10
+ after_task do |task|
11
+ File.open(TEST_FILE, 'w'){|f| f << task[:result]}
12
+ end
13
+ 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
+ class WorkerTest < Test::Unit::TestCase
5
+
6
+ def setup
7
+ File.delete(HelloWorker::TEST_FILE) if File.exist?(HelloWorker::TEST_FILE)
8
+ end
9
+
10
+ def teardown
11
+ File.delete(HelloWorker::TEST_FILE) if File.exist?(HelloWorker::TEST_FILE)
12
+ end
13
+
14
+ test "daemon" do
15
+ begin
16
+ SweatShop.queue = nil
17
+ SweatShop.logger = :silent
18
+
19
+ worker = File.expand_path(File.dirname(__FILE__) + '/hello_worker')
20
+ sweatd = "#{File.dirname(__FILE__)}/../lib/sweat_shop/sweatd.rb"
21
+ uid = HelloWorker.async_hello('Amos')
22
+
23
+ `ruby #{sweatd} --worker-file #{worker} start`
24
+ `ruby #{sweatd} stop`
25
+
26
+ File.delete('sweatd.log') if File.exist?('sweatd.log')
27
+ assert_equal 'Hi, Amos', File.read(HelloWorker::TEST_FILE)
28
+ rescue Exception => e
29
+ puts e.message
30
+ puts e.backtrace.join("\n")
31
+ fail "\n\n*** Functional test failed, is the rabbit server running on localhost? ***\n"
32
+ end
33
+ end
34
+
35
+ end
@@ -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,50 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+ require File.dirname(__FILE__) + '/../lib/sweat_shop'
3
+
4
+ class JsonSerializerTest < Test::Unit::TestCase
5
+ class UnderTest
6
+ attr_accessor :name, :address, :city, :state, :zip, :id
7
+ def ==(other)
8
+ ![:name, :address, :city, :state, :zip, :id].map{|a| return self.send(a) == other.send(a)}.include?(false)
9
+ end
10
+
11
+ def self.json_create(params)
12
+ obj = new
13
+ for key, value in params
14
+ next if key == 'json_class'
15
+ obj.instance_variable_set "@#{key}", value
16
+ end
17
+ obj
18
+ end
19
+ end
20
+
21
+ def setup
22
+ @under_test = UnderTest.new
23
+ @under_test.id = 87
24
+ @under_test.name = "JsonTest"
25
+ @under_test.address = "555 Rock Ridge Road"
26
+ @under_test.city = "Rock Ridge"
27
+ @under_test.state = "Texas"
28
+ @under_test.zip = "90210"
29
+ end
30
+
31
+ test "should properly serialize a simple data structure" do
32
+ dump = SweatShop::Serializers::JsonSerializer.serialize([{:foo => "bar"}, 23, 87, %w{doug cathy connor tommy}])
33
+ assert_equal '[{"foo":"bar"},23,87,["doug","cathy","connor","tommy"]]', dump
34
+ end
35
+
36
+
37
+ test "should be able to serialize an arbitrary class" do
38
+ dump = SweatShop::Serializers::JsonSerializer.serialize(@under_test)
39
+ assert_equal @under_test, JSON.parse(dump) # deserialize the dump because hash keys are getting set in different order from time to time & making tests fail
40
+ end
41
+
42
+ test "should be able to deserialize a simple data structure" do
43
+ assert_equal [{"foo" => "bar"}, 23, 87, %w{doug cathy connor tommy}], SweatShop::Serializers::JsonSerializer.deserialize('[{"foo":"bar"},23,87,["doug","cathy","connor","tommy"]]')
44
+ end
45
+
46
+ test "should be able to deserialize an arbitrary class" do
47
+ assert_equal @under_test, SweatShop::Serializers::JsonSerializer.deserialize('{"city":"Rock Ridge","name":"JsonTest","zip":"90210","json_class":"JsonSerializerTest::UnderTest","id":87,"address":"555 Rock Ridge Road","state":"Texas"}')
48
+ end
49
+
50
+ end
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+ require File.dirname(__FILE__) + '/../lib/sweat_shop'
3
+
4
+ class MarshalSerializerTest < Test::Unit::TestCase
5
+
6
+ class UnderTest
7
+ attr_accessor :name, :address, :city, :state, :zip, :id
8
+ def ==(other)
9
+ ![:name, :address, :city, :state, :zip, :id].map{|a| return self.send(a) == other.send(a)}.include?(false)
10
+ end
11
+
12
+ end
13
+
14
+ def setup
15
+ @under_test = UnderTest.new
16
+ @under_test.id = 87
17
+ @under_test.name = "MarshalTest"
18
+ @under_test.address = "555 Rock Ridge Road"
19
+ @under_test.city = "Rock Ridge"
20
+ @under_test.state = "Texas"
21
+ @under_test.zip = "90210"
22
+ end
23
+
24
+ test "should properly serialize a simple data structure" do
25
+ dump = SweatShop::Serializers::MarshalSerializer.serialize([{:foo => "bar"}, 23, 87, %w{doug cathy connor tommy}])
26
+ assert_equal Marshal.dump([{:foo => "bar"}, 23, 87, %w{doug cathy connor tommy}]), dump
27
+ end
28
+
29
+ test "should be able to serialize an arbitrary class" do
30
+ dump = SweatShop::Serializers::MarshalSerializer.serialize(@under_test)
31
+ assert_equal Marshal.dump(@under_test), dump
32
+ end
33
+
34
+ test "should be able to deserialize a simple data structure" do
35
+ assert_equal [{:foo => "bar"}, 23, 87, %w{doug cathy connor tommy}], SweatShop::Serializers::MarshalSerializer.deserialize(Marshal.dump([{:foo => "bar"}, 23, 87, %w{doug cathy connor tommy}]))
36
+ end
37
+
38
+ test "should be able to deserialize an arbitrary class" do
39
+ assert_equal @under_test, SweatShop::Serializers::MarshalSerializer.deserialize(Marshal.dump(@under_test))
40
+ end
41
+
42
+ end
@@ -0,0 +1,95 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+ require File.dirname(__FILE__) + '/../lib/sweat_shop'
3
+
4
+ class SerializerTest < Test::Unit::TestCase
5
+
6
+ # no serializer specified
7
+ class DefaultSerialWorker < SweatShop::Worker
8
+ def hello(name)
9
+ "Hi, #{name}"
10
+ end
11
+ end
12
+
13
+ # Marshall specified serializer
14
+ class MarshalWorker < SweatShop::Worker
15
+ serialize_with :marshal
16
+ def hello(name)
17
+ "Hi, #{name}"
18
+ end
19
+ end
20
+
21
+ # JSON specified serializer
22
+ class JsonWorker < SweatShop::Worker
23
+ serialize_with :json
24
+ def hello(name)
25
+ "Hi, #{name}"
26
+ end
27
+ end
28
+
29
+ # YAML specified serializer
30
+ class YamlWorker < SweatShop::Worker
31
+ serialize_with :yaml
32
+ def hello(name)
33
+ "Hi, #{name}"
34
+ end
35
+ end
36
+
37
+ class NoNameSerializer < SweatShop::Serializer
38
+ def self.serialize(payload)
39
+ "x"
40
+ end
41
+
42
+ def self.deserialize(payload)
43
+ "y"
44
+ end
45
+ end
46
+
47
+ class NamedSerializer < SweatShop::Serializer
48
+ serializer_name :silly
49
+ def self.serialize(payload)
50
+ "x"
51
+ end
52
+
53
+ def self.deserialize(payload)
54
+ "y"
55
+ end
56
+
57
+ end
58
+
59
+ test "should use default serializer if none is specified" do
60
+ assert_equal SweatShop::Serializers::MarshalSerializer, DefaultSerialWorker.serializer
61
+ end
62
+
63
+ test "should mark marshal as the default serializer out of the box" do
64
+ assert_equal SweatShop::Serializer.default, SweatShop::Serializers::MarshalSerializer
65
+ end
66
+ test "should be able to specify the default serializer" do
67
+ SweatShop::Serializer.default = :json
68
+ assert_equal SweatShop::Serializer.default, SweatShop::Serializers::JsonSerializer
69
+ SweatShop::Serializer.default = :marshal # set it back so tests don't break
70
+ end
71
+
72
+ test "should be able to specify json as the serializer on a per class basis" do
73
+ assert_equal JsonWorker.serializer, SweatShop::Serializers::JsonSerializer
74
+ end
75
+
76
+ test "should be able to specify marshal as the serializer on a per class basis" do
77
+ assert_equal MarshalWorker.serializer, SweatShop::Serializers::MarshalSerializer
78
+ end
79
+
80
+ test "should be able to specify yaml as the serializer on a per class basis" do
81
+ assert_equal YamlWorker.serializer, SweatShop::Serializers::YamlSerializer
82
+ end
83
+
84
+ test "should provide a default short name based on class name" do
85
+ assert_equal :no_name, NoNameSerializer.get_name
86
+ end
87
+
88
+ test "should accept a custom short name" do
89
+ assert_equal :silly, NamedSerializer.get_name
90
+ end
91
+
92
+ test "should register all available serializers" do
93
+ assert_equal ["json", "marshal", "no_name", "silly", "yaml"], SweatShop::Serializer.serializers.keys.map{|k| k.to_s}.sort
94
+ end
95
+ end