promiscuous 0.1 → 0.5

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/README.md CHANGED
@@ -23,8 +23,8 @@ Example
23
23
 
24
24
  ```ruby
25
25
  # initializer
26
- Promiscuous::AMQP.configure(:backend => :bunny, :app => 'crowdtap', :logger => Rails.logger,
27
- :server_uri => 'amqp://user:password@host:port/vhost')
26
+ Promiscuous::AMQP.configure(:app => 'crowdtap',
27
+ :server_uri => 'amqp://user:password@host:port/vhost')
28
28
 
29
29
  # publisher
30
30
  class ModelPublisher < Promiscuous::Publisher::Mongoid
@@ -38,10 +38,9 @@ end
38
38
 
39
39
  ```ruby
40
40
  # initializer
41
- Promiscuous::AMQP.configure(:backend => :rubyamqp, :app => 'sniper', :logger => Rails.logger,
42
- :server_uri => 'amqp://user:password@host:port/vhost',
43
- :queue_options => {:durable => true, :arguments => {'x-ha-policy' => 'all'}},
44
- :error_handler => some_proc)
41
+ Promiscuous::AMQP.configure(:app => 'sniper',
42
+ :server_uri => 'amqp://user:password@host:port/vhost',
43
+ :error_handler => some_proc)
45
44
 
46
45
  # subscriber
47
46
  class ModelSubscriber < Promiscuous::Subscriber::Mongoid
@@ -53,7 +52,7 @@ end
53
52
 
54
53
  ### Starting the subscriber worker
55
54
 
56
- rake promiscuous:run[./path/to/promiscuous_initializer.rb]
55
+ rake promiscuous:replicate
57
56
 
58
57
  How does it work ?
59
58
  ------------------
@@ -3,20 +3,24 @@ module Promiscuous
3
3
  module Bunny
4
4
  mattr_accessor :connection
5
5
 
6
- def self.configure(options)
6
+ def self.connect
7
7
  require 'bunny'
8
- self.connection = ::Bunny.new(options[:server_uri])
8
+ self.connection = ::Bunny.new(Promiscuous::Config.server_uri)
9
9
  self.connection.start
10
10
  end
11
11
 
12
+ def self.disconnect
13
+ self.connection.stop
14
+ end
15
+
12
16
  def self.publish(msg)
13
- AMQP.info "[publish] #{msg[:key]} -> #{msg[:payload]}"
17
+ Promiscuous.info "[publish] #{msg[:key]} -> #{msg[:payload]}"
14
18
  exchange = connection.exchange('promiscuous', :type => :topic, :durable => true)
15
19
  exchange.publish(msg[:payload], :key => msg[:key], :persistent => true)
16
20
  end
17
21
 
18
- def self.close
19
- self.connection.stop
22
+ def self.subscribe(options={}, &block)
23
+ raise "Cannot subscribe with bunny"
20
24
  end
21
25
  end
22
26
  end
@@ -1,19 +1,16 @@
1
1
  module Promiscuous
2
2
  module AMQP
3
3
  module Null
4
- def self.configure(options)
4
+ def self.connect
5
5
  end
6
6
 
7
- def self.publish(msg)
8
- end
9
-
10
- def self.subscribe(options={}, &block)
7
+ def self.disconnect
11
8
  end
12
9
 
13
- def self.clear
10
+ def self.publish(msg)
14
11
  end
15
12
 
16
- def self.close
13
+ def self.subscribe(options={}, &block)
17
14
  end
18
15
  end
19
16
  end
@@ -3,16 +3,11 @@ module Promiscuous
3
3
  module RubyAMQP
4
4
  mattr_accessor :channel, :queue_options
5
5
 
6
- def self.configure(options)
6
+ def self.connect
7
7
  require 'amqp'
8
- connection = ::AMQP.connect(build_connection_options(options))
9
- self.channel = ::AMQP::Channel.new(connection)
10
- self.queue_options = options[:queue_options] || {}
11
- end
12
8
 
13
- def self.build_connection_options(options)
14
- if options[:server_uri]
15
- uri = URI.parse(options[:server_uri])
9
+ amqp_options = if Promiscuous::Config.server_uri
10
+ uri = URI.parse(Promiscuous::Config.server_uri)
16
11
  raise "Please use amqp://user:password@host:port/vhost" if uri.scheme != 'amqp'
17
12
 
18
13
  {
@@ -24,6 +19,14 @@ module Promiscuous
24
19
  :vhost => uri.path.empty? ? "/" : uri.path,
25
20
  }
26
21
  end
