multiple_man 0.5.21 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f395c0bc53fe2aab8ffde4426d0013cf9a0ee0ba
4
- data.tar.gz: ce5554a9a4e526be5216796f0b34cecf7d7a1fa0
3
+ metadata.gz: d8c65c40b50ae40908c59ef6c0cd66d724f461ea
4
+ data.tar.gz: 3403383d7f31f579dcc4b450a65f5f482ea7c5cb
5
5
  SHA512:
6
- metadata.gz: 163c9dad1a4ee94f7e74a442721b2be649e64af233a0105d4f885695effd21c58eb4933053f5a9abdb01a1570ad1fc33ca1318c6d709753012c363c6b2843cda
7
- data.tar.gz: 87c49def2141da2c11297facc6618c93984f31aceacbcbd40ab606ce951fcdc408d6db1e472a62fc09bcb4430dd6e954b54d46ceda053e670e69f49108381d35
6
+ metadata.gz: b487f16fa2d71cf90898bd0024667b41c1d46fd84d53b9c650ecbc98a0d582ed2793bd68b2dab1de491473763d1fb5c82f6558f058e30a58f8daa3cc731a9e5f
7
+ data.tar.gz: fb7be9925bfa4d7b26747029bf0b313b236af14b2b03e71a37ca548e860d62fe21eee772601fb5922b600c29d8f4c8bd59366050c6f6001b522332bd3c81e80c
data/README.md CHANGED
@@ -8,7 +8,7 @@ MultipleMan synchronizes your ActiveRecord models between Rails
8
8
  apps, using RabbitMQ to send messages between your applications.
9
9
  It's heavily inspired by Promiscuous, but differs in a few ways:
10
10
 
11
- - MultipleMan makes a hard assumption that you're using
11
+ - MultipleMan makes a hard assumption that you're using
12
12
  ActiveRecord for your models. This simplifies how models
13
13
  are sychronized, which offers a few benefits like:
14
14
  - Transactions are fully supported in MultipleMan. Records
@@ -38,9 +38,9 @@ calling MultipleMan.configure like so:
38
38
 
39
39
  MultipleMan.configure do |config|
40
40
  # A connection string to your local server. Defaults to localhost.
41
- config.connection = "amqp://example.com"
41
+ config.connection = "amqp://example.com"
42
42
 
43
- # The topic name to push to. If you have multiple
43
+ # The topic name to push to. If you have multiple
44
44
  # multiple man apps, this should be unique per application. Publishers
45
45
  # and subscribers should always use the same topic.
46
46
  config.topic_name = "multiple_man"
@@ -50,10 +50,10 @@ calling MultipleMan.configure like so:
50
50
  config.app_name = "MyApp"
51
51
 
52
52
  # Specify what should happen when MultipleMan
53
- # encounters an exception.
53
+ # encounters an exception.
54
54
  config.on_error do |exception|
55
55
  ErrorLogger.log(exception)
56
- end
56
+ end
57
57
 
58
58
  # Where you want to log errors to. Should be an instance of Logger
59
59
  # Defaults to the Rails logger (for Rails) or STDOUT otherwise.
@@ -95,16 +95,16 @@ You can use the following options when publishing:
95
95
  an ActiveRecord serializer (or anything that takes your
96
96
  record in the constructor and has an `as_json` method) to
97
97
  serialize models instead.
98
- - `as` - If you want the name of the model from the
98
+ - `as` - If you want the name of the model from the
99
99
  perspective of MultipleMan to be different than the model
100
- name in Rails, specify `as` with the name you want to use.
100
+ name in Rails, specify `as` with the name you want to use.
101
101
  Useful for STI.
102
102
  - `identify_by` - Specify an array of fields that MultipleMan
103
103
  should use to identify your record on the subscriber end.
104
- `id` is used by default and is generally fine, unless you're working in a multi-tenant environment where ids may
104
+ `id` is used by default and is generally fine, unless you're working in a multi-tenant environment where ids may
105
105
  be shared between two different models of the same class.
106
106
  - (DEPRECATED) `identifier` - Either a symbol or a proc used by MultipleMan to identify your model.
107
-
107
+
108
108
  ### Publishing
109
109
 
110
110
  By default, MultipleMan will publish all of your models whenever you save a model (in an `after_commit` hook). If you need to manually publish models, you can do so with the `multiple_man_publish` method, which acts like a scope on your models, like so:
@@ -120,7 +120,7 @@ Widget.where(published: true).multiple_man_publish
120
120
  Widget.first.multiple_man_publish
