reactor 0.15.1 → 0.16.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
  SHA1:
3
- metadata.gz: c4fa5081f033ae7a323804804d1cbc15a2b8dcf6
4
- data.tar.gz: 9019ae68c897b156598209b62d6e108965431341
3
+ metadata.gz: 84d1960b3d89f85ef116fbf0e2d97d93972cfee9
4
+ data.tar.gz: d6a69705d1496e401a93f8dcd86ab18de483b220
5
5
  SHA512:
6
- metadata.gz: f9fd84fb796b3a8cedd5830c5052d0409ebd6e16f60510ba37725f59a3b6b79690716e16e4c3e012b58e528b197da31682e03b01bf44d699c78b7e500049347f
7
- data.tar.gz: a10b609164beff5b4eb21d6bdc167ef14a3c02a13e5a983eb8a0da257560c6abd34391be108141cd0816a32675d1e8eaa4daebe25530e823c7b527c14e5fb07d
6
+ metadata.gz: 46258859a0c80d002aa59b6e9c9ab382bbacae87219afe46c3507dcf613cd2f3382ec8bb8ebcedd02f1d8792390a55499dceadd4ade4ad0707c30af38503f0b8
7
+ data.tar.gz: 5b388408b18555bb93eda610c29eeef2d2e77798e9c529f3ca5544c9ce382a6d27671c88a2a8128e7c82ab6d3b427409a6555ea28240b4fca8a1035892bd3091
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- reactor (0.15.1)
4
+ reactor (0.16.0)
5
5
  rails
6
6
  sidekiq (> 4.0)
7
7
 
@@ -174,4 +174,4 @@ DEPENDENCIES
174
174
  test_after_commit
175
175
 
176
176
  BUNDLED WITH
177
- 1.15.2
177
+ 1.15.3
data/README.md CHANGED
@@ -234,10 +234,35 @@ end
234
234
 
235
235
  for your testing convenience.
236
236
 
237
+
238
+ ### Production Deployments
239
+
240
+ TLDR; Everything is a Sidekiq::Job, so all the same gotchas apply with regard to removing & renaming jobs that may have a live reference sitting in the queue. (AKA, you'll start seeing 'const undefined' exceptions when the job gets picked up if you've already deleted/renamed the job code.)
241
+
242
+ #### Adding Events and Subscribers
243
+
244
+ This is as easy as write + deploy. Of course your events getting fired won't have a subscriber pick them up until the new subscriber code is deployed in your sidekiq instances, but that's not too surprising.
245
+
246
+ #### Removing Events and Subscribers
247
+
248
+ Removing an event is as simple as deleting the line of code that `publish`es it.
249
+ Removing a subscriber requires awareness of basic Sidekiq principles.
250
+
251
+ **Is the subscriber that you're deleting virtually guaranteed to have a worker for it sitting in the queue when your deletion is deployed?**
252
+
253
+ If yes -> deprecate your subscriber first to ensure there are no references left in Redis. This will prevent Reactor from enqueuing more workers for it and make it safe for you delete in a secondry deploy.
254
+ ```
255
+ on_event :high_frequency_event, :do_something, deprecated: true
256
+ ```
257
+
258
+ If no -> you can probably just delete the subscriber.
259
+ In the worst case scenario, you get some background exceptions for a job you didn't intend to have run anyway. Pick your poison.
260
+
261
+
237
262
  ## Contributing
238
263
 
239
264
  1. Fork it
240
- 2. Create your feature branch (`git checkout -b my-new-feature`)
265
+ 2. Create your feature/fix branch (`git checkout -b my-new-feature`)
241
266
  3. Commit your changes (`git commit -am 'Add some feature'`)
242
267
  4. Push to the branch (`git push origin my-new-feature`)
243
268
  5. Create new Pull Request
@@ -1,7 +1,8 @@
1
1
  module Reactor
2
2
  class Subscription
3
3
 
4
- attr_reader :source, :event_name, :action, :handler_name, :delay, :async, :worker_class
4
+ attr_reader :source, :event_name, :action, :handler_name, :delay, :async, :worker_class,
5
+ :deprecated
5
6
 
