queue-bus 0.9.1 → 0.13.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -0
- data/README.mdown +25 -3
- data/lib/queue-bus.rb +2 -1
- data/lib/queue_bus/application.rb +46 -9
- data/lib/queue_bus/config.rb +22 -1
- data/lib/queue_bus/dispatch.rb +33 -0
- data/lib/queue_bus/publishing.rb +1 -0
- data/lib/queue_bus/task_manager.rb +7 -0
- data/lib/queue_bus/tasks.rb +12 -4
- data/lib/queue_bus/version.rb +1 -1
- data/spec/application_spec.rb +50 -8
- data/spec/config_spec.rb +35 -0
- data/spec/dispatch_spec.rb +131 -0
- data/spec/publish_spec.rb +22 -0
- data/spec/subscription_list_spec.rb +26 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 511b1d30315adbfe76e6fa303aaba3158d56d39bea93355a0d28c18c8b0578cb
|
4
|
+
data.tar.gz: ba55e76505d56b98a416ff60c64c2c384734cd18af332768f6631e490caf4ed5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0f0d47e2be417d33b1f396043fd604ff936d9a1f258550b725673b4a9649d6e6153edaca77a064996ab3f0c43b59849ab04a66c49f3afb4f2d058a7c788f1e3
|
7
|
+
data.tar.gz: 941c73b6e35945a181651bb03746fff2022947fe89102daebd76f72e211150dfd2382ebd41bbd35857b1c5b9a2f099389b25392ba8e0c210615b624ef5898fb0
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [0.13.0]
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- Adds `Dispatch#on_heartbeat` which is a helper function for specifying heartbeat subscriptions.
|
14
|
+
|
15
|
+
## [0.12.0]
|
16
|
+
|
17
|
+
### Changed
|
18
|
+
- Pipelines fetching all queue subscriptions when using `QueueBus::Application.all`
|
19
|
+
|
20
|
+
## [0.11.0]
|
21
|
+
|
22
|
+
### Added
|
23
|
+
|
24
|
+
- Adds `QueueBus.in_context` method. Useful when working with a multithreaded environment to add a description for all events published within this scope.
|
25
|
+
|
26
|
+
## [0.10.0]
|
27
|
+
|
28
|
+
### Added
|
29
|
+
- Ability to unsubscribe from specific queues in an application (`Application#unsubscribe_queue`).
|
30
|
+
- `rake queuebus:unsubscribe` can now take two parameters to unsubscribe from specific queues, e.g. `rake queuebus:unsubscribe[my_app_key, my_queue_name]`.
|
31
|
+
|
9
32
|
## [0.9.1]
|
10
33
|
|
11
34
|
### Added
|
data/README.mdown
CHANGED
@@ -63,6 +63,16 @@ QueueBus.dispatch("app_b") do
|
|
63
63
|
subscribe "my_key", { "user_id" => :present, "page" => "homepage"} do
|
64
64
|
Mixpanel.homepage_action!(attributes["action"])
|
65
65
|
end
|
66
|
+
|
67
|
+
# You may also declare a subscription to heartbeat events. This is a helper function
|
68
|
+
# that works along with subscribe to make scheduling regular events easier.
|
69
|
+
#
|
70
|
+
# minute_interval: Executes every n minutes
|
71
|
+
# hour_interval: Executes every n hours
|
72
|
+
# minute: Executes on this minute
|
73
|
+
# hour: Executes on this hour
|
74
|
+
on_heartbeat "my_heartbeat_event", minute_interval: 5 do |attributes|
|
75
|
+
end
|
66
76
|
end
|
67
77
|
```
|
68
78
|
|
@@ -141,13 +151,16 @@ event to the appropriate code block.
|
|
141
151
|
You can also say `QueueBus.local_mode = :suppress` to turn off publishing altogether.
|
142
152
|
This can be helpful inside some sort of migration, for example.
|
143
153
|
|
144
|
-
#### Thread Safe
|
154
|
+
#### Thread Safe Options
|
145
155
|
|
146
156
|
**!! This is important if you are using workers that utilize multiple threads, such as Sidekiq !!**
|
147
157
|
|
148
158
|
The above setting is global to the ruby process and modifying it will impact all threads that are
|
149
159
|
currently using QueueBus. If you want to isolate a thread or block of code from QueueBus, you can
|
150
|
-
use the
|
160
|
+
use the methods `with_local_mode` or `in_context`:
|
161
|
+
|
162
|
+
|
163
|
+
With local mode
|
151
164
|
|
152
165
|
```ruby
|
153
166
|
QueueBus.with_local_mode(:suppress) do
|
@@ -155,7 +168,16 @@ QueueBus.with_local_mode(:suppress) do
|
|
155
168
|
end
|
156
169
|
```
|
157
170
|
|
158
|
-
|
171
|
+
In context
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
QueueBus.in_context('some_context') do
|
175
|
+
# Context attribute will be set for all events published within this scope.
|
176
|
+
end
|
177
|
+
```
|
178
|
+
|
179
|
+
|
180
|
+
The previous values will be restored after the block exits.
|
159
181
|
|
160
182
|
### TODO
|
161
183
|
|
data/lib/queue-bus.rb
CHANGED
@@ -43,7 +43,8 @@ module QueueBus
|
|
43
43
|
:hostname=, :hostname,
|
44
44
|
:adapter=, :adapter, :has_adapter?,
|
45
45
|
:incoming_queue=, :incoming_queue,
|
46
|
-
:redis, :worker_middleware_stack
|
46
|
+
:redis, :worker_middleware_stack,
|
47
|
+
:context=, :context, :in_context
|
47
48
|
|
48
49
|
def_delegators :_dispatchers, :dispatch, :dispatchers, :dispatcher_by_key, :dispatcher_execute
|
49
50
|
|
@@ -7,7 +7,22 @@ module QueueBus
|
|
7
7
|
class << self
|
8
8
|
def all
|
9
9
|
# note the names arent the same as we started with
|
10
|
-
::QueueBus.redis
|
10
|
+
::QueueBus.redis do |redis|
|
11
|
+
app_keys = redis.smembers(app_list_key)
|
12
|
+
apps = app_keys.collect { |val| new(val) }
|
13
|
+
|
14
|
+
hashes = redis.pipelined do
|
15
|
+
apps.each do |app|
|
16
|
+
redis.hgetall(app.redis_key)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
apps.zip(hashes).each do |app, hash|
|
21
|
+
app._hydrate_redis_hash(hash)
|
22
|
+
end
|
23
|
+
|
24
|
+
apps
|
25
|
+
end
|
11
26
|
end
|
12
27
|
end
|
13
28
|
|
@@ -32,6 +47,7 @@ module QueueBus
|
|
32
47
|
|
33
48
|
::QueueBus.redis do |redis|
|
34
49
|
redis_hash = subscription_list.to_redis
|
50
|
+
|
35
51
|
redis_hash.each do |key, hash|
|
36
52
|
redis.hset(temp_key, key, QueueBus::Util.encode(hash))
|
37
53
|
end
|
@@ -46,8 +62,17 @@ module QueueBus
|
|
46
62
|
true
|
47
63
|
end
|
48
64
|
|
65
|
+
def unsubscribe_queue(queue)
|
66
|
+
# Filters out all subscriptions that match the supplied queue name.
|
67
|
+
::QueueBus.redis do |redis|
|
68
|
+
read_redis_hash.each do |key, hash_details|
|
69
|
+
redis.hdel(redis_key, key) if queue == hash_details["queue_name"]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
49
74
|
def unsubscribe
|
50
|
-
#
|
75
|
+
# Remove everything.
|
51
76
|
::QueueBus.redis do |redis|
|
52
77
|
redis.srem(self.class.app_list_key, app_key)
|
53
78
|
redis.del(redis_key)
|
@@ -80,6 +105,10 @@ module QueueBus
|
|
80
105
|
out
|
81
106
|
end
|
82
107
|
|
108
|
+
def _hydrate_redis_hash(hash)
|
109
|
+
@raw_redis_hash = hash
|
110
|
+
end
|
111
|
+
|
83
112
|
protected
|
84
113
|
|
85
114
|
def self.normalize(val)
|
@@ -104,16 +133,24 @@ module QueueBus
|
|
104
133
|
|
105
134
|
def read_redis_hash
|
106
135
|
out = {}
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
out[key] = val
|
113
|
-
end
|
136
|
+
raw_redis_hash.each do |key, val|
|
137
|
+
begin
|
138
|
+
out[key] = ::QueueBus::Util.decode(val)
|
139
|
+
rescue ::QueueBus::Util::DecodeException
|
140
|
+
out[key] = val
|
114
141
|
end
|
115
142
|
end
|
116
143
|
out
|
117
144
|
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def raw_redis_hash
|
149
|
+
return @raw_redis_hash if @raw_redis_hash
|
150
|
+
|
151
|
+
::QueueBus.redis do |redis|
|
152
|
+
redis.hgetall(redis_key)
|
153
|
+
end
|
154
|
+
end
|
118
155
|
end
|
119
156
|
end
|
data/lib/queue_bus/config.rb
CHANGED
@@ -9,7 +9,7 @@ module QueueBus
|
|
9
9
|
attr_accessor :default_queue, :hostname, :incoming_queue, :logger
|
10
10
|
|
11
11
|
attr_reader :worker_middleware_stack
|
12
|
-
attr_writer :local_mode
|
12
|
+
attr_writer :local_mode, :context
|
13
13
|
|
14
14
|
def initialize
|
15
15
|
@worker_middleware_stack = QueueBus::Middleware::Stack.new
|
@@ -24,6 +24,7 @@ module QueueBus
|
|
24
24
|
Wrap = Struct.new(:value)
|
25
25
|
|
26
26
|
LOCAL_MODE_VAR = :queue_bus_local_mode
|
27
|
+
CONTEXT_VAR = :queue_bus_context
|
27
28
|
|
28
29
|
# Returns the current local mode of QueueBus
|
29
30
|
def local_mode
|
@@ -34,6 +35,15 @@ module QueueBus
|
|
34
35
|
end
|
35
36
|
end
|
36
37
|
|
38
|
+
# Returns the current context of QueueBus
|
39
|
+
def context
|
40
|
+
if Thread.current.thread_variable_get(CONTEXT_VAR).is_a?(Wrap)
|
41
|
+
Thread.current.thread_variable_get(CONTEXT_VAR).value
|
42
|
+
else
|
43
|
+
@context
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
37
47
|
# Overrides the current local mode for the duration of a block. This is a threadsafe
|
38
48
|
# implementation. After, the global setting will be resumed.
|
39
49
|
#
|
@@ -46,6 +56,17 @@ module QueueBus
|
|
46
56
|
Thread.current.thread_variable_set(LOCAL_MODE_VAR, previous)
|
47
57
|
end
|
48
58
|
|
59
|
+
# Overrides the current bus context (if any) for the duration of a block, adding a
|
60
|
+
# `bus_context` attribute set to this value for all events published in this scope.
|
61
|
+
# This is a threadsafe implementation. After, the global setting will be resumed.
|
62
|
+
def in_context(context)
|
63
|
+
previous = Thread.current.thread_variable_get(CONTEXT_VAR)
|
64
|
+
Thread.current.thread_variable_set(CONTEXT_VAR, Wrap.new(context))
|
65
|
+
yield if block_given?
|
66
|
+
ensure
|
67
|
+
Thread.current.thread_variable_set(CONTEXT_VAR, previous)
|
68
|
+
end
|
69
|
+
|
49
70
|
def adapter=(val)
|
50
71
|
raise "Adapter already set to #{@adapter_instance.class.name}" if has_adapter?
|
51
72
|
|
data/lib/queue_bus/dispatch.rb
CHANGED
@@ -16,6 +16,39 @@ module QueueBus
|
|
16
16
|
@subscriptions.size
|
17
17
|
end
|
18
18
|
|
19
|
+
def on_heartbeat(key, minute: nil, hour: nil, minute_interval: nil, hour_interval: nil, &block) # rubocop:disable Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/ParameterLists, Metrics/CyclomaticComplexity, Metrics/AbcSize
|
20
|
+
if minute_interval && !minute_interval.positive?
|
21
|
+
raise ArgumentError, 'minute_interval must be a positive integer'
|
22
|
+
end
|
23
|
+
|
24
|
+
if hour_interval && !hour_interval.positive?
|
25
|
+
raise ArgumentError, 'hour_interval must be a positive integer'
|
26
|
+
end
|
27
|
+
|
28
|
+
matcher = { bus_event_type: :heartbeat_minutes }
|
29
|
+
|
30
|
+
if minute
|
31
|
+
raise ArgumentError, 'minute must be a positive integer' unless minute.positive?
|
32
|
+
|
33
|
+
matcher['minute'] = minute
|
34
|
+
end
|
35
|
+
|
36
|
+
if hour
|
37
|
+
raise ArgumentError, 'hour must be a positive integer' unless hour.positive?
|
38
|
+
|
39
|
+
matcher['hour'] = hour
|
40
|
+
end
|
41
|
+
|
42
|
+
subscribe(key, matcher) do |event|
|
43
|
+
if (minute_interval.nil? || (event['minute'] % minute_interval).zero?) &&
|
44
|
+
(hour_interval.nil? || (event['hour'] % hour_interval).zero?)
|
45
|
+
|
46
|
+
# Yield the block passed in.
|
47
|
+
block.call
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
19
52
|
def subscribe(key, matcher_hash = nil, &block)
|
20
53
|
dispatch_event('default', key, matcher_hash, block)
|
21
54
|
end
|
data/lib/queue_bus/publishing.rb
CHANGED
@@ -30,6 +30,7 @@ module QueueBus
|
|
30
30
|
bus_attr = { 'bus_published_at' => Time.now.to_i, 'bus_event_type' => event_type }
|
31
31
|
bus_attr['bus_id'] = "#{Time.now.to_i}-#{generate_uuid}"
|
32
32
|
bus_attr['bus_app_hostname'] = ::QueueBus.hostname
|
33
|
+
bus_attr['bus_context'] = ::QueueBus.context unless ::QueueBus.context.nil?
|
33
34
|
if defined?(I18n) && I18n.respond_to?(:locale) && I18n.locale
|
34
35
|
bus_attr['bus_locale'] = I18n.locale.to_s
|
35
36
|
end
|
@@ -24,6 +24,13 @@ module QueueBus
|
|
24
24
|
count
|
25
25
|
end
|
26
26
|
|
27
|
+
def unsubscribe_queue!(app_key, queue)
|
28
|
+
log "Unsubcribing #{queue} from #{app_key}"
|
29
|
+
app = ::QueueBus::Application.new(app_key)
|
30
|
+
app.unsubscribe_queue(queue)
|
31
|
+
log " ...done"
|
32
|
+
end
|
33
|
+
|
27
34
|
def unsubscribe!
|
28
35
|
count = 0
|
29
36
|
::QueueBus.dispatchers.each do |dispatcher|
|
data/lib/queue_bus/tasks.rb
CHANGED
@@ -12,11 +12,19 @@ namespace :queuebus do
|
|
12
12
|
raise 'No subscriptions created' if count == 0
|
13
13
|
end
|
14
14
|
|
15
|
-
desc
|
16
|
-
task unsubscribe: [:preload] do
|
15
|
+
desc "Unsubscribes this application from QueueBus events"
|
16
|
+
task :unsubscribe, [:app_key, :queue] => [ :preload ] do |task, args|
|
17
|
+
app_key = args[:app_key]
|
18
|
+
queue = args[:queue]
|
17
19
|
manager = ::QueueBus::TaskManager.new(true)
|
18
|
-
|
19
|
-
|
20
|
+
|
21
|
+
if app_key && queue
|
22
|
+
manager.unsubscribe_queue!(app_key, queue)
|
23
|
+
else
|
24
|
+
manager = ::QueueBus::TaskManager.new(true)
|
25
|
+
count = manager.unsubscribe!
|
26
|
+
puts "No subscriptions unsubscribed" if count == 0
|
27
|
+
end
|
20
28
|
end
|
21
29
|
|
22
30
|
desc 'List QueueBus queues that need worked'
|
data/lib/queue_bus/version.rb
CHANGED
data/spec/application_spec.rb
CHANGED
@@ -102,16 +102,58 @@ module QueueBus
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
-
describe
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
105
|
+
describe "#unsubscribe" do
|
106
|
+
context "when a queue is not specified" do
|
107
|
+
it "removes all subscriptions" do
|
108
|
+
myapp_list = SubscriptionList.new
|
109
|
+
other_list = SubscriptionList.new
|
110
|
+
|
111
|
+
subscription_1 = Subscription.new("myapp_default", "key1", "MyClass1", {"bus_event_type" => "event_one"})
|
112
|
+
subscription_2 = Subscription.new("myapp_default", "key2", "MyClass2", {"bus_event_type" => "event_two"})
|
113
|
+
subscription_3 = Subscription.new("myapp_other_queue", "key1", "MyClass1", {"bus_event_type" => "event_one"})
|
114
|
+
|
115
|
+
myapp_list.add(subscription_1)
|
116
|
+
myapp_list.add(subscription_2)
|
117
|
+
other_list.add(subscription_3)
|
118
|
+
|
119
|
+
Application.new("myapp").subscribe(myapp_list)
|
120
|
+
Application.new("other").subscribe(other_list)
|
121
|
+
|
122
|
+
expect(QueueBus.redis { |redis| redis.hgetall("bus_app:myapp") }).to eq({
|
123
|
+
"key1" => "{\"queue_name\":\"myapp_default\",\"key\":\"key1\",\"class\":\"MyClass1\",\"matcher\":{\"bus_event_type\":\"event_one\"}}",
|
124
|
+
"key2" => "{\"queue_name\":\"myapp_default\",\"key\":\"key2\",\"class\":\"MyClass2\",\"matcher\":{\"bus_event_type\":\"event_two\"}}"
|
125
|
+
})
|
126
|
+
|
127
|
+
Application.new("myapp").unsubscribe
|
128
|
+
|
129
|
+
expect(QueueBus.redis { |redis| redis.smembers("bus_apps") }).to eq(["other"])
|
130
|
+
expect(QueueBus.redis { |redis| redis.hlen("bus_app:myapp") }).to eq(0)
|
131
|
+
expect(QueueBus.redis { |redis| redis.hlen("bus_app:other") }).to eq(1)
|
132
|
+
end
|
133
|
+
end
|
110
134
|
|
111
|
-
|
135
|
+
context "when a queue is specified" do
|
136
|
+
it "removes only that key" do
|
137
|
+
list = SubscriptionList.new
|
112
138
|
|
113
|
-
|
114
|
-
|
139
|
+
subscription_1 = Subscription.new("myapp_default", "key1", "MyClass1", {"bus_event_type" => "event_one"})
|
140
|
+
subscription_2 = Subscription.new("myapp_other_queue", "key2", "MyClass2", {"bus_event_type" => "event_two"})
|
141
|
+
|
142
|
+
list.add(subscription_1)
|
143
|
+
list.add(subscription_2)
|
144
|
+
|
145
|
+
Application.new("myapp").subscribe(list)
|
146
|
+
|
147
|
+
expect(QueueBus.redis { |redis| redis.hgetall("bus_app:myapp") }).to eq({
|
148
|
+
"key1" => "{\"queue_name\":\"myapp_default\",\"key\":\"key1\",\"class\":\"MyClass1\",\"matcher\":{\"bus_event_type\":\"event_one\"}}",
|
149
|
+
"key2" => "{\"queue_name\":\"myapp_other_queue\",\"key\":\"key2\",\"class\":\"MyClass2\",\"matcher\":{\"bus_event_type\":\"event_two\"}}"
|
150
|
+
})
|
151
|
+
|
152
|
+
Application.new("myapp").unsubscribe_queue("myapp_default")
|
153
|
+
|
154
|
+
expect(QueueBus.redis { |redis| redis.smembers("bus_apps") }).to eq(["myapp"])
|
155
|
+
expect(QueueBus.redis { |redis| redis.hgetall("bus_app:myapp") }).to eq({"key2" => "{\"queue_name\":\"myapp_other_queue\",\"key\":\"key2\",\"class\":\"MyClass2\",\"matcher\":{\"bus_event_type\":\"event_two\"}}"})
|
156
|
+
end
|
115
157
|
end
|
116
158
|
end
|
117
159
|
|
data/spec/config_spec.rb
CHANGED
@@ -61,6 +61,41 @@ describe 'QueueBus config' do
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
+
describe '#in_context' do
|
65
|
+
it 'sets the context on the thread' do
|
66
|
+
QueueBus.in_context(:batch_processing) do
|
67
|
+
expect(QueueBus.context).to eq(:batch_processing)
|
68
|
+
Thread.new { expect(QueueBus.context).to eq nil }.join
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'supports nesting' do
|
73
|
+
QueueBus.in_context(:batch_processing) do
|
74
|
+
expect(QueueBus.context).to eq :batch_processing
|
75
|
+
QueueBus.in_context(:processing) do
|
76
|
+
expect(QueueBus.context).to eq :processing
|
77
|
+
end
|
78
|
+
expect(QueueBus.context).to eq :batch_processing
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'respects an override of nil' do
|
83
|
+
QueueBus.context = :batch_processing
|
84
|
+
QueueBus.in_context(nil) do
|
85
|
+
expect(QueueBus.context).to eq nil
|
86
|
+
end
|
87
|
+
QueueBus.context = :batch_processing
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'resets to the original context after the block' do
|
91
|
+
expect(QueueBus.context).to eq nil
|
92
|
+
QueueBus.in_context(:batch_processing) do
|
93
|
+
expect(QueueBus.context).to eq :batch_processing
|
94
|
+
end
|
95
|
+
expect(QueueBus.context).to eq nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
64
99
|
it 'sets the hostname' do
|
65
100
|
expect(QueueBus.hostname).not_to eq(nil)
|
66
101
|
QueueBus.hostname = 'whatever'
|
data/spec/dispatch_spec.rb
CHANGED
@@ -28,6 +28,137 @@ module QueueBus
|
|
28
28
|
end.not_to raise_error
|
29
29
|
end
|
30
30
|
|
31
|
+
describe '#on_heartbeat' do
|
32
|
+
let(:dispatch) { Dispatch.new('heartbeat') }
|
33
|
+
let(:event) { { bus_event_type: :heartbeat_minutes } }
|
34
|
+
let(:event_name) { 'my-event' }
|
35
|
+
|
36
|
+
context 'when not declaring anything' do
|
37
|
+
before do
|
38
|
+
dispatch.on_heartbeat event_name do |_event|
|
39
|
+
Runner2.run({})
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'runs on every heart beat' do
|
44
|
+
(0..24).each do |hour|
|
45
|
+
(0..60).each do |minute|
|
46
|
+
expect do
|
47
|
+
dispatch.execute(
|
48
|
+
event_name, event.merge('hour' => hour, 'minute' => minute)
|
49
|
+
)
|
50
|
+
end.to change(Runner2, :value).by(1)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when running on hour 8' do
|
57
|
+
before do
|
58
|
+
dispatch.on_heartbeat event_name, hour: 8 do |_event|
|
59
|
+
Runner2.run({})
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'subscribes to hour 8' do
|
64
|
+
expect(dispatch.subscriptions.all.first.matcher.filters).to eq('bus_event_type' => 'heartbeat_minutes', 'hour' => '8')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'when running on minute 4' do
|
69
|
+
before do
|
70
|
+
dispatch.on_heartbeat event_name, minute: 4 do |_event|
|
71
|
+
Runner2.run({})
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'subscribes to minute 4' do
|
76
|
+
expect(dispatch.subscriptions.all.first.matcher.filters).to eq('bus_event_type' => 'heartbeat_minutes', 'minute' => '4')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'when running on minute 4 and hour 8' do
|
81
|
+
before do
|
82
|
+
dispatch.on_heartbeat event_name, hour: 8, minute: 4 do |_event|
|
83
|
+
Runner2.run({})
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'subscribes to minute 4 and hour 8' do
|
88
|
+
expect(dispatch.subscriptions.all.first.matcher.filters)
|
89
|
+
.to eq('bus_event_type' => 'heartbeat_minutes', 'minute' => '4', 'hour' => '8')
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when declaring minute intervals' do
|
94
|
+
before do
|
95
|
+
dispatch.on_heartbeat event_name, minute_interval: 5 do |_event|
|
96
|
+
Runner2.run({})
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'runs the runner when the minute buzzes (modulos to 5)' do
|
101
|
+
(0..60).each do |minute|
|
102
|
+
if minute % 5 == 0
|
103
|
+
expect { dispatch.execute(event_name, event.merge('minute' => minute)) }
|
104
|
+
.to change(Runner2, :value).by(1)
|
105
|
+
else
|
106
|
+
expect { dispatch.execute(event_name, event.merge('minute' => minute)) }
|
107
|
+
.not_to change(Runner2, :value)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'when declaring hour intervals' do
|
114
|
+
before do
|
115
|
+
dispatch.on_heartbeat event_name, hour_interval: 3 do |_event|
|
116
|
+
Runner2.run({})
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'runs the runner when the hour fizzes (modulos to 3)' do
|
121
|
+
(0..60).each do |hour|
|
122
|
+
if hour % 3 == 0
|
123
|
+
expect { dispatch.execute(event_name, event.merge('hour' => hour)) }
|
124
|
+
.to change(Runner2, :value).by(1)
|
125
|
+
else
|
126
|
+
expect { dispatch.execute(event_name, event.merge('hour' => hour)) }
|
127
|
+
.not_to change(Runner2, :value)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'when declaring hour and minute intervals' do
|
134
|
+
before do
|
135
|
+
dispatch.on_heartbeat event_name, minute_interval: 5, hour_interval: 3 do |_event|
|
136
|
+
Runner2.run({})
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'runs the runner when the time fizzbuzzes (modulos to 3 and 5)' do
|
141
|
+
(0..24).each do |hour|
|
142
|
+
(0..60).each do |minute|
|
143
|
+
if hour % 3 == 0 && minute % 5 == 0
|
144
|
+
expect do
|
145
|
+
dispatch.execute(
|
146
|
+
event_name, event.merge('hour' => hour, 'minute' => minute)
|
147
|
+
)
|
148
|
+
end.to change(Runner2, :value).by(1)
|
149
|
+
else
|
150
|
+
expect do
|
151
|
+
dispatch.execute(
|
152
|
+
event_name, event.merge('hour' => hour, 'minute' => minute)
|
153
|
+
)
|
154
|
+
end.not_to change(Runner2, :value)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
31
162
|
describe 'Top Level' do
|
32
163
|
before(:each) do
|
33
164
|
QueueBus.dispatch('testit') do
|
data/spec/publish_spec.rb
CHANGED
@@ -71,6 +71,28 @@ describe 'Publishing an event' do
|
|
71
71
|
expect(myval).to eq(1)
|
72
72
|
end
|
73
73
|
|
74
|
+
it 'should add context metadata if wrapping publisher with in_context' do
|
75
|
+
expect(QueueBus.context).to be_nil
|
76
|
+
|
77
|
+
bus_context = 'batch_processing'
|
78
|
+
hash = { :one => 1, 'two' => 'here', 'bus_id' => 'app-given' }
|
79
|
+
|
80
|
+
event_name = 'event_name'
|
81
|
+
|
82
|
+
QueueBus.in_context(:batch_processing) do
|
83
|
+
QueueBus.publish(event_name, hash)
|
84
|
+
end
|
85
|
+
|
86
|
+
val = QueueBus.redis { |redis| redis.lpop('queue:bus_incoming') }
|
87
|
+
hash = JSON.parse(val)
|
88
|
+
|
89
|
+
att = JSON.parse(hash['args'].first)
|
90
|
+
expect(att['bus_context']).to eq(bus_context)
|
91
|
+
|
92
|
+
expect(QueueBus.context).to be_nil
|
93
|
+
|
94
|
+
end
|
95
|
+
|
74
96
|
it 'should set the timezone and locale if available' do
|
75
97
|
expect(defined?(I18n)).to be_nil
|
76
98
|
expect(Time.respond_to?(:zone)).to eq(false)
|
@@ -48,5 +48,31 @@ module QueueBus
|
|
48
48
|
'key2' => { 'queue_name' => 'else_ok', 'key' => 'key2', 'class' => 'MyClass', 'matcher' => { 'bus_event_type' => 'event_two' } })
|
49
49
|
end
|
50
50
|
end
|
51
|
+
|
52
|
+
context "when modifying the subscription" do
|
53
|
+
let(:list) { SubscriptionList.new }
|
54
|
+
let(:subscription_1) { Subscription.new("default", "key1", "MyClass", {"bus_event_type" => "event_one"}) }
|
55
|
+
let(:subscription_2) { Subscription.new("else_ok", "key2", "MyClass", {"bus_event_type" => "event_two"}) }
|
56
|
+
|
57
|
+
context "when adding subscriptions" do
|
58
|
+
it "adds the subscription successfully" do
|
59
|
+
list.add(subscription_1)
|
60
|
+
list.add(subscription_2)
|
61
|
+
|
62
|
+
expect(list.to_redis).to eq(
|
63
|
+
{
|
64
|
+
"key1" => {"queue_name" => "default", "key" => "key1", "class" => "MyClass", "matcher" => {"bus_event_type" => "event_one"}},
|
65
|
+
"key2" => {"queue_name" => "else_ok", "key" => "key2", "class" => "MyClass", "matcher" => {"bus_event_type" => "event_two"}}
|
66
|
+
}
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "errors if the subscription already exists" do
|
71
|
+
list.add(subscription_1)
|
72
|
+
|
73
|
+
expect { list.add(subscription_1) }.to raise_exception(RuntimeError, /Duplicate key/)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
51
77
|
end
|
52
78
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: queue-bus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Leonard
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: multi_json
|