dispatch-rider 1.4.0 → 1.4.2

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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +20 -0
  3. data/.hound.yml +2 -0
  4. data/.rubocop.yml +50 -0
  5. data/.travis.yml +6 -0
  6. data/CHANGELOG.md +363 -0
  7. data/Gemfile +25 -0
  8. data/LICENSE.txt +1 -1
  9. data/README.md +32 -3
  10. data/Rakefile +38 -0
  11. data/dispatch-rider.gemspec +46 -0
  12. data/lib/dispatch-rider/notification_services/aws_sns.rb +9 -0
  13. data/lib/dispatch-rider/version.rb +1 -1
  14. data/spec/fixtures/handlers/another_test_handler.rb +2 -0
  15. data/spec/fixtures/handlers/test_handler.rb +2 -0
  16. data/spec/lib/dispatch-rider/airbrake_error_handler_spec.rb +16 -0
  17. data/spec/lib/dispatch-rider/callbacks/access_spec.rb +62 -0
  18. data/spec/lib/dispatch-rider/callbacks/storage_spec.rb +43 -0
  19. data/spec/lib/dispatch-rider/configuration_spec.rb +74 -0
  20. data/spec/lib/dispatch-rider/default_error_handler_spec.rb +14 -0
  21. data/spec/lib/dispatch-rider/demultiplexer_spec.rb +117 -0
  22. data/spec/lib/dispatch-rider/dispatcher_spec.rb +69 -0
  23. data/spec/lib/dispatch-rider/handlers/base_spec.rb +81 -0
  24. data/spec/lib/dispatch-rider/handlers/inheritance_tracking_spec.rb +27 -0
  25. data/spec/lib/dispatch-rider/message_spec.rb +59 -0
  26. data/spec/lib/dispatch-rider/notification_services/aws_sns_spec.rb +28 -0
  27. data/spec/lib/dispatch-rider/notification_services/base_spec.rb +65 -0
  28. data/spec/lib/dispatch-rider/notification_services/file_system/channel_spec.rb +28 -0
  29. data/spec/lib/dispatch-rider/notification_services/file_system/notifier_spec.rb +14 -0
  30. data/spec/lib/dispatch-rider/notification_services/file_system_spec.rb +23 -0
  31. data/spec/lib/dispatch-rider/notification_services_spec.rb +4 -0
  32. data/spec/lib/dispatch-rider/publisher/base_spec.rb +79 -0
  33. data/spec/lib/dispatch-rider/publisher/configuration/destination_spec.rb +100 -0
  34. data/spec/lib/dispatch-rider/publisher/configuration/notification_service_spec.rb +53 -0
  35. data/spec/lib/dispatch-rider/publisher/configuration_reader_spec.rb +129 -0
  36. data/spec/lib/dispatch-rider/publisher/configuration_spec.rb +149 -0
  37. data/spec/lib/dispatch-rider/publisher/configuration_support_spec.rb +89 -0
  38. data/spec/lib/dispatch-rider/publisher_spec.rb +123 -0
  39. data/spec/lib/dispatch-rider/queue_services/aws_sqs_spec.rb +193 -0
  40. data/spec/lib/dispatch-rider/queue_services/base_spec.rb +147 -0
  41. data/spec/lib/dispatch-rider/queue_services/file_system_spec.rb +88 -0
  42. data/spec/lib/dispatch-rider/queue_services/received_message_spec.rb +23 -0
  43. data/spec/lib/dispatch-rider/queue_services/simple_spec.rb +63 -0
  44. data/spec/lib/dispatch-rider/queue_services_spec.rb +6 -0
  45. data/spec/lib/dispatch-rider/registrars/base_spec.rb +68 -0
  46. data/spec/lib/dispatch-rider/registrars/file_system_channel_spec.rb +12 -0
  47. data/spec/lib/dispatch-rider/registrars/handler_spec.rb +16 -0
  48. data/spec/lib/dispatch-rider/registrars/notification_service_spec.rb +13 -0
  49. data/spec/lib/dispatch-rider/registrars/publishing_destination_spec.rb +11 -0
  50. data/spec/lib/dispatch-rider/registrars/queue_service_spec.rb +13 -0
  51. data/spec/lib/dispatch-rider/registrars/sns_channel_spec.rb +14 -0
  52. data/spec/lib/dispatch-rider/registrars_spec.rb +4 -0
  53. data/spec/lib/dispatch-rider/runner_spec.rb +25 -0
  54. data/spec/lib/dispatch-rider/subscriber_spec.rb +140 -0
  55. data/spec/lib/dispatch-rider_spec.rb +27 -0
  56. data/spec/spec_helper.rb +21 -0
  57. metadata +107 -86