6
7
  def self.build_handler_name(event_name, handler_name_option = nil)
7
8
  if handler_name_option
@@ -24,6 +25,7 @@ module Reactor
24
25
 
25
26
  @delay = options[:delay].to_i
26
27
  @async = determine_async(options)
28
+ @deprecated = !!options[:deprecated]
27
29
  build_worker_class
28
30
  end
29
31
 
@@ -82,6 +84,7 @@ module Reactor
82
84
  self.action = subscription.action
83
85
  self.async = subscription.async
84
86
  self.delay = subscription.delay
87
+ self.deprecated = subscription.deprecated
85
88
  end
86
89
  end
87
90
 
@@ -92,6 +95,7 @@ module Reactor
92
95
  self.action = subscription.action
93
96
  self.delay = subscription.delay
94
97
  self.async = subscription.async
98
+ self.deprecated = subscription.deprecated
95
99
  end
96
100
  end
97
101
 
@@ -1,3 +1,3 @@
1
1
  module Reactor
2
- VERSION = "0.15.1"
2
+ VERSION = "0.16.0"
3
3
  end
@@ -0,0 +1,66 @@
1
+ module Reactor
2
+ module Workers
3
+ module Configuration
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ include Sidekiq::Worker
8
+
9
+ CONFIG = [:source, :action, :async, :delay, :deprecated]
10
+
11
+ class_attribute *CONFIG
12
+ end
13
+
14
+ class_methods do
15
+ def configured?
16
+ CONFIG.all? {|field| !self.send(field).nil? }
17
+ end
18
+
19
+ def perform_where_needed(data)
20
+ if deprecated
21
+ return
22
+ elsif delay > 0
23
+ perform_in(delay, data)
24
+ elsif async
25
+ perform_async(data)
26
+ else
27
+ new.perform(data)
28
+ end
29
+ source
30
+ end
31
+ end
32
+
33
+ def configured?
34
+ self.class.configured?
35
+ end
36
+
37
+ def perform(data)
38
+ raise_unconfigured! unless configured?
39
+ return :__perform_aborted__ unless should_perform?
40
+ event = Reactor::Event.new(data)
41
+ if action.is_a?(Symbol)
42
+ source.send(action, event)
43
+ else
44
+ action.call(event)
45
+ end
46
+ end
47
+
48
+ def should_perform?
49
+ if Reactor.test_mode?
50
+ Reactor.test_mode_subscriber_enabled? source
51
+ else
52
+ true
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def raise_unconfigured!
59
+ settings = Hash[CONFIG.map {|s| [s, self.class.send(s)] }]
60
+ raise UnconfiguredWorkerError.new(
61
+ "#{self.class.name} is not properly configured! Here are the settings: #{settings}"
62
+ )
63
+ end
64
+ end
65
+ end
66
+ end
@@ -8,58 +8,8 @@ module Reactor
8
8
  module Workers
9
9
  class EventWorker
10
10
 
11
- include Sidekiq::Worker
11
+ include Reactor::Workers::Configuration
12
12
 
13
- CONFIG = [:source, :action, :async, :delay]
14
-
15
- class_attribute *CONFIG
16
-
17
- def self.configured?
18
- CONFIG.all? {|field| !self.send(field).nil? }
19
- end
20
-
21
- def self.perform_where_needed(data)
22
- if delay > 0
23
- perform_in(delay, data)
24
- elsif async
25
- perform_async(data)
26
- else
27
- new.perform(data)
28
- end
29
- source
30
- end
31
-
32
- def configured?
33
- self.class.configured?
34
- end
35
-
36
- def perform(data)
37
- raise_unconfigured! unless configured?
38
- return :__perform_aborted__ unless should_perform?
39
- event = Reactor::Event.new(data)
40
- if action.is_a?(Symbol)
41
- source.send(action, event)
42
- else
43
- action.call(event)
44
- end
45
- end
46
-
47
- def should_perform?
48
- if Reactor.test_mode?
49
- Reactor.test_mode_subscriber_enabled? source
50
- else
51
- true
52
- end
53
- end
54
-
55
- private
56
-
57
- def raise_unconfigured!
58
- settings = Hash[CONFIG.map {|s| [s, self.class.send(s)] }]
59
- raise UnconfiguredWorkerError.new(
60
- "#{self.class.name} is not properly configured! Here are the settings: #{settings}"
61
- )
62
- end
63
13
  end
