philotic 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ require 'philotic/tasks/init_queues'
@@ -0,0 +1,28 @@
1
+ desc "initialize named durable queues"
2
+ namespace :philotic do
3
+ task :init_queues, :filename do |t, args|
4
+ raise "You must specify a file name for #{t.name}: rake #{t.name}[FILENAME] #yes, you need the brackets, no space." if !args[:filename]
5
+
6
+ # ENV['INITIALIZE_NAMED_QUEUE'] must equal 'true' to run Philotic.initialize_named_queue!
7
+ ENV['INITIALIZE_NAMED_QUEUE'] = 'true'
8
+
9
+ require 'philotic'
10
+
11
+ @filename = args[:filename]
12
+ queues = YAML.load_file(@filename)
13
+
14
+ EM.run do
15
+ def init_queues queues, index = 0
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
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module Philotic
2
+ VERSION = '0.0.1'
3
+ end
data/philotic.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/philotic/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
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
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = 'philotic'
15
+ gem.require_paths = ['lib']
16
+ gem.version = Philotic::VERSION
17
+ gem.licenses = ['MIT']
18
+
19
+ gem.add_dependency 'activesupport', '~> 4.0'
20
+ gem.add_dependency 'activerecord', '~> 4.0'
21
+ gem.add_dependency 'amqp', '~> 1.2'
22
+ gem.add_dependency 'awesome_print', '~> 1.2'
23
+ gem.add_dependency 'json', '~> 1.8'
24
+ end
@@ -0,0 +1,36 @@
1
+ defaults: &defaults
2
+ #connection settings
3
+ rabbit_host: localhost
4
+ # connection_failed_handler: method/proc
5
+ # connection_loss_handler: method/proc
6
+ timeout: 2
7
+
8
+ #exchange settings
9
+ exchange_name: philotic.headers
10
+ # message_return_handler: method/proc
11
+
12
+ #message settings
13
+ routing_key: ~
14
+ persistent: true
15
+ # immediate: true
16
+ mandatory: true
17
+ content_type: ~
18
+ content_encoding: ~
19
+ priority: ~
20
+ message_id: ~
21
+ correlation_id: ~
22
+ reply_to: ~
23
+ type: ~
24
+ user_id: ~
25
+ app_id: MY_APP
26
+ timestamp: ~
27
+ expiration: ~
28
+
29
+ development:
30
+ <<: *defaults
31
+
32
+ test:
33
+ <<: *defaults
34
+
35
+ production:
36
+ <<: *defaults
@@ -0,0 +1,19 @@
1
+ available_or_male:
2
+ gender: Male
3
+ available: true
4
+ x-match: any
5
+
6
+ available_male:
7
+ gender: Male
8
+ available: true
9
+ x-match: all
10
+
11
+ available_or_female:
12
+ gender: Female
13
+ available: true
14
+ x-match: any
15
+
16
+ available_female:
17
+ gender: Female
18
+ available: true
19
+ x-match: all
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Philotic::Connection do
4
+ let(:connection){ Philotic::Connection }
5
+ subject { connection }
6
+
7
+ describe "config" do
8
+ it "should return the Philotic::Config singleton" do
9
+ subject.config.should == Philotic::Config
10
+ end
11
+ end
12
+
13
+ describe "exchange" do
14
+ #TODO make sure rabbit is running for CI to run this
15
+ xit "should return an instance of AMQP::Exchange" do
16
+ subject.exchange.should be_a AMQP::Exchange
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,44 @@
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 { subject.class.methods.should include method_name.to_sym }
15
+ end
16
+
17
+ Philotic::MESSAGE_OPTIONS.each do |method_name|
18
+ specify { subject.methods.should include method_name.to_sym }
19
+ specify { subject.methods.should include "#{method_name}=".to_sym }
20
+ end
21
+
22
+ Philotic::PHILOTIC_HEADERS.each do |method_name|
23
+ specify { subject.methods.should include method_name.to_sym }
24
+ specify { subject.methods.should include "#{method_name}=".to_sym }
25
+ end
26
+
27
+ describe "message_metadata" do
28
+ it "should have a timestamp" do
29
+ Timecop.freeze
30
+ subject.message_metadata.should == {timestamp: Time.now.to_i}
31
+ end
32
+
33
+ it "should reflect changes in the event properties" do
34
+ subject.message_metadata[:app_id]. should == nil
35
+ subject.app_id = 'ANSIBLE'
36
+ subject.message_metadata[:app_id]. should == 'ANSIBLE'
37
+ end
38
+ end
39
+ describe "headers" do
40
+ it "should include :philotic_product" do
41
+ subject.headers.keys.should include :philotic_product
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+ require 'philotic/dummy_event'
3
+
4
+ describe Philotic::Publisher do
5
+ before(:each) do
6
+ @event = Philotic::DummyEvent.new
7
+ @event.subject = "Hello"
8
+ @event.message = "How are you?"
9
+ @event.gender = :M
10
+ @event.available = true
11
+ end
12
+ let(:publisher) { Philotic::Publisher }
13
+ subject { publisher }
14
+
15
+ describe "config" do
16
+ it "should return the Philotic::Config singleton" do
17
+ subject.config.should == Philotic::Config
18
+ end
19
+ end
20
+
21
+ describe "exchange" do
22
+ #TODO make sure rabbit is running for CI to run this
23
+ xit "should return an instance of AMQP::Exchange" do
24
+ subject.exchange.should be_a AMQP::Exchange
25
+ end
26
+ end
27
+
28
+ describe "publish" do
29
+ it "should call raw_publish with the right values" do
30
+ Timecop.freeze
31
+ subject.should_receive(:raw_publish).with(
32
+ {
33
+ subject: 'Hello',
34
+ message: "How are you?"
35
+ },
36
+ {
37
+ headers: {
38
+ philotic_firehose: true,
39
+ philotic_product: nil,
40
+ philotic_component: nil,
41
+ philotic_event_type: nil,
42
+ gender: :M,
43
+ available: true
44
+ },
45
+ timestamp: Time.now.to_i
46
+ }
47
+ )
48
+ subject.publish(@event)
49
+ end
50
+
51
+ end
52
+
53
+ describe "raw_publish" do
54
+
55
+ xit "should call exchange.publish with the right values" do
56
+ Timecop.freeze
57
+ Philotic::Connection.instance.should_receive(:connected?).and_return { true }
58
+
59
+ AMQP::Exchange.any_instance.should_receive(:publish).with(
60
+ {
61
+ subject: 'Hello',
62
+ message: "How are you?"
63
+ }.to_json,
64
+ {
65
+ routing_key: nil,
66
+ persistent: true,
67
+ mandatory: true,
68
+ content_type: nil,
69
+ content_encoding: nil,
70
+ priority: nil,
71
+ message_id: nil,
72
+ correlation_id: nil,
73
+ reply_to: nil,
74
+ type: nil,
75
+ user_id: nil,
76
+ app_id: nil,
77
+ expiration: nil,
78
+ headers: {
79
+ philotic_firehose: true,
80
+ philotic_product: nil,
81
+ philotic_component: nil,
82
+ philotic_event_type: nil,
83
+ gender: :M,
84
+ available: true
85
+ },
86
+ timestamp: Time.now.to_i
87
+ }
88
+ )
89
+ subject.publish(@event)
90
+ end
91
+
92
+ xit "should log an error when there is no connection" do
93
+
94
+ 4.times do
95
+ Philotic::Connection.instance.should_receive(:connected?).and_return { false }
96
+ end
97
+ Philotic.logger.should_receive(:error)
98
+ subject.publish(@event)
99
+ end
100
+
101
+ end
102
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe Philotic::Routable do
4
+ context "including the module on class" do
5
+ let(:routable_event_class){
6
+ Class.new do
7
+ include Philotic::Routable
8
+ attr_routable :routable_attr
9
+ attr_payload :payload_attr
10
+ end
11
+ }
12
+ subject { routable_event_class}
13
+
14
+ %w{ attr_payload_reader attr_payload_readers
15
+ attr_payload_writer attr_payload_writers
16
+ attr_payload
17
+
18
+ attr_routable_reader attr_routable_readers
19
+ attr_routable_writers attr_routable_writer
20
+ attr_routable }.each do |method_name|
21
+ specify { subject.methods.should include method_name.to_sym }
22
+ end
23
+
24
+ context " and then instantiating it" do
25
+ let(:routable_event_instance){ routable_event_class.new }
26
+ subject { routable_event_instance }
27
+
28
+ it 'should have proper headers' do
29
+ subject.headers.should == { routable_attr: nil }
30
+ end
31
+
32
+ it 'should have proper payload' do
33
+ subject.payload.should == { payload_attr: nil }
34
+ end
35
+
36
+ it 'should have proper attributes' do
37
+ subject.attributes.should == { routable_attr: nil,
38
+ payload_attr: nil }
39
+ end
40
+
41
+ it 'should call Philotic::Publisher.publish with subject' do
42
+ Philotic::Publisher.should_receive(:publish).with(subject)
43
+ subject.publish
44
+ end
45
+
46
+ it 'should have empty message_metadata' do
47
+ subject.message_metadata.should == {}
48
+ end
49
+
50
+ context " overriding a value with message_metadata=" do
51
+ before do
52
+ routable_event_instance.message_metadata = { mandatory: false }
53
+ end
54
+ its(:message_metadata) { should eq( mandatory: false ) }
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,23 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'support'))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'config'))
5
+
6
+
7
+ require 'rubygems'
8
+ require 'bundler/setup'
9
+ require 'philotic'
10
+
11
+ Bundler.require(:default, :test)
12
+
13
+ RSpec.configure do |config|
14
+ #Run any specs tagged with focus: true or all specs if none tagged
15
+ config.filter_run focus: true
16
+ config.run_all_when_everything_filtered = true
17
+
18
+ config.after do
19
+ Timecop.return
20
+ end
21
+
22
+ end
23
+ #Philotic.logger = Logger.new("/dev/null")
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+ require 'philotic/dummy_event'
3
+
4
+ describe Philotic::Subscriber do
5
+ let(:subscriber) do
6
+ subscriber = Philotic::Subscriber.new(arguments: {'x-match' => 'any', philotic_firehose: true}) do |metadata, payload|
7
+ true
8
+ end
9
+ subscriber
10
+ end
11
+
12
+ describe "subscribe" do
13
+ xit "should call AMQP.channel.queue with the right values" do
14
+ queue = double(AMQP::Queue)
15
+ queue.stub(:bind) { queue }
16
+ queue.stub(:subscribe) { queue }
17
+ channel = double(AMQP::Channel)
18
+ exchange = double(AMQP::Exchange)
19
+ channel.stub(:queue) { queue }
20
+ channel.stub(:headers) { exchange }
21
+ AMQP.stub(:channel) { channel }
22
+
23
+ channel.should_receive(:queue).with("", {auto_delete: true, durable: false})
24
+ queue.should_receive(:bind).with(exchange, {arguments: {"x-match" => "any", philotic_firehose: true}})
25
+ queue.should_receive(:subscribe).with({})
26
+ Philotic::Subscriber.subscribe(arguments: {'x-match' => 'any', philotic_firehose: true}) do |metadata, payload|
27
+ true
28
+ end
29
+ end
30
+ end
31
+
32
+ describe "subscribe_to_any_of" do
33
+ xit "should call AMQP.channel.queue with the right values" do
34
+ queue = double(AMQP::Queue)
35
+ queue.stub(:bind) { queue }
36
+ queue.stub(:subscribe) { queue }
37
+ channel = double(AMQP::Channel)
38
+ exchange = double(AMQP::Exchange)
39
+ channel.stub(:queue) { queue }
40
+ channel.stub(:headers) { exchange }
41
+ AMQP.stub(:channel) { channel }
42
+
43
+ channel.should_receive(:queue).with("", {auto_delete: true, durable: false})
44
+ queue.should_receive(:bind).with(exchange, {arguments: {"x-match" => "any", philotic_firehose: true}})
45
+ queue.should_receive(:subscribe).with({})
46
+ Philotic::Subscriber.subscribe_to_any_of(arguments: {philotic_firehose: true}) do |metadata, payload|
47
+ true
48
+ end
49
+ end
50
+ end
51
+
52
+ describe "subscribe_to_all_of" do
53
+ xit "should call AMQP.channel.queue with the right values" do
54
+ queue = double(AMQP::Queue)
55
+ queue.stub(:bind) { queue }
56
+ queue.stub(:subscribe) { queue }
57
+ channel = double(AMQP::Channel)
58
+ exchange = double(AMQP::Exchange)
59
+ channel.stub(:queue) { queue }
60
+ channel.stub(:headers) { exchange }
61
+ AMQP.stub(:channel) { channel }
62
+
63
+ channel.should_receive(:queue).with("", {auto_delete: true, durable: false})
64
+ queue.should_receive(:bind).with(exchange, {arguments: {"x-match" => "all", philotic_firehose: true}})
65
+ queue.should_receive(:subscribe).with({})
66
+ Philotic::Subscriber.subscribe_to_all_of(arguments: {philotic_firehose: true}) do |metadata, payload|
67
+ true
68
+ end
69
+ end
70
+ end
71
+
72
+ end