data/Gemfile ADDED
@@ -0,0 +1,25 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ # only add dev/test gems here, runtime gems should go in the gemspec
6
+
7
+ # Base
8
+ gem "bundler"
9
+ gem "rake"
10
+
11
+ # Gem Stuff
12
+ gem 'rubygems-tasks'
13
+ gem 'github_changelog_generator'
14
+ gem 'yard'
15
+
16
+ # Testing
17
+ gem "rspec", "~> 2.0"
18
+ gem 'rspec-its', "~> 1.0"
19
+
20
+ # CI
21
+ gem "travis-lint"
22
+
23
+ # Dev/Debugging
24
+ gem "byebug", platform: :ruby_20, require: !ENV['CI']
25
+ gem "aws-sdk", "~> 1"
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 Suman Mukherjee
1
+ Copyright (c) 2015 PayrollHero Pte. Ltd.
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -257,7 +257,7 @@ class ReadNews < DispatchRider::Handlers::Base
257
257
  puts headline
258
258
  end
259
259
  end
260
-
260
+
261
261
  def retry_timeout
262
262
  10.minutes
263
263
  end
@@ -277,7 +277,7 @@ extend_timeout will tell the queue you wish to hold this item longer.
277
277
  class LongRunning < DispatchRider::Handlers::Base
278
278
  def process(body)
279
279
  my_loop.each do |item|
280
-
280
+
281
281
  #... do some work ...
282
282
  extend_timeout(1.hour)
283
283
  end
@@ -344,6 +344,35 @@ DispatchRider.config do |config|
344
344
  config.error_handler = DispatchRider::AirbrakeErrorHandler
345
345
  end