64
14
  end
65
15
  end
@@ -6,30 +6,7 @@ module Reactor
6
6
  module Workers
7
7
  class MailerWorker
8
8
 
9
- include Sidekiq::Worker
10
-
11
- CONFIG = [:source, :action, :async, :delay]
12
-
13
- class_attribute *CONFIG
14
-
15
- def self.configured?
16
- CONFIG.all? {|field| field.present? }
17
- end
18
-
19
- def self.perform_where_needed(data)
20
- if delay > 0
21
- perform_in(delay, data)
22
- elsif async
23
- perform_async(data)
24
- else
25
- new.perform(data)
26
- end
27
- source
28
- end
29
-
30
- def configured?
31
- self.class.configured?
32
- end
9
+ include Reactor::Workers::Configuration
33
10
 
34
11
  def perform(data)
35
12
  raise_unconfigured! unless configured?
@@ -59,22 +36,6 @@ module Reactor
59
36
  msg.respond_to?(:deliver_now) || msg.respond_to?(:deliver)
60
37
  end
61
38
 
62
- def should_perform?
63
- if Reactor.test_mode?
64
- Reactor.test_mode_subscriber_enabled? source
65
- else
66
- true
67
- end
68
- end
69
-
70
- private
71
-
72
- def raise_unconfigured!
73
- settings = Hash[CONFIG.map {|s| [s, self.class.send(s)] }]
74
- raise UnconfiguredWorkerError.new(
75
- "#{self.class.name} is not properly configured! Here are the settings: #{settings}"
76
- )
77
- end
78
39
  end
79
40
  end
80
41
  end
data/lib/reactor.rb CHANGED
@@ -5,6 +5,7 @@ require "action_mailer"
5
5
  require "reactor/version"
6
6
  require "reactor/errors"
7
7
  require "reactor/static_subscribers"
8
+ require "reactor/workers/concerns/configuration"
8
9
  require "reactor/workers"
9
10
  require "reactor/subscription"
10
11
  require "reactor/models"
@@ -15,10 +15,14 @@ class Auction < ActiveRecord::Base
15
15
  event.actor.more_puppies! if event.name == 'another_event'
16
16
  end
17
17
 
18
- on_event :cat_delivered, in_memory: true do |event|
18
+ on_event :cat_delivered, async: false do |event|
19
19
  puppies!
20
20
  end
21
21
 
22
+ on_event :a_high_frequency_event, deprecated: true do |event|
23
+ raise 'hell'
24
+ end
25
+
22
26
  def self.ring_bell(event)
23
27
  "ring ring! #{event}"
24
28
  end
@@ -123,19 +127,28 @@ describe Reactor::Subscribable do
123
127
  expect { Reactor::Event.publish :auction }.not_to raise_error
124
128
  end
125
129
 
126
- describe 'in_memory flag' do
127
- it 'doesnt fire perform_async when true' do
130
+ describe 'async flag' do
131
+ it 'doesnt fire perform_async when false' do
128
132
  expect(Auction).to receive(:puppies!)
129
133
  expect(Reactor::StaticSubscribers::Auction::CatDeliveredHandler).not_to receive(:perform_async)
130
134
  Reactor::Event.publish(:cat_delivered)
131
135
  end
132
136
 
133
- it 'fires perform_async when falsey' do
137
+ it 'fires perform_async when true / default' do
134
138
  expect(Reactor::StaticSubscribers::Auction::WildcardHandler).to receive(:perform_async)
135
139
  Reactor::Event.publish(:puppy_delivered)
136
140
  end
137
141
  end
138
142
 
143
+ describe 'deprecate flag for high-frequency events in production deployments' do
144
+ it 'doesnt enqueue subscriber worker when true' do
145
+ # so subscriber can be safely deleted in next deploy
146
+ expect {
147
+ Reactor::Event.publish(:a_high_frequency_event)
148
+ }.to_not raise_exception
149
+ end
150
+ end
151
+
139
152
  describe '#perform' do
140
153
  around(:each) do |example|
141
154
  Reactor.in_test_mode { example.run }
data/spec/spec_helper.rb CHANGED
@@ -3,6 +3,7 @@ require 'bundler/setup'
3
3
  require 'pry'
4
4
 
5
5
  require 'support/active_record'
6
+ require 'support/shared_examples'
6
7
  require 'sidekiq'
7
8
  require 'sidekiq/testing/inline'
8
9
  require 'sidekiq/api'
@@ -0,0 +1,47 @@
1
+ shared_examples_for 'configurable subscriber worker' do
2
+ describe '.configured?' do
3
+ context 'for unconfigured class' do
4
+ subject { FailingEventWorker.configured? }
5
+
6
+ it { is_expected.to eq(false) }
7
+ end
8
+
9
+ context 'for configured class' do
10
+ subject { MyEventWorker.configured? }
11
+
12
+ it { is_expected.to eq(true) }
13
+ end
14
+ end
15
+
16
+ describe '.perform_where_needed?' do
17
+ context 'for delayed worker' do
18
+ let(:klass) { MyDelayedWorker }
19
+ subject { klass.perform_where_needed(event_data) }
20
+
21
+ it 'uses perform_in to delay execution' do
22
+ expect(klass).to receive(:perform_in).with(1, event_data)
23
+ subject
24
+ end
25
+ end
26
+
27
+ context 'for async workers' do
28
+ let(:klass) { MyEventWorker }
29
+ subject { klass.perform_where_needed(event_data) }
30
+
31
+ it 'uses perform_async to execute wherever' do
32
+ expect(klass).to receive(:perform_async).with(event_data)
33
+ subject
34
+ end
35
+ end
36
+
37
+ context 'for immediate workers' do
38
+ let(:klass) { MyImmediateWorker }
39
+ subject { klass.perform_where_needed(event_data) }
40
+
41
+ it 'creates and executes new instance' do
42
+ expect_any_instance_of(klass).to receive(:perform).with(event_data)
43
+ subject
44
+ end
45
+ end
46
+ end
47
+ end
@@ -16,6 +16,7 @@ class MyEventWorker < Reactor::Workers::EventWorker
16
16
  self.action = :fire_worker
17
17
  self.async = true
18
18
  self.delay = 0
19
+ self.deprecated = false
19
20
  end
20
21
 
21
22
  class MyBlockWorker < Reactor::Workers::EventWorker
@@ -23,6 +24,7 @@ class MyBlockWorker < Reactor::Workers::EventWorker
23
24
  self.action = lambda { |event| :block_ran }
24
25
  self.async = true
25
26
  self.delay = 0
27
+ self.deprecated = false
26
28
  end
27
29
 
28
30
  class MyDelayedWorker < Reactor::Workers::EventWorker
@@ -30,6 +32,7 @@ class MyDelayedWorker < Reactor::Workers::EventWorker
30
32
  self.action = :fire_worker
31
33
  self.async = true
32
34
  self.delay = 1 # seconds
35
+ self.deprecated = false
33
36
  end
34
37
 
35
38
  class MyImmediateWorker < Reactor::Workers::EventWorker
@@ -37,57 +40,14 @@ class MyImmediateWorker < Reactor::Workers::EventWorker
37
40
  self.action = :fire_worker
38
41
  self.async = false
39
42
  self.delay = 0
43
+ self.deprecated = false
40
44
  end
41
45
 
42
46
  describe Reactor::Workers::EventWorker do
43
47
  let(:event_name) { :fire_worker }
44
48
  let(:event_data) { Hash[my_event_data: true] }
45
49
 
