resque-bus 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.rbenv-version +1 -0
- data/.rvmrc +2 -0
- data/Gemfile +6 -0
- data/MIT-LICENSE +20 -0
- data/README.mdown +152 -0
- data/Rakefile +3 -0
- data/lib/resque-bus.rb +250 -0
- data/lib/resque_bus/application.rb +110 -0
- data/lib/resque_bus/dispatch.rb +59 -0
- data/lib/resque_bus/driver.rb +28 -0
- data/lib/resque_bus/local.rb +32 -0
- data/lib/resque_bus/matcher.rb +81 -0
- data/lib/resque_bus/publisher.rb +9 -0
- data/lib/resque_bus/rider.rb +52 -0
- data/lib/resque_bus/server/views/bus.erb +101 -0
- data/lib/resque_bus/server.rb +29 -0
- data/lib/resque_bus/subscriber.rb +60 -0
- data/lib/resque_bus/subscription.rb +50 -0
- data/lib/resque_bus/subscription_list.rb +49 -0
- data/lib/resque_bus/task_manager.rb +50 -0
- data/lib/resque_bus/tasks.rb +105 -0
- data/lib/resque_bus/util.rb +42 -0
- data/lib/resque_bus/version.rb +5 -0
- data/lib/tasks/resquebus.rake +2 -0
- data/resque-bus.gemspec +34 -0
- data/spec/application_spec.rb +152 -0
- data/spec/dispatch_spec.rb +76 -0
- data/spec/driver_spec.rb +100 -0
- data/spec/integration_spec.rb +53 -0
- data/spec/matcher_spec.rb +143 -0
- data/spec/publish_at_spec.rb +75 -0
- data/spec/publish_spec.rb +48 -0
- data/spec/redis_spec.rb +13 -0
- data/spec/rider_spec.rb +83 -0
- data/spec/spec_helper.rb +75 -0
- data/spec/subscriber_spec.rb +233 -0
- data/spec/subscription_list_spec.rb +43 -0
- data/spec/subscription_spec.rb +53 -0
- metadata +232 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
module ResqueBus
|
2
|
+
class Matcher
|
3
|
+
SPECIAL_PREPEND = "bus_special_value_"
|
4
|
+
attr_reader :filters
|
5
|
+
def initialize(hash)
|
6
|
+
@filters = encode(hash)
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_redis
|
10
|
+
@filters
|
11
|
+
end
|
12
|
+
|
13
|
+
def match?(attribute_name, attributes)
|
14
|
+
mine = filters[attribute_name].to_s
|
15
|
+
return false if mine.size == 0
|
16
|
+
|
17
|
+
given = attributes[attribute_name]
|
18
|
+
case mine
|
19
|
+
when "#{SPECIAL_PREPEND}key"
|
20
|
+
return true if attributes.has_key?(attribute_name)
|
21
|
+
return false
|
22
|
+
when "#{SPECIAL_PREPEND}blank"
|
23
|
+
return true if given.to_s.strip.size == 0
|
24
|
+
return false
|
25
|
+
when "#{SPECIAL_PREPEND}empty"
|
26
|
+
return false if given == nil
|
27
|
+
return true if given.to_s.size == 0
|
28
|
+
return false
|
29
|
+
when "#{SPECIAL_PREPEND}nil"
|
30
|
+
return true if given == nil
|
31
|
+
return false
|
32
|
+
when "#{SPECIAL_PREPEND}value"
|
33
|
+
return false if given == nil
|
34
|
+
return true
|
35
|
+
when "#{SPECIAL_PREPEND}present"
|
36
|
+
return true if given.to_s.strip.size > 0
|
37
|
+
return false
|
38
|
+
end
|
39
|
+
|
40
|
+
given = given.to_s
|
41
|
+
|
42
|
+
return true if mine == given
|
43
|
+
begin
|
44
|
+
# if it's already a regex, don't mess with it
|
45
|
+
# otherwise, it should have start and end line situation
|
46
|
+
if mine[0..6] == "(?-mix:"
|
47
|
+
regex = Regexp.new(mine)
|
48
|
+
else
|
49
|
+
regex = Regexp.new("^#{mine}$")
|
50
|
+
end
|
51
|
+
return !!regex.match(given)
|
52
|
+
rescue
|
53
|
+
return false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def matches?(attributes)
|
58
|
+
return false if filters.empty?
|
59
|
+
return false if attributes == nil
|
60
|
+
|
61
|
+
filters.keys.each do |key|
|
62
|
+
return false unless match?(key, attributes)
|
63
|
+
end
|
64
|
+
|
65
|
+
true
|
66
|
+
end
|
67
|
+
|
68
|
+
def encode(hash)
|
69
|
+
out = {}
|
70
|
+
hash.each do |key, value|
|
71
|
+
case value
|
72
|
+
when :key, :blank, :nil, :present, :empty, :value
|
73
|
+
value = "#{SPECIAL_PREPEND}#{value}"
|
74
|
+
end
|
75
|
+
out[key.to_s] = value.to_s
|
76
|
+
end
|
77
|
+
out
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'resque-retry'
|
2
|
+
|
3
|
+
module ResqueBus
|
4
|
+
# queue'd in each
|
5
|
+
class Rider
|
6
|
+
extend Resque::Plugins::ExponentialBackoff
|
7
|
+
|
8
|
+
def self.perform(attributes = {})
|
9
|
+
sub_key = attributes["bus_rider_sub_key"]
|
10
|
+
app_key = attributes["bus_rider_app_key"]
|
11
|
+
raise "No application key passed" if app_key.to_s == ""
|
12
|
+
raise "No subcription key passed" if sub_key.to_s == ""
|
13
|
+
|
14
|
+
attributes ||= {}
|
15
|
+
|
16
|
+
ResqueBus.log_worker("Rider received: #{app_key} #{sub_key} #{attributes.inspect}")
|
17
|
+
|
18
|
+
# attributes that should be available
|
19
|
+
# attributes["bus_event_type"]
|
20
|
+
# attributes["bus_app_key"]
|
21
|
+
# attributes["bus_published_at"]
|
22
|
+
# attributes["bus_driven_at"]
|
23
|
+
|
24
|
+
# allow the real Reqsue to be used inside the callback while in a worker
|
25
|
+
Resque.redis = ResqueBus.original_redis if ResqueBus.original_redis
|
26
|
+
|
27
|
+
# (now running with the real app that subscribed)
|
28
|
+
ResqueBus.dispatcher_execute(app_key, sub_key, attributes.merge("bus_executed_at" => Time.now.to_i))
|
29
|
+
ensure
|
30
|
+
# put this back if running in the thread
|
31
|
+
Resque.redis = ResqueBus.redis if ResqueBus.original_redis
|
32
|
+
end
|
33
|
+
|
34
|
+
# @failure_hooks_already_ran on https://github.com/defunkt/resque/tree/1-x-stable
|
35
|
+
# to prevent running twice
|
36
|
+
def self.queue
|
37
|
+
@my_queue
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.on_failure_aaa(exception, *args)
|
41
|
+
# note: sorted alphabetically
|
42
|
+
# queue needs to be set for rety to work (know what queue in Requeue.class_to_queue)
|
43
|
+
@my_queue = args[0]["bus_rider_queue"]
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.on_failure_zzz(exception, *args)
|
47
|
+
# note: sorted alphabetically
|
48
|
+
@my_queue = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
|
2
|
+
<script LANGUAGE="JavaScript">
|
3
|
+
<!--
|
4
|
+
function confirmSubmit()
|
5
|
+
{
|
6
|
+
var agree=confirm("Are you sure you wish to continue?");
|
7
|
+
if (agree)
|
8
|
+
return true ;
|
9
|
+
else
|
10
|
+
return false ;
|
11
|
+
}
|
12
|
+
// -->
|
13
|
+
</script>
|
14
|
+
|
15
|
+
<%
|
16
|
+
app_hash = {}
|
17
|
+
event_hash = {}
|
18
|
+
|
19
|
+
# collect each differently
|
20
|
+
ResqueBus::Application.all.each do |app|
|
21
|
+
app_key = app.app_key
|
22
|
+
|
23
|
+
app_hash[app_key] ||= []
|
24
|
+
app.event_display_tuples.each do |tuple|
|
25
|
+
event, queue = tuple
|
26
|
+
app_hash[app_key] << [event, queue]
|
27
|
+
|
28
|
+
event_hash[event] ||= []
|
29
|
+
event_hash[event] << [app_key, queue]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# sort each list item by secondary label
|
34
|
+
event_hash.each do |_, array|
|
35
|
+
array.sort!{ |a,b| a.first <=> b.first }
|
36
|
+
end
|
37
|
+
event_hash.each do |_, array|
|
38
|
+
array.sort!{ |a,b| a.first <=> b.first }
|
39
|
+
end
|
40
|
+
|
41
|
+
# helper to display either
|
42
|
+
def display_row(name, val, button=nil, first=false)
|
43
|
+
form = ""
|
44
|
+
if button
|
45
|
+
text, url = button
|
46
|
+
form = "<form method='POST' action='#{u url}' style='float:left; padding:0 5px 0 0;margin:0;'><input type='submit' name='' value='#{text}' style='padding:0;margin:0;' onclick=\"return confirmSubmit();\"/><input type='hidden' name='name' value='#{h(name)}' /></form>"
|
47
|
+
end
|
48
|
+
|
49
|
+
if !val
|
50
|
+
out = "<td> </td><td> </td>"
|
51
|
+
else
|
52
|
+
detail, queue = val
|
53
|
+
out = "<td>#{h(detail)}</td><td><a href='#{u("queues/#{queue}")}'>#{h(queue)}</a></td>"
|
54
|
+
end
|
55
|
+
|
56
|
+
if first
|
57
|
+
"<tr><td>#{h(name)}#{form}</td>#{out}</tr>\n"
|
58
|
+
else
|
59
|
+
"<tr><td> </td>#{out}</tr>\n"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def output_hash(hash, action=nil)
|
64
|
+
out = ""
|
65
|
+
hash.keys.sort.each do |item|
|
66
|
+
display = hash[item]
|
67
|
+
first = display.shift
|
68
|
+
out << display_row(item, first, action, true)
|
69
|
+
display.each do |val|
|
70
|
+
out << display_row(item, val, action)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
out
|
74
|
+
end
|
75
|
+
%>
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
<h1 class='wi'>Applications</h1>
|
80
|
+
<p class='intro'>The apps below have registered the given event types and queues.</p>
|
81
|
+
<table class='queues'>
|
82
|
+
<tr>
|
83
|
+
<th>App Key</th>
|
84
|
+
<th>Event Type</th>
|
85
|
+
<th>Queue</th>
|
86
|
+
</tr>
|
87
|
+
<%= output_hash(app_hash, ["Unsubscribe", "bus/unsubscribe"]) %>
|
88
|
+
</table>
|
89
|
+
|
90
|
+
<p> </p>
|
91
|
+
|
92
|
+
<h1 class='wi'>Events</h1>
|
93
|
+
<p class='intro'>The event types below have been registered by the given applications and queues.</p>
|
94
|
+
<table class='queues'>
|
95
|
+
<tr>
|
96
|
+
<th>Event Type</th>
|
97
|
+
<th>App Key</th>
|
98
|
+
<th>Queue</th>
|
99
|
+
</tr>
|
100
|
+
<%= output_hash(event_hash, false) %>
|
101
|
+
</table>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'resque-bus'
|
2
|
+
require 'resque/server'
|
3
|
+
|
4
|
+
# Extend Resque::Server to add tabs.
|
5
|
+
module ResqueBus
|
6
|
+
module Server
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.class_eval {
|
10
|
+
|
11
|
+
get "/bus" do
|
12
|
+
erb File.read(File.join(File.dirname(__FILE__), "server/views/bus.erb"))
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
post '/bus/unsubscribe' do
|
17
|
+
app = Application.new(params[:name]).unsubscribe
|
18
|
+
redirect u('bus')
|
19
|
+
end
|
20
|
+
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
Resque::Server.tabs << 'Bus'
|
27
|
+
Resque::Server.class_eval do
|
28
|
+
include ResqueBus::Server
|
29
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module ResqueBus
|
2
|
+
module Subscriber
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
def application(app_key)
|
11
|
+
@app_key = ::ResqueBus::Application.normalize(app_key)
|
12
|
+
end
|
13
|
+
|
14
|
+
def app_key
|
15
|
+
return @app_key if @app_key
|
16
|
+
@app_key = ::ResqueBus.default_app_key
|
17
|
+
return @app_key if @app_key
|
18
|
+
# module or class_name
|
19
|
+
val = self.name.to_s.split("::").first
|
20
|
+
@app_key = ::ResqueBus::Util.underscore(val)
|
21
|
+
end
|
22
|
+
|
23
|
+
def subscribe(method_name, matcher_hash = nil)
|
24
|
+
queue_name = ::Resque.queue_from_class(self)
|
25
|
+
queue_name ||= ::ResqueBus.default_queue
|
26
|
+
queue_name ||= "#{app_key}_default"
|
27
|
+
subscribe_queue(queue_name, method_name, matcher_hash)
|
28
|
+
end
|
29
|
+
|
30
|
+
def subscribe_queue(queue_name, method_name, matcher_hash = nil)
|
31
|
+
klass = self
|
32
|
+
matcher_hash ||= {"bus_event_type" => method_name}
|
33
|
+
sub_key = "#{self.name}.#{method_name}"
|
34
|
+
dispatcher = ::ResqueBus.dispatcher_by_key(app_key)
|
35
|
+
dispatcher.add_subscription(queue_name, sub_key, klass.name.to_s, matcher_hash, lambda{ |att| klass.perform(att) })
|
36
|
+
end
|
37
|
+
|
38
|
+
def transform(method_name)
|
39
|
+
@transform = method_name
|
40
|
+
end
|
41
|
+
def perform(attributes)
|
42
|
+
sub_key = attributes["bus_rider_sub_key"]
|
43
|
+
meth_key = sub_key.split(".").last
|
44
|
+
resque_bus_execute(meth_key, attributes)
|
45
|
+
end
|
46
|
+
|
47
|
+
def resque_bus_execute(key, attributes)
|
48
|
+
args = attributes
|
49
|
+
args = send(@transform, attributes) if @transform
|
50
|
+
args = [args] unless args.is_a?(Array)
|
51
|
+
if self.respond_to?(:subscriber_with_attributes)
|
52
|
+
me = self.subscriber_with_attributes(attributes)
|
53
|
+
else
|
54
|
+
me = self.new
|
55
|
+
end
|
56
|
+
me.send(key, *args)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ResqueBus
|
2
|
+
class Subscription
|
3
|
+
def self.register(queue, key, class_name, matcher, block)
|
4
|
+
Subscription.new(queue, key, class_name, matcher, block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.from_redis(hash)
|
8
|
+
queue_name = hash["queue_name"].to_s
|
9
|
+
key = hash["key"].to_s
|
10
|
+
class_name = hash["class"].to_s
|
11
|
+
matcher = hash["matcher"]
|
12
|
+
return nil if key.length == 0 || queue_name.length == 0
|
13
|
+
Subscription.new(queue_name, key, class_name, matcher, nil)
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_redis
|
17
|
+
out = {}
|
18
|
+
out["queue_name"] = queue_name
|
19
|
+
out["key"] = key
|
20
|
+
out["class"] = class_name
|
21
|
+
out["matcher"] = matcher.to_redis
|
22
|
+
out
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :matcher, :executor, :queue_name, :key, :class_name
|
26
|
+
attr_accessor :app_key # dyanmically set on return from subscription_matches
|
27
|
+
def initialize(queue_name, key, class_name, filters, executor=nil)
|
28
|
+
@queue_name = self.class.normalize(queue_name)
|
29
|
+
@key = key.to_s
|
30
|
+
@class_name = class_name.to_s
|
31
|
+
@matcher = Matcher.new(filters)
|
32
|
+
@executor = executor
|
33
|
+
end
|
34
|
+
|
35
|
+
def execute!(attributes)
|
36
|
+
attributes = attributes.with_indifferent_access if attributes.respond_to?(:with_indifferent_access)
|
37
|
+
executor.call(attributes)
|
38
|
+
end
|
39
|
+
|
40
|
+
def matches?(attributes)
|
41
|
+
@matcher.matches?(attributes)
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def self.normalize(val)
|
47
|
+
val.to_s.gsub(/\W/, "_").downcase
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module ResqueBus
|
2
|
+
class SubscriptionList
|
3
|
+
def self.from_redis(redis_hash)
|
4
|
+
out = SubscriptionList.new
|
5
|
+
|
6
|
+
redis_hash.each do |key, value|
|
7
|
+
sub = Subscription.from_redis(value)
|
8
|
+
out.add(sub) if sub
|
9
|
+
end
|
10
|
+
out
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_redis
|
14
|
+
out = {}
|
15
|
+
@subscriptions.values.each do |sub|
|
16
|
+
out[sub.key] = sub.to_redis
|
17
|
+
end
|
18
|
+
out
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@subscriptions = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def add(sub)
|
26
|
+
@subscriptions[sub.key] = sub
|
27
|
+
end
|
28
|
+
|
29
|
+
def size
|
30
|
+
@subscriptions.size
|
31
|
+
end
|
32
|
+
|
33
|
+
def key(key)
|
34
|
+
@subscriptions[key.to_s]
|
35
|
+
end
|
36
|
+
|
37
|
+
def all
|
38
|
+
@subscriptions.values
|
39
|
+
end
|
40
|
+
|
41
|
+
def matches(attributes)
|
42
|
+
out = []
|
43
|
+
all.each do |sub|
|
44
|
+
out << sub if sub.matches?(attributes)
|
45
|
+
end
|
46
|
+
out
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ResqueBus
|
2
|
+
class TaskManager
|
3
|
+
attr_reader :logging
|
4
|
+
def initialize(logging)
|
5
|
+
@logging = logging
|
6
|
+
end
|
7
|
+
|
8
|
+
def subscribe!
|
9
|
+
count = 0
|
10
|
+
ResqueBus.dispatchers.each do |dispatcher|
|
11
|
+
subscriptions = dispatcher.subscriptions
|
12
|
+
if subscriptions.size > 0
|
13
|
+
count += subscriptions.size
|
14
|
+
log "Subscribing #{dispatcher.app_key} to #{subscriptions.size} subscriptions"
|
15
|
+
app = ResqueBus::Application.new(dispatcher.app_key)
|
16
|
+
app.subscribe(subscriptions, logging)
|
17
|
+
log " ...done"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
count
|
21
|
+
end
|
22
|
+
|
23
|
+
def unsubscribe!
|
24
|
+
count = 0
|
25
|
+
ResqueBus.dispatchers.each do |dispatcher|
|
26
|
+
log "Unsubcribing from #{dispatcher.app_key}"
|
27
|
+
app = ResqueBus::Application.new(dispatcher.app_key)
|
28
|
+
app.unsubscribe
|
29
|
+
count += 1
|
30
|
+
log " ...done"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def queue_names
|
35
|
+
# let's not talk to redis in here. Seems to screw things up
|
36
|
+
queues = []
|
37
|
+
ResqueBus.dispatchers.each do |dispatcher|
|
38
|
+
dispatcher.subscriptions.all.each do |sub|
|
39
|
+
queues << sub.queue_name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
queues.uniq
|
44
|
+
end
|
45
|
+
|
46
|
+
def log(message)
|
47
|
+
puts(message) if logging
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# require 'resquebus/tasks'
|
2
|
+
# will give you the resquebus tasks
|
3
|
+
|
4
|
+
|
5
|
+
require "resque/tasks"
|
6
|
+
namespace :resquebus do
|
7
|
+
|
8
|
+
desc "Setup will configure a resque task to run before resque:work"
|
9
|
+
task :setup => [ :preload ] do
|
10
|
+
|
11
|
+
if ENV['QUEUES'].nil?
|
12
|
+
manager = ::ResqueBus::TaskManager.new(true)
|
13
|
+
queues = manager.queue_names
|
14
|
+
ENV['QUEUES'] = queues.join(",")
|
15
|
+
else
|
16
|
+
queues = ENV['QUEUES'].split(",")
|
17
|
+
end
|
18
|
+
|
19
|
+
if queues.size == 1
|
20
|
+
puts " >> Working Queue : #{queues.first}"
|
21
|
+
else
|
22
|
+
puts " >> Working Queues: #{queues.join(", ")}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "Subscribes this application to ResqueBus events"
|
27
|
+
task :subscribe => [ :preload ] do
|
28
|
+
manager = ::ResqueBus::TaskManager.new(true)
|
29
|
+
count = manager.subscribe!
|
30
|
+
raise "No subscriptions created" if count == 0
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Unsubscribes this application from ResqueBus events"
|
34
|
+
task :unsubscribe => [ :preload ] do
|
35
|
+
require 'resque-bus'
|
36
|
+
manager = ::ResqueBus::TaskManager.new(true)
|
37
|
+
count = manager.unsubscribe!
|
38
|
+
puts "No subscriptions unsubscribed" if count == 0
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Sets the queue to work the driver Use: `rake resquebus:driver resque:work`"
|
42
|
+
task :driver => [ :preload ] do
|
43
|
+
ENV['QUEUES'] = "resquebus_incoming"
|
44
|
+
end
|
45
|
+
|
46
|
+
# Preload app files if this is Rails
|
47
|
+
task :preload do
|
48
|
+
require "resque"
|
49
|
+
require "resque-bus"
|
50
|
+
require "resque/failure/redis"
|
51
|
+
|
52
|
+
# change the namespace to be the ones used by ResqueBus
|
53
|
+
# save the old one for handling later
|
54
|
+
ResqueBus.original_redis = Resque.redis
|
55
|
+
Resque.redis = ResqueBus.redis
|
56
|
+
|
57
|
+
Resque::Failure::MultipleWithRetrySuppression.classes = [Resque::Failure::Redis]
|
58
|
+
Resque::Failure.backend = Resque::Failure::MultipleWithRetrySuppression
|
59
|
+
|
60
|
+
Rake::Task["resque:setup"].invoke # loads the environment and such if defined
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
# examples to test out the system
|
65
|
+
namespace :example do
|
66
|
+
desc "Publishes events to example applications"
|
67
|
+
task :publish => [ "resquebus:preload", "resquebus:setup" ] do
|
68
|
+
which = ["one", "two", "three", "other"][rand(4)]
|
69
|
+
ResqueBus.publish("event_#{which}", { "rand" => rand(99999)})
|
70
|
+
ResqueBus.publish("event_all", { "rand" => rand(99999)})
|
71
|
+
ResqueBus.publish("none_subscribed", { "rand" => rand(99999)})
|
72
|
+
puts "published event_#{which}, event_all, none_subscribed"
|
73
|
+
end
|
74
|
+
|
75
|
+
desc "Sets up an example config"
|
76
|
+
task :register => [ "resquebus:preload"] do
|
77
|
+
ResqueBus.dispatch("example") do
|
78
|
+
subscribe "event_one" do
|
79
|
+
puts "event1 happened"
|
80
|
+
end
|
81
|
+
|
82
|
+
subscribe "event_two" do
|
83
|
+
puts "event2 happened"
|
84
|
+
end
|
85
|
+
|
86
|
+
high "event_three" do
|
87
|
+
puts "event3 happened (high)"
|
88
|
+
end
|
89
|
+
|
90
|
+
low "event_.*" do |attributes|
|
91
|
+
puts "LOG ALL: #{attributes.inspect}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
desc "Subscribes this application to ResqueBus example events"
|
97
|
+
task :subscribe => [ :register, "resquebus:subscribe" ]
|
98
|
+
|
99
|
+
desc "Start a ResqueBus example worker"
|
100
|
+
task :work => [ :register, "resquebus:setup", "resque:work" ]
|
101
|
+
|
102
|
+
desc "Start a ResqueBus example worker"
|
103
|
+
task :driver => [ :register, "resquebus:driver", "resque:work" ]
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module ResqueBus
|
2
|
+
module Util
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def underscore(camel_cased_word)
|
6
|
+
word = camel_cased_word.to_s.dup
|
7
|
+
word.gsub!('::', '/')
|
8
|
+
# word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
|
9
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
|
10
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
11
|
+
word.tr!("-", "_")
|
12
|
+
word.downcase!
|
13
|
+
word
|
14
|
+
end
|
15
|
+
|
16
|
+
def constantize(camel_cased_word)
|
17
|
+
names = camel_cased_word.split('::')
|
18
|
+
names.shift if names.empty? || names.first.empty?
|
19
|
+
|
20
|
+
names.inject(Object) do |constant, name|
|
21
|
+
if constant == Object
|
22
|
+
constant.const_get(name)
|
23
|
+
else
|
24
|
+
candidate = constant.const_get(name)
|
25
|
+
next candidate if constant.const_defined?(name, false)
|
26
|
+
next candidate unless Object.const_defined?(name)
|
27
|
+
|
28
|
+
# Go down the ancestors to check it it's owned
|
29
|
+
# directly before we reach Object or the end of ancestors.
|
30
|
+
constant = constant.ancestors.inject do |const, ancestor|
|
31
|
+
break const if ancestor == Object
|
32
|
+
break ancestor if ancestor.const_defined?(name, false)
|
33
|
+
const
|
34
|
+
end
|
35
|
+
|
36
|
+
# owner is in Object, so raise
|
37
|
+
constant.const_get(name, false)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/resque-bus.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "resque_bus/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "resque-bus"
|
7
|
+
s.version = Resque::Bus::VERSION
|
8
|
+
s.authors = ["Brian Leonard"]
|
9
|
+
s.email = ["brian@bleonard.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{A simple event bus on top of Resque}
|
12
|
+
s.description = %q{A simple event bus on top of Resque.
|
13
|
+
Publish and subscribe to events as they occur through a queue.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "resque-bus"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
# specify any dependencies here; for example:
|
23
|
+
# s.add_development_dependency "rspec"
|
24
|
+
# s.add_runtime_dependency "rest-client"
|
25
|
+
s.add_dependency('resque', ['>= 1.10.0', '< 2.0'])
|
26
|
+
s.add_dependency('resque-scheduler', '>= 2.0.1')
|
27
|
+
s.add_dependency('resque-retry')
|
28
|
+
s.add_dependency("redis-namespace")
|
29
|
+
s.add_dependency("redis")
|
30
|
+
|
31
|
+
s.add_development_dependency("rspec")
|
32
|
+
s.add_development_dependency("timecop")
|
33
|
+
s.add_development_dependency("json_pure")
|
34
|
+
end
|