multiple_man 0.2.8 → 0.3.2
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/lib/multiple_man/attribute_extractor.rb +7 -19
- data/lib/multiple_man/connection.rb +2 -1
- data/lib/multiple_man/identity.rb +18 -0
- data/lib/multiple_man/listener.rb +6 -4
- data/lib/multiple_man/mixins/subscriber.rb +7 -1
- data/lib/multiple_man/model_populator.rb +37 -0
- data/lib/multiple_man/model_publisher.rb +15 -2
- data/lib/multiple_man/subscribers/base.rb +34 -0
- data/lib/multiple_man/subscribers/model_subscriber.rb +33 -0
- data/lib/multiple_man/subscribers/registry.rb +12 -0
- data/lib/multiple_man/version.rb +1 -1
- data/lib/multiple_man.rb +5 -1
- data/spec/attribute_extractor_spec.rb +6 -18
- data/spec/identity_spec.rb +19 -0
- data/spec/listener_spec.rb +6 -7
- data/spec/model_populator_spec.rb +39 -0
- data/spec/model_publisher_spec.rb +20 -2
- data/spec/subscriber_spec.rb +4 -4
- data/spec/subscribers/base_spec.rb +17 -0
- data/spec/subscribers/model_subscriber_spec.rb +30 -0
- data/spec/subscribers/registry_spec.rb +11 -0
- metadata +17 -5
- data/lib/multiple_man/model_subscriber.rb +0 -49
- data/spec/model_subscriber_spec.rb +0 -46
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: e9fe7925c120cc5b704a13b62378b3b459da3b31
         | 
| 4 | 
            +
              data.tar.gz: 21d3d6b079125195a0e85b398b5f106e21836a65
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 0bfec446ad1b8f131216f4493a2a63b61febbcefd1a5974ae7d8a1a9e91f22401f4bd20ab080a560e48e728afdfc0bee8b094fa696e8c7b6636eab856c644d48
         | 
| 7 | 
            +
              data.tar.gz: adf476aed9b285617450d42cb823febeda32864bc1ebe9e844abd15dd21c30c22abc5b5d661ca87cc601bff75f8c01f35492b24f9fb1d855081da61b1f0c9fd8
         | 
| @@ -3,33 +3,21 @@ require 'json' | |
| 3 3 | 
             
            module MultipleMan
         | 
| 4 4 | 
             
              class AttributeExtractor
         | 
| 5 5 |  | 
| 6 | 
            -
                def initialize(record, fields | 
| 6 | 
            +
                def initialize(record, fields)
         | 
| 7 7 | 
             
                  raise "Fields must be specified" unless fields
         | 
| 8 8 |  | 
| 9 9 | 
             
                  self.record = record
         | 
| 10 | 
            -
                  self.fields = fields | 
| 11 | 
            -
                  self.identifier = identifier || :id
         | 
| 10 | 
            +
                  self.fields = fields
         | 
| 12 11 | 
             
                end
         | 
| 13 12 |  | 
| 14 13 | 
             
                def data
         | 
| 15 | 
            -
                   | 
| 16 | 
            -
                     | 
| 17 | 
            -
             | 
| 18 | 
            -
                      [field, record.send(field)]
         | 
| 19 | 
            -
                    end]
         | 
| 20 | 
            -
                  }
         | 
| 14 | 
            +
                  Hash[fields.map do |field|
         | 
| 15 | 
            +
                    [field, record.send(field)]
         | 
| 16 | 
            +
                  end]
         | 
| 21 17 | 
             
                end
         | 
| 22 18 |  | 
| 23 | 
            -
                def  | 
| 24 | 
            -
                   | 
| 25 | 
            -
                    identifier.call(record)
         | 
| 26 | 
            -
                  else
         | 
| 27 | 
            -
                    record.send(identifier)
         | 
| 28 | 
            -
                  end
         | 
| 29 | 
            -
                end
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                def to_json
         | 
| 32 | 
            -
                  data.to_json
         | 
| 19 | 
            +
                def as_json
         | 
| 20 | 
            +
                  data
         | 
| 33 21 | 
             
                end
         | 
| 34 22 |  | 
| 35 23 | 
             
              private
         | 
| @@ -1,12 +1,13 @@ | |
| 1 1 | 
             
            require 'bunny'
         | 
| 2 2 | 
             
            require 'connection_pool'
         | 
| 3 | 
            +
            require 'active_support/core_ext/module'
         | 
| 3 4 |  | 
| 4 5 | 
             
            module MultipleMan
         | 
| 5 6 | 
             
              class Connection
         | 
| 6 7 | 
             
                def self.connection
         | 
| 7 8 | 
             
                  @connection ||= begin
         | 
| 8 9 | 
             
                    connection = Bunny.new(MultipleMan.configuration.connection)
         | 
| 9 | 
            -
                    MultipleMan.logger. | 
| 10 | 
            +
                    MultipleMan.logger.debug "Connecting to #{MultipleMan.configuration.connection}"
         | 
| 10 11 | 
             
                    connection.start
         | 
| 11 12 | 
             
                    connection
         | 
| 12 13 | 
             
                  end
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            module MultipleMan
         | 
| 2 | 
            +
              class Identity
         | 
| 3 | 
            +
                def initialize(record, identifier = :id)
         | 
| 4 | 
            +
                  self.record = record
         | 
| 5 | 
            +
                  self.identifier = identifier || :id
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def value
         | 
| 9 | 
            +
                  if identifier.class == Proc
         | 
| 10 | 
            +
                    identifier.call(record).to_s
         | 
| 11 | 
            +
                  else
         | 
| 12 | 
            +
                    record.send(identifier).to_s
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                attr_accessor :record, :identifier
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
| @@ -6,9 +6,11 @@ module MultipleMan | |
| 6 6 |  | 
| 7 7 | 
             
                class << self
         | 
| 8 8 | 
             
                  def start(connection)
         | 
| 9 | 
            -
                    MultipleMan.logger. | 
| 9 | 
            +
                    MultipleMan.logger.debug "Starting listeners."
         | 
| 10 10 | 
             
                    self.connection = connection
         | 
| 11 | 
            -
                     | 
| 11 | 
            +
                    MultipleMan.logger.debug Subscribers::Registry.subscriptions.to_json
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    Subscribers::Registry.subscriptions.each do |subscription|
         | 
| 12 14 | 
             
                      new(subscription).listen
         | 
| 13 15 | 
             
                    end
         | 
| 14 16 | 
             
                  end
         | 
| @@ -35,10 +37,10 @@ module MultipleMan | |
| 35 37 | 
             
                    operation = delivery_info.routing_key.split(".").last
         | 
| 36 38 | 
             
                    subscription.send(operation, JSON.parse(payload).with_indifferent_access)
         | 
| 37 39 | 
             
                  rescue Exception => ex
         | 
| 38 | 
            -
                    MultipleMan.logger.error "   Error - #{ex.message}"
         | 
| 40 | 
            +
                    MultipleMan.logger.error "   Error - #{ex.message}\n\n#{ex.backtrace}"
         | 
| 39 41 | 
             
                    MultipleMan.error(ex)
         | 
| 40 42 | 
             
                  else
         | 
| 41 | 
            -
                    MultipleMan.logger. | 
| 43 | 
            +
                    MultipleMan.logger.debug "   Successfully processed!"
         | 
| 42 44 | 
             
                  end
         | 
| 43 45 | 
             
                end
         | 
| 44 46 |  | 
| @@ -1,7 +1,13 @@ | |
| 1 1 | 
             
            module MultipleMan
         | 
| 2 2 | 
             
              module Subscriber
         | 
| 3 3 | 
             
                def Subscriber.included(base)
         | 
| 4 | 
            -
                   | 
| 4 | 
            +
                  base.extend(ClassMethods)
         | 
| 5 | 
            +
                end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                module ClassMethods
         | 
| 8 | 
            +
                  def subscribe(options = {})
         | 
| 9 | 
            +
                    Subscribers::Registry.register(Subscribers::ModelSubscriber.new(self, options))
         | 
| 10 | 
            +
                  end
         | 
| 5 11 | 
             
                end
         | 
| 6 12 | 
             
              end
         | 
| 7 13 | 
             
            end
         | 
| @@ -0,0 +1,37 @@ | |
| 1 | 
            +
            module MultipleMan
         | 
| 2 | 
            +
              class ModelPopulator
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                def initialize(record, fields)
         | 
| 5 | 
            +
                  self.record = record
         | 
| 6 | 
            +
                  self.fields = fields
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def populate(data)
         | 
| 10 | 
            +
                  fields_for(data).each do |field|
         | 
| 11 | 
            +
                    populate_field(field, data[field])
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                  record
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              private 
         | 
| 17 | 
            +
                attr_accessor :record, :fields
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                # Raise an exception if explicit fields were provided.
         | 
| 20 | 
            +
                def should_raise?
         | 
| 21 | 
            +
                  fields.present?
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def populate_field(field, value)
         | 
| 25 | 
            +
                  setter = "#{field}="
         | 
| 26 | 
            +
                  if record.respond_to?(setter)
         | 
| 27 | 
            +
                    record.send(setter, value)
         | 
| 28 | 
            +
                  elsif should_raise?
         | 
| 29 | 
            +
                    raise "Record #{record} does not respond to #{setter}"
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def fields_for(data)
         | 
| 34 | 
            +
                  fields || data.keys
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
            end
         | 
| @@ -22,7 +22,7 @@ module MultipleMan | |
| 22 22 | 
             
                def push_record(connection, record)
         | 
| 23 23 | 
             
                  data = record_data(record)
         | 
| 24 24 | 
             
                  routing_key = RoutingKey.new(record_type(record), operation).to_s
         | 
| 25 | 
            -
                  MultipleMan.logger. | 
| 25 | 
            +
                  MultipleMan.logger.debug("  Record Data: #{data} | Routing Key: #{routing_key}")
         | 
| 26 26 | 
             
                  connection.topic.publish(data, routing_key: routing_key)
         | 
| 27 27 | 
             
                rescue Exception => ex
         | 
| 28 28 | 
             
                  MultipleMan.error(ex)
         | 
| @@ -33,7 +33,20 @@ module MultipleMan | |
| 33 33 | 
             
                end
         | 
| 34 34 |  | 
| 35 35 | 
             
                def record_data(record)
         | 
| 36 | 
            -
                   | 
| 36 | 
            +
                  { 
         | 
| 37 | 
            +
                    id: Identity.new(record, identifier).value,
         | 
| 38 | 
            +
                    data: serializer(record).as_json
         | 
| 39 | 
            +
                  }.to_json
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                # Todo - can we unify the constructor for serializers
         | 
| 43 | 
            +
                # and attribute extractors and simplify this?
         | 
| 44 | 
            +
                def serializer(record)
         | 
| 45 | 
            +
                  if options[:with]
         | 
| 46 | 
            +
                    options[:with].new(record)
         | 
| 47 | 
            +
                  else
         | 
| 48 | 
            +
                    AttributeExtractor.new(record, fields)
         | 
| 49 | 
            +
                  end
         | 
| 37 50 | 
             
                end
         | 
| 38 51 |  | 
| 39 52 | 
             
                def fields
         | 
| @@ -0,0 +1,34 @@ | |
| 1 | 
            +
            module MultipleMan::Subscribers
         | 
| 2 | 
            +
              class Base
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                def initialize(klass)
         | 
| 5 | 
            +
                  self.klass = klass
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                attr_reader :klass
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def create
         | 
| 11 | 
            +
                  # noop
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def update
         | 
| 15 | 
            +
                  # noop
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def destroy
         | 
| 19 | 
            +
                  # noop
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def routing_key
         | 
| 23 | 
            +
                  MultipleMan::RoutingKey.new(klass).to_s
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def queue_name
         | 
| 27 | 
            +
                  "#{MultipleMan.configuration.topic_name}.#{MultipleMan.configuration.app_name}.#{klass.name}"
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              private
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                attr_writer :klass
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            module MultipleMan::Subscribers 
         | 
| 2 | 
            +
              class ModelSubscriber < Base
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                def initialize(klass, options)
         | 
| 5 | 
            +
                  super(klass)
         | 
| 6 | 
            +
                  self.options = options
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                attr_accessor :options
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def create(payload)
         | 
| 12 | 
            +
                  model = find_model(payload[:id])
         | 
| 13 | 
            +
                  MultipleMan::ModelPopulator.new(model, options[:fields]).populate(payload[:data])
         | 
| 14 | 
            +
                  model.save!
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                alias_method :update, :create
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def destroy(payload)
         | 
| 20 | 
            +
                  model = find_model(payload[:id])
         | 
| 21 | 
            +
                  model.destroy!
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              private
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def find_model(id)
         | 
| 27 | 
            +
                  klass.find_or_initialize_by(multiple_man_identifier: id)
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                attr_writer :klass
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
    
        data/lib/multiple_man/version.rb
    CHANGED
    
    
    
        data/lib/multiple_man.rb
    CHANGED
    
    | @@ -5,13 +5,17 @@ module MultipleMan | |
| 5 5 |  | 
| 6 6 | 
             
              require 'multiple_man/mixins/publisher'
         | 
| 7 7 | 
             
              require 'multiple_man/mixins/subscriber'
         | 
| 8 | 
            +
              require 'multiple_man/subscribers/base'
         | 
| 9 | 
            +
              require 'multiple_man/subscribers/model_subscriber'
         | 
| 10 | 
            +
              require 'multiple_man/subscribers/registry'
         | 
| 8 11 | 
             
              require 'multiple_man/configuration'
         | 
| 9 | 
            -
              require 'multiple_man/model_subscriber'
         | 
| 10 12 | 
             
              require 'multiple_man/model_publisher'
         | 
| 11 13 | 
             
              require 'multiple_man/attribute_extractor'
         | 
| 12 14 | 
             
              require 'multiple_man/connection'
         | 
| 13 15 | 
             
              require 'multiple_man/routing_key'
         | 
| 14 16 | 
             
              require 'multiple_man/listener'
         | 
