microbunny 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/lib/microbunny/connection.rb +21 -0
- data/lib/microbunny/event.rb +49 -0
- data/lib/microbunny/event_lookup.rb +26 -0
- data/lib/microbunny/event_trigger.rb +25 -0
- data/lib/microbunny/payload.rb +22 -0
- data/lib/microbunny/record.rb +59 -0
- data/lib/microbunny/settings.rb +9 -0
- data/lib/microbunny/subscriber.rb +37 -0
- data/lib/microbunny/subscriptions.rb +33 -0
- data/lib/microbunny.rb +64 -0
- metadata +74 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b6004da088017f240efedf6234513db4f7e3a3b8
|
4
|
+
data.tar.gz: 2f97d08358e659eac313cccb0ad789ff5e19ab44
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7eb1fd169b8b4020c2c32058fa2214f2d4e7f3ccf85c48fdc3ccc092b68e366965c117050cae1e4d589578da71724f4a526846563743d488938379bacbca8f52
|
7
|
+
data.tar.gz: 188db2b1c7338f611915dcaacb62edad977a32a6ffe87ec49bb6eb507b9164564b1802afa7bd7564cb6879534b36649cfa75d23ea9e7ef91d94327247e219114
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module MicroBunny
|
2
|
+
class Connection
|
3
|
+
def initialize(host:, vhost: nil, username: 'guest', password: 'guest')
|
4
|
+
@conn = Bunny.new(
|
5
|
+
host: host,
|
6
|
+
vhost: vhost,
|
7
|
+
username: username,
|
8
|
+
password: password
|
9
|
+
)
|
10
|
+
conn.start
|
11
|
+
end
|
12
|
+
|
13
|
+
def channel
|
14
|
+
@channel ||= conn.create_channel
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
attr_reader :conn
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'json'
|
2
|
+
require_relative './record'
|
3
|
+
require_relative './payload'
|
4
|
+
|
5
|
+
module MicroBunny
|
6
|
+
class Event
|
7
|
+
def self.exchange(name)
|
8
|
+
define_method(:exchange) do
|
9
|
+
name.to_sym
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(channel)
|
14
|
+
@channel = channel
|
15
|
+
end
|
16
|
+
|
17
|
+
def publish(message, record_id = nil)
|
18
|
+
x = channel.topic(self.exchange)
|
19
|
+
payload = build_payload(name, message)
|
20
|
+
|
21
|
+
if record_id
|
22
|
+
record = Record.new(record_id)
|
23
|
+
payload.record = record.id
|
24
|
+
x.publish(payload.serialize, routing_key: record.routing_key_for(self))
|
25
|
+
else
|
26
|
+
x.publish(payload.serialize, routing_key: name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def name
|
31
|
+
self.class.name.gsub(/Event$/, '')
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :channel
|
37
|
+
|
38
|
+
def build_payload(name, message)
|
39
|
+
Payload.new(event: name, message: message)
|
40
|
+
end
|
41
|
+
|
42
|
+
def serialize(payload)
|
43
|
+
payload.to_json
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module MicroBunny
|
2
|
+
class EventLookup
|
3
|
+
def initialize(name)
|
4
|
+
@name = name.is_a?(Symbol) ? camelize(name.to_s) : name
|
5
|
+
end
|
6
|
+
|
7
|
+
def event
|
8
|
+
constantize(name + "Event")
|
9
|
+
end
|
10
|
+
|
11
|
+
def consumer
|
12
|
+
constantize(name + "Consumer")
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
attr_reader :name
|
17
|
+
|
18
|
+
def camelize(string)
|
19
|
+
string.split('_').map(&:capitalize).join
|
20
|
+
end
|
21
|
+
|
22
|
+
def constantize(string)
|
23
|
+
Object.const_get(string)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module MicroBunny
|
2
|
+
class EventTrigger
|
3
|
+
def initialize(channel, name, lookup = EventLookup)
|
4
|
+
@event = lookup.new(name).event.new(channel)
|
5
|
+
end
|
6
|
+
|
7
|
+
def with(payload)
|
8
|
+
@payload = payload
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def for(record)
|
13
|
+
@record = record
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def broadcast
|
18
|
+
record ? @event.trigger(payload, record) : @event.trigger(payload)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
attr_reader :channel, :event, :payload, :record
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module MicroBunny
|
4
|
+
class Payload
|
5
|
+
attr_accessor :event, :message, :record
|
6
|
+
|
7
|
+
def self.deserialize(json)
|
8
|
+
new JSON.parse(json, symbolize_names: true)
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(event:, message:, record: nil)
|
12
|
+
@event = event
|
13
|
+
@message = message
|
14
|
+
@record = record
|
15
|
+
end
|
16
|
+
|
17
|
+
def serialize
|
18
|
+
{event: event, message: message, record: record}.to_json
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module MicroBunny
|
2
|
+
class Record
|
3
|
+
attr_reader :label
|
4
|
+
|
5
|
+
def initialize(record, settings = MicroBunny.settings)
|
6
|
+
@record = record
|
7
|
+
@label = extract_label(record)
|
8
|
+
@settings = settings
|
9
|
+
end
|
10
|
+
|
11
|
+
def id
|
12
|
+
if string?
|
13
|
+
record
|
14
|
+
elsif active_record?
|
15
|
+
record.id
|
16
|
+
else
|
17
|
+
raise_invalid_record
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def routing_key_matcher
|
22
|
+
"*.#{label}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def routing_key_for(event)
|
26
|
+
"#{event.name}.#{label}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def address
|
30
|
+
"#{settings.consumer_app}-#{label}"
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_reader :record, :settings
|
36
|
+
|
37
|
+
def extract_label(record)
|
38
|
+
return record if string?
|
39
|
+
return activerecord_label if active_record?
|
40
|
+
raise_invalid_record
|
41
|
+
end
|
42
|
+
|
43
|
+
def string?
|
44
|
+
record.is_a? String
|
45
|
+
end
|
46
|
+
|
47
|
+
def active_record?
|
48
|
+
record.is_a? ActiveRecord::Base
|
49
|
+
end
|
50
|
+
|
51
|
+
def activerecord_label
|
52
|
+
"#{record.class.name.downcase}-#{record.id}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def raise_invalid_record
|
56
|
+
raise "A record must be either a String or an ActiveRecord object"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module MicroBunny
|
2
|
+
class Subscriber
|
3
|
+
def initialize(channel, exchange, callback)
|
4
|
+
@channel = channel
|
5
|
+
@exchange = exchange
|
6
|
+
@callback = callback
|
7
|
+
@settings = settings
|
8
|
+
end
|
9
|
+
|
10
|
+
def topic(topic)
|
11
|
+
queue = find_queue(topic)
|
12
|
+
queue.bind(exchange, routing_key: "*")
|
13
|
+
queue.subscribe { |_info, _meta, payload| callback.call(payload) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def records(records)
|
17
|
+
records.each do |record|
|
18
|
+
queue = find_queue_for_record(record)
|
19
|
+
queue.bind(exchange, routing_key: record.routing_key_matcher)
|
20
|
+
queue.subscribe { |_info, _meta, payload| callback.call(payload) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :channel, :exchange, :callback, :settings
|
27
|
+
|
28
|
+
def find_queue(name)
|
29
|
+
channel.queue(name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_queue_for_record(record)
|
33
|
+
find_queue(record.address)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative './subscriber'
|
2
|
+
|
3
|
+
module MicroBunny
|
4
|
+
class Subscriptions
|
5
|
+
def initialize(channel, topics, records, &block)
|
6
|
+
@channel = channel
|
7
|
+
@topics = topics
|
8
|
+
@records = records
|
9
|
+
@callback = block
|
10
|
+
end
|
11
|
+
|
12
|
+
def create
|
13
|
+
all(topics).each do |topic|
|
14
|
+
exchange = create_exchange(topic)
|
15
|
+
sub = Subscriber.new(channel, exchange, callback)
|
16
|
+
records.empty? ? sub.topic(topic) : sub.records(records)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :channel, :topics, :records, :callback
|
23
|
+
|
24
|
+
def all(topics)
|
25
|
+
Array(topics).flatten.map(&:to_sym)
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_exchange(topic)
|
29
|
+
channel.topic(topic)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
data/lib/microbunny.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bunny'
|
3
|
+
|
4
|
+
require_relative 'microbunny/settings'
|
5
|
+
require_relative 'microbunny/connection'
|
6
|
+
require_relative 'microbunny/record'
|
7
|
+
require_relative 'microbunny/subscriptions'
|
8
|
+
require_relative 'microbunny/event_lookup'
|
9
|
+
require_relative 'microbunny/event_trigger'
|
10
|
+
require_relative 'microbunny/event'
|
11
|
+
require_relative 'microbunny/payload'
|
12
|
+
|
13
|
+
module MicroBunny
|
14
|
+
class << self
|
15
|
+
attr_accessor :settings, :channel
|
16
|
+
|
17
|
+
def configure
|
18
|
+
self.settings ||= Settings.new
|
19
|
+
yield(settings)
|
20
|
+
end
|
21
|
+
|
22
|
+
def start
|
23
|
+
self.channel = Connection.new(
|
24
|
+
host: self.settings.host,
|
25
|
+
vhost: self.settings.vhost,
|
26
|
+
username: self.settings.username,
|
27
|
+
password: self.settings.password
|
28
|
+
).channel
|
29
|
+
end
|
30
|
+
|
31
|
+
def subscribe(topics, record_ids = [])
|
32
|
+
raise_bad_start unless self.channel
|
33
|
+
|
34
|
+
records = record_ids.map(&Record.method(:new))
|
35
|
+
subs = Subscriptions.new(self.channel, topics, records) do |payload|
|
36
|
+
handle(payload)
|
37
|
+
end
|
38
|
+
|
39
|
+
subs.create
|
40
|
+
end
|
41
|
+
|
42
|
+
def event(name)
|
43
|
+
EventTrigger.new(channel, name)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def handle(json)
|
49
|
+
payload = Payload.deserialize(json)
|
50
|
+
consumer = EventLookup.new(payload.event).consumer.new
|
51
|
+
|
52
|
+
if payload.record
|
53
|
+
consumer.process(payload.message, payload.record)
|
54
|
+
else
|
55
|
+
consumer.process(payload.message)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def raise_bad_start
|
60
|
+
raise "Must call MicroBunny.start before you can subscribe"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: microbunny
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pierre Lebrun
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-09-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bunny
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
description: |2
|
28
|
+
MicroBunny is an small opinionated RabbitMQ framework built on top of bunny that
|
29
|
+
implements a routing scheme and client API optimized to help you build asynchronous,
|
30
|
+
event-based communication between web services (such as microservices). If you're
|
31
|
+
interesting in building resilient, loosely coupled and self contained services, then
|
32
|
+
this framework may be for you.
|
33
|
+
email:
|
34
|
+
- plebrun@on-site.com
|
35
|
+
- anthonylebrun@gmail.com
|
36
|
+
executables: []
|
37
|
+
extensions: []
|
38
|
+
extra_rdoc_files: []
|
39
|
+
files:
|
40
|
+
- lib/microbunny.rb
|
41
|
+
- lib/microbunny/connection.rb
|
42
|
+
- lib/microbunny/event.rb
|
43
|
+
- lib/microbunny/event_lookup.rb
|
44
|
+
- lib/microbunny/event_trigger.rb
|
45
|
+
- lib/microbunny/payload.rb
|
46
|
+
- lib/microbunny/record.rb
|
47
|
+
- lib/microbunny/settings.rb
|
48
|
+
- lib/microbunny/subscriber.rb
|
49
|
+
- lib/microbunny/subscriptions.rb
|
50
|
+
homepage: https://github.com/on-site/microbunny
|
51
|
+
licenses:
|
52
|
+
- MIT
|
53
|
+
metadata: {}
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options: []
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
requirements: []
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 2.4.5
|
71
|
+
signing_key:
|
72
|
+
specification_version: 4
|
73
|
+
summary: A RabbitMQ framework for asynchronous communication between services
|
74
|
+
test_files: []
|