elecnix-workling 0.3.1
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/CHANGES.markdown +72 -0
- data/README.markdown +386 -0
- data/TODO.markdown +27 -0
- data/VERSION.yml +4 -0
- data/lib/rude_q/client.rb +11 -0
- data/lib/workling/base.rb +71 -0
- data/lib/workling/clients/amqp_client.rb +56 -0
- data/lib/workling/clients/base.rb +57 -0
- data/lib/workling/clients/memcache_queue_client.rb +83 -0
- data/lib/workling/discovery.rb +14 -0
- data/lib/workling/remote/invokers/base.rb +124 -0
- data/lib/workling/remote/invokers/basic_poller.rb +41 -0
- data/lib/workling/remote/invokers/eventmachine_subscriber.rb +41 -0
- data/lib/workling/remote/invokers/threaded_poller.rb +140 -0
- data/lib/workling/remote/runners/backgroundjob_runner.rb +35 -0
- data/lib/workling/remote/runners/base.rb +42 -0
- data/lib/workling/remote/runners/client_runner.rb +45 -0
- data/lib/workling/remote/runners/not_remote_runner.rb +23 -0
- data/lib/workling/remote/runners/rudeq_runner.rb +23 -0
- data/lib/workling/remote/runners/spawn_runner.rb +38 -0
- data/lib/workling/remote/runners/starling_runner.rb +13 -0
- data/lib/workling/remote.rb +42 -0
- data/lib/workling/return/store/base.rb +42 -0
- data/lib/workling/return/store/iterator.rb +24 -0
- data/lib/workling/return/store/memory_return_store.rb +26 -0
- data/lib/workling/return/store/rudeq_return_store.rb +24 -0
- data/lib/workling/return/store/starling_return_store.rb +31 -0
- data/lib/workling/routing/base.rb +13 -0
- data/lib/workling/routing/class_and_method_routing.rb +55 -0
- data/lib/workling/rudeq/client.rb +17 -0
- data/lib/workling/rudeq/poller.rb +116 -0
- data/lib/workling/rudeq.rb +7 -0
- data/lib/workling.rb +150 -0
- data/test/class_and_method_routing_test.rb +18 -0
- data/test/clients/memory_queue_client.rb +36 -0
- data/test/discovery_test.rb +13 -0
- data/test/invoker_basic_poller_test.rb +29 -0
- data/test/invoker_eventmachine_subscription_test.rb +26 -0
- data/test/invoker_threaded_poller_test.rb +34 -0
- data/test/memcachequeue_client_test.rb +36 -0
- data/test/memory_return_store_test.rb +32 -0
- data/test/mocks/client.rb +9 -0
- data/test/mocks/logger.rb +5 -0
- data/test/mocks/rude_queue.rb +9 -0
- data/test/mocks/spawn.rb +5 -0
- data/test/not_remote_runner_test.rb +11 -0
- data/test/remote_runner_test.rb +58 -0
- data/test/rescue_test.rb +24 -0
- data/test/return_store_test.rb +24 -0
- data/test/rudeq_client_test.rb +30 -0
- data/test/rudeq_poller_test.rb +14 -0
- data/test/rudeq_return_store_test.rb +20 -0
- data/test/rudeq_runner_test.rb +22 -0
- data/test/runners/thread_runner.rb +22 -0
- data/test/spawn_runner_test.rb +10 -0
- data/test/starling_return_store_test.rb +29 -0
- data/test/starling_runner_test.rb +8 -0
- data/test/test_helper.rb +50 -0
- data/test/workers/analytics/invites.rb +10 -0
- data/test/workers/util.rb +25 -0
- metadata +129 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require "workling/remote/runners/not_remote_runner"
|
|
2
|
+
require "workling/remote/runners/spawn_runner"
|
|
3
|
+
require "workling/remote/runners/starling_runner"
|
|
4
|
+
require "workling/remote/runners/backgroundjob_runner"
|
|
5
|
+
|
|
6
|
+
require 'digest/md5'
|
|
7
|
+
|
|
8
|
+
#
|
|
9
|
+
# Scoping Module for Runners.
|
|
10
|
+
#
|
|
11
|
+
module Workling
|
|
12
|
+
module Remote
|
|
13
|
+
|
|
14
|
+
# set the desired runner here. this is initialized with Workling.default_runner.
|
|
15
|
+
mattr_accessor :dispatcher
|
|
16
|
+
|
|
17
|
+
# set the desired invoker. this class grabs work from the job broker and executes it.
|
|
18
|
+
mattr_accessor :invoker
|
|
19
|
+
@@invoker ||= Workling::Remote::Invokers::ThreadedPoller
|
|
20
|
+
|
|
21
|
+
# retrieve the dispatcher or instantiate it using the defaults
|
|
22
|
+
def self.dispatcher
|
|
23
|
+
@@dispatcher ||= Workling.default_runner
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# generates a unique identifier for this particular job.
|
|
27
|
+
def self.generate_uid(clazz, method)
|
|
28
|
+
uid = ::Digest::MD5.hexdigest("#{ clazz }:#{ method }:#{ rand(1 << 64) }:#{ Time.now }")
|
|
29
|
+
"#{ clazz.to_s.tableize }/#{ method }/#{ uid }".split("/").join(":")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# dispatches to a workling. writes the :uid for this work into the options hash, so make
|
|
33
|
+
# sure you pass in a hash if you want write to a return store in your workling.
|
|
34
|
+
def self.run(clazz, method, options = {})
|
|
35
|
+
uid = Workling::Remote.generate_uid(clazz, method)
|
|
36
|
+
options[:uid] = uid if options.kind_of?(Hash) && !options[:uid]
|
|
37
|
+
Workling.find(clazz, method) # this line raises a WorklingError if the method does not exist.
|
|
38
|
+
dispatcher.run(clazz, method, options)
|
|
39
|
+
uid
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Basic interface for getting and setting Data which needs to be passed between Workers and
|
|
3
|
+
# client code.
|
|
4
|
+
#
|
|
5
|
+
module Workling
|
|
6
|
+
module Return
|
|
7
|
+
module Store
|
|
8
|
+
mattr_accessor :instance
|
|
9
|
+
|
|
10
|
+
# set a value in the store with the given key. delegates to the returnstore.
|
|
11
|
+
def self.set(key, value)
|
|
12
|
+
self.instance.set(key, value)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# get a value from the store. this should be destructive. delegates to the returnstore.
|
|
16
|
+
def self.get(key)
|
|
17
|
+
self.instance.get(key)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
#
|
|
21
|
+
# Base Class for Return Stores. Subclasses need to implement set and get.
|
|
22
|
+
#
|
|
23
|
+
class Base
|
|
24
|
+
|
|
25
|
+
# set a value in the store with the given key.
|
|
26
|
+
def set(key, value)
|
|
27
|
+
raise NotImplementedError.new("set(key, value) not implemented in #{ self.class }")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# get a value from the store. this should be destructive.
|
|
31
|
+
def get(key)
|
|
32
|
+
raise NotImplementedError.new("get(key) not implemented in #{ self.class }")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def iterator(key)
|
|
36
|
+
Workling::Return::Store::Iterator.new(key)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Iterator class for iterating over return values.
|
|
3
|
+
#
|
|
4
|
+
module Workling
|
|
5
|
+
module Return
|
|
6
|
+
module Store
|
|
7
|
+
class Iterator
|
|
8
|
+
|
|
9
|
+
include Enumerable
|
|
10
|
+
|
|
11
|
+
def initialize(uid)
|
|
12
|
+
@uid = uid
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def each
|
|
16
|
+
while item = Workling.return.get(@uid)
|
|
17
|
+
yield item
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'workling/return/store/base'
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Stores directly into memory. This is for tests only - not for production use. aight?
|
|
5
|
+
#
|
|
6
|
+
module Workling
|
|
7
|
+
module Return
|
|
8
|
+
module Store
|
|
9
|
+
class MemoryReturnStore < Base
|
|
10
|
+
attr_accessor :sky
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
self.sky = Hash.new([])
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def set(key, value)
|
|
17
|
+
self.sky[key] << value
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def get(key)
|
|
21
|
+
self.sky[key].shift
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'workling/return/store/base'
|
|
2
|
+
require 'workling/rudeq/client'
|
|
3
|
+
|
|
4
|
+
module Workling
|
|
5
|
+
module Return
|
|
6
|
+
module Store
|
|
7
|
+
class RudeqReturnStore < Base
|
|
8
|
+
cattr_accessor :client
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
self.class.client = Workling::Rudeq::Client.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def set(key, value)
|
|
15
|
+
self.class.client.set(key, value)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def get(key)
|
|
19
|
+
self.class.client.get(key)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'workling/return/store/base'
|
|
2
|
+
require 'workling/clients/memcache_queue_client'
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# Recommended Return Store if you are using the Starling Runner. This
|
|
6
|
+
# Simply sets and gets values against queues. 'key' is the name of the respective Queue.
|
|
7
|
+
#
|
|
8
|
+
module Workling
|
|
9
|
+
module Return
|
|
10
|
+
module Store
|
|
11
|
+
class StarlingReturnStore < Base
|
|
12
|
+
cattr_accessor :client
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
self.client = Workling::Clients::MemcacheQueueClient.new
|
|
16
|
+
self.client.connect
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# set a value in the queue 'key'.
|
|
20
|
+
def set(key, value)
|
|
21
|
+
self.class.client.set(key, value)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# get a value from starling queue 'key'.
|
|
25
|
+
def get(key)
|
|
26
|
+
self.class.client.get(key)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Base Class for Routing. Routing takes the worker method TestWorker#something,
|
|
3
|
+
# and serializes the signature in some way.
|
|
4
|
+
#
|
|
5
|
+
module Workling
|
|
6
|
+
module Routing
|
|
7
|
+
class Base < Hash
|
|
8
|
+
def method_name
|
|
9
|
+
raise Exception.new("method_name not implemented.")
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'workling/routing/base'
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Holds a hash of routes. Each Worker method has a corresponding hash entry after building.
|
|
5
|
+
#
|
|
6
|
+
module Workling
|
|
7
|
+
module Routing
|
|
8
|
+
class ClassAndMethodRouting < Base
|
|
9
|
+
|
|
10
|
+
# initializes and builds routing hash.
|
|
11
|
+
def initialize
|
|
12
|
+
super
|
|
13
|
+
|
|
14
|
+
build
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# returns the worker method name, given the routing string.
|
|
18
|
+
def method_name(queue)
|
|
19
|
+
queue.split("__").last
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# returns the routing string, given a class and method. delegating.
|
|
23
|
+
def queue_for(clazz, method)
|
|
24
|
+
ClassAndMethodRouting.queue_for(clazz, method)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# returns the routing string, given a class and method.
|
|
28
|
+
def self.queue_for(clazz, method)
|
|
29
|
+
"#{ clazz.to_s.tableize }/#{ method }".split("/").join("__") # Don't split with : because it messes up memcache stats
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# returns all routed
|
|
33
|
+
def queue_names
|
|
34
|
+
self.keys
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# dare you to remove this! go on!
|
|
38
|
+
def queue_names_routing_class(clazz)
|
|
39
|
+
self.select { |x, y| y.is_a?(clazz) }.map { |x, y| x }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
def build
|
|
44
|
+
Workling::Discovery.discovered.each do |clazz|
|
|
45
|
+
methods = clazz.instance_methods(false)
|
|
46
|
+
methods.each do |method|
|
|
47
|
+
next if method == 'create' # Skip the create method
|
|
48
|
+
queue = queue_for(clazz, method)
|
|
49
|
+
self[queue] = clazz.new
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'workling/rudeq'
|
|
2
|
+
|
|
3
|
+
module Workling
|
|
4
|
+
module Rudeq
|
|
5
|
+
class Client
|
|
6
|
+
attr_reader :queue
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@queue = Workling::Rudeq.config[:queue_class].constantize
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def method_missing(method, *args)
|
|
13
|
+
@queue.send(method, *args)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
require 'workling/rudeq'
|
|
2
|
+
|
|
3
|
+
module Workling
|
|
4
|
+
module Rudeq
|
|
5
|
+
|
|
6
|
+
class Poller
|
|
7
|
+
|
|
8
|
+
cattr_accessor :sleep_time # Seconds to sleep before looping
|
|
9
|
+
cattr_accessor :reset_time # Seconds to wait while resetting connection
|
|
10
|
+
|
|
11
|
+
def initialize(routing)
|
|
12
|
+
Poller.sleep_time = Workling::Rudeq.config[:sleep_time] || 2
|
|
13
|
+
Poller.reset_time = Workling::Rudeq.config[:reset_time] || 30
|
|
14
|
+
|
|
15
|
+
@routing = routing
|
|
16
|
+
@workers = ThreadGroup.new
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def logger
|
|
20
|
+
Workling::Base.logger
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def listen
|
|
24
|
+
|
|
25
|
+
# Allow concurrency for our tasks
|
|
26
|
+
ActiveRecord::Base.allow_concurrency = true
|
|
27
|
+
|
|
28
|
+
# Create a thread for each worker.
|
|
29
|
+
Workling::Discovery.discovered.each do |clazz|
|
|
30
|
+
logger.debug("Discovered listener #{clazz}")
|
|
31
|
+
@workers.add(Thread.new(clazz) { |c| clazz_listen(c) })
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Wait for all workers to complete
|
|
35
|
+
@workers.list.each { |t| t.join }
|
|
36
|
+
|
|
37
|
+
# Clean up all the connections.
|
|
38
|
+
ActiveRecord::Base.verify_active_connections!
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# gracefully stop processing
|
|
42
|
+
def stop
|
|
43
|
+
@workers.list.each { |w| w[:shutdown] = true }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
## Thread procs
|
|
48
|
+
##
|
|
49
|
+
|
|
50
|
+
# Listen for one worker class
|
|
51
|
+
def clazz_listen(clazz)
|
|
52
|
+
|
|
53
|
+
logger.debug("Listener thread #{clazz.name} started")
|
|
54
|
+
|
|
55
|
+
# Read thread configuration if available
|
|
56
|
+
if Rudeq.config.has_key?(:listeners)
|
|
57
|
+
if Rudeq.config[:listeners].has_key?(clazz.to_s)
|
|
58
|
+
config = Rudeq.config[:listeners][clazz.to_s].symbolize_keys
|
|
59
|
+
thread_sleep_time = config[:sleep_time] if config.has_key?(:sleep_time)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
hread_sleep_time ||= self.class.sleep_time
|
|
64
|
+
|
|
65
|
+
connection = Workling::Rudeq::Client.new
|
|
66
|
+
puts "** Starting Workling::Rudeq::Client for #{clazz.name} queue"
|
|
67
|
+
|
|
68
|
+
# Start dispatching those messages
|
|
69
|
+
while (!Thread.current[:shutdown]) do
|
|
70
|
+
begin
|
|
71
|
+
|
|
72
|
+
# Keep MySQL connection alive
|
|
73
|
+
unless ActiveRecord::Base.connection.active?
|
|
74
|
+
unless ActiveRecord::Base.connection.reconnect!
|
|
75
|
+
logger.fatal("FAILED - Database not available")
|
|
76
|
+
break
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Dispatch and process the messages
|
|
81
|
+
n = dispatch!(connection, clazz)
|
|
82
|
+
logger.debug("Listener thread #{clazz.name} processed #{n.to_s} queue items") if n > 0
|
|
83
|
+
sleep(self.class.sleep_time) unless n > 0
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
logger.debug("Listener thread #{clazz.name} ended")
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Dispatcher for one worker class.
|
|
91
|
+
# Returns the number of worker methods called
|
|
92
|
+
def dispatch!(connection, clazz)
|
|
93
|
+
n = 0
|
|
94
|
+
for queue in @routing.queue_names_routing_class(clazz)
|
|
95
|
+
begin
|
|
96
|
+
result = connection.get(queue)
|
|
97
|
+
if result
|
|
98
|
+
n += 1
|
|
99
|
+
handler = @routing[queue]
|
|
100
|
+
method_name = @routing.method_name(queue)
|
|
101
|
+
logger.debug("Calling #{handler.class.to_s}\##{method_name}(#{result.inspect})")
|
|
102
|
+
handler.send(method_name, result)
|
|
103
|
+
end
|
|
104
|
+
rescue
|
|
105
|
+
logger.error("FAILED to connect with queue #{ queue }: #{ e } }")
|
|
106
|
+
raise e
|
|
107
|
+
rescue Object => e
|
|
108
|
+
logger.error("FAILED to process queue #{ queue }. #{ @routing[queue] } could not handle invocation of #{ @routing.method_name(queue) } with #{ result.inspect }: #{ e }.\n#{ e.backtrace.join("\n") }")
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
return n
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
data/lib/workling.rb
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#
|
|
2
|
+
# I can haz am in your Workling are belong to us!
|
|
3
|
+
#
|
|
4
|
+
module Workling
|
|
5
|
+
class WorklingError < StandardError; end
|
|
6
|
+
class WorklingNotFoundError < WorklingError; end
|
|
7
|
+
class WorklingConnectionError < WorklingError; end
|
|
8
|
+
class QueueserverNotFoundError < WorklingError
|
|
9
|
+
def initialize
|
|
10
|
+
super "config/workling.yml configured to connect to queue server on #{ Workling.config[:listens_on] } for this environment. could not connect to queue server on this host:port. for starling users: pass starling the port with -p flag when starting it. If you don't want to use Starling, then explicitly set Workling::Remote.dispatcher (see README for an example)"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class ConfigurationError < WorklingError
|
|
15
|
+
def initialize
|
|
16
|
+
super File.exist?(File.join(RAILS_ROOT, 'config', 'starling.yml')) ?
|
|
17
|
+
"config/starling.yml has been depracated. rename your config file to config/workling.yml then try again!" :
|
|
18
|
+
"config/workling.yml could not be loaded. check out README.markdown to see what this file should contain. "
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
mattr_accessor :load_path
|
|
23
|
+
@@load_path = [ File.expand_path(File.join(File.dirname(__FILE__), '../../../../app/workers')) ]
|
|
24
|
+
VERSION = "0.4.2.2"
|
|
25
|
+
|
|
26
|
+
#
|
|
27
|
+
# determine the runner to use if nothing is specifically set. workling will try to detect
|
|
28
|
+
# starling, spawn, or bj, in that order. if none of these are found, notremoterunner will
|
|
29
|
+
# be used.
|
|
30
|
+
#
|
|
31
|
+
# this can be overridden by setting Workling::Remote.dispatcher, eg:
|
|
32
|
+
# Workling::Remote.dispatcher = Workling::Remote::Runners::StarlingRunner.new
|
|
33
|
+
#
|
|
34
|
+
def self.default_runner
|
|
35
|
+
if RAILS_ENV == "test"
|
|
36
|
+
Workling::Remote::Runners::NotRemoteRunner.new
|
|
37
|
+
elsif starling_installed?
|
|
38
|
+
Workling::Remote::Runners::StarlingRunner.new
|
|
39
|
+
elsif spawn_installed?
|
|
40
|
+
Workling::Remote::Runners::SpawnRunner.new
|
|
41
|
+
elsif bj_installed?
|
|
42
|
+
Workling::Remote::Runners::BackgroundjobRunner.new
|
|
43
|
+
else
|
|
44
|
+
Workling::Remote::Runners::NotRemoteRunner.new
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
#
|
|
49
|
+
# gets the worker instance, given a class. the optional method argument will cause an
|
|
50
|
+
# exception to be raised if the worker instance does not respoind to said method.
|
|
51
|
+
#
|
|
52
|
+
def self.find(clazz, method = nil)
|
|
53
|
+
begin
|
|
54
|
+
inst = clazz.to_s.camelize.constantize.new
|
|
55
|
+
rescue NameError
|
|
56
|
+
raise_not_found(clazz, method)
|
|
57
|
+
end
|
|
58
|
+
raise_not_found(clazz, method) if method && !inst.respond_to?(method)
|
|
59
|
+
inst
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# returns Workling::Return::Store.instance.
|
|
63
|
+
def self.return
|
|
64
|
+
Workling::Return::Store.instance
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# is spawn installed?
|
|
68
|
+
def self.spawn_installed?
|
|
69
|
+
begin
|
|
70
|
+
require 'spawn'
|
|
71
|
+
rescue LoadError
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
Object.const_defined? "Spawn"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# is starling installed?
|
|
78
|
+
def self.starling_installed?
|
|
79
|
+
begin
|
|
80
|
+
require 'starling'
|
|
81
|
+
rescue LoadError
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
Object.const_defined? "Starling"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# is bj installed?
|
|
88
|
+
def self.bj_installed?
|
|
89
|
+
Object.const_defined? "Bj"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# tries to load fiveruns-memcache-client. if this isn't found,
|
|
93
|
+
# memcache-client is searched for. if that isn't found, don't do anything.
|
|
94
|
+
def self.try_load_a_memcache_client
|
|
95
|
+
begin
|
|
96
|
+
gem 'fiveruns-memcache-client'
|
|
97
|
+
require 'memcache'
|
|
98
|
+
rescue Gem::LoadError
|
|
99
|
+
begin
|
|
100
|
+
gem 'memcache-client'
|
|
101
|
+
require 'memcache'
|
|
102
|
+
rescue Gem::LoadError
|
|
103
|
+
Workling::Base.logger.info "WORKLING: couldn't find a memcache client - you need one for the starling runner. "
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# attempts to load amqp and writes out descriptive error message if not present
|
|
109
|
+
def self.try_load_an_amqp_client
|
|
110
|
+
begin
|
|
111
|
+
require 'mq'
|
|
112
|
+
rescue Exception => e
|
|
113
|
+
raise WorklingError.new(
|
|
114
|
+
"WORKLING: couldn't find the ruby amqp client - you need it for the amqp runner. " \
|
|
115
|
+
"Install from github: gem sources -a http://gems.github.com/ && sudo gem install tmm1-amqp "
|
|
116
|
+
)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
#
|
|
121
|
+
# returns a config hash. reads RAILS_ROOT/config/workling.yml
|
|
122
|
+
#
|
|
123
|
+
def self.config
|
|
124
|
+
begin
|
|
125
|
+
config_path = File.join(RAILS_ROOT, 'config', 'workling.yml')
|
|
126
|
+
@@config ||= YAML.load_file(config_path)[RAILS_ENV || 'development'].symbolize_keys
|
|
127
|
+
@@config[:memcache_options].symbolize_keys! if @@config[:memcache_options]
|
|
128
|
+
@@config
|
|
129
|
+
rescue
|
|
130
|
+
# config files could not be read correctly
|
|
131
|
+
raise ConfigurationError.new
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
#
|
|
136
|
+
# Raises exceptions thrown inside of the worker. normally, these are logged to
|
|
137
|
+
# logger.error. it's easy to miss these log calls while developing, though.
|
|
138
|
+
#
|
|
139
|
+
mattr_accessor :raise_exceptions
|
|
140
|
+
@@raise_exceptions = (RAILS_ENV == "test" || RAILS_ENV == "development")
|
|
141
|
+
|
|
142
|
+
def self.raise_exceptions?
|
|
143
|
+
@@raise_exceptions
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
private
|
|
147
|
+
def self.raise_not_found(clazz, method)
|
|
148
|
+
raise Workling::WorklingNotFoundError.new("could not find #{ clazz }:#{ method } workling. ")
|
|
149
|
+
end
|
|
150
|
+
end
|