22
+
23
+ connection = ::AMQP.connect(amqp_options)
24
+ self.channel = ::AMQP::Channel.new(connection)
25
+ self.queue_options = Promiscuous::Config.queue_options
26
+ end
27
+
28
+ def self.disconnect
29
+ channel.close
27
30
  end
28
31
 
29
32
  def self.subscribe(options={}, &block)
@@ -34,20 +37,16 @@ module Promiscuous
34
37
  exchange = channel.topic('promiscuous', :durable => true)
35
38
  bindings.each do |binding|
36
39
  queue.bind(exchange, :routing_key => binding)
37
- AMQP.info "[bind] #{queue_name} -> #{binding}"
40
+ Promiscuous.info "[bind] #{queue_name} -> #{binding}"
38
41
  end
39
42
  queue.subscribe(:ack => true, &block)
40
43
  end
41
44
 
42
45
  def self.publish(msg)
43
- AMQP.info "[publish] #{msg[:key]} -> #{msg[:payload]}"
46
+ Promiscuous.info "[publish] #{msg[:key]} -> #{msg[:payload]}"
44
47
  exchange = channel.topic('promiscuous', :durable => true)
45
48
  exchange.publish(msg[:payload], :routing_key => msg[:key], :persistent => true)
46
49
  end
47
-
48
- def self.close
49
- channel.close
50
- end
51
50
  end
52
51
  end
53
52
  end
@@ -1,34 +1,15 @@
1
1
  require 'promiscuous/amqp/bunny'
2
- require 'promiscuous/amqp/fake'
3
- require 'promiscuous/amqp/ruby-amqp'
2
+ require 'promiscuous/amqp/rubyamqp'
4
3
  require 'promiscuous/amqp/null'
5
4
 
6
5
  module Promiscuous
7
6
  module AMQP
8
- mattr_accessor :backend, :app, :logger, :error_handler
9
-
10
- def self.configure(options={}, &block)
11
- options.symbolize_keys!
12
-
13
- self.backend = "Promiscuous::AMQP::#{options[:backend].to_s.camelize.gsub(/amqp/, 'AMQP')}".constantize
14
- self.backend.configure(options, &block)
15
- self.app = options[:app]
16
- self.logger = options[:logger] || Logger.new(STDOUT).tap { |l| l.level = Logger::WARN }
17
- self.error_handler = options[:error_handler]
18
- self
19
- end
20
-
21
7
  class << self
22
- [:info, :error, :warn, :fatal].each do |level|
23
- define_method(level) do |msg|
24
- self.logger.__send__(level, "[AMQP] #{msg}")
25
- end
8
+ def backend
9
+ Promiscuous::Config.backend
26
10
  end
27
- end
28
11
 
29
- # TODO Evaluate the performance hit of method_missing
30
- def self.method_missing(method, *args, &block)
31
- self.backend.__send__(method, *args, &block)
12
+ delegate :connect, :disconnect, :publish, :subscribe, :to => :backend
32
13
  end
33
14
  end
34
15
  end
@@ -0,0 +1,20 @@
1
+ module Promiscuous
2
+ module Config
3
+ mattr_accessor :app, :logger, :error_handler, :backend, :server_uri, :queue_options
4
+
5
+ def self.backend=(value)
6
+ @@backend = "Promiscuous::AMQP::#{value.to_s.camelize.gsub(/amqp/, 'AMQP')}".constantize
7
+ end
8
+
9
+ def self.configure(&block)
10
+ class_variables.each { |var| class_variable_set(var, nil) }
11
+
12
+ block.call(self)
13
+ self.backend ||= defined?(EventMachine) && EventMachine.reactor_running? ? :rubyamqp : :bunny
14
+ self.logger ||= defined?(Rails) ? Rails.logger : Logger.new(STDERR).tap { |l| l.level = Logger::WARN }
15
+ self.queue_options ||= {:durable => true, :arguments => {'x-ha-policy' => 'all'}}
16
+
17
+ Promiscuous::AMQP.connect
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ module Promiscuous
2
+ module Loader
3
+ def self.load_descriptors(descriptors=[:publishers, :subscribers])
4
+ [descriptors].flatten.each do |descriptor|
5
+ dir, file_matcher = case descriptor
6
+ when :publishers
7
+ require 'promiscuous/publisher'
8
+ # TODO Cleanup publishers
9
+ %w(publishers **_publisher.rb)
10
+ when :subscribers
11
+ require 'promiscuous/subscriber'
12
+ Promiscuous::Subscriber.subscribers.clear
13
+ %w(subscribers **_subscriber.rb)
14
+ end
15
+
16
+ Dir[Rails.root.join('app', dir, file_matcher)].map do |file|
17
+ File.basename(file, ".rb").camelize.constantize
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,12 +1,18 @@
1
- namespace :replicable do
1
+ namespace :promiscuous do
2
2
  desc 'Run the subscribers worker'
