philotic 0.0.1

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.
@@ -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