46
- describe '.configured?' do
47
- context 'for unconfigured class' do
48
- subject { FailingEventWorker.configured? }
49
-
50
- it { is_expected.to eq(false) }
51
- end
52
-
53
- context 'for configured class' do
54
- subject { MyEventWorker.configured? }
55
-
56
- it { is_expected.to eq(true) }
57
- end
58
- end
59
-
60
- describe '.perform_where_needed?' do
61
- context 'for delayed worker' do
62
- let(:klass) { MyDelayedWorker }
63
- subject { klass.perform_where_needed(event_data) }
64
-
65
- it 'uses perform_in to delay execution' do
66
- expect(klass).to receive(:perform_in).with(1, event_data)
67
- subject
68
- end
69
- end
70
-
71
- context 'for async workers' do
72
- let(:klass) { MyEventWorker }
73
- subject { klass.perform_where_needed(event_data) }
74
-
75
- it 'uses perform_async to execute wherever' do
76
- expect(klass).to receive(:perform_async).with(event_data)
77
- subject
78
- end
79
- end
80
-
81
- context 'for immediate workers' do
82
- let(:klass) { MyImmediateWorker }
83
- subject { klass.perform_where_needed(event_data) }
84
-
85
- it 'creates and executes new instance' do
86
- expect_any_instance_of(klass).to receive(:perform).with(event_data)
87
- subject
88
- end
89
- end
90
- end
50
+ it_behaves_like 'configurable subscriber worker'
91
51
 
92
52
  describe '#perform' do
93
53
  let(:klass) { MyEventWorker }
@@ -16,6 +16,7 @@ class MyMailerWorker < Reactor::Workers::MailerWorker
16
16
  self.action = :fire_mailer
17
17
  self.async = false
18
18
  self.delay = 0
19
+ self.deprecated = false
19
20
  end
20
21
 
21
22
  class MyBlockMailerWorker < Reactor::Workers::MailerWorker
@@ -23,6 +24,7 @@ class MyBlockMailerWorker < Reactor::Workers::MailerWorker
23
24
  self.async = false
24
25
  self.delay = 0
25
26
  self.action = lambda { |event| fire_mailer(event) }
27
+ self.deprecated = false
26
28
  end
27
29
 
28
30
  describe Reactor::Workers::MailerWorker do
@@ -34,6 +36,8 @@ describe Reactor::Workers::MailerWorker do
34
36
  allow_any_instance_of(klass).to receive(:should_perform?).and_return(true)
35
37
  end
36
38
 
39
+ it_behaves_like 'configurable subscriber worker'
40
+
37
41
  it 'sends an email from symbol method name' do
38
42
  expect { subject }.to change { ActionMailer::Base.deliveries.count }.by(1)
39
43
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reactor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.1
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - winfred
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2017-07-18 00:00:00.000000000 Z
14
+ date: 2017-08-18 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rails
@@ -228,6 +228,7 @@ files:
228
228
  - lib/reactor/testing/matchers.rb
229
229
  - lib/reactor/version.rb
230
230
  - lib/reactor/workers.rb
231
+ - lib/reactor/workers/concerns/configuration.rb
231
232
  - lib/reactor/workers/database_subscriber_worker.rb
232
233
  - lib/reactor/workers/event_worker.rb
233
234
  - lib/reactor/workers/mailer_worker.rb
@@ -241,6 +242,7 @@ files:
241
242
  - spec/spec_helper.rb
242
243
  - spec/subscription_spec.rb
243
244
  - spec/support/active_record.rb
245
+ - spec/support/shared_examples.rb
244
246
  - spec/workers/database_subscriber_worker_spec.rb
245
247
  - spec/workers/event_worker_spec.rb
246
248
  - spec/workers/mailer_worker_spec.rb
@@ -264,7 +266,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
264
266
  version: '0'
265
267
  requirements: []
266
268
  rubyforge_project:
267
- rubygems_version: 2.6.11
269
+ rubygems_version: 2.6.12
268
270
  signing_key:
269
271
  specification_version: 4
270
272
  summary: Sidekiq/ActiveRecord pubsub lib
@@ -278,6 +280,7 @@ test_files:
278
280
  - spec/spec_helper.rb
279
281
  - spec/subscription_spec.rb
280
282
  - spec/support/active_record.rb
283
+ - spec/support/shared_examples.rb
281
284
  - spec/workers/database_subscriber_worker_spec.rb
282
285
  - spec/workers/event_worker_spec.rb
283
286
  - spec/workers/mailer_worker_spec.rb