3
- task :run, [:initializer] => :environment do |t, args|
4
- require 'replicable/worker'
3
+ task :replicate => :environment do |t|
4
+ require 'promiscuous/worker'
5
5
  require 'eventmachine'
6
6
  require 'em-synchrony'
7
+
7
8
  EM.synchrony do
8
- load args.initializer
9
- Replicable::Worker.run
9
+ Promiscuous::Loader.load_descriptors :subscribers
10
+ Promiscuous::AMQP.disconnect
11
+ Promiscuous::Config.backend = :rubyamqp
12
+ Promiscuous::AMQP.connect
13
+
14
+ Promiscuous::Worker.replicate
15
+ puts "Replicating with #{Promiscuous::Subscriber.subscribers.count} subscribers"
10
16
  end
11
17
  end
12
18
  end
@@ -3,24 +3,10 @@ module Promiscuous
3
3
  rake_tasks { load 'promiscuous/railtie/replicate.rake' }
4
4
 
5
5
  initializer 'load promiscuous' do
6
- # TODO clean that up
7
6
  config.after_initialize do
8
- Dir[Rails.root.join('app', 'publishers', '**_publisher.rb')].map do |file|
9
- file.split('/')[-1].split('.')[0].camelize.constantize
10
- end
11
- ActionDispatch::Reloader.to_prepare do
12
- Dir[Rails.root.join('app', 'publishers', '**_publisher.rb')].map do |file|
13
- file.split('/')[-1].split('.')[0].camelize.constantize
14
- end
15
- end
16
-
17
- Dir[Rails.root.join('app', 'subscribers', '**_subscriber.rb')].map do |file|
18
- file.split('/')[-1].split('.')[0].camelize.constantize
19
- end
7
+ Promiscuous::Loader.load_descriptors(:publishers)
20
8
  ActionDispatch::Reloader.to_prepare do
21
- Dir[Rails.root.join('app', 'subscribers', '**_subscriber.rb')].map do |file|
22
- file.split('/')[-1].split('.')[0].camelize.constantize
23
- end
9
+ Promiscuous::Loader.load_descriptors(:publishers)
24
10
  end
25
11
  end
26
12
  end
@@ -3,30 +3,14 @@ require 'promiscuous/subscriber/envelope'
3
3
  module Promiscuous::Subscriber::AMQP
4
4
  extend ActiveSupport::Concern
5
5
 
6
- mattr_accessor :subscribers
7
- self.subscribers = {}
8
-
9
- def self.subscriber_for(payload)
10
- origin = payload.is_a?(Hash) ? payload['__amqp__'] : nil
11
- if origin
12
- unless subscribers.has_key?(origin)
13
- raise "FATAL: Unknown binding: '#{origin}'"
14
- end
15
- subscribers[origin]
16
- end
6
+ def self.subscriber_key(payload)
7
+ payload.is_a?(Hash) ? payload['__amqp__'] : nil
17
8
  end
18
9
 
19
10
  module ClassMethods
20
11
  def subscribe(options)
21
12
  super
22
-
23
- subscribers = Promiscuous::Subscriber::AMQP.subscribers
24
- from = options[:from]
25
-
26
- if subscribers.has_key?(from)
27
- raise "The subscriber '#{subscribers[from]}' already listen on '#{from}'"
28
- end
29
- subscribers[from] = self
13
+ Promiscuous::Subscriber.bind(options[:from], self)
30
14
  end
31
15
  end
32
16
  end
@@ -5,7 +5,7 @@ module Promiscuous::Subscriber::Mongoid::Upsert
5
5
  begin
6
6
  super
7
7
  rescue Mongoid::Errors::DocumentNotFound
8
- Promiscuous::AMQP.warn "[receive] upserting #{payload}"
8
+ Promiscuous.warn "[receive] upserting #{payload}"
9
9
  klass.new.tap { |o| o.id = id }
10
10
  end
11
11
  end
@@ -3,12 +3,32 @@ module Promiscuous::Subscriber
3
3
  require 'promiscuous/subscriber/mongoid'
4
4
  require 'promiscuous/subscriber/amqp'
5
5
 
6
+ mattr_accessor :subscribers
7
+ self.subscribers = {}
8
+
9
+ def self.bind(key, subscriber)
10
+ if self.subscribers.has_key?(key)
11
+ raise "The subscriber '#{self.subscribers[key]}' already listen on '#{key}'"
12
+ end
13
+ self.subscribers[key] = subscriber
14
+ end
15
+
16
+ def self.get_subscriber(payload, options={})
17
+ key = Promiscuous::Subscriber::AMQP.subscriber_key(payload)
18
+
19
+ if key
20
+ raise "FATAL: Unknown binding: '#{key}'" unless self.subscribers.has_key?(key)
21
+ self.subscribers[key]
22
+ else
23
+ Promiscuous::Subscriber::Base
24
+ end
25
+ end
26
+
6
27
  def self.process(payload, options={})
7
- subscriber = Promiscuous::Subscriber::AMQP.subscriber_for(payload)
8
- return payload if subscriber.nil?
28
+ subscriber_klass = get_subscriber(payload)
9
29
 
10
- sub = subscriber.new(options.merge(:payload => payload))
11
- sub.process if sub.respond_to?(:process)
30
+ sub = subscriber_klass.new(options.merge(:payload => payload))
31
+ sub.process
12
32
  sub.instance
13
33
  end
14
34
  end
@@ -1,3 +1,3 @@
1
1
  module Promiscuous
2
- VERSION = '0.1'
2
+ VERSION = '0.5'
3
3
  end
@@ -1,25 +1,42 @@
1
1
  module Promiscuous
2
2
  module Worker
3
- def self.run
4
- queue_name = "#{Promiscuous::AMQP.app}.promiscuous"
5
-
3
+ def self.replicate
6
4
  stop = false
7
- Promiscuous::AMQP.subscribe(:queue_name => queue_name,
8
- :bindings => Promiscuous::Subscriber::AMQP.subscribers.keys) do |metadata, payload|
5
+ lock = Mutex.new
6
+
7
+ %w(SIGTERM SIGINT).each do |signal|
8
+ Signal.trap(signal) do
9
+ lock.synchronize do
10
+ stop = true
11
+ EM.stop
12
+ end
13
+ end
14
+ end
15
+
16
+ Promiscuous::AMQP.subscribe(subscribe_options) do |metadata, payload|
9
17
  begin
10
- unless stop
11
- Promiscuous::AMQP.info "[receive] #{payload}"
12
- Promiscuous::Subscriber.process(JSON.parse(payload))
13
- metadata.ack
18
+ lock.synchronize do
19
+ unless stop
20
+ Promiscuous.info "[receive] #{payload}"
21
+ Promiscuous::Subscriber.process(JSON.parse(payload))
22
+ metadata.ack
23
+ end
14
24
  end
15
25
  rescue Exception => e
16
26
  e = Promiscuous::Subscriber::Error.new(e, payload)
27
+
17
28
  stop = true
18
- Promiscuous::AMQP.close
19
- Promiscuous::AMQP.error "[receive] FATAL #{e}"
20
- Promiscuous::AMQP.error_handler.call(e) if Promiscuous::AMQP.error_handler
29
+ Promiscuous::AMQP.disconnect
30
+ Promiscuous.error "[receive] FATAL #{e}"
31
+ Promiscuous::Config.error_handler.call(e) if Promiscuous::Config.error_handler
21
32
  end
22
33
  end
23
34
  end
35
+
36
+ def self.subscribe_options
37
+ queue_name = "#{Promiscuous::Config.app}.promiscuous"
38
+ bindings = Promiscuous::Subscriber.subscribers.keys
39
+ {:queue_name => queue_name, :bindings => bindings}
40
+ end
24
41
  end
25
42
  end
data/lib/promiscuous.rb CHANGED
@@ -1,5 +1,19 @@
1
1
  require 'active_support/core_ext'
2
+ require 'promiscuous/config'
2
3
  require 'promiscuous/amqp'
3
- require 'promiscuous/publisher'
4
- require 'promiscuous/subscriber'
4
+ require 'promiscuous/loader'
5
5
  require 'promiscuous/railtie' if defined?(Rails)
6
+
7
+ module Promiscuous
8
+ class << self
9
+ def configure(&block)
10
+ Config.configure(&block)
11
+ end
12
+
13
+ [:info, :error, :warn, :fatal].each do |level|
14
+ define_method(level) do |msg|
15
+ Promiscuous::Config.logger.__send__(level, "[promiscuous] #{msg}")
16
+ end
17
+ end
18
+ end
19
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: promiscuous
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: '0.5'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-08-11 00:00:00.000000000 Z
13
+ date: 2012-08-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: mongoid
@@ -100,39 +100,40 @@ executables: []
100
100
  extensions: []
101
101
  extra_rdoc_files: []
