active_pubsub 0.0.1
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 +7 -0
- data/.gitignore +22 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/Guardfile +38 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/active_pubsub.gemspec +42 -0
- data/bin/subscriber +22 -0
- data/examples/publishing_service_example.rb +7 -0
- data/examples/subscribing_service_example.rb +19 -0
- data/lib/active_pubsub/config.rb +14 -0
- data/lib/active_pubsub/connection.rb +17 -0
- data/lib/active_pubsub/event.rb +27 -0
- data/lib/active_pubsub/publishable.rb +80 -0
- data/lib/active_pubsub/publisher.rb +71 -0
- data/lib/active_pubsub/railtie.rb +28 -0
- data/lib/active_pubsub/subscriber.rb +76 -0
- data/lib/active_pubsub/version.rb +3 -0
- data/lib/active_pubsub.rb +59 -0
- data/spec/active_pubsub/connection_spec.rb +8 -0
- data/spec/active_pubsub/publishable_spec.rb +50 -0
- data/spec/active_pubsub/publisher_spec.rb +56 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/support/db/setup.rb +21 -0
- data/spec/support/models/post.rb +10 -0
- data/spec/support/models.rb +1 -0
- data/spec/test.db +0 -0
- metadata +349 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7ea43e949553e6313e96caa956b829708b0aecf7
|
4
|
+
data.tar.gz: 852017f1bc06dc74f14b4b828fa7f9bf23f73c84
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b179fd78488d123798f1db4cb90cbf9e6661628b3b6a91f38cdb37b38be04cbe9800c41bb44b925c70f974754d55fe09ef0dcd032b5c667cf118e139c16d6d0d
|
7
|
+
data.tar.gz: 2fe5a53c4d20a69292d9dbe4993aaeff5df5802c8d3a0faa469615286fb89269a0b3923a313b3caa67b17fcc6dd0b7e3921f6ef3c42faea522eff877e1a3efe1
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard :bundler do
|
5
|
+
watch('Gemfile')
|
6
|
+
# Uncomment next line if your Gemfile contains the `gemspec' command.
|
7
|
+
# watch(/^.+\.gemspec/)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
11
|
+
# rspec may be run, below are examples of the most common uses.
|
12
|
+
# * bundler: 'bundle exec rspec'
|
13
|
+
# * bundler binstubs: 'bin/rspec'
|
14
|
+
# * spring: 'bin/rsspec' (This will use spring if running and you have
|
15
|
+
# installed the spring binstubs per the docs)
|
16
|
+
# * zeus: 'zeus rspec' (requires the server to be started separetly)
|
17
|
+
# * 'just' rspec: 'rspec'
|
18
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
19
|
+
watch(%r{^spec/.+_spec\.rb$})
|
20
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
21
|
+
watch('spec/spec_helper.rb') { "spec" }
|
22
|
+
|
23
|
+
# Rails example
|
24
|
+
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
25
|
+
watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
26
|
+
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
27
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
28
|
+
watch('config/routes.rb') { "spec/routing" }
|
29
|
+
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
30
|
+
watch('spec/rails_helper.rb') { "spec" }
|
31
|
+
|
32
|
+
# Capybara features specs
|
33
|
+
watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
|
34
|
+
|
35
|
+
# Turnip features and steps
|
36
|
+
watch(%r{^spec/acceptance/(.+)\.feature$})
|
37
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
38
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Jason Ayre
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# ActivePubsub
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'active_pubsub'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install active_pubsub
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it ( https://github.com/[my-github-username]/active_pubsub/fork )
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'active_pubsub/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "active_pubsub"
|
8
|
+
spec.version = ActivePubsub::VERSION
|
9
|
+
spec.authors = ["Jason Ayre"]
|
10
|
+
spec.email = ["jasonayre@gmail.com"]
|
11
|
+
spec.summary = %q{Pubsub using RabbitMQ and ActiveRecord, observe model events from different services.}
|
12
|
+
spec.description = %q{Uses RabbitMQ and ActiveRecord for publishing and consuming model events from any service}
|
13
|
+
spec.homepage = "https://github.com/jasonayre/active_pubsub"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "active_attr"
|
22
|
+
spec.add_dependency "celluloid-io"
|
23
|
+
spec.add_dependency "json"
|
24
|
+
spec.add_dependency "bunny"
|
25
|
+
|
26
|
+
spec.add_development_dependency 'activerecord'
|
27
|
+
spec.add_development_dependency 'sqlite3'
|
28
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
29
|
+
spec.add_development_dependency "rake"
|
30
|
+
spec.add_development_dependency "rspec"
|
31
|
+
spec.add_development_dependency "rspec-pride"
|
32
|
+
spec.add_development_dependency "pry-nav"
|
33
|
+
spec.add_development_dependency "simplecov"
|
34
|
+
spec.add_development_dependency 'rspec-its', '~> 1'
|
35
|
+
spec.add_development_dependency 'rspec-collection_matchers', '~> 1'
|
36
|
+
spec.add_development_dependency 'guard', '~> 2'
|
37
|
+
spec.add_development_dependency 'guard-rspec', '~> 4'
|
38
|
+
spec.add_development_dependency 'guard-bundler', '~> 2'
|
39
|
+
spec.add_development_dependency 'rb-fsevent'
|
40
|
+
spec.add_development_dependency 'terminal-notifier-guard'
|
41
|
+
|
42
|
+
end
|
data/bin/subscriber
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
require 'active_pubsub'
|
5
|
+
require './config/environment.rb'
|
6
|
+
|
7
|
+
class Subscriber < ::Thor
|
8
|
+
desc "start", "Start Rabbit Subscriber"
|
9
|
+
def start
|
10
|
+
puts "Starting Rabbit Subscriber SERVER"
|
11
|
+
|
12
|
+
::ActivePubsub.load_subscribers
|
13
|
+
|
14
|
+
::ActivePubsub.start_subscribers
|
15
|
+
|
16
|
+
puts "Subscribers Started"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
::Subscriber.start
|
21
|
+
|
22
|
+
sleep
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# this is an example of a service that wants to do something when posts are created,
|
2
|
+
# updated, or destroyed, in the publishing service
|
3
|
+
|
4
|
+
class PostSubscriber < ::ActivePubsub::Subscriber
|
5
|
+
observes "cms"
|
6
|
+
as "aggregator"
|
7
|
+
|
8
|
+
on :created do |record|
|
9
|
+
puts record.inspect
|
10
|
+
end
|
11
|
+
|
12
|
+
on :destroyed do |record|
|
13
|
+
puts record.inspect
|
14
|
+
end
|
15
|
+
|
16
|
+
on :updated do |record|
|
17
|
+
puts record.inspect
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'active_support/ordered_options'
|
2
|
+
|
3
|
+
module ActivePubsub
|
4
|
+
### IMPORTANT ###
|
5
|
+
# Set service namespace if your subscriber has namespace set or it wont get events"
|
6
|
+
class Config < ::ActiveSupport::OrderedOptions
|
7
|
+
def initialize(options = {})
|
8
|
+
options[:address] = ENV['RABBITMQ_URL']
|
9
|
+
options[:publish_as] = nil
|
10
|
+
options[:service_namespace] = nil
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "bunny"
|
2
|
+
|
3
|
+
module ActivePubsub
|
4
|
+
class Connection < Delegator
|
5
|
+
attr_accessor :connection, :channel
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@connection = ::Bunny.new(::ActivePubsub.config.try(:address) || "amqp://guest:guest@localhost:5672")
|
9
|
+
@connection.start
|
10
|
+
@channel = connection.create_channel
|
11
|
+
end
|
12
|
+
|
13
|
+
def __getobj__
|
14
|
+
@connection
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
|
3
|
+
module ActivePubsub
|
4
|
+
class Event
|
5
|
+
include ::ActiveAttr::Model
|
6
|
+
include ::ActiveModel::AttributeMethods
|
7
|
+
|
8
|
+
attribute :id
|
9
|
+
attribute :exchange
|
10
|
+
attribute :name
|
11
|
+
attribute :occured_at
|
12
|
+
attribute :record
|
13
|
+
attribute :record_type
|
14
|
+
attribute :routing_key
|
15
|
+
|
16
|
+
#attributes have to be set for purposes of marshaling
|
17
|
+
def initialize(exchange, name, record)
|
18
|
+
self[:exchange] = exchange
|
19
|
+
self[:name] = name
|
20
|
+
self[:record] = record
|
21
|
+
self[:id] = ::SecureRandom.hex
|
22
|
+
self[:record_type] = record.class.name
|
23
|
+
self[:occured_at] ||= ::DateTime.now
|
24
|
+
self[:routing_key] ||= [exchange, name].join('.')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module ActivePubsub
|
2
|
+
module Publishable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
PUBLISHABLE_ACTIONS = ["updated", "created", "destroyed"]
|
6
|
+
|
7
|
+
included do
|
8
|
+
include ::ActiveModel::Dirty
|
9
|
+
|
10
|
+
after_create :publish_created_event
|
11
|
+
after_update :publish_updated_event
|
12
|
+
after_destroy :publish_destroyed_event
|
13
|
+
class_attribute :exchange_prefix
|
14
|
+
|
15
|
+
self.publishable_actions ||= []
|
16
|
+
|
17
|
+
::ActivePubsub::Publisher.increment_publishable_model_count!
|
18
|
+
end
|
19
|
+
|
20
|
+
def attributes_hash
|
21
|
+
attributes.merge!("changes" => changes)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def publish_updated_event
|
27
|
+
record_updated_event = ::ActivePubsub::Event.new(self.class.exchange_key, "updated", serialized_resource)
|
28
|
+
|
29
|
+
::ActivePubsub.publish_event(record_updated_event)
|
30
|
+
end
|
31
|
+
|
32
|
+
def publish_created_event
|
33
|
+
record_created_event = ::ActivePubsub::Event.new(self.class.exchange_key, "created", serialized_resource)
|
34
|
+
|
35
|
+
::ActivePubsub.publish_event(record_created_event)
|
36
|
+
end
|
37
|
+
|
38
|
+
def publish_destroyed_event
|
39
|
+
record_destroyed_event = ::ActivePubsub::Event.new(self.class.exchange_key, "destroyed", serialized_resource)
|
40
|
+
|
41
|
+
::ActivePubsub.publish_event(record_destroyed_event)
|
42
|
+
end
|
43
|
+
|
44
|
+
def serialized_resource
|
45
|
+
Marshal.dump(attributes_hash)
|
46
|
+
end
|
47
|
+
|
48
|
+
module ClassMethods
|
49
|
+
def exchange_key
|
50
|
+
[
|
51
|
+
try(:exchange_prefix) { ::ActivePubsub.config.try(:publish_as) },
|
52
|
+
name.demodulize.underscore
|
53
|
+
].flatten.compact.join(".")
|
54
|
+
end
|
55
|
+
|
56
|
+
#this is the publishing service namespace which will be used to build exchange name
|
57
|
+
|
58
|
+
def publish_as(prefix)
|
59
|
+
self.exchange_prefix = prefix
|
60
|
+
|
61
|
+
::ActivePubsub::Publisher.start unless ::ActivePubsub::Publisher.started
|
62
|
+
|
63
|
+
::ActivePubsub.publisher.register_exchange(exchange_key)
|
64
|
+
end
|
65
|
+
|
66
|
+
#todo: make publishable actions filterable/appendable
|
67
|
+
def publishable_actions(*actions)
|
68
|
+
@publishable_actions = actions
|
69
|
+
end
|
70
|
+
|
71
|
+
def routing_key
|
72
|
+
resource_routing_keys.join(".")
|
73
|
+
end
|
74
|
+
|
75
|
+
def resource_routing_keys
|
76
|
+
[try(:exchange_prefix), name.demodulize.underscore].flatten.compact
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
'celluloid/io'
|
2
|
+
|
3
|
+
module ActivePubsub
|
4
|
+
class Publisher
|
5
|
+
include ::Celluloid
|
6
|
+
|
7
|
+
attr_accessor :connection
|
8
|
+
finalizer :clear_connections!
|
9
|
+
|
10
|
+
### Class Methods ###
|
11
|
+
class << self
|
12
|
+
attr_accessor :started
|
13
|
+
attr_accessor :publishable_model_count
|
14
|
+
end
|
15
|
+
|
16
|
+
@started = false
|
17
|
+
@publishable_model_count = 0
|
18
|
+
|
19
|
+
def self.increment_publishable_model_count!
|
20
|
+
self.publishable_model_count += 1
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.start
|
24
|
+
supervise_as :rabbit_publisher
|
25
|
+
|
26
|
+
self.started = true
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.started?
|
30
|
+
self.started
|
31
|
+
end
|
32
|
+
|
33
|
+
### Instance Methods ###
|
34
|
+
def initialize
|
35
|
+
connection
|
36
|
+
end
|
37
|
+
|
38
|
+
def connection
|
39
|
+
@connection ||= ::ActivePubsub::Connection.new
|
40
|
+
end
|
41
|
+
|
42
|
+
def clear_connections!
|
43
|
+
channel.close
|
44
|
+
connection.close
|
45
|
+
end
|
46
|
+
|
47
|
+
def channel
|
48
|
+
connection.channel
|
49
|
+
end
|
50
|
+
|
51
|
+
def exchanges
|
52
|
+
@exchanges ||= {}
|
53
|
+
end
|
54
|
+
|
55
|
+
def publish_event(event)
|
56
|
+
::ActiveRecord::Base.connection_pool.with_connection do
|
57
|
+
puts event.inspect
|
58
|
+
|
59
|
+
exchanges[event.exchange].publish(serialize_event(event), :routing_key => event.routing_key)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def serialize_event(event)
|
64
|
+
::Marshal.dump(event)
|
65
|
+
end
|
66
|
+
|
67
|
+
def register_exchange(exchange_name)
|
68
|
+
exchanges[exchange_name] ||= channel.topic(exchange_name, :auto_delete => true)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rails/railtie'
|
2
|
+
|
3
|
+
module ActivePubsub
|
4
|
+
class Railtie < ::Rails::Railtie
|
5
|
+
# we only need publisher started if service has a publishable model
|
6
|
+
::ActiveSupport.on_load(:active_record) do
|
7
|
+
puts "Active Record LOADED"
|
8
|
+
|
9
|
+
if(::ActivePubsub::Publisher.publishable_model_count > 0) && !::ActivePubsub::Publisher.started?
|
10
|
+
::ActivePubsub::Publisher.start
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
#todo: make redis configurable
|
15
|
+
def self.load_config_yml
|
16
|
+
config_file = ::YAML.load_file(config_yml_filepath)
|
17
|
+
return unless config_file.is_a?(Hash)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.config_yml_exists?
|
21
|
+
::File.exists? config_yml_filepath
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.config_yml_filepath
|
25
|
+
::Rails.root.join('config', 'rabbit.yml')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'active_support/all'
|
2
|
+
|
3
|
+
module ActivePubsub
|
4
|
+
class Subscriber
|
5
|
+
attr_accessor :connection
|
6
|
+
|
7
|
+
class_attribute :events
|
8
|
+
class_attribute :exchange_name
|
9
|
+
class_attribute :connection
|
10
|
+
class_attribute :local_service_namespace
|
11
|
+
|
12
|
+
self.events = {}
|
13
|
+
self.connection = ::ActivePubsub::Connection.new
|
14
|
+
|
15
|
+
### Class Methods ###
|
16
|
+
def self.channel
|
17
|
+
connection.channel
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.exchange
|
21
|
+
channel.topic(exchange_name, :auto_delete => true)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.as(service_namespace)
|
25
|
+
self.local_service_namespace = service_namespace
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.on(event_name, &block)
|
29
|
+
events[event_name] = block
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.bind_subscriptions!
|
33
|
+
events.each_pair do |event_name, block|
|
34
|
+
channel.queue(queue_for_event(event_name.to_s))
|
35
|
+
.bind(exchange, :routing_key => routing_key_for_event(event_name))
|
36
|
+
.subscribe do |delivery_info, properties, payload|
|
37
|
+
event = deserialize_event(payload)
|
38
|
+
resource = deserialize_record(event[:record])
|
39
|
+
|
40
|
+
block.call(resource)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.deserialize_event(event)
|
46
|
+
@current_event = Marshal.load(event)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.deserialize_record(record)
|
50
|
+
@current_record = Marshal.load(record)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.observes(target_exchange)
|
54
|
+
self.exchange_name = target_exchange
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.queue_for_event(event_name)
|
58
|
+
[local_service_namespace, exchange_name, event_name].compact.join('.')
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.routing_key_for_event(event_name)
|
62
|
+
[exchange_name, event_name].join(".")
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.print_subscriptions!
|
66
|
+
message = "Watching: \n"
|
67
|
+
events.each_pair do |event_name, block|
|
68
|
+
message << "Queue: #{queue_for_event(event_name.to_s)} \n" <<
|
69
|
+
"Routing Key: #{routing_key_for_event(event_name)} \n" <<
|
70
|
+
"\n"
|
71
|
+
end
|
72
|
+
|
73
|
+
puts message
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "active_pubsub/version"
|
2
|
+
|
3
|
+
ENV['RABBITMQ_URL'] ||= "amqp://guest:guest@localhost:5672"
|
4
|
+
|
5
|
+
require "bunny"
|
6
|
+
require "celluloid"
|
7
|
+
require "active_support/all"
|
8
|
+
require "active_attr"
|
9
|
+
|
10
|
+
module ActivePubsub
|
11
|
+
class << self
|
12
|
+
attr_accessor :configuration
|
13
|
+
alias_method :config, :configuration
|
14
|
+
|
15
|
+
delegate :publish_event, :to => :publisher
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.configure
|
19
|
+
self.configuration ||= ::ActivePubsub::Config.new
|
20
|
+
|
21
|
+
yield(configuration)
|
22
|
+
|
23
|
+
::ActiveSupport.run_load_hooks(:active_pubsub, self)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.load_subscribers
|
27
|
+
::Dir.glob(::Rails.root.join('app', 'subscribers', "*.rb")).each{ |file| load file }
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.publisher
|
31
|
+
::Celluloid::Actor[:rabbit_publisher]
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.start_publisher
|
35
|
+
::ActivePubsub::Publisher.start unless ::ActivePubsub::Publisher.started?
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.start_subscribers
|
39
|
+
puts "Starting subscriber"
|
40
|
+
::ActivePubsub::Subscriber.subclasses.each do |subscriber|
|
41
|
+
subscriber.bind_subscriptions!
|
42
|
+
subscriber.print_subscriptions!
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.symbolize_routing_key(routing_key)
|
47
|
+
:"#{routing_key.split('.').join('_')}"
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
require "active_pubsub/connection"
|
53
|
+
require "active_pubsub/config"
|
54
|
+
require "active_pubsub/event"
|
55
|
+
require "active_pubsub/publisher"
|
56
|
+
require "active_pubsub/publishable"
|
57
|
+
require "active_pubsub/subscriber"
|
58
|
+
|
59
|
+
require 'active_pubsub/railtie' if defined?(Rails)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ::ActivePubsub::Publishable do
|
4
|
+
subject { ::Fake::Blog::Post }
|
5
|
+
let(:instance_subject) { ::Fake::Blog::Post.new }
|
6
|
+
let(:publisher) { ::ActivePubsub.publisher }
|
7
|
+
|
8
|
+
context "when included" do
|
9
|
+
it "should increment publishable model count on publisher class" do
|
10
|
+
expect(::ActivePubsub::Publisher.publishable_model_count).to be > 0
|
11
|
+
end
|
12
|
+
|
13
|
+
context "Instance Methods" do
|
14
|
+
describe "#publish_updated_event" do
|
15
|
+
let(:created_record) { ::Fake::Blog::Post.create!(:title => "Post about nothing") }
|
16
|
+
|
17
|
+
it "should build updated action event when record is updated" do
|
18
|
+
::ActivePubsub.should_receive(:publish_event).with(instance_of(::ActivePubsub::Event)).once
|
19
|
+
created_record
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#publish_created_event" do
|
24
|
+
let(:created_record) { ::Fake::Blog::Post.create!(:title => "Post about nothing") }
|
25
|
+
|
26
|
+
it "should publish when record is updated" do
|
27
|
+
::ActivePubsub.should_receive(:publish_event).with(instance_of(::ActivePubsub::Event)).twice
|
28
|
+
|
29
|
+
created_record
|
30
|
+
created_record.title = "asdasd"
|
31
|
+
created_record.save
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#publish_destroyed_event" do
|
36
|
+
let(:created_record) { ::Fake::Blog::Post.create!(:title => "Post about nothing") }
|
37
|
+
|
38
|
+
it "should publish when record is updated" do
|
39
|
+
::ActivePubsub.should_receive(:publish_event).with(instance_of(::ActivePubsub::Event)).twice
|
40
|
+
|
41
|
+
created_record.destroy
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "Class Methods" do
|
47
|
+
its(:exchange_key) { should eq "test.post" }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ::ActivePubsub::Publisher do
|
4
|
+
let(:fake_record) { ::Fake::Blog::Post.new }
|
5
|
+
let(:exchange_key) { "test.post" }
|
6
|
+
let(:event_name) { "test.post.created" }
|
7
|
+
let(:routing_key) { "test.post.created" }
|
8
|
+
let(:record_created_routing_key) { "test.post.created" }
|
9
|
+
let(:record_updated_routing_key) { "test.post.updated" }
|
10
|
+
let(:record_destroyed_routing_key) { "test.post.destroyed" }
|
11
|
+
|
12
|
+
let(:fake_event) {
|
13
|
+
::ActivePubsub::Event.new(routing_key, event_name, fake_record)
|
14
|
+
}
|
15
|
+
|
16
|
+
subject {
|
17
|
+
::Celluloid::Actor[:rabbit_publisher]
|
18
|
+
}
|
19
|
+
|
20
|
+
describe "#connection" do
|
21
|
+
it "should be active pubsub connection instance" do
|
22
|
+
subject.connection.should be_a(ActivePubsub::Connection)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#clear_connections!" do
|
27
|
+
# todo: fix this test, it was causing strange network failure error
|
28
|
+
# it "should close channel" do
|
29
|
+
# subject.should_receive(:channel)
|
30
|
+
# subject.stub_chain(:channel, :close)
|
31
|
+
# subject.stub_chain(:connection, :close)
|
32
|
+
#
|
33
|
+
# subject.clear_connections!
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# it "should close connection" do
|
37
|
+
# subject.should_receive(:connection)
|
38
|
+
# subject.stub_chain(:channel, :close)
|
39
|
+
# subject.stub_chain(:connection, :close)
|
40
|
+
#
|
41
|
+
# subject.clear_connections!
|
42
|
+
# end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#exchanges" do
|
46
|
+
its(:exchanges) { should include(exchange_key) }
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#publish_event" do
|
50
|
+
it "should receive publish event with instance of event when publishable record is saved" do
|
51
|
+
subject.should_receive(:publish_event).with(instance_of(::ActivePubsub::Event))
|
52
|
+
|
53
|
+
fake_record.save
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
require 'simplecov'
|
4
|
+
require 'pry'
|
5
|
+
require 'active_pubsub'
|
6
|
+
|
7
|
+
SimpleCov.start do
|
8
|
+
add_filter '/spec/'
|
9
|
+
end
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
end
|
13
|
+
|
14
|
+
Bundler.require(:default, :development, :test)
|
15
|
+
|
16
|
+
::Dir["#{::File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f }
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection(
|
4
|
+
:adapter => "sqlite3",
|
5
|
+
:database => "spec/test.db"
|
6
|
+
)
|
7
|
+
|
8
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
9
|
+
ActiveRecord::Base.connection.drop_table(table)
|
10
|
+
end
|
11
|
+
|
12
|
+
ActiveRecord::Schema.define(:version => 1) do
|
13
|
+
create_table :posts do |t|
|
14
|
+
t.string :body
|
15
|
+
t.string :title
|
16
|
+
t.string :slug
|
17
|
+
t.integer :user_id
|
18
|
+
|
19
|
+
t.timestamps
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'support/models/post'
|
data/spec/test.db
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,349 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_pubsub
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jason Ayre
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: active_attr
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: celluloid-io
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: json
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bunny
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: activerecord
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: sqlite3
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: bundler
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.6'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.6'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rake
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rspec-pride
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: pry-nav
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: simplecov
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: rspec-its
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '1'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '1'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: rspec-collection_matchers
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - "~>"
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '1'
|
202
|
+
type: :development
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - "~>"
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '1'
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: guard
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - "~>"
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '2'
|
216
|
+
type: :development
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - "~>"
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '2'
|
223
|
+
- !ruby/object:Gem::Dependency
|
224
|
+
name: guard-rspec
|
225
|
+
requirement: !ruby/object:Gem::Requirement
|
226
|
+
requirements:
|
227
|
+
- - "~>"
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: '4'
|
230
|
+
type: :development
|
231
|
+
prerelease: false
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
233
|
+
requirements:
|
234
|
+
- - "~>"
|
235
|
+
- !ruby/object:Gem::Version
|
236
|
+
version: '4'
|
237
|
+
- !ruby/object:Gem::Dependency
|
238
|
+
name: guard-bundler
|
239
|
+
requirement: !ruby/object:Gem::Requirement
|
240
|
+
requirements:
|
241
|
+
- - "~>"
|
242
|
+
- !ruby/object:Gem::Version
|
243
|
+
version: '2'
|
244
|
+
type: :development
|
245
|
+
prerelease: false
|
246
|
+
version_requirements: !ruby/object:Gem::Requirement
|
247
|
+
requirements:
|
248
|
+
- - "~>"
|
249
|
+
- !ruby/object:Gem::Version
|
250
|
+
version: '2'
|
251
|
+
- !ruby/object:Gem::Dependency
|
252
|
+
name: rb-fsevent
|
253
|
+
requirement: !ruby/object:Gem::Requirement
|
254
|
+
requirements:
|
255
|
+
- - ">="
|
256
|
+
- !ruby/object:Gem::Version
|
257
|
+
version: '0'
|
258
|
+
type: :development
|
259
|
+
prerelease: false
|
260
|
+
version_requirements: !ruby/object:Gem::Requirement
|
261
|
+
requirements:
|
262
|
+
- - ">="
|
263
|
+
- !ruby/object:Gem::Version
|
264
|
+
version: '0'
|
265
|
+
- !ruby/object:Gem::Dependency
|
266
|
+
name: terminal-notifier-guard
|
267
|
+
requirement: !ruby/object:Gem::Requirement
|
268
|
+
requirements:
|
269
|
+
- - ">="
|
270
|
+
- !ruby/object:Gem::Version
|
271
|
+
version: '0'
|
272
|
+
type: :development
|
273
|
+
prerelease: false
|
274
|
+
version_requirements: !ruby/object:Gem::Requirement
|
275
|
+
requirements:
|
276
|
+
- - ">="
|
277
|
+
- !ruby/object:Gem::Version
|
278
|
+
version: '0'
|
279
|
+
description: Uses RabbitMQ and ActiveRecord for publishing and consuming model events
|
280
|
+
from any service
|
281
|
+
email:
|
282
|
+
- jasonayre@gmail.com
|
283
|
+
executables:
|
284
|
+
- subscriber
|
285
|
+
extensions: []
|
286
|
+
extra_rdoc_files: []
|
287
|
+
files:
|
288
|
+
- ".gitignore"
|
289
|
+
- ".rspec"
|
290
|
+
- Gemfile
|
291
|
+
- Guardfile
|
292
|
+
- LICENSE.txt
|
293
|
+
- README.md
|
294
|
+
- Rakefile
|
295
|
+
- active_pubsub.gemspec
|
296
|
+
- bin/subscriber
|
297
|
+
- examples/publishing_service_example.rb
|
298
|
+
- examples/subscribing_service_example.rb
|
299
|
+
- lib/active_pubsub.rb
|
300
|
+
- lib/active_pubsub/config.rb
|
301
|
+
- lib/active_pubsub/connection.rb
|
302
|
+
- lib/active_pubsub/event.rb
|
303
|
+
- lib/active_pubsub/publishable.rb
|
304
|
+
- lib/active_pubsub/publisher.rb
|
305
|
+
- lib/active_pubsub/railtie.rb
|
306
|
+
- lib/active_pubsub/subscriber.rb
|
307
|
+
- lib/active_pubsub/version.rb
|
308
|
+
- spec/active_pubsub/connection_spec.rb
|
309
|
+
- spec/active_pubsub/publishable_spec.rb
|
310
|
+
- spec/active_pubsub/publisher_spec.rb
|
311
|
+
- spec/spec_helper.rb
|
312
|
+
- spec/support/db/setup.rb
|
313
|
+
- spec/support/models.rb
|
314
|
+
- spec/support/models/post.rb
|
315
|
+
- spec/test.db
|
316
|
+
homepage: https://github.com/jasonayre/active_pubsub
|
317
|
+
licenses:
|
318
|
+
- MIT
|
319
|
+
metadata: {}
|
320
|
+
post_install_message:
|
321
|
+
rdoc_options: []
|
322
|
+
require_paths:
|
323
|
+
- lib
|
324
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
325
|
+
requirements:
|
326
|
+
- - ">="
|
327
|
+
- !ruby/object:Gem::Version
|
328
|
+
version: '0'
|
329
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
330
|
+
requirements:
|
331
|
+
- - ">="
|
332
|
+
- !ruby/object:Gem::Version
|
333
|
+
version: '0'
|
334
|
+
requirements: []
|
335
|
+
rubyforge_project:
|
336
|
+
rubygems_version: 2.2.2
|
337
|
+
signing_key:
|
338
|
+
specification_version: 4
|
339
|
+
summary: Pubsub using RabbitMQ and ActiveRecord, observe model events from different
|
340
|
+
services.
|
341
|
+
test_files:
|
342
|
+
- spec/active_pubsub/connection_spec.rb
|
343
|
+
- spec/active_pubsub/publishable_spec.rb
|
344
|
+
- spec/active_pubsub/publisher_spec.rb
|
345
|
+
- spec/spec_helper.rb
|
346
|
+
- spec/support/db/setup.rb
|
347
|
+
- spec/support/models.rb
|
348
|
+
- spec/support/models/post.rb
|
349
|
+
- spec/test.db
|