untied-publisher 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml CHANGED
@@ -3,3 +3,5 @@ rvm:
3
3
  - 1.9.2
4
4
  - 1.9.3
5
5
  - 1.8.7
6
+ services:
7
+ - rabbitmq
data/CHANGELOG.mkd CHANGED
@@ -1,3 +1,9 @@
1
+ # 0.0.6
2
+
3
+ - Refactor event and payload representer
4
+ - Implements sync producer (and makes it possible to choose through config)
5
+ - Improve failsafeness (issue #5 and #6)
6
+
1
7
  # 0.0.4
2
8
 
3
9
  - Don't relying on ActiveSupport for serializing Untied::Event
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  This is the publisher part of Untied. Untied is an observer pattern implementation to distributed systems.
4
4
 
5
- For usage information, please visit the [Untied](http://github.com.br/redu/untied) page.
5
+ For usage information, please visit the [Untied](http://github.com/redu/untied) page.
6
6
 
7
7
  [![Build Status](https://travis-ci.org/redu/untied-publisher.png)](https://travis-ci.org/redu/untied-publisher)
8
8
 
@@ -2,37 +2,58 @@ require "untied-publisher/version"
2
2
 
3
3
  require 'rubygems'
4
4
  require 'bundler/setup'
5
- require 'amqp/utilities/event_loop_helper'
5
+
6
+ require 'untied-publisher/event_representer'
7
+ require 'untied-publisher/event'
8
+ require 'untied-publisher/doorkeeper'
9
+ require 'untied-publisher/default_doorkeeper'
10
+ require 'untied-publisher/config'
11
+ require 'untied-publisher/observer'
12
+ require 'untied-publisher/base_producer'
13
+ require 'untied-publisher/amqp'
14
+ require 'untied-publisher/bunny'
15
+ require 'untied-publisher/base'
6
16
 
7
17
  module Untied
8
18
  module Publisher
9
- def self.start
10
- Thread.abort_on_exception = false
11
-
12
- self.run do
13
- AMQP.start
19
+ # Configures untied-publisher.
20
+ def self.configure(&block)
21
+ yield(config) if block_given?
22
+ if config.deliver_messages
23
+ adapter.start
24
+ else
25
+ config.adapter = :Base
26
+ adapter.start
14
27
  end
15
28
  end
16
29
 
17
- def self.run(&block)
18
- @block = block
19
- if defined?(PhusionPassenger)
20
- PhusionPassenger.on_event(:starting_worker_process) do |forked|
21
- EM.stop if forked && EM.reactor_running?
22
- Thread.new { EM.run { @block.call } }
30
+ def self.config
31
+ @config ||= Config.new
32
+ end
33
+
34
+ def self.adapter
35
+ producer_booter = "Untied::Publisher::#{self.config.adapter}"
36
+
37
+ @adapter ||= begin
38
+ begin
39
+ constantize(producer_booter)
40
+ rescue NameError
41
+ config.logger.info "#{producer_booter} is not defined. Falling back " +\
42
+ "to Untied::Publisher::Bunny"
43
+ Untied::Publisher::Bunny
23
44
  end
24
- else
25
- AMQP::Utilities::EventLoopHelper.run { @block.call }
45
+ end
46
+ end
47
+
48
+ # Transforms string into constant
49
+ def self.constantize(class_name)
50
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ class_name
51
+ raise NameError, "#{class_name.inspect} is not a valid constant name!"
26
52
  end
53
+
54
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
27
55
  end
28
56
  end
29
57
  end
30
58
 
31
-
32
- require 'untied-publisher/event_representer'
33
- require 'untied-publisher/event'
34
- require 'untied-publisher/config'
35
- require 'untied-publisher/doorkeeper'
36
- require 'untied-publisher/observer'
37
- require 'untied-publisher/producer'
38
59
  require 'untied-publisher/railtie' if defined?(Rails)
@@ -0,0 +1,16 @@
1
+ require 'amqp/utilities/event_loop_helper'
2
+ require 'untied-publisher/amqp/producer'
3
+
4
+ module Untied
5
+ module Publisher
6
+ module AMQP
7
+ def self.start
8
+ config.channel ||= ::AMQP::Channel.new(::AMQP.connection)
9
+ end
10
+
11
+ def self.producer
12
+ Untied::Publisher::AMQP::Producer
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,44 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'amqp'
3
+
4
+ module Untied
5
+ module Publisher
6
+ module AMQP
7
+ class Producer < BaseProducer
8
+ # Encapsulates both the Channel and Exchange (AMQP).
9
+
10
+ def initialize(opts={})
11
+ super
12
+ check_em_reactor
13
+ if ::AMQP.channel || opts[:channel]
14
+ say "Using defined AMQP.channel"
15
+ @channel = ::AMQP.channel || opts[:channel]
16
+ end
17
+ end
18
+
19
+ # Publish the given event.
20
+ # event: object which is going to be serialized and sent through the
21
+ # wire. It should respond to #to_json.
22
+ def safe_publish(e)
23
+ on_exchange do |exchange|
24
+ exchange.publish(e.to_json, :routing_key => @routing_key)
25
+ end
26
+ end
27
+
28
+ # Creates a new exchange and yields it to the block passed when it's ready
29
+ def on_exchange(&block)
30
+ return unless @channel
31
+ @channel.topic('untied', :auto_delete => true, &block)
32
+ end
33
+
34
+ def check_em_reactor
35
+ if !defined?(EventMachine) || !EM.reactor_running?
36
+ raise "In order to use the producer you must be running inside an " + \
37
+ "eventmachine loop"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,12 @@
1
+ module Untied
2
+ module Publisher
3
+ module Base
4
+ def self.start
5
+ end
6
+
7
+ def self.producer
8
+ Untied::Publisher::BaseProducer
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,62 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module Untied
4
+ module Publisher
5
+ # Generic class to provide message publishing.
6
+ class BaseProducer
7
+ attr_reader :routing_key, :service_name, :deliver_messages
8
+
9
+ def initialize(opts={})
10
+ extract_options!(opts)
11
+ end
12
+
13
+ # Publish the given event.
14
+ # event: object which is going to be serialized and sent through the
15
+ # wire. It should respond to #to_json.
16
+ def publish(event)
17
+ if deliver_messages
18
+ safe_publish(event)
19
+ say "Publishing event with routing key #{routing_key}: #{event.to_json}"
20
+ else
21
+ say "Event produced but not delivered. If you want to deliver it " + \
22
+ "try to set Untied::Publisher.config.deliver_messages to true. " + \
23
+ "Event: #{event.to_json}"
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def extract_options!(opts)
30
+ options = {
31
+ :service_name => Publisher.config.service_name,
32
+ :deliver_messages => Publisher.config.deliver_messages,
33
+ :channel => nil,
34
+ }.merge(opts)
35
+ options[:routing_key] = "untied.#{options[:service_name]}"
36
+
37
+ options.each do |k,v|
38
+ instance_variable_set(:"@#{k}", v)
39
+ end
40
+
41
+ unless options[:service_name]
42
+ msg = "you should inform service_name option or configure it " + \
43
+ "through Untied::Publisher.configure block"
44
+ raise ArgumentError.new(msg)
45
+ end
46
+
47
+ say "Producer intialized with options " + \
48
+ "#{options.inspect} and routing key #{routing_key}"
49
+
50
+ if !deliver_messages
51
+ say "Channel was not setted up because message delivering is disabled."
52
+ end
53
+ end
54
+
55
+ protected
56
+
57
+ def say(msg)
58
+ Publisher.config.logger.info "#{self.class.to_s}: #{msg}"
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,15 @@
1
+ require 'untied-publisher/bunny/producer'
2
+
3
+ module Untied
4
+ module Publisher
5
+ module Bunny
6
+ def self.start
7
+ # Nothing to do here
8
+ end
9
+
10
+ def self.producer
11
+ Untied::Publisher::Bunny::Producer
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,45 @@
1
+ require 'bunny'
2
+
3
+ module Untied
4
+ module Publisher
5
+ module Bunny
6
+ class Producer < BaseProducer
7
+ def initialize(opts={})
8
+ super
9
+
10
+ begin
11
+ connection.start
12
+ rescue ::Bunny::TCPConnectionFailed => e
13
+ say "Can't connect to RabbitMQ: #{e.message}"
14
+ end
15
+ end
16
+
17
+ # Publish the given event.
18
+ # event: object which is going to be serialized and sent through the
19
+ # wire. It should respond to #to_json.
20
+ def safe_publish(event)
21
+ if connection.status == :open
22
+ exchange.publish(event.to_json, :routing_key => routing_key)
23
+ else
24
+ say "Event not sent. Connection status is #{connection.status}: " + \
25
+ "#{event.to_json}"
26
+ end
27
+ end
28
+
29
+ protected
30
+
31
+ def connection
32
+ @connection ||= ::Bunny.new
33
+ end
34
+
35
+ def exchange
36
+ @exchange ||= channel.topic('untied', :auto_delete => true)
37
+ end
38
+
39
+ def channel
40
+ @channel ||= connection.create_channel
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -4,28 +4,19 @@ require 'logger'
4
4
 
5
5
  module Untied
6
6
  module Publisher
7
- def self.configure(&block)
8
- yield(config) if block_given?
9
- if config.deliver_messages
10
- Untied::Publisher.start
11
- EventMachine.next_tick do
12
- config.channel ||= AMQP::Channel.new(AMQP.connection)
13
- end
14
- end
15
- end
16
-
17
- def self.config
18
- @config ||= Config.new
19
- end
20
-
21
7
  class Config
22
8
  include Configurable
23
9
 
10
+ # Logger used by untied. Default: STDOUT
24
11
  config :logger, Logger.new(STDOUT)
12
+ # Deliver the messages to AMQP broker. Default: true
25
13
  config :deliver_messages, true
26
- config :service_name
27
- config :doorkeeper, nil
28
- config :channel, nil
14
+ # An unique identifier to the publisher. Default: untied_publisher
15
+ config :service_name, 'untied_publisher'
16
+ # Doorkeeper class name. Default: DefaultDoorkeeper
17
+ config :doorkeeper, Untied::Publisher::DefaultDoorkeeper
18
+ # RabbitMQ adapter
19
+ config :adapter, :Bunny
29
20
  end
30
21
  end
31
22
  end
@@ -0,0 +1,8 @@
1
+ module Untied
2
+ module Publisher
3
+ # Default Doorkeeper. Don't let anyone pass.
4
+ class DefaultDoorkeeper
5
+ include Doorkeeper
6
+ end
7
+ end
8
+ end
@@ -8,14 +8,30 @@ module Untied
8
8
  @config = {
9
9
  :name => "after_create",
10
10
  :payload => nil,
11
- :origin => nil
11
+ :origin => nil,
12
+ :payload_representer => nil
12
13
  }.merge(attrs)
13
14
 
14
15
  raise "You should inform the origin service" unless @config[:origin]
15
16
 
16
17
  @name = @config.delete(:name)
17
- @payload = @config.delete(:payload)
18
+ @payload = represent(@config.delete(:payload))
18
19
  @origin = @config.delete(:origin)
20
+
21
+ self.extend(Publisher::EventRepresenter)
22
+ end
23
+
24
+ protected
25
+
26
+ # Extends payload with representer and returns the hash. In cases there is
27
+ # no representer, the raw payload is returned
28
+ def represent(payload)
29
+ if representer = @config[:payload_representer]
30
+ payload.extend(representer)
31
+ payload.to_hash
32
+ else
33
+ payload
34
+ end
19
35
  end
20
36
  end
21
37
  end
@@ -3,13 +3,17 @@
3
3
  require 'representable/json'
4
4
 
5
5
  module Untied
6
- module EventRepresenter
7
- include Representable::JSON
6
+ module Publisher
7
+ module EventRepresenter
8
+ # Publisher::Event is extended with this module at runtime. It defines
9
+ # how the event will be serialized.
8
10
 
9
- self.representation_wrap = true
11
+ include Representable::JSON
12
+ self.representation_wrap = true
10
13
 
11
- property :name
12
- property :payload
13
- property :origin
14
+ property :name
15
+ property :payload
16
+ property :origin
17
+ end
14
18
  end
15
19
  end
@@ -28,17 +28,14 @@ module Untied
28
28
  protected
29
29
 
30
30
  def produce_event(callback, model, options={})
31
- if representer = options[:with_representer]
32
- model = model.extend(representer)
33
- end
34
- e = Event.new(:name => callback,
35
- :payload => model, :origin => Publisher.config.service_name)
36
- e.extend(EventRepresenter)
31
+ e = Event.new(:name => callback, :payload => model,
32
+ :origin => Publisher.config.service_name,
33
+ :payload_representer => options[:represent_with])
37
34
  producer.publish(e)
38
35
  end
39
36
 
40
37
  def producer
41
- Producer.new
38
+ Publisher.adapter.producer.new
42
39
  end
43
40
 
44
41
  def publisher
@@ -8,10 +8,8 @@ module Untied
8
8
  config.active_record.observers ||= []
9
9
  ActiveRecord::Base.observers << Untied::Publisher::Observer
10
10
  config.active_record.observers << Untied::Publisher::Observer
11
- unless File.basename($0) == 'rake'
12
- Publisher.config.logger.debug "Untied::Publisher: initializing observer"
13
- Untied::Publisher::Observer.instance
14
- end
11
+ Publisher.config.logger.debug "Untied::Publisher: initializing observer"
12
+ Untied::Publisher::Observer.instance
15
13
  end
16
14
 
17
15
  config.to_prepare do
@@ -1,5 +1,5 @@
1
1
  module Untied
2
2
  module Publisher
3
- VERSION = "0.0.5"
3
+ VERSION = "0.0.6"
4
4
  end
5
5
  end
@@ -0,0 +1,66 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ module Untied
5
+ module Publisher
6
+ module AMQP
7
+ describe Producer do
8
+ context "configuration" do
9
+ it "should use service name configured" do
10
+ mock_reactor_and_amqp do |c|
11
+ Producer.new.service_name.should == 'core'
12
+ end
13
+ end
14
+ end
15
+
16
+ it "should initilize producer" do
17
+ mock_reactor_and_amqp do |c|
18
+ Producer.new.should be_a Producer
19
+ end
20
+ end
21
+
22
+ it "should raise RuntimeError when trying to run without connection" do
23
+ expect {
24
+ Producer.new(:deliver_messages => true)
25
+ }.to raise_error
26
+ end
27
+
28
+ it "should raise RuntimeError if EM is not running" do
29
+ mock_reactor_and_amqp do |c|
30
+ EM.stub("reactor_running?" => false)
31
+ expect {
32
+ Producer.new(:channel => c, :deliver_messages => true)
33
+ }.to raise_error
34
+ end
35
+ end
36
+
37
+ context "#publish" do
38
+ let(:event) do
39
+ Event.new(:name => "create", :payload => { :foo => 'bar' }, :origin => :core)
40
+ end
41
+
42
+ it "should call Channel#publish" do
43
+ mock_reactor_and_amqp do |channel|
44
+ producer = Producer.new(:channel => channel, :deliver_messages => true)
45
+ producer.should_receive(:safe_publish).with(event)
46
+ producer.publish(event)
47
+ end
48
+ end
49
+ end
50
+
51
+ def mock_reactor_and_amqp
52
+ # Do nothing when calling start
53
+ Untied.stub(:start).and_return(nil)
54
+ # Simulate reactor running
55
+ EM.stub(:reactor_running?).and_return(true)
56
+
57
+ exchange = double('Exchange')
58
+ channel = double('Channel')
59
+ channel.stub(:topic).and_return(exchange)
60
+
61
+ yield(channel)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ module Untied
4
+ module Publisher
5
+ describe BaseProducer do
6
+ context ".new" do
7
+ it "should raise exception when there is no service_name" do
8
+ Publisher.config.stub(:service_name).and_return(nil)
9
+ expect {
10
+ BaseProducer.new
11
+ }.to raise_error(ArgumentError, /you should inform service_name/)
12
+ end
13
+
14
+ %w(routing_key service_name deliver_messages).each do |attr|
15
+ it "should have #{attr} attr reader" do
16
+ BaseProducer.new.should respond_to(attr)
17
+ end
18
+ end
19
+
20
+ it "should log when message delivering is setted to false" do
21
+ Publisher.config.logger.should_receive(:info).
22
+ with(/Producer intialized with options/)
23
+ Publisher.config.logger.should_receive(:info).
24
+ with(/Channel was not/)
25
+ BaseProducer.new(:deliver_messages => false)
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ module Untied
4
+ module Publisher
5
+ module Bunny
6
+ describe Producer do
7
+ context ".new" do
8
+ it "call super" do
9
+ Producer.should_receive(:new)
10
+ Producer.new
11
+ end
12
+ end
13
+
14
+ context "failing connection" do
15
+ let(:bunny) do
16
+ bunny = double('Bunny')
17
+ end
18
+ before do
19
+ bunny.stub(:start).
20
+ and_raise(::Bunny::TCPConnectionFailed.new(nil, "host", "port"))
21
+
22
+ Producer.any_instance.stub(:connection).and_return(bunny)
23
+ end
24
+ let(:logger) { Publisher.config.logger }
25
+
26
+ context ".new" do
27
+
28
+ it "should log when connection is refused" do
29
+ Producer.any_instance.stub(:connection).and_return(bunny)
30
+ expect { Producer.new }.to_not raise_error
31
+ end
32
+
33
+ it "should log properly" do
34
+ logger.should_receive(:info).with(/Channel was not setted up/)
35
+ logger.should_receive(:info).with(/Producer intialized/)
36
+ logger.should_receive(:info).with(/Can't connect to RabbitMQ/)
37
+
38
+ Producer.new
39
+ end
40
+ end
41
+
42
+ context "#publish" do
43
+ it "should log properly" do
44
+ subject = Producer.new
45
+ bunny.stub(:status).and_return(:closed)
46
+ logger.should_receive(:info).with(/Event not sent/)
47
+ subject.safe_publish({})
48
+ end
49
+ end
50
+ end
51
+
52
+ context "#publish" do
53
+ let(:connection) { ::Bunny.new }
54
+ let(:channel) { connection.create_channel }
55
+ let(:exchange) { channel.topic('untied', :auto_delete => true) }
56
+ let(:event) do
57
+ Event.new(:name => "create", :payload => { :foo => 'bar' },
58
+ :origin => :core)
59
+ end
60
+ before do
61
+ connection.start
62
+ Publisher.config.deliver_messages = true
63
+ end
64
+ after do
65
+ connection.close
66
+ Publisher.config.deliver_messages = false
67
+ end
68
+
69
+ it "should route the message correctly" do
70
+ queue = channel.queue("", :exclusive => true).
71
+ bind(exchange, :routing_key => "untied.core")
72
+ subject.publish(event)
73
+
74
+ sleep(0.5)
75
+
76
+ queue.message_count.should == 1
77
+ channel.close
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ module Untied
4
+ describe Event do
5
+ before(:all) do
6
+ module PostRepresenter
7
+ include Representable::JSON
8
+
9
+ property :title
10
+ end
11
+ end
12
+ let(:post) { Post.create(:title => "DIY") }
13
+
14
+ context "#to_json" do
15
+ it "should serialize according with EventRepresenter" do
16
+ event = Event.new(:name => "foo", :payload => {}, :origin => :bar)
17
+ JSON.parse(event.to_json).to_a.sort.should ==
18
+ { "event" =>
19
+ { "name" => "foo", "origin" => "bar", "payload" => { } } }.to_a.sort
20
+ end
21
+
22
+ it "should represent payload" do
23
+ event = Event.new(:name => "foo", :payload => post, :origin => :bar,
24
+ :payload_representer => PostRepresenter)
25
+
26
+ JSON.parse(event.to_json).to_a.sort.should ==
27
+ { "event" =>
28
+ { "name" => "foo", "origin" => "bar",
29
+ "payload" => { "title" => "DIY" } } }.to_a.sort
30
+ end
31
+
32
+ it "should fallback to default to_json when there is no representer" do
33
+ person = User.create(:name => "Guila")
34
+ event = Event.new(:name => "foo", :payload => person, :origin => :bar)
35
+
36
+ JSON.parse(event.to_json).to_a.sort.should ==
37
+ { "event" =>
38
+ { "name" => "foo", "origin" => "bar",
39
+ "payload" => JSON.parse(person.to_json) } }.to_a.sort
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,16 +1,21 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  require 'spec_helper'
3
+ require 'representable/json'
3
4
 
4
5
  module Untied
5
6
  module Publisher
6
7
  describe Observer do
8
+ before(:all) do
9
+ module UserRepresenter
10
+ include Representable::JSON
11
+ property :name
12
+ end
13
+ end
7
14
  before do
8
15
  class ::FooDoorkeeper
9
16
  include Untied::Publisher::Doorkeeper
10
17
  end
11
18
  Untied::Publisher.config.doorkeeper = ::FooDoorkeeper
12
- module UserRepresenter
13
- end
14
19
  end
15
20
  let(:doorkeeper) { ::FooDoorkeeper.new }
16
21
 
@@ -32,11 +37,12 @@ module Untied
32
37
  Observer.instance.send(:after_create, user)
33
38
  end
34
39
 
35
- context "passing :with_representer" do
36
- it "should call user.extend(UserRepresenter)" do
37
- user.should_receive(:extend).with(UserRepresenter)
40
+ context "passing :represent_with" do
41
+ it "should create a new Event with :payload_representer option" do
42
+ Event.should_receive(:new).
43
+ with(hash_including(:payload_representer))
38
44
  Observer.instance.
39
- send(:after_create, user, :with_representer => UserRepresenter)
45
+ send(:after_create, user, :represent_with => UserRepresenter)
40
46
  end
41
47
  end
42
48
  end
@@ -12,6 +12,7 @@ module SetupActiveRecord
12
12
  ActiveRecord::Schema.define do
13
13
  create_table :posts, :force => true do |t|
14
14
  t.string :title
15
+ t.timestamps
15
16
  end
16
17
  create_table :users, :force => true do |t|
17
18
  t.string :name
@@ -26,6 +26,7 @@ Gem::Specification.new do |gem|
26
26
  gem.add_runtime_dependency "configurable"
27
27
  gem.add_runtime_dependency "json"
28
28
  gem.add_runtime_dependency "representable"
29
+ gem.add_runtime_dependency "bunny", "0.9.0.pre6"
29
30
 
30
31
  if RUBY_VERSION < "1.9"
31
32
  gem.add_development_dependency "ruby-debug"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: untied-publisher
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 5
10
- version: 0.0.5
9
+ - 6
10
+ version: 0.0.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Guilherme Cavalcanti
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-11-20 00:00:00 Z
18
+ date: 2013-02-22 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  version_requirements: &id001 !ruby/object:Gem::Requirement
@@ -131,6 +131,24 @@ dependencies:
131
131
  requirement: *id008
132
132
  - !ruby/object:Gem::Dependency
133
133
  version_requirements: &id009 !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - "="
137
+ - !ruby/object:Gem::Version
138
+ hash: 2794158613
139
+ segments:
140
+ - 0
141
+ - 9
142
+ - 0
143
+ - pre
144
+ - 6
145
+ version: 0.9.0.pre6
146
+ prerelease: false
147
+ type: :runtime
148
+ name: bunny
149
+ requirement: *id009
150
+ - !ruby/object:Gem::Dependency
151
+ version_requirements: &id010 !ruby/object:Gem::Requirement
134
152
  none: false
135
153
  requirements:
136
154
  - - ">="
@@ -142,7 +160,7 @@ dependencies:
142
160
  prerelease: false
143
161
  type: :development
144
162
  name: ruby-debug
145
- requirement: *id009
163
+ requirement: *id010
146
164
  description: Provides the Publisher part of the Untied gem.
147
165
  email:
148
166
  - guiocavalcanti@gmail.com
@@ -162,18 +180,27 @@ files:
162
180
  - README.md
163
181
  - Rakefile
164
182
  - lib/untied-publisher.rb
183
+ - lib/untied-publisher/amqp.rb
184
+ - lib/untied-publisher/amqp/producer.rb
185
+ - lib/untied-publisher/base.rb
186
+ - lib/untied-publisher/base_producer.rb
187
+ - lib/untied-publisher/bunny.rb
188
+ - lib/untied-publisher/bunny/producer.rb
165
189
  - lib/untied-publisher/config.rb
190
+ - lib/untied-publisher/default_doorkeeper.rb
166
191
  - lib/untied-publisher/doorkeeper.rb
167
192
  - lib/untied-publisher/event.rb
168
193
  - lib/untied-publisher/event_representer.rb
169
194
  - lib/untied-publisher/observer.rb
170
- - lib/untied-publisher/producer.rb
171
195
  - lib/untied-publisher/railtie.rb
172
196
  - lib/untied-publisher/version.rb
197
+ - spec/amqp/amqp_producer_spec.rb
198
+ - spec/base_producer_spec.rb
199
+ - spec/bunny/bunny_producer_spec.rb
173
200
  - spec/doorkeeper_spec.rb
201
+ - spec/event_representer_spec.rb
174
202
  - spec/event_spec.rb
175
- - spec/producer_spec.rb
176
- - spec/publisher_observer_spec.rb
203
+ - spec/observer_spec.rb
177
204
  - spec/spec_helper.rb
178
205
  - spec/support/setup_ar_and_schema.rb
179
206
  - untied-publisher.gemspec
@@ -211,9 +238,12 @@ signing_key:
211
238
  specification_version: 3
212
239
  summary: Untied is a Observer Pattern implementation for distributed systems. Think as a cross-application ActiveRecord::Observer. This gem handles the publishing of events
213
240
  test_files:
241
+ - spec/amqp/amqp_producer_spec.rb
242
+ - spec/base_producer_spec.rb
243
+ - spec/bunny/bunny_producer_spec.rb
214
244
  - spec/doorkeeper_spec.rb
245
+ - spec/event_representer_spec.rb
215
246
  - spec/event_spec.rb
216
- - spec/producer_spec.rb
217
- - spec/publisher_observer_spec.rb
247
+ - spec/observer_spec.rb
218
248
  - spec/spec_helper.rb
219
249
  - spec/support/setup_ar_and_schema.rb
@@ -1,73 +0,0 @@
1
- # -*- encoding : utf-8 -*-
2
- require 'amqp'
3
-
4
- module Untied
5
- module Publisher
6
- class Producer
7
- # Encapsulates both the Channel and Exchange (AMQP).
8
-
9
- def initialize(opts={})
10
- @opts = {
11
- :service_name => Publisher.config.service_name,
12
- :deliver_messages => Publisher.config.deliver_messages,
13
- :channel => nil,
14
- }.merge(opts)
15
-
16
- Publisher.config.logger.info "Untied::Publisher: Producer intialized with options #{@opts.inspect}"
17
-
18
- @routing_key = "untied.#{@opts[:service_name]}"
19
-
20
- if !@opts[:deliver_messages]
21
- Publisher.config.logger.info \
22
- "AMQP.channel was not setted up because message delivering is disabled."
23
- return
24
- end
25
-
26
- check_em_reactor
27
-
28
- if AMQP.channel || @opts[:channel]
29
- Publisher.config.logger.info "Using defined AMQP.channel"
30
- @channel = AMQP.channel || @opts[:channel]
31
- end
32
- end
33
-
34
- # Publish the given event.
35
- # event: object which is going to be serialized and sent through the
36
- # wire. It should respond to #to_json.
37
- def publish(event)
38
- safe_publish(event)
39
- end
40
-
41
- protected
42
-
43
- # Publishes if message delivering is enabled. Otherwise just warns.
44
- def safe_publish(e)
45
- if @opts[:deliver_messages]
46
- on_exchange do |exchange|
47
- exchange.publish(e.to_json, :routing_key => @routing_key) do
48
- Publisher.config.logger.info \
49
- "Publishing event #{e.inspect} with routing key #{@routing_key}"
50
- end
51
- end
52
- else
53
- Publisher.config.logger.info \
54
- "The event #{ e.inspect} was not delivered. Try to set " + \
55
- "Untied::Publisher.config.deliver_messages to true"
56
- end
57
- end
58
-
59
- # Creates a new exchange and yields it to the block passed when it's ready
60
- def on_exchange(&block)
61
- return unless @channel
62
- @channel.topic('untied', :auto_delete => true, &block)
63
- end
64
-
65
- def check_em_reactor
66
- if !defined?(EventMachine) || !EM.reactor_running?
67
- raise "In order to use the producer you must be running inside an " + \
68
- "eventmachine loop"
69
- end
70
- end
71
- end
72
- end
73
- end
@@ -1,61 +0,0 @@
1
- # -*- encoding : utf-8 -*-
2
- require 'spec_helper'
3
-
4
- module Untied
5
- module Publisher
6
- describe Producer do
7
- context "configuration" do
8
- it "should use service name configured" do
9
- opts = Producer.new.instance_variable_get(:@opts)
10
- opts[:service_name].should == 'core'
11
- end
12
- end
13
-
14
- it "should initilize producer" do
15
- Producer.new.should be_a Producer
16
- end
17
-
18
- it "should raise RuntimeError when trying to run without connection" do
19
- expect {
20
- Producer.new(:deliver_messages => true)
21
- }.to raise_error
22
- end
23
-
24
- it "should raise RuntimeError if EM is not running" do
25
- mock_reactor_and_amqp do |c|
26
- EM.stub("reactor_running?" => false)
27
- expect {
28
- Producer.new(:channel => c, :deliver_messages => true)
29
- }.to raise_error
30
- end
31
- end
32
-
33
- context "#publish" do
34
- let(:event) do
35
- Event.new(:name => "create", :payload => { :foo => 'bar' }, :origin => :core)
36
- end
37
-
38
- it "should call Channel#publish" do
39
- mock_reactor_and_amqp do |channel|
40
- producer = Producer.new(:channel => channel, :deliver_messages => true)
41
- producer.should_receive(:safe_publish).with(event)
42
- producer.publish(event)
43
- end
44
- end
45
- end
46
-
47
- def mock_reactor_and_amqp
48
- # Do nothing when calling start
49
- Untied.stub(:start).and_return(nil)
50
- # Simulate reactor running
51
- EM.stub(:reactor_running?).and_return(true)
52
-
53
- exchange = double('Exchange')
54
- channel = double('Channel')
55
- channel.stub(:topic).and_return(exchange)
56
-
57
- yield(channel)
58
- end
59
- end
60
- end
61
- end