102
102
  files:
103
- - lib/promiscuous/amqp/fake.rb
104
- - lib/promiscuous/amqp/null.rb
105
- - lib/promiscuous/amqp/bunny.rb
106
- - lib/promiscuous/amqp/ruby-amqp.rb
107
- - lib/promiscuous/railtie/replicate.rake
108
- - lib/promiscuous/publisher/mongoid/embedded.rb
109
- - lib/promiscuous/publisher/mongoid/root.rb
110
- - lib/promiscuous/publisher/base.rb
103
+ - lib/promiscuous/version.rb
111
104
  - lib/promiscuous/publisher/class_bind.rb
112
- - lib/promiscuous/publisher/envelope.rb
113
- - lib/promiscuous/publisher/mongoid.rb
114
- - lib/promiscuous/publisher/polymorphic.rb
115
105
  - lib/promiscuous/publisher/attributes.rb
106
+ - lib/promiscuous/publisher/mongoid.rb
116
107
  - lib/promiscuous/publisher/generic.rb
108
+ - lib/promiscuous/publisher/mongoid/root.rb
109
+ - lib/promiscuous/publisher/mongoid/embedded.rb
110
+ - lib/promiscuous/publisher/envelope.rb
117
111
  - lib/promiscuous/publisher/amqp.rb
112
+ - lib/promiscuous/publisher/base.rb
113
+ - lib/promiscuous/publisher/polymorphic.rb
114
+ - lib/promiscuous/amqp/null.rb
115
+ - lib/promiscuous/amqp/rubyamqp.rb
116
+ - lib/promiscuous/amqp/bunny.rb
117
+ - lib/promiscuous/publisher.rb
118
+ - lib/promiscuous/subscriber/attributes.rb
119
+ - lib/promiscuous/subscriber/mongoid.rb
120
+ - lib/promiscuous/subscriber/error.rb
121
+ - lib/promiscuous/subscriber/generic.rb
122
+ - lib/promiscuous/subscriber/mongoid/root.rb
118
123
  - lib/promiscuous/subscriber/mongoid/embedded.rb
119
124
  - lib/promiscuous/subscriber/mongoid/upsert.rb
120
- - lib/promiscuous/subscriber/mongoid/root.rb
121
- - lib/promiscuous/subscriber/attributes.rb
122
- - lib/promiscuous/subscriber/base.rb
123
- - lib/promiscuous/subscriber/custom_class.rb
124
125
  - lib/promiscuous/subscriber/envelope.rb
125
- - lib/promiscuous/subscriber/generic.rb
126
- - lib/promiscuous/subscriber/polymorphic.rb
127
- - lib/promiscuous/subscriber/mongoid.rb
128
126
  - lib/promiscuous/subscriber/amqp.rb
129
- - lib/promiscuous/subscriber/error.rb
130
- - lib/promiscuous/version.rb
131
- - lib/promiscuous/publisher.rb
127
+ - lib/promiscuous/subscriber/custom_class.rb
128
+ - lib/promiscuous/subscriber/base.rb
129
+ - lib/promiscuous/subscriber/polymorphic.rb
130
+ - lib/promiscuous/worker.rb
131
+ - lib/promiscuous/railtie/replicate.rake
132
132
  - lib/promiscuous/railtie.rb
133
+ - lib/promiscuous/loader.rb
133
134
  - lib/promiscuous/amqp.rb
135
+ - lib/promiscuous/config.rb
134
136
  - lib/promiscuous/subscriber.rb
135
- - lib/promiscuous/worker.rb
136
137
  - lib/promiscuous.rb
137
138
  - README.md
138
139
  homepage: http://github.com/crowdtap/promiscuous
@@ -155,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
155
156
  version: '0'
156
157
  requirements: []
157
158
  rubyforge_project:
158
- rubygems_version: 1.8.24
159
+ rubygems_version: 1.8.22
159
160
  signing_key:
160
161
  specification_version: 3
161
162
  summary: Model replication over RabbitMQ
@@ -1,27 +0,0 @@
1
- module Promiscuous
2
- module AMQP
3
- module Fake
4
- mattr_accessor :messages, :subscribe_options
5
- self.messages = []
6
-
7
- def self.configure(options)
8
- end
9
-
10
- def self.publish(msg)
11
- self.messages << msg
12
- end
13
-
14
- def self.subscribe(options={}, &block)
15
- self.subscribe_options = options
16
- end
17
-
18
- def self.clear
19
- self.messages.clear
20
- self.subscribe_options = nil
21
- end
22
-
23
- def self.close
24
- end
25
- end
26
- end
27
- end