queue-bus 0.9.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f35ec72b2a6256350aabd9f2f6fa6591a1b625f404a3013ea73fb24876fb9e3
4
- data.tar.gz: 00eb73580e6160d3932f43110c182832163ab5d3f9db34d44980b831cf4f7fdc
3
+ metadata.gz: 511b1d30315adbfe76e6fa303aaba3158d56d39bea93355a0d28c18c8b0578cb
4
+ data.tar.gz: ba55e76505d56b98a416ff60c64c2c384734cd18af332768f6631e490caf4ed5
5
5
  SHA512:
6
- metadata.gz: 887ddce8443a1c17bc4db578a8d6b51d1a81906287db94ccd1f47681f30032cf485e1ed30e1a0feb56cc05b17162fae8f2ce28d07bad4b943a6dce7b40302c40
7
- data.tar.gz: 34394be52205690b867bc2402563a9e970fa899250009481ed460f4b4909b8b8f3782bb4c5915d33eaccf6bebf2eebcedf8961628e539af6cde08646517f792f
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 Local Modes
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 method `with_local_mode`:
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
- The previous value of `local_mode` will be restored after the block exits.
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 { |redis| redis.smembers(app_list_key).collect { |val| new(val) } }
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
- # TODO: clean up known queues?
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
- ::QueueBus.redis do |redis|
108
- redis.hgetall(redis_key).each do |key, val|
109
- begin
110
- out[key] = ::QueueBus::Util.decode(val)
111
- rescue ::QueueBus::Util::DecodeException
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
@@ -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
 
@@ -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
@@ -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|
@@ -12,11 +12,19 @@ namespace :queuebus do
12
12
  raise 'No subscriptions created' if count == 0
13
13
  end
14
14
 
15
- desc 'Unsubscribes this application from QueueBus events'
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
- count = manager.unsubscribe!
19
- puts 'No subscriptions unsubscribed' if count == 0
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'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module QueueBus
4
- VERSION = '0.9.1'
4
+ VERSION = '0.13.0'
5
5
  end
@@ -102,16 +102,58 @@ module QueueBus
102
102
  end
103
103
  end
104
104
 
105
- describe '#unsubscribe' do
106
- it 'should remove items' do
107
- QueueBus.redis { |redis| redis.sadd('bus_apps', 'myapp') }
108
- QueueBus.redis { |redis| redis.sadd('bus_apps', 'other') }
109
- QueueBus.redis { |redis| redis.hset('bus_app:myapp', 'event_one', 'myapp_default') }
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
- Application.new('myapp').unsubscribe
135
+ context "when a queue is specified" do
136
+ it "removes only that key" do
137
+ list = SubscriptionList.new
112
138
 
113
- expect(QueueBus.redis { |redis| redis.smembers('bus_apps') }).to eq(['other'])
114
- expect(QueueBus.redis { |redis| redis.get('bus_app:myapp') }).to be_nil
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'
@@ -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.9.1
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: 2020-09-16 00:00:00.000000000 Z
11
+ date: 2021-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json