Skip to content

Commit 0bcebe7

Browse files
authored
Merge pull request #14 from ninech/reuse_bunny_connection
Reuse an (existing) Bunny::Session in/with Cony
2 parents 31d701e + 5260c06 commit 0bcebe7

File tree

9 files changed

+228
-133
lines changed

9 files changed

+228
-133
lines changed

README.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ Cony.configure do |config|
3030
end
3131
```
3232

33+
### Using an existing Bunny connection
34+
35+
You can share your already established `Bunny::Session` with Cony.
36+
37+
```ruby
38+
Cony::AMQPConnection.instance.connection = your_connection
39+
```
40+
41+
Cony will only accept the given connection if it's current connection is closed or if there is no current
42+
connection. There will be an error if Cony already has a connection!
43+
This restriction is imposed because else Cony might leak connections.
3344

3445
## Getting Started
3546

@@ -64,7 +75,7 @@ The sent JSON structure will look like this:
6475
{ "description": { "old": null, "new": "value" } }
6576
],
6677
"event": "created",
67-
"model": "Example::Model",
78+
"model": "Example::Model"
6879
}
6980
```
7081

@@ -83,7 +94,7 @@ The sent JSON structure will look like this:
8394
{ "name": { "old": "old-value", "new": "new-value" } }
8495
],
8596
"event": "updated",
86-
"model": "Example::Model",
97+
"model": "Example::Model"
8798
}
8899
```
89100

@@ -102,7 +113,7 @@ The sent JSON structure will look like this:
102113
{ "name": { "old": "value", "new": null } }
103114
],
104115
"event": "destroyed",
105-
"model": "Example::Model",
116+
"model": "Example::Model"
106117
}
107118
```
108119

lib/cony.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,21 @@
99

1010
require 'cony/active_record'
1111

12+
##
13+
# To configure Cony:
14+
# <code>
15+
# Cony.configure do |config|
16+
# config.amqp = {
17+
# host: 'localhost',
18+
# exchange: 'organization.application',
19+
# ssl: true,
20+
# user: 'username',
21+
# pass: 'secret',
22+
# }
23+
# config.test_mode = Rails.env.test?
24+
# # config.durable = false
25+
# end
26+
# </code>
1227
module Cony
1328
include ActiveSupport::Configurable
1429

lib/cony/active_record.rb

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
require 'active_support/core_ext/string/inflections'
22

33
require 'cony'
4-
require 'cony/amqp_connection_handler'
4+
require 'cony/amqp_connection'
55

66
module Cony
7+
##
8+
# Usage:
9+
# <code>
10+
# class FooBar < ActiveRecord::Base
11+
# include Cony::ActiveRecord
12+
# # your things here
13+
# end
14+
# </code>
15+
#
716
module ActiveRecord
8-
917
extend ActiveSupport::Concern
1018

1119
included do
@@ -30,13 +38,11 @@ def cony_save_destroy_notify_data
3038

3139
def cony_publish
3240
return if Cony.config.test_mode
33-
cony_amqp_connection.publish(cony_notify_hash, cony_notify_routing_key)
41+
42+
Cony::AMQPConnection.publish(cony_notify_hash, cony_notify_routing_key)
3443
end
3544

3645
private
37-
def cony_amqp_connection
38-
@cony_amqp_connection ||= Cony::AMQPConnectionHandler.new(Cony.config.amqp)
39-
end
4046

4147
def cony_mapped_changes
4248
changes.map do |name, change|

