From 9b15f0d51657d285a185503fcda599d5561aa86f Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 10 Sep 2019 12:15:34 -0400 Subject: [PATCH 1/3] Fixed: has been renamed to . is deprecated and will be removed in Rails 6.1 --- lib/multiple_man/configuration.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/multiple_man/configuration.rb b/lib/multiple_man/configuration.rb index 6734650..b47cf8f 100644 --- a/lib/multiple_man/configuration.rb +++ b/lib/multiple_man/configuration.rb @@ -18,7 +18,7 @@ class Configuration def initialize self.topic_name = "multiple_man" - self.app_name = Rails.application.class.parent.to_s if defined?(Rails) + self.app_name = Rails.application.class.module_parent.to_s if defined?(Rails) self.enabled = true self.worker_concurrency = 1 self.reraise_errors = true From 0df318d54f33b5c5853b70e56f0577457586a832 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 30 Oct 2019 10:25:08 -0400 Subject: [PATCH 2/3] Fixed missing "number" attr --- lib/multiple_man/channel_maintenance/gc.rb | 2 +- lib/multiple_man/channel_maintenance/reaper.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/multiple_man/channel_maintenance/gc.rb b/lib/multiple_man/channel_maintenance/gc.rb index 1aea1c3..817b132 100644 --- a/lib/multiple_man/channel_maintenance/gc.rb +++ b/lib/multiple_man/channel_maintenance/gc.rb @@ -37,7 +37,7 @@ def push(channel) queue << AddCommand.new(thread_id, channel) - puts "Opened channel #{channel.number}" + puts "Opened channel #{channel.try(:number)}" self end diff --git a/lib/multiple_man/channel_maintenance/reaper.rb b/lib/multiple_man/channel_maintenance/reaper.rb index 01c06f2..d0d7abc 100644 --- a/lib/multiple_man/channel_maintenance/reaper.rb +++ b/lib/multiple_man/channel_maintenance/reaper.rb @@ -10,7 +10,7 @@ def initialize(config) channel = queue.pop begin channel.close unless channel.closed? - puts "Channel #{channel.number} closed!" + puts "Channel #{channel.try(:number)} closed!" rescue Bunny::Exception, Timeout::Error sleep config.connection_recovery[:time_between_retries] retry From 735413951028d156c14cdf46a7c3ab03af352e66 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 30 Oct 2019 10:25:33 -0400 Subject: [PATCH 3/3] Added rspec utilities --- README.md | 28 ++++++++++++++--- lib/multiple_man/runner.rb | 23 ++++++++++++-- spec/runner_spec.rb | 61 +++++++++++++++++++++++++++++++------- 3 files changed, 95 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index dc0cf22..1ad6109 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # MultipleMan -# WARNING: THIS PROJECT IS NO LONGER MAINTAINED. There will be no future updates unless another maintainer wishes to own it. - -[![CircleCI](https://circleci.com/gh/influitive/multiple_man.png)](https://circleci.com/gh/influitive/multiple_man) +NOTE: This is a fork from https://github.com/influitive/multiple_man with some +fixes applied because the original one is not maintained anymore. MultipleMan synchronizes your ActiveRecord models between Rails apps, using RabbitMQ to send messages between your applications. @@ -20,7 +19,7 @@ It's heavily inspired by Promiscuous, but differs in a few ways: Add this line to your application's Gemfile: ```ruby -gem 'multiple_man' +gem 'multiple_man', github: 'owen2345/multiple_man' ``` And then execute: @@ -205,6 +204,27 @@ MyModel.multiple_man_publish(:seed) 3. Stop the seeder rake task when all of your messages have been processed. You can check your RabbitMQ server +## Fixes and improvements +- Rails 6 deprecation warnings +- Easy rspec integration (Added: ```publish_test_message```, ```listener_for``` methods) + ``` + describe 'Sample subscribe sync' do + let(:service) { MultipleMan::Runner.new(mode: :general) } + let(:model) { build(:my_model) } + let(:model_name) { model.class.name } + let(:subscribed_fields) { service.listener_for(model_name).options[:fields] } + before { service.run } + + it "Create a new Item when listen a new Item event" do + expect do + service.publish_test_message(model_name, {name: 'sample name'}, operation: :create) + end.to change { model.class.count }.by(1) + end + end + ``` +- Fixed exchange not found error +- Fixed missing attribute ```number``` when using bunny-mock gem + ## Contributing 1. Fork it diff --git a/lib/multiple_man/runner.rb b/lib/multiple_man/runner.rb index ff27432..12a1215 100644 --- a/lib/multiple_man/runner.rb +++ b/lib/multiple_man/runner.rb @@ -5,6 +5,7 @@ class Runner class ShutDown < Error; end extend Forwardable + attr_reader :listener, :queue MODES = [:general, :seed].freeze def initialize(options = {}) @@ -22,6 +23,22 @@ def run connection.close end + # publish a test message using current connection + def publish_test_message(model_name, data, operation: :create, id: :id) + message = { + type: model_name, + data: data.merge(operation: operation) + } + message[:id] = { id => data[:id] } + routing_key = MultipleMan::RoutingKey.new(model_name, operation).to_s + queue.publish(message.to_json, routing_key: routing_key) + end + + # find a subscriber listener for a specific class name + def listener_for(klass_name) + listener.send(:subscribers).values.find { |a| a.klass == klass_name } + end + private attr_reader :mode @@ -51,8 +68,10 @@ def preload_framework! end def build_listener - listener_class.new( - queue: channel.queue(*queue_params), + @queue = channel.queue(*queue_params) + @queue.channel.exchange(topic_name) + @listener = listener_class.new( + queue: queue, subscribers: listeners, topic: topic_name ) diff --git a/spec/runner_spec.rb b/spec/runner_spec.rb index 18e6c30..9e1b894 100644 --- a/spec/runner_spec.rb +++ b/spec/runner_spec.rb @@ -1,28 +1,67 @@ require 'spec_helper' describe MultipleMan::Runner do - let(:mock_channel) { double("Channel", prefetch: true, queue: true) } + let(:mock_queue) { double('Queue') } + let(:mock_channel) { double("Channel", prefetch: true, queue: mock_queue) } let(:mock_connection) { double("Connection", create_channel: mock_channel) } let(:mock_consumer) { double("Consumer", listen: true) } - it 'boots app and listens on new channel' do - expect(MultipleMan::Connection).to receive(:connection).and_return(mock_connection) - expect(MultipleMan::Consumers::General).to receive(:new).and_return(mock_consumer) - expect(mock_consumer).to receive(:listen) + describe 'run' do + let(:runner) { described_class.new(mode: :general) } + before do + allow(mock_queue).to receive(:channel).and_return(mock_channel) + allow(mock_channel).to receive(:exchange) + allow(MultipleMan::Connection).to receive(:connection).and_return(mock_connection) + allow(MultipleMan::Consumers::General).to receive(:new).and_return(mock_consumer) + end + + it 'boots app and listens on new channel' do + expect(mock_consumer).to receive(:listen) + runner.run + end - runner = described_class.new(mode: :general) - runner.run + it 'listener and queue availability' do + runner.run + expect(runner.listener).not_to be_nil + expect(runner.queue).not_to be_nil + end end - context "shutdown" do + context 'shutdown' do let(:connection) { MultipleMan::Connection.connection } it 'closes connections and exits gracefully' do - MultipleMan::Consumers::General.stub(:new) { Process.kill('INT', 0) } + klass = MultipleMan::Consumers::General + allow_any_instance_of(klass).to receive(:listen) do + raise MultipleMan::Runner::ShutDown + end + expect(connection).to receive(:close).at_least(1) + MultipleMan::Runner.new.run + end + end - expect(connection).to receive(:close) + describe 'rspec utilities' do + class MockClass + include MultipleMan::Subscriber + subscribe fields: %i[id name] + attr_accessor :name + end - MultipleMan::Runner.new.run + let(:runner) { MultipleMan::Runner.new(mode: :general) } + before do + allow_any_instance_of(MultipleMan::Consumers::General).to receive(:listen) + end + + it '#publish_test_message' do + runner.run + expect(runner.queue).to receive(:publish) + runner.publish_test_message('MockClass', name: 'name') + end + + it '#listener_for' do + runner.run + res = runner.listener_for('MockClass') + expect(res).to be_an_instance_of(MultipleMan::Subscribers::ModelSubscriber) end end end