| 17 | 
            +
              require 'multiple_man/model_populator'
         | 
| 18 | 
            +
              require 'multiple_man/identity'
         | 
| 15 19 |  | 
| 16 20 | 
             
              def self.logger
         | 
| 17 21 | 
             
                configuration.logger
         | 
| @@ -4,9 +4,8 @@ describe MultipleMan::AttributeExtractor do | |
| 4 4 |  | 
| 5 5 | 
             
              MockClass = Struct.new(:a, :b, :c, :id)
         | 
| 6 6 | 
             
              let(:object) { MockClass.new(1,2,3,10) }
         | 
| 7 | 
            -
              subject { described_class.new(object, fields | 
| 7 | 
            +
              subject { described_class.new(object, fields) }
         | 
| 8 8 | 
             
              let(:fields) { nil }
         | 
| 9 | 
            -
              let(:identifier) { nil }
         | 
| 10 9 |  | 
| 11 10 | 
             
              context "without fields" do
         | 
| 12 11 | 
             
                it "should not be allowed" do
         | 
| @@ -16,22 +15,11 @@ describe MultipleMan::AttributeExtractor do | |
| 16 15 |  | 
| 17 16 | 
             
              context "with fields" do
         | 
| 18 17 | 
             
                let(:fields) { [:a, :c] }
         | 
| 19 | 
            -
                its(:data) { should == { | 
| 20 | 
            -
                its(: | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
                let(:fields) { [:a] }
         | 
| 25 | 
            -
                context "symbol" do
         | 
| 26 | 
            -
                  let(:identifier) { :a }
         | 
| 27 | 
            -
                  its(:data) { should == {id:1, data:{a: 1}}}
         | 
| 28 | 
            -
                  its(:to_json) { should == '{"id":1,"data":{"a":1}}'}
         | 
| 29 | 
            -
                end
         | 
| 30 | 
            -
                context "proc" do
         | 
| 31 | 
            -
                  let(:identifier) { lambda{|record| record.b } }
         | 
| 32 | 
            -
                  its(:data) { should == {id:2, data:{a: 1}}}
         | 
| 33 | 
            -
                  its(:to_json) { should == '{"id":2,"data":{"a":1}}'}
         | 
| 34 | 
            -
                end
         | 
| 18 | 
            +
                its(:data) { should == {a: 1, c: 3}}
         | 
| 19 | 
            +
                its(:as_json) { should == {
         | 
| 20 | 
            +
                  a: 1,
         | 
| 21 | 
            +
                  c: 3
         | 
| 22 | 
            +
                } }
         | 
| 35 23 | 
             
              end
         | 
| 36 24 |  | 
| 37 25 | 
             
            end
         | 
| @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe MultipleMan::Identity do 
         | 
| 4 | 
            +
              let(:record) { double(:model, id: 1, foo: 'foo', bar: 'bar' )}
         | 
| 5 | 
            +
              subject { described_class.new(record, identifier).value }
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              context "proc identifier" do
         | 
| 8 | 
            +
                let(:identifier) { lambda{|record| "#{record.foo}-#{record.id}" } }
         | 
| 9 | 
            +
                it { should == "foo-1" }
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
              context "symbol identifier" do
         | 
| 12 | 
            +
                let(:identifier) { :foo }
         | 
| 13 | 
            +
                it { should == "foo" }
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
              context "no identifier" do
         | 
| 16 | 
            +
                let(:identifier) { :id }
         | 
| 17 | 
            +
                it { should == "1" }
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
            end
         | 
    
        data/spec/listener_spec.rb
    CHANGED
    
    | @@ -6,9 +6,9 @@ describe MultipleMan::Listener do | |
| 6 6 |  | 
| 7 7 | 
             
              describe "start" do
         | 
| 8 8 | 
             
                it "should listen to each subscription" do
         | 
| 9 | 
            -
                  MultipleMan:: | 
| 10 | 
            -
                    mock1 = double(MultipleMan::ModelSubscriber, klass: MockClass1),
         | 
| 11 | 
            -
                    mock2 = double(MultipleMan::ModelSubscriber, klass: MockClass2)
         | 
| 9 | 
            +
                  MultipleMan::Subscribers::Registry.stub(:subscriptions).and_return([
         | 
| 10 | 
            +
                    mock1 = double(MultipleMan::Subscribers::ModelSubscriber, klass: MockClass1),
         | 
| 11 | 
            +
                    mock2 = double(MultipleMan::Subscribers::ModelSubscriber, klass: MockClass2)
         | 
| 12 12 | 
             
                  ])
         | 
| 13 13 |  | 
| 14 14 | 
             
                  mock_listener = double(MultipleMan::Listener)
         | 
| @@ -23,22 +23,21 @@ describe MultipleMan::Listener do | |
| 23 23 | 
             
              end
         | 
| 24 24 |  | 
| 25 25 | 
             
              describe "listen" do
         | 
| 26 | 
            -
                let(:connection_stub) { double( | 
| 27 | 
            -
                let(:channel_stub) { double(Bunny::Channel, queue: queue_stub) }
         | 
| 26 | 
            +
                let(:connection_stub) { double(MultipleMan::Connection, queue: queue_stub, topic: 'app') }
         | 
| 28 27 | 
             
                let(:queue_stub) { double(Bunny::Queue, bind: bind_stub) }
         | 
| 29 28 | 
             
                let(:bind_stub) { double(:bind, subscribe: nil)}
         | 
| 30 29 |  | 
| 31 30 | 
             
                before { MultipleMan::Listener.stub(:connection).and_return(connection_stub) }
         | 
| 32 31 |  | 
| 33 32 | 
             
                it "should listen to the right topic, and for all updates to a model" do
         | 
| 34 | 
            -
                  listener = MultipleMan::Listener.new(double(MultipleMan::ModelSubscriber, klass: MockClass1, routing_key: "MockClass1.#", queue_name: "MockClass1"))
         | 
| 33 | 
            +
                  listener = MultipleMan::Listener.new(double(MultipleMan::Subscribers::ModelSubscriber, klass: MockClass1, routing_key: "MockClass1.#", queue_name: "MockClass1"))
         | 
| 35 34 | 
             
                  queue_stub.should_receive(:bind).with('app', routing_key: "MockClass1.#")
         | 
| 36 35 | 
             
                  listener.listen
         | 
| 37 36 | 
             
                end
         | 
| 38 37 | 
             
              end
         | 
| 39 38 |  | 
| 40 39 | 
             
              specify "process_message should send the correct data" do
         | 
| 41 | 
            -
                subscriber = double(MultipleMan::ModelSubscriber, klass: MockClass1, routing_key: "MockClass1.#")
         | 
| 40 | 
            +
                subscriber = double(MultipleMan::Subscribers::ModelSubscriber, klass: MockClass1, routing_key: "MockClass1.#")
         | 
| 42 41 | 
             
                listener = MultipleMan::Listener.new(subscriber)
         | 
| 43 42 | 
             
                subscriber.should_receive(:create).with({"a" => 1, "b" => 2})
         | 
| 44 43 | 
             
                listener.process_message(OpenStruct.new(routing_key: "app.MockClass1.create"), '{"a":1,"b":2}')
         | 
| @@ -0,0 +1,39 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe MultipleMan::ModelPopulator do 
         | 
| 4 | 
            +
              class MockModel
         | 
| 5 | 
            +
                attr_accessor :a, :b
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              describe "populate" do
         | 
| 9 | 
            +
                let(:model) { MockModel.new }
         | 
| 10 | 
            +
                let(:data) { { a: 1, b: 2 } }
         | 
| 11 | 
            +
                subject { described_class.new(model, fields).populate(data) }
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                context "with fields defined" do
         | 
| 14 | 
            +
                  let(:fields) { [:a] }
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  its(:b) { should == nil }
         | 
| 17 | 
            +
                  its(:a) { should == 1 }
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  context "when a property doesn't exist" do
         | 
| 20 | 
            +
                    before { model.stub(:respond_to?) { false } }
         | 
| 21 | 
            +
                    it "should raise an error" do
         | 
| 22 | 
            +
                      expect { subject }.to raise_error
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
                context "without fields defined" do
         | 
| 27 | 
            +
                  let(:fields) { nil }
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  its(:b) { should == 2 }
         | 
| 30 | 
            +
                  its(:a) { should == 1 }
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  context "when a property doesn't exist" do
         | 
| 33 | 
            +
                    it "should go on it's merry way" do
         | 
| 34 | 
            +
                      expect { subject }.to_not raise_error
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
            end
         | 
| @@ -29,8 +29,8 @@ describe MultipleMan::ModelPublisher do | |
| 29 29 |  | 
| 30 30 | 
             
              describe "after_commit" do
         | 
| 31 31 | 
             
                it "should send the jsonified version of the model to the correct routing key" do
         | 
| 32 | 
            -
                  MultipleMan::AttributeExtractor.any_instance.should_receive(: | 
| 33 | 
            -
                  topic_stub.should_receive(:publish).with('{"id":10,"data":{"foo": | 
| 32 | 
            +
                  MultipleMan::AttributeExtractor.any_instance.should_receive(:as_json).and_return({foo: "bar"})
         | 
| 33 | 
            +
                  topic_stub.should_receive(:publish).with('{"id":"10","data":{"foo":"bar"}}', routing_key: "app.MockObject.create")
         | 
| 34 34 | 
             
                  described_class.new(:create, fields: [:foo]).after_commit(MockObject.new)
         | 
| 35 35 | 
             
                end
         | 
| 36 36 |  | 
| @@ -42,4 +42,22 @@ describe MultipleMan::ModelPublisher do | |
| 42 42 | 
             
                end
         | 
| 43 43 | 
             
              end
         | 
| 44 44 |  | 
| 45 | 
            +
              describe "with a serializer" do
         | 
| 46 | 
            +
                class MySerializer
         | 
| 47 | 
            +
                  def initialize(record)
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
                  def as_json
         | 
| 50 | 
            +
                    {a: "yes"}
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                subject { described_class.new(:create, with: MySerializer) }
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                it "should get its data from the serializer" do
         | 
| 57 | 
            +
                  obj = MockObject.new
         | 
| 58 | 
            +
                  topic_stub.should_receive(:publish).with('{"id":"10","data":{"a":"yes"}}', routing_key: "app.MockObject.create")
         | 
| 59 | 
            +
                  subject.after_commit(obj)
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 45 63 | 
             
            end
         | 
    
        data/spec/subscriber_spec.rb
    CHANGED
    
    | @@ -2,13 +2,13 @@ require 'spec_helper' | |
| 2 2 |  | 
| 3 3 | 
             
            describe MultipleMan::Subscriber do 
         | 
| 4 4 | 
             
              class MockClass
         | 
| 5 | 
            -
             | 
| 5 | 
            +
                include MultipleMan::Subscriber
         | 
| 6 6 | 
             
              end
         | 
| 7 7 |  | 
| 8 | 
            -
              describe " | 
| 8 | 
            +
              describe "subscribe" do
         | 
| 9 9 | 
             
                it "should register itself" do
         | 
| 10 | 
            -
                  MultipleMan:: | 
| 11 | 
            -
                  MockClass. | 
| 10 | 
            +
                  MultipleMan::Subscribers::Registry.should_receive(:register).with(instance_of(MultipleMan::Subscribers::ModelSubscriber))
         | 
| 11 | 
            +
                  MockClass.subscribe fields: [:foo, :bar]
         | 
| 12 12 | 
             
                end 
         | 
| 13 13 | 
             
              end
         | 
| 14 14 | 
             
            end
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe MultipleMan::Subscribers::Base do
         | 
| 4 | 
            +
              class MockClass
         | 
| 5 | 
            +
              end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              specify "routing_key should be the model name and a wildcard" do
         | 
| 8 | 
            +
                described_class.new(MockClass).routing_key.should == "app.MockClass.#"
         | 
| 9 | 
            +
              end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              specify "queue name should be the app name + class" do
         | 
| 12 | 
            +
                MultipleMan.configure do |config|
         | 
| 13 | 
            +
                  config.app_name = "test"
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
                described_class.new(MockClass).queue_name.should == "app.test.MockClass"
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe MultipleMan::Subscribers::ModelSubscriber do
         | 
| 4 | 
            +
              class MockClass
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              describe "create" do
         | 
| 9 | 
            +
                it "should create a new model" do
         | 
| 10 | 
            +
                  mock_object = MockClass.new
         | 
| 11 | 
            +
                  MockClass.stub(:find_or_initialize_by).with(multiple_man_identifier: 5).and_return(mock_object)
         | 
| 12 | 
            +
                  mock_populator = double(MultipleMan::ModelPopulator)
         | 
| 13 | 
            +
                  MultipleMan::ModelPopulator.should_receive(:new).and_return(mock_populator)
         | 
| 14 | 
            +
                  mock_populator.should_receive(:populate).with({a: 1, b: 2})
         | 
| 15 | 
            +
                  mock_object.should_receive(:save!)
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  described_class.new(MockClass, {}).create({id: 5, data:{a: 1, b: 2}})
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              describe "destroy" do
         | 
| 22 | 
            +
                it "should destroy the model" do
         | 
| 23 | 
            +
                  mock_object = MockClass.new
         | 
| 24 | 
            +
                  MockClass.should_receive(:find_or_initialize_by).and_return(mock_object)
         | 
| 25 | 
            +
                  mock_object.should_receive(:destroy!)
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  described_class.new(MockClass, {}).destroy({id: 1})
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
            end
         | 
| @@ -0,0 +1,11 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe MultipleMan::Subscribers::Registry do 
         | 
| 4 | 
            +
              describe "register" do 
         | 
| 5 | 
            +
                it "should add a subscriber" do
         | 
| 6 | 
            +
                  subscription = double(:subscriber)
         | 
| 7 | 
            +
                  described_class.register(subscription)
         | 
| 8 | 
            +
                  described_class.subscriptions[0].should == subscription
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: multiple_man
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.2 | 
| 4 | 
            +
              version: 0.3.2
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Ryan Brunner
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2014-01- | 
| 11 | 
            +
            date: 2014-01-24 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: bundler
         | 
| @@ -111,26 +111,34 @@ files: | |
| 111 111 | 
             
            - lib/multiple_man/attribute_extractor.rb
         | 
| 112 112 | 
             
            - lib/multiple_man/configuration.rb
         | 
| 113 113 | 
             
            - lib/multiple_man/connection.rb
         | 
| 114 | 
            +
            - lib/multiple_man/identity.rb
         | 
| 114 115 | 
             
            - lib/multiple_man/listener.rb
         | 
| 115 116 | 
             
            - lib/multiple_man/mixins/publisher.rb
         | 
| 116 117 | 
             
            - lib/multiple_man/mixins/subscriber.rb
         | 
| 118 | 
            +
            - lib/multiple_man/model_populator.rb
         | 
| 117 119 | 
             
            - lib/multiple_man/model_publisher.rb
         | 
| 118 | 
            -
            - lib/multiple_man/model_subscriber.rb
         | 
| 119 120 | 
             
            - lib/multiple_man/railtie.rb
         | 
| 120 121 | 
             
            - lib/multiple_man/routing_key.rb
         | 
| 122 | 
            +
            - lib/multiple_man/subscribers/base.rb
         | 
| 123 | 
            +
            - lib/multiple_man/subscribers/model_subscriber.rb
         | 
| 124 | 
            +
            - lib/multiple_man/subscribers/registry.rb
         | 
| 121 125 | 
             
            - lib/multiple_man/tasks/worker.rake
         | 
| 122 126 | 
             
            - lib/multiple_man/version.rb
         | 
| 123 127 | 
             
            - multiple_man.gemspec
         | 
| 124 128 | 
             
            - spec/attribute_extractor_spec.rb
         | 
| 125 129 | 
             
            - spec/connection_spec.rb
         | 
| 130 | 
            +
            - spec/identity_spec.rb
         | 
| 126 131 | 
             
            - spec/listener_spec.rb
         | 
| 127 132 | 
             
            - spec/logger_spec.rb
         | 
| 133 | 
            +
            - spec/model_populator_spec.rb
         | 
| 128 134 | 
             
            - spec/model_publisher_spec.rb
         | 
| 129 | 
            -
            - spec/model_subscriber_spec.rb
         | 
| 130 135 | 
             
            - spec/publisher_spec.rb
         | 
| 131 136 | 
             
            - spec/routing_key_spec.rb
         | 
| 132 137 | 
             
            - spec/spec_helper.rb
         | 
| 133 138 | 
             
            - spec/subscriber_spec.rb
         | 
| 139 | 
            +
            - spec/subscribers/base_spec.rb
         | 
| 140 | 
            +
            - spec/subscribers/model_subscriber_spec.rb
         | 
| 141 | 
            +
            - spec/subscribers/registry_spec.rb
         | 
| 134 142 | 
             
            homepage: ''
         | 
| 135 143 | 
             
            licenses:
         | 
| 136 144 | 
             
            - MIT
         | 
| @@ -158,11 +166,15 @@ summary: MultipleMan syncs changes to ActiveRecord models via AMQP | |
| 158 166 | 
             
            test_files:
         | 
| 159 167 | 
             
            - spec/attribute_extractor_spec.rb
         | 
| 160 168 | 
             
            - spec/connection_spec.rb
         | 
| 169 | 
            +
            - spec/identity_spec.rb
         | 
| 161 170 | 
             
            - spec/listener_spec.rb
         | 
| 162 171 | 
             
            - spec/logger_spec.rb
         | 
| 172 | 
            +
            - spec/model_populator_spec.rb
         | 
| 163 173 | 
             
            - spec/model_publisher_spec.rb
         | 
| 164 | 
            -
            - spec/model_subscriber_spec.rb
         | 
| 165 174 | 
             
            - spec/publisher_spec.rb
         | 
| 166 175 | 
             
            - spec/routing_key_spec.rb
         | 
| 167 176 | 
             
            - spec/spec_helper.rb
         | 
| 168 177 | 
             
            - spec/subscriber_spec.rb
         | 
| 178 | 
            +
            - spec/subscribers/base_spec.rb
         | 
| 179 | 
            +
            - spec/subscribers/model_subscriber_spec.rb
         | 
| 180 | 
            +
            - spec/subscribers/registry_spec.rb
         | 
| @@ -1,49 +0,0 @@ | |
| 1 | 
            -
            module MultipleMan
         | 
| 2 | 
            -
              class ModelSubscriber
         | 
| 3 | 
            -
             | 
| 4 | 
            -
                @subscriptions = []
         | 
| 5 | 
            -
                class << self
         | 
| 6 | 
            -
                  attr_accessor :subscriptions
         | 
| 7 | 
            -
             | 
| 8 | 
            -
                  def register(klass)
         | 
| 9 | 
            -
                    self.subscriptions << new(klass)
         | 
| 10 | 
            -
                  end
         | 
| 11 | 
            -
                end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                def initialize(klass)
         | 
| 14 | 
            -
                  self.klass = klass
         | 
| 15 | 
            -
                end
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                attr_reader :klass    
         | 
| 18 | 
            -
             | 
| 19 | 
            -
                def create(payload)
         | 
| 20 | 
            -
                  model = find_model(payload[:id])
         | 
| 21 | 
            -
                  model.attributes = payload[:data]
         | 
| 22 | 
            -
                  model.save!
         | 
| 23 | 
            -
                end
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                alias_method :update, :create
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                def destroy(payload)
         | 
| 28 | 
            -
                  model = find_model(payload[:id])
         | 
| 29 | 
            -
                  model.destroy!
         | 
| 30 | 
            -
                end
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                def routing_key
         | 
| 33 | 
            -
                  RoutingKey.new(klass).to_s
         | 
| 34 | 
            -
                end
         | 
| 35 | 
            -
             | 
| 36 | 
            -
                def queue_name
         | 
| 37 | 
            -
                  "#{MultipleMan.configuration.topic_name}.#{MultipleMan.configuration.app_name}.#{klass.name}"
         | 
| 38 | 
            -
                end
         | 
| 39 | 
            -
             | 
| 40 | 
            -
              private
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                def find_model(id)
         | 
| 43 | 
            -
                  klass.find_or_initialize_by(multiple_man_identifier: id)
         | 
| 44 | 
            -
                end
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                attr_writer :klass
         | 
| 47 | 
            -
             | 
| 48 | 
            -
              end
         | 
| 49 | 
            -
            end
         | 
| @@ -1,46 +0,0 @@ | |
| 1 | 
            -
            require 'spec_helper'
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            describe MultipleMan::ModelSubscriber do
         | 
| 4 | 
            -
              class MockClass
         | 
| 5 | 
            -
             | 
| 6 | 
            -
              end
         | 
| 7 | 
            -
             | 
| 8 | 
            -
              describe "register" do 
         | 
| 9 | 
            -
                it "should add a subscriber" do
         | 
| 10 | 
            -
                  MultipleMan::ModelSubscriber.register(MockClass)
         | 
| 11 | 
            -
                  MultipleMan::ModelSubscriber.subscriptions[0].klass.should == MockClass
         | 
| 12 | 
            -
                end
         | 
| 13 | 
            -
              end
         | 
| 14 | 
            -
             | 
| 15 | 
            -
              describe "create" do
         | 
| 16 | 
            -
                it "should create a new model" do
         | 
| 17 | 
            -
                  mock_object = MockClass.new
         | 
| 18 | 
            -
                  MockClass.stub(:find_or_initialize_by).with(multiple_man_identifier: 5).and_return(mock_object)
         | 
| 19 | 
            -
                  mock_object.should_receive(:attributes=)
         | 
| 20 | 
            -
                  mock_object.should_receive(:save!)
         | 
| 21 | 
            -
             | 
| 22 | 
            -
                  MultipleMan::ModelSubscriber.new(MockClass).create({id: 5, data:{a: 1, b: 2}})
         | 
| 23 | 
            -
                end
         | 
| 24 | 
            -
              end
         | 
| 25 | 
            -
             | 
| 26 | 
            -
              describe "destroy" do
         | 
| 27 | 
            -
                it "should destroy the model" do
         | 
| 28 | 
            -
                  mock_object = MockClass.new
         | 
| 29 | 
            -
                  MockClass.should_receive(:find_or_initialize_by).and_return(mock_object)
         | 
| 30 | 
            -
                  mock_object.should_receive(:destroy!)
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                  MultipleMan::ModelSubscriber.new(MockClass).destroy({id: 1})
         | 
| 33 | 
            -
                end
         | 
| 34 | 
            -
              end
         | 
| 35 | 
            -
             | 
| 36 | 
            -
              specify "routing_key should be the model name and a wildcard" do
         | 
| 37 | 
            -
                MultipleMan::ModelSubscriber.new(MockClass).routing_key.should == "app.MockClass.#"
         | 
| 38 | 
            -
              end
         | 
| 39 | 
            -
             | 
| 40 | 
            -
              specify "queue name should be the app name + class" do
         | 
| 41 | 
            -
                MultipleMan.configure do |config|
         | 
| 42 | 
            -
                  config.app_name = "test"
         | 
| 43 | 
            -
                end
         | 
| 44 | 
            -
                MultipleMan::ModelSubscriber.new(MockClass).queue_name.should == "app.test.MockClass"
         | 
| 45 | 
            -
              end
         | 
| 46 | 
            -
            end
         |