lib/cony/amqp_connection.rb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
require 'bunny'
2+
require 'json'
3+
require 'singleton'
4+
require 'cony/valid_connection_already_defined'
5+
6+
module Cony
7+
##
8+
# The only place in cony that deals with AMQP connection and it's lifecycle
9+
class AMQPConnection
10+
include Singleton
11+
12+
##
13+
# Shorthand for +Cony::AMQPConnection.instance.publish+
14+
def self.publish(message, routing_key)
15+
instance.publish(message, routing_key)
16+
end
17+
18+
##
19+
# :category: Internal
20+
# Sends the status change message to the configured AMQP destination
21+
def publish(message, routing_key)
22+
exchange.publish(message.to_json,
23+
key: routing_key,
24+
mandatory: false,
25+
immediate: false,
26+
persistent: Cony.config.durable,
27+
content_type: 'application/json')
28+
rescue => error
29+
Airbrake.notify(error) if defined? Airbrake
30+
Rails.logger.error("#{error.class}: #{error}") if defined? Rails
31+
end
32+
33+
##
34+
# Sets a custom connection if no valid_connection? is already provided
35+
def connection=(connection)
36+
fail Cony::ValidConnectionAlreadyDefined, 'A connection has already been set.' if valid_connection_present?
37+
@connection = connection
38+
end
39+
40+
##
41+
# Returns the existing connection or creates a new connection
42+
def connection
43+
return @connection if valid_connection_present?
44+
45+
@connection = Bunny.new Cony.config.amqp
46+
ObjectSpace.define_finalizer(self, proc { cleanup })
47+
@connection.start
48+
end
49+
50+
##
51+
# +false+ if invalid_connection? is +true+
52+
def valid_connection_present?
53+
!@connection.nil? && @connection.open?
54+
end
55+
56+
private
57+
58+
def exchange
59+
connection.
60+
create_channel.
61+
topic(Cony.config.amqp[:exchange], durable: Cony.config.durable)
62+
end
63+
64+
def cleanup
65+
@connection.close if valid_connection_present?
66+
end
67+
end
68+
end

lib/cony/amqp_connection_handler.rb

Lines changed: 0 additions & 28 deletions
This file was deleted.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module Cony
2+
class ValidConnectionAlreadyDefined < StandardError
3+
end
4+
end

spec/cony/active_record_spec.rb

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
require 'cony/active_record'
44

55
describe Cony::ActiveRecord do
6-
7-
let(:amqp_connection) { double('Cony::AMQPConnectionHandler') }
6+
let(:amqp_connection) { double('Cony::AMQPConnection') }
87
let(:id) { 1337 }
9-
let(:active_record_changes) { {name: ['old', 'new']} }
10-
let(:active_record_attributes) { {name: 'value'} }
11-
let(:cony_changes) { [{name: {old: 'old', new: 'new'}}] }
8+
let(:active_record_changes) { { name: %w(old new) } }
9+
let(:active_record_attributes) { { name: 'value' } }
10+
let(:cony_changes) { [{ name: { old: 'old', new: 'new' } }] }
1211
let(:expected_payload) do
1312
{
1413
id: id,
@@ -36,7 +35,7 @@ def attributes; #{active_record_attributes}; end
3635
end
3736

3837
before do
39-
allow(Cony::AMQPConnectionHandler).to receive(:new).and_return(amqp_connection)
38+
allow(Cony::AMQPConnection).to receive(:instance).and_return(amqp_connection)
4039
end
4140

4241
subject { model.new }
@@ -61,7 +60,7 @@ def attributes; #{active_record_attributes}; end
6160

6261
describe '#cony_send_destroy_notify' do
6362
let(:event) { :destroyed }
64-
let(:cony_changes) { [{name: {old: 'value', new: nil}}] }
63+
let(:cony_changes) { [{ name: { old: 'value', new: nil } }] }
6564
it 'uses the amqp connection to send the notify' do
6665
expect(amqp_connection).to receive(:publish).with(expected_payload, 'anonymaus/klass.mutation.destroyed')
6766
subject.cony_save_destroy_notify_data
@@ -74,10 +73,9 @@ def attributes; #{active_record_attributes}; end
7473
allow(Cony.config).to receive(:test_mode).and_return(true)
7574
end
7675
it 'does not send the message' do
77-
expect(Cony::AMQPConnectionHandler).to_not receive(:new)
76+
expect(Cony::AMQPConnection).to_not receive(:instance)
7877
subject.cony_save_create_notify_data
7978
subject.cony_publish
8079
end
8180
end
82-
8381
end

spec/cony/amqp_connection_handler_spec.rb

Lines changed: 0 additions & 87 deletions
This file was deleted.

0 commit comments

Comments
 (0)