121
121
  ```
122
122
 
123
- If you're publishing multiple models, it's best to use the
123
+ If you're publishing multiple models, it's best to use the
124
124
  version of multiple_man_publish that operates on a collection. By calling the individual version, a channel is opened and closed for each model, which can impact the thoroughput of MultipleMan.
125
125
 
126
126
  ### Subscribing to models
@@ -143,6 +143,22 @@ By default, MultipleMan will attempt to identify which model on the subscriber m
143
143
 
144
144
  If your publisher specifies an `identifier` option, you *must* include a column on the subscriber side called `multiple_man_identifier`. MultipleMan will attempt to locate models on the subscriber side by this column.
145
145
 
146
+ ## Listening for model changes
147
+
148
+ If you want to do something other than populate a model on MultipleMan messages,
149
+ you can listen for messages and process them however you'd like:
150
+
151
+ ```
152
+ def ListenerClass
153
+ include MultipleMan::Listener
154
+ listen_to 'ModelName'
155
+
156
+ def create(payload)
157
+ # do something when a model is created
158
+ end
159
+ end
160
+ ``
161
+
146
162
  ## Listening for subscriptions
147
163
 
148
164
  Once you've set up your subscribers, you'll need to run a background worker to manage
@@ -152,9 +168,9 @@ the subscription process. Just run the following:
152
168
 
153
169
  ## Seeding
154
170
 
155
- One common problem when using MultipleMan on an existing project is that there's already a lot of data that you want to process using your listeners. MultipleMan provides a mechanism called "seeding" to accomplish this.
171
+ One common problem when using MultipleMan on an existing project is that there's already a lot of data that you want to process using your listeners. MultipleMan provides a mechanism called "seeding" to accomplish this.
156
172
 
157
- 1. On the subscriber side, start listening for seed requests with the following rake task:
173
+ 1. On the subscriber side, start listening for seed requests with the following rake task:
158
174
 
159
175
  ```
160
176
  rake multiple_man:seed
@@ -166,7 +182,7 @@ rake multiple_man:seed
166
182
  MyModel.multiple_man_publish(:seed)
167
183
  ```
168
184
 
169
- 3. Stop the seeder rake task when all of your messages have been processed. You can check your RabbitMQ server
185
+ 3. Stop the seeder rake task when all of your messages have been processed. You can check your RabbitMQ server
170
186
 
171
187
  ## Contributing
172
188
 
@@ -202,4 +218,3 @@ THE SOFTWARE.
202
218
 
203
219
 
