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 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,9 @@
1
+ module MicroBunny
2
+ class Settings
3
+ attr_accessor :host, :vhost, :username, :password, :consumer_app
4
+
5
+ def complete?
6
+ @complete ||= connection && consumer_app
7
+ end
8
+ end
9
+ 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: []