banter 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|