philotic 0.0.1 → 0.1.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 +4 -4
- data/.travis.yml +6 -1
- data/Gemfile +0 -7
- data/README.md +2 -0
- data/Rakefile +1 -1
- data/examples/README.md +1 -1
- data/examples/creating_named_queues/manually.rb +8 -21
- data/examples/creating_named_queues/with_rake.rb +2 -2
- data/examples/publishing/publish.rb +26 -33
- data/examples/subscribing/acks.rb +25 -0
- data/examples/subscribing/anonymous_queue.rb +6 -10
- data/examples/subscribing/multiple_named_queues.rb +9 -24
- data/examples/subscribing/named_queue.rb +7 -17
- data/lib/philotic.rb +37 -81
- data/lib/philotic/config.rb +63 -24
- data/lib/philotic/connection.rb +35 -51
- data/lib/philotic/constants.rb +49 -0
- data/lib/philotic/event.rb +27 -14
- data/lib/philotic/logging.rb +8 -0
- data/lib/philotic/logging/event.rb +18 -0
- data/lib/philotic/logging/logger.rb +39 -0
- data/lib/philotic/publisher.rb +28 -31
- data/lib/philotic/routable.rb +40 -40
- data/lib/philotic/subscriber.rb +61 -42
- data/lib/philotic/tasks/init_queues.rb +4 -14
- data/lib/philotic/version.rb +1 -1
- data/philotic.gemspec +21 -10
- data/spec/philotic/config_spec.rb +40 -0
- data/spec/philotic/connection_spec.rb +58 -0
- data/spec/philotic/event_spec.rb +69 -0
- data/spec/philotic/logging/logger_spec.rb +26 -0
- data/spec/philotic/publisher_spec.rb +99 -0
- data/spec/{routable_spec.rb → philotic/routable_spec.rb} +15 -14
- data/spec/philotic/subscriber_spec.rb +111 -0
- data/spec/philotic_spec.rb +66 -0
- data/spec/spec_helper.rb +12 -4
- data/tasks/bump.rake +10 -10
- metadata +173 -36
- data/spec/connection_spec.rb +0 -19
- data/spec/event_spec.rb +0 -44
- data/spec/publisher_spec.rb +0 -102
- data/spec/subscriber_spec.rb +0 -72
data/lib/philotic/routable.rb
CHANGED
@@ -11,46 +11,6 @@ module Philotic
|
|
11
11
|
base.extend ClassMethods
|
12
12
|
end
|
13
13
|
|
14
|
-
def payload
|
15
|
-
attribute_hash = {}
|
16
|
-
self.class.attr_payload_readers.each do |attr|
|
17
|
-
attr = attr.to_sym
|
18
|
-
attribute_hash[attr] = send(attr)
|
19
|
-
end
|
20
|
-
attribute_hash
|
21
|
-
end
|
22
|
-
|
23
|
-
def headers
|
24
|
-
attribute_hash = {}
|
25
|
-
self.class.attr_routable_readers.each do |attr|
|
26
|
-
attr = attr.to_sym
|
27
|
-
attribute_hash[attr] = send(attr)
|
28
|
-
end
|
29
|
-
attribute_hash
|
30
|
-
end
|
31
|
-
|
32
|
-
def attributes
|
33
|
-
attribute_hash = {}
|
34
|
-
(self.class.attr_payload_readers + self.class.attr_routable_readers).each do |attr|
|
35
|
-
attr = attr.to_sym
|
36
|
-
attribute_hash[attr] = send(attr)
|
37
|
-
end
|
38
|
-
attribute_hash
|
39
|
-
end
|
40
|
-
|
41
|
-
def message_metadata
|
42
|
-
@message_metadata ||= {}
|
43
|
-
end
|
44
|
-
|
45
|
-
def message_metadata= options
|
46
|
-
@message_metadata ||= {}
|
47
|
-
@message_metadata.merge! options
|
48
|
-
end
|
49
|
-
|
50
|
-
def publish &block
|
51
|
-
Philotic::Publisher.publish(self, &block)
|
52
|
-
end
|
53
|
-
|
54
14
|
module ClassMethods
|
55
15
|
def attr_payload_reader *names
|
56
16
|
attr_payload_readers.concat(names)
|
@@ -101,6 +61,46 @@ module Philotic
|
|
101
61
|
attr_routable_writers.concat(names)
|
102
62
|
attr_accessor(*names)
|
103
63
|
end
|
64
|
+
|
65
|
+
def publish(*args, &block)
|
66
|
+
self.new(*args).publish(&block)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def payload
|
71
|
+
_payload_or_headers(:payload)
|
72
|
+
end
|
73
|
+
|
74
|
+
def headers
|
75
|
+
_payload_or_headers(:routable)
|
76
|
+
end
|
77
|
+
|
78
|
+
def attributes
|
79
|
+
payload.merge headers
|
80
|
+
end
|
81
|
+
|
82
|
+
def message_metadata
|
83
|
+
@message_metadata ||= {}
|
84
|
+
end
|
85
|
+
|
86
|
+
def message_metadata= options
|
87
|
+
@message_metadata ||= {}
|
88
|
+
@message_metadata.merge! options
|
89
|
+
end
|
90
|
+
|
91
|
+
def publish &block
|
92
|
+
Philotic::Publisher.publish(self, &block)
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def _payload_or_headers(payload_or_headers)
|
98
|
+
attribute_hash = {}
|
99
|
+
self.class.send("attr_#{payload_or_headers}_readers").each do |attr|
|
100
|
+
attr = attr.to_sym
|
101
|
+
attribute_hash[attr] = send(attr)
|
102
|
+
end
|
103
|
+
attribute_hash
|
104
104
|
end
|
105
105
|
end
|
106
106
|
end
|
data/lib/philotic/subscriber.rb
CHANGED
@@ -1,67 +1,86 @@
|
|
1
1
|
module Philotic
|
2
2
|
class Subscriber
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
_subscribe(options, subscribe_options, &block)
|
9
|
-
end
|
3
|
+
class Metadata
|
4
|
+
attr_accessor :attributes
|
5
|
+
|
6
|
+
def initialize(attributes)
|
7
|
+
self.attributes = attributes
|
10
8
|
end
|
11
9
|
end
|
12
10
|
|
13
|
-
def self.
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
arguments['x-match'] = 'any'
|
11
|
+
def self.subscription_callback
|
12
|
+
lambda do |delivery_info, metadata, payload|
|
13
|
+
hash_payload = JSON.parse payload
|
18
14
|
|
19
|
-
|
15
|
+
event = {
|
16
|
+
payload: hash_payload,
|
17
|
+
headers: metadata[:headers],
|
18
|
+
delivery_info: delivery_info,
|
19
|
+
attributes: metadata[:headers] ? hash_payload.merge(metadata[:headers]) : hash_payload
|
20
|
+
}
|
21
|
+
yield(Metadata.new(metadata), event)
|
22
|
+
end
|
20
23
|
end
|
21
24
|
|
22
|
-
def self.
|
23
|
-
|
24
|
-
|
25
|
+
def self.subscribe(subscription = {}, subscribe_options = Philotic::DEFAULT_SUBSCRIBE_OPTIONS, &block)
|
26
|
+
Philotic.connect!
|
27
|
+
@exchange = Philotic::Connection.exchange
|
28
|
+
|
29
|
+
subscription_settings = get_subscription_settings subscription, subscribe_options
|
30
|
+
|
31
|
+
q = Philotic::Connection.channel.queue(subscription_settings[:queue_name], subscription_settings[:queue_options])
|
25
32
|
|
26
|
-
arguments[
|
33
|
+
q.bind(@exchange, arguments: subscription_settings[:arguments]) if subscription_settings[:arguments]
|
34
|
+
|
35
|
+
q.subscribe(subscription_settings[:subscribe_options], &subscription_callback(&block))
|
27
36
|
|
28
|
-
self.subscribe(options, &block)
|
29
37
|
end
|
30
38
|
|
31
|
-
|
32
|
-
def self._subscribe(options = {}, subscribe_options = Philotic::DEFAULT_SUBSCRIBE_OPTIONS, &block)
|
33
|
-
@@exchange = Philotic::Connection.exchange
|
39
|
+
def self.get_subscription_settings(subscription, subscribe_options)
|
34
40
|
|
35
|
-
if
|
36
|
-
queue_name
|
41
|
+
if subscription.is_a? String
|
42
|
+
queue_name = subscription
|
43
|
+
subscription = subscribe_options
|
37
44
|
queue_options = Philotic::DEFAULT_NAMED_QUEUE_OPTIONS
|
38
|
-
else
|
39
|
-
queue_name = options[:queue_name] || ""
|
40
45
|
|
41
|
-
|
42
|
-
|
43
|
-
|
46
|
+
else
|
47
|
+
queue_name = subscription[:queue_name] || ''
|
48
|
+
queue_options = Philotic::DEFAULT_ANONYMOUS_QUEUE_OPTIONS
|
49
|
+
subscribe_options = subscribe_options.merge(subscription[:subscribe_options]) if subscription[:subscribe_options]
|
50
|
+
arguments = subscription[:arguments] || subscription
|
44
51
|
arguments['x-match'] ||= 'all'
|
45
52
|
end
|
46
53
|
|
47
|
-
queue_options[:
|
54
|
+
queue_options.merge!(subscription[:queue_options] || {})
|
48
55
|
|
49
|
-
|
50
|
-
hash_payload = JSON.parse payload
|
56
|
+
queue_options[:auto_delete] ||= true if queue_name == ''
|
51
57
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
q = AMQP.channel.queue(queue_name, queue_options)
|
58
|
+
{
|
59
|
+
queue_name: queue_name,
|
60
|
+
queue_options: queue_options,
|
61
|
+
arguments: arguments,
|
62
|
+
subscribe_options: subscribe_options,
|
63
|
+
}
|
64
|
+
end
|
60
65
|
|
61
|
-
|
66
|
+
def self.acknowledge(message, up_to_and_including=false)
|
67
|
+
Philotic::Connection.channel.acknowledge(message[:delivery_info].delivery_tag, up_to_and_including)
|
68
|
+
end
|
62
69
|
|
63
|
-
|
70
|
+
def self.reject(message, requeue=true)
|
71
|
+
Philotic::Connection.channel.reject(message[:delivery_info].delivery_tag, requeue)
|
72
|
+
end
|
64
73
|
|
74
|
+
def self.subscribe_to_any(options = {})
|
75
|
+
if block_given?
|
76
|
+
self.subscribe(options.merge(:'x-match' => :any), &Proc.new)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.endure
|
81
|
+
while true
|
82
|
+
Thread.pass
|
83
|
+
end
|
65
84
|
end
|
66
85
|
end
|
67
|
-
end
|
86
|
+
end
|
@@ -9,20 +9,10 @@ namespace :philotic do
|
|
9
9
|
require 'philotic'
|
10
10
|
|
11
11
|
@filename = args[:filename]
|
12
|
-
queues
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
Philotic.initialize_named_queue!("#{queues.keys.sort[index]}", queues[queues.keys.sort[index]]) do |q|
|
17
|
-
if index == queues.size - 1
|
18
|
-
Philotic::Connection.close { EM.stop }
|
19
|
-
else
|
20
|
-
init_queues queues, index + 1
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
init_queues queues
|
12
|
+
queues = YAML.load_file(@filename)
|
13
|
+
Philotic.connect!
|
14
|
+
queues.each_pair do |queue_name, queue_options|
|
15
|
+
Philotic.initialize_named_queue!(queue_name, queue_options)
|
26
16
|
end
|
27
17
|
end
|
28
18
|
end
|
data/lib/philotic/version.rb
CHANGED
data/philotic.gemspec
CHANGED
@@ -2,23 +2,34 @@
|
|
2
2
|
require File.expand_path('../lib/philotic/version', __FILE__)
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
|
-
gem.authors
|
6
|
-
gem.email
|
7
|
-
gem.description
|
8
|
-
gem.summary
|
9
|
-
gem.homepage
|
5
|
+
gem.authors = ['Nathan Keyes']
|
6
|
+
gem.email = ['nkeyes@gmail.com']
|
7
|
+
gem.description = %q{Lightweight, opinionated wrapper for using RabbitMQ headers exchanges}
|
8
|
+
gem.summary = %q{Lightweight, opinionated wrapper for using RabbitMQ headers exchanges}
|
9
|
+
gem.homepage = 'https://github.com/nkeyes/philotic'
|
10
10
|
|
11
11
|
gem.files = `git ls-files`.split($\)
|
12
|
-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
13
13
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
14
|
gem.name = 'philotic'
|
15
15
|
gem.require_paths = ['lib']
|
16
16
|
gem.version = Philotic::VERSION
|
17
|
-
gem.licenses
|
17
|
+
gem.licenses = ['MIT']
|
18
18
|
|
19
|
-
|
20
|
-
gem.
|
21
|
-
gem.
|
19
|
+
|
20
|
+
gem.add_development_dependency 'codeclimate-test-reporter'
|
21
|
+
gem.add_development_dependency 'bundler', '~> 1.6'
|
22
|
+
gem.add_development_dependency 'evented-spec', '~> 0.9'
|
23
|
+
gem.add_development_dependency 'pry', '~> 0.10'
|
24
|
+
gem.add_development_dependency 'rake', '~> 10.3'
|
25
|
+
gem.add_development_dependency 'rspec', '~> 3.1'
|
26
|
+
gem.add_development_dependency 'rspec-its', '~> 1.1'
|
27
|
+
gem.add_development_dependency 'timecop', '~> 0.7'
|
28
|
+
gem.add_development_dependency 'simplecov'
|
29
|
+
|
30
|
+
gem.add_dependency 'activesupport', '>= 3.2'
|
31
|
+
gem.add_dependency 'activerecord', '>= 3.2'
|
22
32
|
gem.add_dependency 'awesome_print', '~> 1.2'
|
33
|
+
gem.add_dependency 'bunny', '~> 1.2.1'
|
23
34
|
gem.add_dependency 'json', '~> 1.8'
|
24
35
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Philotic::Config do
|
4
|
+
|
5
|
+
describe '.defaults' do
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '.load' do
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '.load_file' do
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '.parse_rabbit_uri' do
|
15
|
+
let(:url) { 'amqp://user:pass@host:12345/vhost' }
|
16
|
+
before do
|
17
|
+
Philotic::Config.rabbit_url = url
|
18
|
+
end
|
19
|
+
subject { lambda { Philotic::Config.parse_rabbit_uri } }
|
20
|
+
|
21
|
+
it do
|
22
|
+
should change {
|
23
|
+
[
|
24
|
+
Philotic::Config.rabbit_user,
|
25
|
+
Philotic::Config.rabbit_password,
|
26
|
+
Philotic::Config.rabbit_host,
|
27
|
+
Philotic::Config.rabbit_port,
|
28
|
+
Philotic::Config.rabbit_vhost,
|
29
|
+
]
|
30
|
+
}
|
31
|
+
.to [
|
32
|
+
'user',
|
33
|
+
'pass',
|
34
|
+
'host',
|
35
|
+
12345,
|
36
|
+
'vhost',
|
37
|
+
]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Philotic::Connection do
|
4
|
+
|
5
|
+
describe '.config' do
|
6
|
+
its(:config) { should eq Philotic::Config }
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '.connect!' do
|
10
|
+
context 'not connected' do
|
11
|
+
context 'success' do
|
12
|
+
specify do
|
13
|
+
expect(subject).to receive(:connected?).and_return(false, true)
|
14
|
+
expect(subject).to receive(:start_connection!)
|
15
|
+
expect(subject).to receive(:set_exchange_return_handler!)
|
16
|
+
|
17
|
+
subject.connect!
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'failure' do
|
23
|
+
specify do
|
24
|
+
expect(subject).to receive(:connected?).and_return(false, false)
|
25
|
+
expect(subject).to receive(:start_connection!)
|
26
|
+
expect(subject).not_to receive(:set_exchange_return_handler!)
|
27
|
+
expect(Philotic.logger).to receive(:error)
|
28
|
+
|
29
|
+
subject.connect!
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'not connected' do
|
36
|
+
context 'success' do
|
37
|
+
specify do
|
38
|
+
expect(subject).to receive(:connected?).and_return(true)
|
39
|
+
expect(subject).not_to receive(:start_connection!)
|
40
|
+
expect(subject).not_to receive(:set_exchange_return_handler!)
|
41
|
+
|
42
|
+
subject.connect!
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '.start_connection!' do
|
50
|
+
let(:connection) { double }
|
51
|
+
specify do
|
52
|
+
expect(Bunny).to receive(:new).with(Philotic::Config.rabbit_url, Philotic::Connection.connection_settings).and_return(connection)
|
53
|
+
expect(connection).to receive(:start)
|
54
|
+
|
55
|
+
Philotic::Connection.start_connection!
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# create 'deep' inheritance to test self.inherited
|
4
|
+
class TestEventParent < Philotic::Event
|
5
|
+
end
|
6
|
+
class TestEvent < TestEventParent
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Philotic::Event do
|
10
|
+
let(:event) { TestEvent.new }
|
11
|
+
subject { event }
|
12
|
+
|
13
|
+
Philotic::Routable::ClassMethods.instance_methods.sort.each do |method_name|
|
14
|
+
specify { expect(subject.class.methods).to include method_name.to_sym }
|
15
|
+
end
|
16
|
+
|
17
|
+
Philotic::MESSAGE_OPTIONS.each do |method_name|
|
18
|
+
specify { expect(subject.methods).to include method_name.to_sym }
|
19
|
+
specify { expect(subject.methods).to include "#{method_name}=".to_sym }
|
20
|
+
end
|
21
|
+
|
22
|
+
Philotic::PHILOTIC_HEADERS.each do |method_name|
|
23
|
+
specify { expect(subject.methods).to include method_name.to_sym }
|
24
|
+
specify { expect(subject.methods).to include "#{method_name}=".to_sym }
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'message_metadata' do
|
28
|
+
it 'should have a timestamp' do
|
29
|
+
Timecop.freeze
|
30
|
+
expect(subject.message_metadata).to eq(timestamp: Time.now.to_i)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should reflect changes in the event properties' do
|
34
|
+
expect(subject.message_metadata[:app_id]).to eq nil
|
35
|
+
subject.app_id = 'ANSIBLE'
|
36
|
+
expect(subject.message_metadata[:app_id]).to eq 'ANSIBLE'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
describe 'headers' do
|
40
|
+
it 'should include :philotic_product' do
|
41
|
+
expect(subject.headers.keys).to include :philotic_product
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'generic event' do
|
46
|
+
let(:headers) do
|
47
|
+
{
|
48
|
+
header1: 'h1',
|
49
|
+
header2: 'h2',
|
50
|
+
header3: 'h3',
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
let(:payloads) do
|
55
|
+
{
|
56
|
+
payload1: 'h1',
|
57
|
+
payload2: 'h2',
|
58
|
+
payload3: 'h3',
|
59
|
+
}
|
60
|
+
end
|
61
|
+
it 'builds an event with dynamic headers and payloads' do
|
62
|
+
event = Philotic::Event.new(headers, payloads)
|
63
|
+
|
64
|
+
expect(event.headers).to include(headers)
|
65
|
+
expect(event.payload).to eq payloads
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|