346
346
  ```
347
+ ## Deployment
348
+
349
+ In order to deploy a new version of the gem into the wild ...
350
+
351
+ You will need to configure your github api token for the changelog.
352
+
353
+ Generate a new token for changelogs [here](https://github.com/settings/tokens/new).
354
+
355
+ add:
356
+
357
+ ```bash
358
+ export CHANGELOG_GITHUB_TOKEN=YOUR_CHANGELOG_API_TOKEN
359
+ ```
360
+
361
+ somewhere in your shell init. (ie .zshrc or simillar)
362
+
363
+
364
+ ```bash
365
+ vim lib/dispatch-rider/version.rb
366
+ # set the new version
367
+ rake gemspec
368
+ # commit any changed files (should be only version and the gemspec)
369
+ # name your commit with the version number eg: "1.8.0"
370
+ rake release
371
+ # to push the gem to rubygems.org
372
+ rake changelog
373
+ # commit the changed changelog
374
+ # name your commit with the version again eg: "changelog for 1.8.0"
375
+ ```
347
376
 
348
377
  ## Contributing
349
378
 
@@ -357,7 +386,7 @@ end
357
386
 
358
387
  ### Licence
359
388
 
360
- Copyright (c) 2013 Suman Mukherjee
389
+ Copyright (c) 2015 PayrollHero Pte. Ltd.
361
390
 
362
391
  MIT License
363
392
 
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'bundler'
7
+ rescue LoadError => e
8
+ warn e.message
9
+ warn "Run `gem install bundler` to install Bundler."
10
+ exit -1
11
+ end
12
+
13
+ begin
14
+ Bundler.setup(:development)
15
+ rescue Bundler::BundlerError => e
16
+ warn e.message
17
+ warn "Run `bundle install` to install missing gems."
18
+ exit e.status_code
19
+ end
20
+
21
+ require 'rake'
22
+ require 'rubygems/tasks'
23
+ Gem::Tasks.new
24
+
25
+ require 'rspec/core/rake_task'
26
+ RSpec::Core::RakeTask.new
27
+
28
+ task :test => :spec
29
+ task :default => :spec
30
+
31
+ require 'yard'
32
+ YARD::Rake::YardocTask.new
33
+ task :doc => :yard
34
+
35
+ desc "Updates the changelog"
36
+ task :changelog do
37
+ sh "github_changelog_generator payrollhero/dispatch-rider"
38
+ end
@@ -0,0 +1,46 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path('../lib/dispatch-rider/version', __FILE__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "dispatch-rider"
7
+ gem.version = DispatchRider::VERSION
8
+
9
+ gem.summary = %q{
10
+ Messaging system that is customizable based on which
11
+ queueing system we are using.
12
+ }
13
+ gem.description = %q{
14
+ Messaging system based on the reactor pattern.
15
+
16
+ You can publish messages to a queue and then a demultiplexer
17
+ runs an event loop which pops items from the queue and hands
18
+ it over to a dispatcher.
19
+
20
+ The dispatcher hands over the message to the appropriate
21
+ handler.
22
+
23
+ You can choose your own queueing service.
24
+ }
25
+ gem.license = "MIT"
26
+ gem.authors = [
27
+ "Suman Mukherjee",
28
+ "Dane Natoli",
29
+ "Piotr Banasik",
30
+ "Ronald Maravilla",
31
+ ]
32
+ gem.email = [
33
+ "piotr@payrollhero.com",
34
+ "rmaravilla@payrollhero.com",
35
+ ]
36
+ gem.homepage = "https://github.com/payrollhero/dispatch-rider"
37
+ gem.files = `git ls-files`.split($/)
38
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
39
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
40
+ gem.require_paths = ['lib']
41
+
42
+ gem.add_runtime_dependency('activesupport', ">= 0")
43
+ gem.add_runtime_dependency('activemodel', ">= 0")
44
+ gem.add_runtime_dependency('daemons', ">= 0")
45
+ gem.add_runtime_dependency('retries', ">= 0")
46
+ end
@@ -1,3 +1,4 @@
1
+ require 'retries'
1
2
  # This is a basic implementation of the Notification service using Amazon SNS.
2
3
  # The expected usage is as follows :
3
4
  # notification_service = DispatchRider::NotificationServices::AwsSns.new
@@ -15,6 +16,14 @@ module DispatchRider
15
16
  Registrars::SnsChannel
16
17
  end
17
18
 
19
+ def publish(options)
20
+ channels(options[:to]).each do |channel|
21
+ with_retries(max_retries: 10, rescue: AWS::Errors::MissingCredentialsError) do
22
+ channel.publish(serialize(message(options[:message])))
23
+ end
24
+ end
25
+ end
26
+
18
27
  # not really happy with this, but the notification service registrar system is way too rigid to do this cleaner
19
28
  # since you only can have one notifier for the whole service, but you need to create a new one for each region
20
29
  def channel(name)
@@ -1,4 +1,4 @@
1
1
  # This file specifies the current version of the gem.
2
2
  module DispatchRider
3
- VERSION = "1.4.0"
3
+ VERSION = "1.4.2"
4
4
  end
@@ -0,0 +1,2 @@
1
+ class AnotherTestHandler < DispatchRider::Handlers::Base
2
+ end
@@ -0,0 +1,2 @@
1
+ class TestHandler < DispatchRider::Handlers::Base
2
+ end
@@ -0,0 +1,16 @@
1
+ require "spec_helper"
2
+
3
+ describe DispatchRider::AirbrakeErrorHandler do
4
+
5
+ describe ".call" do
6
+ let(:message){ DispatchRider::Message.new(subject: "TestMessage", body: "foo" )}
7
+ let(:exception){ Exception.new("Something went terribly wrong") }
8
+
9
+ example do
10
+ Airbrake.should_receive(:notify).with(exception, controller: "DispatchRider", action: "TestMessage", parameters: { subject: "TestMessage", body: "foo" }, cgi_data: anything)
11
+
12
+ described_class.call(message, exception)
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ describe DispatchRider::Callbacks::Access do
4
+ describe "#invoke" do
5
+
6
+ let(:callback_a1) { proc { |x| x.call } }
7
+ let(:callback_a2) { proc { |x| x.call } }
8
+ let(:callback_a3) { proc { |x| x.call } }
9
+ let(:callbacks_a) { [callback_a1, callback_a2, callback_a3] }
10
+
11
+ let(:callback_b1) { proc { |x| x.call } }
12
+ let(:callbacks_b) { [callback_b1] }
13
+
14
+ let(:storage) { DispatchRider::Callbacks::Storage.new }
15
+ let(:action) { proc { } }
16
+
17
+ subject { described_class.new(storage) }
18
+
19
+ before do
20
+ callbacks_a.each do |cb|
21
+ storage.around :event1, cb
22
+ end
23
+ callbacks_b.each do |cb|
24
+ storage.around :event2, cb
25
+ end
26
+ end
27
+
28
+ example "a bunch of handlers" do
29
+ callback_a1.should_receive(:call).once.and_call_original
30
+ callback_a2.should_receive(:call).once.and_call_original
31
+ callback_a3.should_receive(:call).once.and_call_original
32
+ callback_b1.should_not_receive(:call)
33
+
34
+ action.should_receive(:call).once.and_call_original
35
+
36
+ subject.invoke(:event1, &action)
37
+ end
38
+
39
+ example "single handler" do
40
+ callback_a1.should_not_receive(:call)
41
+ callback_a2.should_not_receive(:call)
42
+ callback_a3.should_not_receive(:call)
43
+ callback_b1.should_receive(:call).once.and_call_original
44
+
45
+ action.should_receive(:call).once.and_call_original
46
+
47
+ subject.invoke(:event2, &action)
48
+ end
49
+
50
+ example "no handlers" do
51
+ callback_a1.should_not_receive(:call)
52
+ callback_a2.should_not_receive(:call)
53
+ callback_a3.should_not_receive(:call)
54
+ callback_b1.should_not_receive(:call)
55
+
56
+ action.should_receive(:call).once.and_call_original
57
+
58
+ subject.invoke(:event3, &action)
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe DispatchRider::Callbacks::Storage do
4
+
5
+ subject{ described_class.new }
6
+
7
+ describe "adding callbacks" do
8
+
9
+ let!(:log) { [] }
10
+ let(:actual) { proc { log << :actual } }
11
+
12
+ describe "#around" do
13
+ example do
14
+ subject.around(:initialize) do |job|
15
+ log << :abefore
16
+ job.call
17
+ log << :aafter
18
+ end
19
+ subject.for(:initialize).first[actual]
20
+ log.should == [:abefore, :actual, :aafter]
21
+ end
22
+ end
23
+
24
+ describe "#before" do
25
+ example do
26
+ subject.before(:initialize) { log << :before }
27
+ subject.for(:initialize).first[actual]
28
+ log.should == [:before, :actual]
29
+ end
30
+ end
31
+
32
+ describe "#after" do
33
+ example do
34
+ subject.after(:initialize) { log << :after }
35
+ subject.for(:initialize).first[actual]
36
+ log.should == [:actual, :after]
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+
43
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ describe DispatchRider::Configuration do
4
+
5
+ subject{ described_class.new }
6
+
7
+ describe "defaults" do
8
+ example do
9
+ subject.handler_path.should =~ /\/app\/handlers/
10
+ subject.error_handler.should == DispatchRider::DefaultErrorHandler
11
+ subject.queue_kind.should == :file_system
12
+ subject.queue_info.should == { path: "tmp/dispatch-rider-queue" }
13
+ subject.subscriber.should == DispatchRider::Subscriber
14
+ end
15
+ end
16
+
17
+ describe "#before" do
18
+ example do
19
+ subject.should respond_to(:before)
20
+ end
21
+ end
22
+
23
+ describe "#after" do
24
+ example do
25
+ subject.should respond_to(:after)
26
+ end
27
+ end
28
+
29
+ describe "#around" do
30
+ example do
31
+ subject.should respond_to(:around)
32
+ end
33
+ end
34
+
35
+ describe "#handlers" do
36
+ before :each do
37
+ subject.handler_path = "./spec/fixtures/handlers"
38
+ end
39
+
40
+ it "loads the files and converts their names to symbols" do
41
+ subject.handlers.should include(:test_handler, :another_test_handler)
42
+ end
43
+ end
44
+
45
+ describe "#default_retry_timeout" do
46
+ it "sets the default timeout" do
47
+ subject.default_retry_timeout = 60
48
+ TestHandler.instance_methods.should include(:retry_timeout)
49
+ #Need to do this so that all the other tests don't have this as default!
50
+ DispatchRider::Handlers::Base.send(:remove_method,:retry_timeout)
51
+ end
52
+ end
53
+
54
+ describe "#logger" do
55
+
56
+ describe "default" do
57
+ example { subject.logger.should be_kind_of(Logger) }
58
+ end
59
+
60
+ example { subject.should respond_to(:logger) }
61
+ end
62
+
63
+ describe "#logger=" do
64
+ let(:new_logger) { double(:logger) }
65
+
66
+ example { subject.should respond_to(:logger=) }
67
+
68
+ example do
69
+ subject.logger = new_logger
70
+ subject.logger.should == new_logger
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,14 @@
1
+ require "spec_helper"
2
+
3
+ describe DispatchRider::DefaultErrorHandler do
4
+
5
+ describe ".call" do
6
+ let(:exception){ Exception.new("Something went terribly wrong") }
7
+ example do
8
+ expect{
9
+ described_class.call("Error", exception)
10
+ }.to raise_exception exception
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1,117 @@
1
+ require 'spec_helper'
2
+
3
+ describe DispatchRider::Demultiplexer, :nodb => true do
4
+ class TestHandler < DispatchRider::Handlers::Base
5
+ def process(options)
6
+ raise "OMG!!!" if options["raise_exception"]
7
+ end
8
+ end
9
+
10
+ let(:dispatcher) do
11
+ dispatcher = DispatchRider::Dispatcher.new
12
+ dispatcher.register(:test_handler)
13
+ dispatcher
14
+ end
15
+
16
+ let(:queue) do
17
+ DispatchRider::QueueServices::Simple.new
18
+ end
19
+
20
+ let(:message){ DispatchRider::Message.new(:subject => "test_handler", :body => {}) }
21
+
22
+ let(:demultiplexer_thread) do
23
+ demultiplexer
24
+ thread = Thread.new do
25
+ demultiplexer.start
26
+ end
27
+ thread[:demultiplexer] = demultiplexer
28
+ thread
29
+ end
30
+
31
+ let(:error_handler){ ->(message, exception){ raise exception }}
32
+
33
+ subject(:demultiplexer) { DispatchRider::Demultiplexer.new(queue, dispatcher, error_handler) }
34
+
35
+ describe "#initialize" do
36
+ it "should assign the queue" do
37
+ demultiplexer.queue.should be_empty
38
+ end
39
+
40
+ it "should assign the dispatcher" do
41
+ demultiplexer.dispatcher.fetch(:test_handler).should eq(TestHandler)
42
+ end
43
+ end
44
+
45
+ describe "#start" do
46
+ after do
47
+ demultiplexer_thread[:demultiplexer].stop
48
+ demultiplexer_thread.join
49
+ end
50
+
51
+ context "when the queue is not empty" do
52
+
53
+ before do
54
+ queue.push message
55
+ end
56
+
57
+ it "should be sending the message to the dispatcher" do
58
+ demultiplexer.should_receive(:dispatch_message).with(message).at_least(:once)
59
+ demultiplexer_thread.run
60
+ sleep 0.01 # give it a chance to process the job async before killing the demux
61
+ end
62
+
63
+ # THIS ALSO TESTS THAT THE JOB IS NOT RUN MULTIPLE TIMES
64
+ # IF THIS FAILS, BE CAREFUL NOT TO INTRODUCE BUGS
65
+ it "should call the correct handler" do
66
+ TestHandler.any_instance.should_receive(:process).with(message.body).at_least(1).times
67
+ demultiplexer_thread.run
68
+ sleep 0.01 # give it a chance to process the job async before killing the demux
69
+ end
70
+ end
71
+
72
+ context "when the queue is empty" do
73
+ it "should not be sending any message to the dispatcher" do
74
+ demultiplexer.should_receive(:dispatch_message).exactly(0).times
75
+ demultiplexer_thread.run
76
+ end
77
+ end
78
+
79
+ context "when the handler crashes" do
80
+ before do
81
+ message.body = { "raise_exception" => true }
82
+ queue.push message
83
+ end
84
+
85
+ it "should call the error handler" do
86
+ error_handler.should_receive(:call).at_least(:once).and_return(true)
87
+ queue.should_not_receive(:delete)
88
+ demultiplexer_thread.run
89
+ sleep 0.01 # give it a chance to process the job async before killing the demux
90
+ end
91
+ end
92
+
93
+ context "when the queue crashes" do
94
+ before do
95
+ queue.stub(:pop){ raise "OMG!!!"}
96
+ end
97
+
98
+ it "should call the error handler" do
99
+ error_handler.should_receive(:call).once
100
+ demultiplexer_thread.run
101
+
102
+ sleep 0.01 # give it a chance to process the job async before killing the demux
103
+ end
104
+ end
105
+ end
106
+
107
+ describe ".stop" do
108
+ it "should stop the demultiplexer" do
109
+ demultiplexer_thread.run
110
+ demultiplexer_thread.should be_alive # looper should be looping
111
+ demultiplexer_thread[:demultiplexer].stop
112
+ demultiplexer_thread.join
113
+ demultiplexer_thread.should_not be_alive # looper should close the loop
114
+ end
115
+ end
116
+
117
+ end