microbunny 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []