untied-publisher 0.0.5 → 0.0.6

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