204
220
  [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/influitive/multiple_man/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
205
-
@@ -8,7 +8,7 @@ module MultipleMan
8
8
  def self.connect
9
9
  channel = connection.create_channel
10
10
  yield new(channel) if block_given?
11
- rescue Bunny::ConnectionClosedError, Bunny::NetworkErrorWrapper
11
+ rescue Bunny::Exception
12
12
  reset!
13
13
  retry
14
14
  ensure
@@ -7,7 +7,6 @@ module MultipleMan::Listeners
7
7
  class << self
8
8
  def start
9
9
  MultipleMan.logger.debug "Starting listeners."
10
- MultipleMan.logger.debug MultipleMan::Subscribers::Registry.subscriptions.to_json
11
10
 
12
11
  MultipleMan::Subscribers::Registry.subscriptions.each do |subscription|
13
12
  new(subscription).listen
@@ -33,7 +32,7 @@ module MultipleMan::Listeners
33
32
  attr_accessor :subscription, :connection
34
33
 
35
34
  def listen
36
-
35
+
37
36
  MultipleMan.logger.info "Listening for #{subscription.klass} with routing key #{routing_key}."
38
37
  queue.bind(connection.topic, routing_key: routing_key).subscribe(ack: true) do |delivery_info, meta_data, payload|
39
38
  process_message(delivery_info, payload)
@@ -0,0 +1,43 @@
1
+ module MultipleMan
2
+ module Listener
3
+ def Listener.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ def routing_key
8
+ MultipleMan::RoutingKey.new(klass, operation).to_s
9
+ end
10
+
11
+ attr_accessor :klass
12
+ attr_accessor :operation
13
+
14
+ def create
15
+ # noop
16
+ end
17
+
18
+ def update
19
+ # noop
20
+ end
21
+
22
+ def destroy
23
+ # noop
24
+ end
25
+
26
+ def seed
27
+ # noop
28
+ end
29
+
30
+ def queue_name
31
+ "#{MultipleMan.configuration.topic_name}.#{MultipleMan.configuration.app_name}.#{klass}"
32
+ end
33
+
34
+ module ClassMethods
35
+ def listen_to(model, operation: '#')
36
+ listener = self.new
37
+ listener.klass = model
38
+ listener.operation = operation
39
+ Subscribers::Registry.register(listener)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -10,4 +10,4 @@ module MultipleMan
10
10
  end
11
11
  end
12
12
  end
13
- end
13
+ end
@@ -9,12 +9,13 @@ module MultipleMan
9
9
  def populate(payload)
10
10
  data = payload[:id].merge(payload[:data])
11
11
  fields_for(data).each do |field|
12
- populate_field(field, data[field])
12
+ source, dest = field.is_a?(Array) ? field : [field, field]
13
+ populate_field(dest, data[source])
13
14
  end
14
15
  record
15
16
  end
16
17
 
17
- private
18
+ private
18
19
  attr_accessor :record, :fields
19
20
 
20
21
  # Raise an exception if explicit fields were provided.
@@ -23,7 +24,7 @@ module MultipleMan
23
24
  end
24
25
 
25
26
  def populate_field(field, value)
26
- # Attempt to populate source id if id is specified
27
+ # Attempts to populate source id if id is specified
27
28
  if field.to_s == 'id' && record.respond_to?('source_id')
28
29
  field = 'source_id'
29
30
  end
@@ -40,4 +41,4 @@ module MultipleMan
40
41
  fields || data.keys
41
42
  end
42
43
  end
43
- end
44
+ end
@@ -11,10 +11,16 @@ module MultipleMan
11
11
  return unless MultipleMan.configuration.enabled
12
12
 
13
13
  Connection.connect do |connection|
14
- all_records(records) do |record|
15
- push_record(connection, record, operation)
14
+ ActiveSupport::Notifications.instrument('multiple_man.publish_messages') do
15
+ all_records(records) do |record|
16
+ ActiveSupport::Notifications.instrument('multiple_man.publish_message') do
17
+ push_record(connection, record, operation)
18
+ end
19
+ end
16
20
  end
17
21
  end
22
+ rescue Exception => ex
23
+ MultipleMan.error(ex)
18
24
  end
19
25
 
20
26
  private
@@ -28,8 +34,6 @@ module MultipleMan
28
34
  MultipleMan.logger.debug(" Record Data: #{data} | Routing Key: #{routing_key}")
29
35
 
30
36
  connection.topic.publish(data.payload, routing_key: routing_key)
31
- rescue Exception => ex
32
- MultipleMan.error(ex)
33
37
  end
34
38
 
35
39
  def all_records(records, &block)
@@ -28,7 +28,7 @@ module MultipleMan::Subscribers
28
28
  end
29
29
 
30
30
  def queue_name
31
- "#{MultipleMan.configuration.topic_name}.#{MultipleMan.configuration.app_name}.#{klass.name}"
31
+ "#{MultipleMan.configuration.topic_name}.#{MultipleMan.configuration.app_name}.#{klass}"
32
32
  end
33
33
 
34
34
  private
@@ -2,7 +2,8 @@ module MultipleMan::Subscribers
2
2
  class ModelSubscriber < Base
3
3
 
4
4
  def initialize(klass, options)
5
- super(klass)
5
+ self.model_class = klass
6
+ super(options[:to] || klass.name)
6
7
  self.options = options
7
8
  end
8
9
 
@@ -26,7 +27,7 @@ module MultipleMan::Subscribers
26
27
  private
27
28
 
28
29
  def find_model(id)
29
- klass.where(find_conditions(id)).first || klass.new
30
+ model_class.where(find_conditions(id)).first || model_class.new
30
31
  end
31
32
 
32
33
  def find_conditions(id)
@@ -43,6 +44,7 @@ module MultipleMan::Subscribers
43
44
  end
44
45
 
45
46
  attr_writer :klass
47
+ attr_accessor :model_class
46
48
 
47
49
  end
48
50
  end
@@ -9,4 +9,4 @@ module MultipleMan::Subscribers
9
9
  end
10
10
  end
11
11
  end
12
- end
12
+ end
@@ -14,8 +14,9 @@ namespace :multiple_man do
14
14
 
15
15
  listener.start
16
16
 
17
- while(true)
18
- sleep 10
19
- end
17
+ Signal.trap("INT") { puts "received INT"; exit }
18
+ Signal.trap("QUIT") { puts "received QUIT"; exit }
19
+
20
+ sleep
20
21
  end
21
22
  end
@@ -1,3 +1,3 @@
1
1
  module MultipleMan
2
- VERSION = "0.5.21"
2
+ VERSION = "0.6.0"
3
3
  end
data/lib/multiple_man.rb CHANGED
@@ -6,6 +6,7 @@ module MultipleMan
6
6
 
7
7
  require 'multiple_man/mixins/publisher'
8
8
  require 'multiple_man/mixins/subscriber'
9
+ require 'multiple_man/mixins/listener'
9
10
  require 'multiple_man/subscribers/base'
10
11
  require 'multiple_man/subscribers/model_subscriber'
11
12
  require 'multiple_man/subscribers/registry'
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe MultipleMan::ModelPopulator do
3
+ describe MultipleMan::ModelPopulator do
4
4
  class MockModel
5
5
  attr_accessor :a, :b, :multiple_man_identifier
6
6
  end
@@ -27,6 +27,13 @@ describe MultipleMan::ModelPopulator do
27
27
  end
28
28
  end
29
29
  end
30
+
31
+ context "with fields as a hash" do
32
+ let(:fields) { { b: :a } }
33
+
34
+ its(:b) { should == nil }
35
+ its(:a) { should == 2 }
36
+ end
30
37
  context "record has source id" do
31
38
  let(:model) { Class.new do
32
39
  attr_accessor :source_id, :id
@@ -57,4 +64,4 @@ describe MultipleMan::ModelPopulator do
57
64
  end
58
65
  end
59
66
  end
60
- end
67
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe MultipleMan::Subscriber do
3
+ describe MultipleMan::Subscriber do
4
4
  class MockClass
5
5
  include MultipleMan::Subscriber
6
6
  end
@@ -9,6 +9,6 @@ describe MultipleMan::Subscriber do
9
9
  it "should register itself" do
10
10
  MultipleMan::Subscribers::Registry.should_receive(:register).with(instance_of(MultipleMan::Subscribers::ModelSubscriber))
11
11
  MockClass.subscribe fields: [:foo, :bar]
12
- end
12
+ end
13
13
  end
14
- end
14
+ end
@@ -5,13 +5,17 @@ describe MultipleMan::Subscribers::Base do
5
5
  end
6
6
 
7
7
  specify "routing_key should be the model name and a wildcard" do
8
- described_class.new(MockClass).routing_key.should == "app.MockClass.#"
8
+ described_class.new(MockClass).routing_key.should =~ /\.MockClass\.\#$/
9
9
  end
10
10
 
11
11
  specify "queue name should be the app name + class" do
12
12
  MultipleMan.configure do |config|
13
13
  config.app_name = "test"
14
14
  end
15
- described_class.new(MockClass).queue_name.should == "app.test.MockClass"
15
+ described_class.new(MockClass).queue_name.should =~ /\.test\.MockClass$/
16
+ end
17
+
18
+ specify "it should be alright to use a string for a class name" do
19
+ described_class.new("MockClass").routing_key.should =~ /\.MockClass\.\#$/
16
20
  end
17
21
  end
@@ -5,6 +5,13 @@ describe MultipleMan::Subscribers::ModelSubscriber do
5
5
 
6
6
  end
7
7
 
8
+ describe "initialize" do
9
+ it "should listen to the object passed in for to" do
10
+ subscriber = described_class.new(MockClass, to: 'PublishedClass')
11
+ expect(subscriber.klass).to eq("PublishedClass")
12
+ end
13
+ end
14
+
8
15
  describe "create" do
9
16
  it "should create a new model" do
10
17
  mock_object = MockClass.new
@@ -40,4 +47,4 @@ describe MultipleMan::Subscribers::ModelSubscriber do
40
47
  described_class.new(MockClass, {}).destroy({id: 1})
41
48
  end
42
49
  end
43
- end
50
+ 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.5.21
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Brunner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-18 00:00:00.000000000 Z
11
+ date: 2015-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -171,6 +171,7 @@ files:
171
171
  - lib/multiple_man/identity.rb
172
172
  - lib/multiple_man/listeners/listener.rb
173
173
  - lib/multiple_man/listeners/seeder_listener.rb
174
+ - lib/multiple_man/mixins/listener.rb
174
175
  - lib/multiple_man/mixins/publisher.rb
175
176
  - lib/multiple_man/mixins/subscriber.rb
176
177
  - lib/multiple_man/model_populator.rb