resque-bus 0.2.3
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/.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
|