banter 0.4.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 +7 -0
- data/.gitignore +22 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +113 -0
- data/Rakefile +1 -0
- data/banter.gemspec +37 -0
- data/bin/start_subscribers +14 -0
- data/config/pubsub.yml +17 -0
- data/lib/banter.rb +47 -0
- data/lib/banter/cli.rb +94 -0
- data/lib/banter/configuration.rb +42 -0
- data/lib/banter/context.rb +70 -0
- data/lib/banter/db_logger.rb +52 -0
- data/lib/banter/exceptions/payload_validation_error.rb +4 -0
- data/lib/banter/logger.rb +49 -0
- data/lib/banter/logging.rb +42 -0
- data/lib/banter/message.rb +50 -0
- data/lib/banter/middleware.rb +14 -0
- data/lib/banter/publisher.rb +93 -0
- data/lib/banter/railtie.rb +27 -0
- data/lib/banter/server.rb +7 -0
- data/lib/banter/server/client_queue_listener.rb +56 -0
- data/lib/banter/server/rabbit_mq_subscriber.rb +75 -0
- data/lib/banter/server/subscriber_server.rb +84 -0
- data/lib/banter/subscriber.rb +100 -0
- data/lib/banter/version.rb +3 -0
- data/spec/banter/cli_spec.rb +145 -0
- data/spec/banter/server/client_queue_listener_spec.rb +76 -0
- data/spec/banter/server/client_worker_spec.rb +161 -0
- data/spec/banter/server/rabbitmq_subscriber_spec.rb +5 -0
- data/spec/config.yml +20 -0
- data/spec/logger_spec.rb +110 -0
- data/spec/message_spec.rb +65 -0
- data/spec/spec_helper.rb +49 -0
- metadata +261 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'celluloid/autostart'
|
3
|
+
require 'celluloid/io'
|
4
|
+
require 'awesome_print'
|
5
|
+
require 'active_support/all'
|
6
|
+
require 'optparse'
|
7
|
+
require 'fileutils'
|
8
|
+
|
9
|
+
require_relative './client_queue_listener'
|
10
|
+
|
11
|
+
# In order to use the server, the caller must be able to bring in the necessary
|
12
|
+
# classes for require before instantiating the instance.
|
13
|
+
|
14
|
+
module Banter
|
15
|
+
module Server
|
16
|
+
|
17
|
+
class SubscriberServer
|
18
|
+
include Celluloid
|
19
|
+
|
20
|
+
def initialize(subscribers)
|
21
|
+
@workers = subscribers.map { |subscriber| create_queue_listeners(subscriber) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def start
|
25
|
+
@workers.each do |worker|
|
26
|
+
puts "Starting worker: #{worker.worker_class.name}"
|
27
|
+
worker.start
|
28
|
+
end
|
29
|
+
|
30
|
+
thread = Thread.current
|
31
|
+
interrupts = ["HUP", "INT", "QUIT", "ABRT", "TERM"]
|
32
|
+
interrupts.each do |signal_name|
|
33
|
+
Signal.trap(signal_name) {
|
34
|
+
puts "Processing #{signal_name}"
|
35
|
+
thread.run
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
Thread.stop
|
40
|
+
|
41
|
+
::Banter::Logger.new.log_service("all_services", :warn, "Starting shutdown of all services")
|
42
|
+
|
43
|
+
@workers.each do |worker|
|
44
|
+
::Banter::Logger.new.log_service("all_services", :warn, "Tearing down worker: #{worker.worker_class.name}")
|
45
|
+
begin
|
46
|
+
STDOUT.puts "Tearing down subscriber for #{worker.worker_class.name}"
|
47
|
+
worker.shutdown
|
48
|
+
rescue => e
|
49
|
+
::Banter::Logger.new.log_service("all_services", :warn, "#{worker.worker_class.name} - did not tear down correctly. Error - #{e.message}")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
ensure
|
53
|
+
Banter::CLI.instance.remove_pid
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def warn(message)
|
59
|
+
old_behavior = ActiveSupport::Deprecation.behavior
|
60
|
+
ActiveSupport::Deprecation.behavior = [:stderr, :log]
|
61
|
+
ActiveSupport::Deprecation.warn(message)
|
62
|
+
ensure
|
63
|
+
ActiveSupport::Deprecation.behavior = old_behavior
|
64
|
+
end
|
65
|
+
|
66
|
+
def create_queue_listeners(subscriber)
|
67
|
+
routing_key = subscriber.subscribed_key
|
68
|
+
subscribed_queue_name = subscriber.subscribed_queue
|
69
|
+
|
70
|
+
if routing_key.blank?
|
71
|
+
raise ArgumentError.new("Routing key must be provided in #{subscriber.name} using `subscribe_to routing_key`")
|
72
|
+
end
|
73
|
+
|
74
|
+
if subscribed_queue_name.blank?
|
75
|
+
raise ArgumentError.new("Queue Name must be provided in #{subscriber.name} using `subscribe_to routing_key, on: queue_name`")
|
76
|
+
end
|
77
|
+
|
78
|
+
STDOUT.puts "Setting up listener for request_key: #{routing_key} and queue:#{subscribed_queue_name}"
|
79
|
+
ClientQueueListener.new(subscriber, routing_key, subscribed_queue_name)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
|
4
|
+
# This class provders the worker for executing the subscriber. It receives a message from rabbitmq and
|
5
|
+
# creates an instance of the subscriber to execute with the message and context
|
6
|
+
|
7
|
+
module Banter
|
8
|
+
class Subscriber
|
9
|
+
@@registered_subscribers = []
|
10
|
+
|
11
|
+
class_attribute :payload_validators, :error_handlers, :subscribed_key, :subscribed_queue
|
12
|
+
attr_accessor :delivery_routing_data, :delivery_properties, :context
|
13
|
+
|
14
|
+
def self.inherited(klass)
|
15
|
+
@@registered_subscribers << klass
|
16
|
+
end
|
17
|
+
|
18
|
+
# Specify the routing key that the subscriber class should listen to.
|
19
|
+
# @param [String] routing_key_name The routing key to subscribe to. Must be characters only separated by periods (.)
|
20
|
+
# @param [Hash] options Allowed option is :on to optionally specify a queue. If not provided, queue name is generated from the routing key
|
21
|
+
def self.subscribe_to(routing_key_name, options = {})
|
22
|
+
options.assert_valid_keys(:on)
|
23
|
+
unless validate_routing_key_name(routing_key_name)
|
24
|
+
raise ArgumentError.new("#{routing_key_name} is not supported. Only lower case characters separated by periods are allowed.")
|
25
|
+
end
|
26
|
+
self.subscribed_key = routing_key_name
|
27
|
+
self.subscribed_queue = generated_queue_name(routing_key_name, options[:on])
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sets the validator for payload
|
31
|
+
#
|
32
|
+
# @param validator The validator to use for validating the payload.
|
33
|
+
# Returns false if the payload is not valid.
|
34
|
+
# Proc must accept a payload as an argument.
|
35
|
+
def self.validates_payload_with(*validators)
|
36
|
+
self.payload_validators ||= []
|
37
|
+
self.payload_validators += validators
|
38
|
+
end
|
39
|
+
|
40
|
+
# Sets an error handler for the class
|
41
|
+
def self.handle_errors_with(handler)
|
42
|
+
error_handler = handler
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param [Hash] context Context from invocation
|
46
|
+
# @param [Object] delivery_routing_data Contains routing information like originator and routing key
|
47
|
+
# @param [Object] delivery_properties
|
48
|
+
def initialize(delivery_routing_data, delivery_properties, context)
|
49
|
+
@delivery_routing_data = delivery_routing_data
|
50
|
+
@delivery_properties = delivery_properties
|
51
|
+
@context = context
|
52
|
+
end
|
53
|
+
|
54
|
+
# Performs validation if validates_payload_with is defined and then calls the perform method
|
55
|
+
# @param [Object] payload Payload of the message
|
56
|
+
def perform!(payload)
|
57
|
+
if !valid_payload?(payload)
|
58
|
+
Banter.logger.error("Payload validation failed for #{self.class.name}")
|
59
|
+
raise ::Banter::PayloadValidationError.new("Invalid Payload for #{self.class.name}")
|
60
|
+
end
|
61
|
+
|
62
|
+
perform(context, payload)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Actual subscribers need to implement perform method. This is the method where the message is actually processed.
|
66
|
+
# @param [Object] payload Payload of the message
|
67
|
+
def perform(payload)
|
68
|
+
raise "Need implementation for your worker."
|
69
|
+
end
|
70
|
+
|
71
|
+
# @return [String] The original routing key with which the current message was published
|
72
|
+
def routing_key
|
73
|
+
delivery_routing_data[:routing_key]
|
74
|
+
end
|
75
|
+
|
76
|
+
# Iterates over all the payload validators and returns false if any of them are false
|
77
|
+
# @param [Object] payload The payload/arguments of the message
|
78
|
+
# @return [Boolen] Should return true or false value - If no validators are specified, then returns true
|
79
|
+
def valid_payload?(payload)
|
80
|
+
return true unless payload_validators.present?
|
81
|
+
|
82
|
+
payload_validators.inject(true) { |is_valid, validator|
|
83
|
+
is_valid && (validator.respond_to?(:call) ? validator.call(payload) : send(validator, payload))
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def self.validate_routing_key_name(key)
|
90
|
+
return true if key.blank?
|
91
|
+
key.match(/\A([a-z]+\.?)*([a-z]+)\Z/).present?
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.generated_queue_name(routing_key, queue_name)
|
95
|
+
return queue_name if queue_name.present?
|
96
|
+
[Banter::Configuration.application_name.to_s.gsub(/[^\w\_]/, ''), routing_key.gsub(".", '_')].reject(&:blank?).join('_')
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'banter/cli'
|
3
|
+
|
4
|
+
describe Banter::CLI do
|
5
|
+
let(:cli) { Banter::CLI.instance }
|
6
|
+
after(:each) do
|
7
|
+
cli.instance_variable_set(:@subscribers, nil)
|
8
|
+
cli.instance_variable_set(:@pidfile, nil)
|
9
|
+
cli.instance_variable_set(:@require_path, nil)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#parse' do
|
13
|
+
let(:options) { [] }
|
14
|
+
before { cli.parse(options) }
|
15
|
+
|
16
|
+
context "pidfile" do
|
17
|
+
it "works when nothing is passed" do
|
18
|
+
expect(cli.pidfile).to be_nil
|
19
|
+
end
|
20
|
+
|
21
|
+
context "passed in as -P" do
|
22
|
+
let(:options) { [ "-P", "mypidfile.pid"]}
|
23
|
+
it "parses" do
|
24
|
+
expect(cli.pidfile).to eq('mypidfile.pid')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "passed in as --pidfile" do
|
29
|
+
let(:options) { [ "--pidfile", "mypidfile.pid"]}
|
30
|
+
it "parses" do
|
31
|
+
expect(cli.pidfile).to eq('mypidfile.pid')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "only" do
|
37
|
+
it "works when nothing is passed" do
|
38
|
+
expect(cli.subscribers).to be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
context "passed in as -o" do
|
42
|
+
let(:options) { [ "-o", "Foobar,Doobar"]}
|
43
|
+
it "parses" do
|
44
|
+
expect(cli.subscribers).to eq([ 'Foobar', 'Doobar'])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "passed in as --only" do
|
49
|
+
let(:options) { [ "--only", "Foobar,Doobar"]}
|
50
|
+
it "parses" do
|
51
|
+
expect(cli.subscribers).to eq([ 'Foobar', 'Doobar'])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "require" do
|
57
|
+
it "works when nothing is passed" do
|
58
|
+
expect(cli.require_path).to eq(".")
|
59
|
+
end
|
60
|
+
|
61
|
+
context "passed in as -r" do
|
62
|
+
let(:options) { [ "-r", "/path/to/my/file"]}
|
63
|
+
it "parses" do
|
64
|
+
expect(cli.require_path).to eq("/path/to/my/file")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "passed in as --require" do
|
69
|
+
let(:options) { [ "--require", "/path/to/my/file"]}
|
70
|
+
it "parses" do
|
71
|
+
expect(cli.require_path).to eq("/path/to/my/file")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '#run' do
|
78
|
+
describe "high-level steps to be completed" do
|
79
|
+
before {
|
80
|
+
allow(cli).to receive(:load_environment)
|
81
|
+
allow(cli).to receive(:write_pidfile)
|
82
|
+
allow(cli).to receive(:load_subscribers)
|
83
|
+
cli.run
|
84
|
+
}
|
85
|
+
|
86
|
+
it "runs" do
|
87
|
+
expect(cli).to have_received(:load_environment)
|
88
|
+
expect(cli).to have_received(:write_pidfile)
|
89
|
+
expect(cli).to have_received(:load_subscribers)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#write_pidfile' do
|
95
|
+
before {
|
96
|
+
cli.pidfile = pidfile
|
97
|
+
allow(File).to receive(:open)
|
98
|
+
cli.send(:write_pidfile)
|
99
|
+
}
|
100
|
+
context "no pidfile passed" do
|
101
|
+
let(:pidfile) { nil }
|
102
|
+
it "doesn't create a pidfile" do
|
103
|
+
expect(File).to_not have_received(:open)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "pidfile path is supplied" do
|
108
|
+
let(:pidfile) { "/path/to/pidfile" }
|
109
|
+
it 'creates file' do
|
110
|
+
expect(File).to have_received(:open).with(pidfile, 'w')
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe '#load_subscribers' do
|
116
|
+
let(:subscribers) { nil }
|
117
|
+
let(:subscriber_server) { double('subscriber_server')}
|
118
|
+
before {
|
119
|
+
cli.subscribers = subscribers
|
120
|
+
allow(subscriber_server).to receive(:start)
|
121
|
+
allow(Banter::Server::SubscriberServer).to receive(:new).and_return(subscriber_server)
|
122
|
+
cli.send(:load_subscribers)
|
123
|
+
}
|
124
|
+
|
125
|
+
context "No subscribers passed through CLI" do
|
126
|
+
let(:all_subscribers) { Banter::Subscriber.class_variable_get(:@@registered_subscribers) }
|
127
|
+
it "loads all subscribers" do
|
128
|
+
expect(all_subscribers).to include(MyTestSubscriber1)
|
129
|
+
expect(all_subscribers).to include(MyTestSubscriber2)
|
130
|
+
expect(all_subscribers).to include(MyTestSubscriber3)
|
131
|
+
expect(Banter::Server::SubscriberServer).to have_received(:new).with(all_subscribers)
|
132
|
+
expect(subscriber_server).to have_received(:start)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context "Subscribers passed through CLI" do
|
137
|
+
let(:subscribers) { ['MyTestSubscriber1', 'MyTestSubscriber2'] }
|
138
|
+
it "loads all subscribers" do
|
139
|
+
expect(Banter::Server::SubscriberServer).to have_received(:new).with([MyTestSubscriber1, MyTestSubscriber2])
|
140
|
+
expect(subscriber_server).to have_received(:start)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Banter::Server::ClientQueueListener do
|
4
|
+
describe "#message_received" do
|
5
|
+
|
6
|
+
subject { -> { queue_listener.message_received( {}, {routing_key: routing_key}, {context: context, payload: payload}) } }
|
7
|
+
let(:queue_listener) { Banter::Server::ClientQueueListener.new(Klass, routing_key, 'queue') }
|
8
|
+
let(:routing_key) { 'some.test.key' }
|
9
|
+
let(:context) { {} }
|
10
|
+
let(:payload) { {hello: 'moto'} }
|
11
|
+
let(:context_before){}
|
12
|
+
let(:validators){ [] }
|
13
|
+
|
14
|
+
before do
|
15
|
+
context_before
|
16
|
+
Object.const_set :Klass, Class.new(Banter::Subscriber)
|
17
|
+
validators.each do |validator|
|
18
|
+
Klass.validates_payload_with validator
|
19
|
+
end
|
20
|
+
|
21
|
+
klass_instance
|
22
|
+
|
23
|
+
allow(Klass).to receive(:new).and_return(klass_instance)
|
24
|
+
subject.call unless respond_to? :no_call
|
25
|
+
end
|
26
|
+
|
27
|
+
after do
|
28
|
+
Object.send :remove_const, :Klass
|
29
|
+
end
|
30
|
+
|
31
|
+
context "Normal execution" do
|
32
|
+
let!(:klass_instance){
|
33
|
+
instance = Klass.new({},{}, {})
|
34
|
+
allow(instance).to receive(:perform)
|
35
|
+
instance
|
36
|
+
}
|
37
|
+
|
38
|
+
context "test exception" do
|
39
|
+
let(:no_call) { true }
|
40
|
+
|
41
|
+
it { should_not raise_exception }
|
42
|
+
end
|
43
|
+
|
44
|
+
context "call" do
|
45
|
+
it { expect(klass_instance).to have_received(:perform).with({}, payload) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "Exception during execution" do
|
50
|
+
let!(:klass_instance) {
|
51
|
+
instance = Klass.new({},{}, {})
|
52
|
+
allow(instance).to receive(:perform){
|
53
|
+
raise StandardError.new("test")
|
54
|
+
}
|
55
|
+
instance
|
56
|
+
}
|
57
|
+
let(:no_call) { true }
|
58
|
+
|
59
|
+
it { should_not raise_exception }
|
60
|
+
end
|
61
|
+
|
62
|
+
context "Payload error" do
|
63
|
+
let!(:klass_instance) {
|
64
|
+
instance = Klass.new({},{}, {})
|
65
|
+
allow(instance).to receive(:perform)
|
66
|
+
instance
|
67
|
+
}
|
68
|
+
|
69
|
+
let!(:validators) { [lambda{ |payload| false }] }
|
70
|
+
|
71
|
+
let(:context_before){ allow(Airbrake).to receive(:notify)}
|
72
|
+
|
73
|
+
it {expect(Airbrake).to have_received(:notify)}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Banter::Subscriber do
|
4
|
+
before(:each) { Object.const_set :Klass, Class.new(Banter::Subscriber) }
|
5
|
+
after(:each) { Object.send :remove_const, :Klass }
|
6
|
+
|
7
|
+
describe "#routing_key" do
|
8
|
+
subject { Banter::Subscriber.new(routing_data, properties, {}).routing_key }
|
9
|
+
|
10
|
+
let(:properties) { {} }
|
11
|
+
|
12
|
+
context "Routing key is 'honest.test.route'" do
|
13
|
+
let(:routing_data) { {routing_key: "honest.test.route"} }
|
14
|
+
|
15
|
+
it { should eq "honest.test.route" }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "self.validates_payload_with" do
|
20
|
+
before do
|
21
|
+
validators.each do |validator|
|
22
|
+
Klass.validates_payload_with validator
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "One validator" do
|
27
|
+
let(:validators){ [lambda{|payload| true}] }
|
28
|
+
it {
|
29
|
+
expect(Klass.new({},{}, {}).payload_validators.length).to eq 1 }
|
30
|
+
end
|
31
|
+
|
32
|
+
context "Two validators" do
|
33
|
+
let(:validators){ [lambda{|payload| true}, lambda{|payload| true}] }
|
34
|
+
it { expect(Klass.new({},{}, {}).payload_validators.length).to eq 2 }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#valid_payload?' do
|
39
|
+
context
|
40
|
+
subject { Klass.new({},{}, {}).valid_payload?({}) }
|
41
|
+
|
42
|
+
before do
|
43
|
+
validators.each do |validator|
|
44
|
+
Klass.validates_payload_with validator
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "Two validators returning true" do
|
49
|
+
let(:validators){ [lambda{|payload| true}, lambda{|payload| true}] }
|
50
|
+
it { should be true }
|
51
|
+
end
|
52
|
+
|
53
|
+
context "One validator returning true one returning false" do
|
54
|
+
let(:validators){ [lambda{|payload| false}, lambda{|payload| true}] }
|
55
|
+
it { should be false }
|
56
|
+
end
|
57
|
+
|
58
|
+
context "No validators" do
|
59
|
+
let(:validators){ [] }
|
60
|
+
it { should be true }
|
61
|
+
end
|
62
|
+
|
63
|
+
context "validators as symbol" do
|
64
|
+
context "method returns true" do
|
65
|
+
before {
|
66
|
+
Klass.class_variable_set(:@@foo_bar_called, false)
|
67
|
+
Klass.send :define_method, :foo_bar do |payload|
|
68
|
+
Klass.class_variable_set(:@@foo_bar_called, true)
|
69
|
+
true
|
70
|
+
end
|
71
|
+
}
|
72
|
+
let(:validators) { [lambda{|payload| true}, :foo_bar] }
|
73
|
+
it {
|
74
|
+
should be true
|
75
|
+
expect(Klass.class_variable_get(:@@foo_bar_called)).to be true
|
76
|
+
}
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
context "method returns false" do
|
81
|
+
before {
|
82
|
+
Klass.class_variable_set(:@@foo_bar_called, false)
|
83
|
+
Klass.send :define_method, :foo_bar do |payload|
|
84
|
+
Klass.class_variable_set(:@@foo_bar_called, true)
|
85
|
+
false
|
86
|
+
end
|
87
|
+
}
|
88
|
+
let(:validators) { [lambda{|payload| true}, :foo_bar] }
|
89
|
+
it {
|
90
|
+
should be false
|
91
|
+
expect(Klass.class_variable_get(:@@foo_bar_called)).to be true
|
92
|
+
}
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
context "proc returns false" do
|
97
|
+
before {
|
98
|
+
Klass.class_variable_set(:@@foo_bar_called, false)
|
99
|
+
Klass.send :define_method, :foo_bar do |payload|
|
100
|
+
Klass.class_variable_set(:@@foo_bar_called, true)
|
101
|
+
true
|
102
|
+
end
|
103
|
+
}
|
104
|
+
let(:validators) { [lambda{|payload| false}, :foo_bar] }
|
105
|
+
it "method shouldn't be called" do
|
106
|
+
should be false
|
107
|
+
expect(Klass.class_variable_get(:@@foo_bar_called)).to be false
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "#validate_routing_key_name" do
|
115
|
+
it { expect(Klass.send(:validate_routing_key_name, 'abcdef')).to eq(true) }
|
116
|
+
it { expect(Klass.send(:validate_routing_key_name, '')).to eq(true) }
|
117
|
+
it { expect(Klass.send(:validate_routing_key_name, 'abcdef.abcdef')).to eq(true) }
|
118
|
+
it { expect(Klass.send(:validate_routing_key_name, 'abcdef.abcdef.asd')).to eq(true) }
|
119
|
+
it { expect(Klass.send(:validate_routing_key_name, 'abcdef.abcdef/')).to eq(false) }
|
120
|
+
it { expect(Klass.send(:validate_routing_key_name, 'abcdef.abcdef.a123')).to eq(false) }
|
121
|
+
it { expect(Klass.send(:validate_routing_key_name, 'abcdef.')).to eq(false) }
|
122
|
+
it { expect(Klass.send(:validate_routing_key_name, 'abcAf')).to eq(false) }
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "#generated_queue_name" do
|
126
|
+
before(:each) { allow(Banter::Configuration).to receive(:application_name).and_return("app_name")}
|
127
|
+
|
128
|
+
it { expect(Klass.send(:generated_queue_name, 'abcdef', 'queue_name')).to eq('queue_name') }
|
129
|
+
it { expect(Klass.send(:generated_queue_name, 'abcdef', nil)).to eq('app_name_abcdef') }
|
130
|
+
it { expect(Klass.send(:generated_queue_name, 'abcdef.abcd', nil)).to eq('app_name_abcdef_abcd') }
|
131
|
+
it { expect(Klass.send(:generated_queue_name, 'a.b.c', nil)).to eq('app_name_a_b_c') }
|
132
|
+
end
|
133
|
+
|
134
|
+
describe '.subscribe_to' do
|
135
|
+
before(:each) { allow(Banter::Configuration).to receive(:application_name).and_return("app_name")}
|
136
|
+
context 'routing_key without queue_name' do
|
137
|
+
before { Klass.subscribe_to 'a.b.c' }
|
138
|
+
it 'sets the routing key and queue name' do
|
139
|
+
expect(Klass.subscribed_key).to eq('a.b.c')
|
140
|
+
expect(Klass.subscribed_queue).to eq('app_name_a_b_c')
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context 'routing_key with queue_name' do
|
145
|
+
before { Klass.subscribe_to 'a.b.c', on: 'foo_bar' }
|
146
|
+
it 'sets the routing key and queue name' do
|
147
|
+
expect(Klass.subscribed_key).to eq('a.b.c')
|
148
|
+
expect(Klass.subscribed_queue).to eq('foo_bar')
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'invalid routing key' do
|
153
|
+
before { }
|
154
|
+
it 'sets the routing key and queue name' do
|
155
|
+
expect {
|
156
|
+
Klass.subscribe_to 'a.b.c.', on: 'foo_bar'
|
157
|
+
}.to raise_error(ArgumentError)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|