rabbitmq-actors 2.0.0

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.
@@ -0,0 +1,105 @@
1
+ require_relative '../../base/consumer'
2
+
3
+ module RabbitMQ
4
+ module Actors
5
+ # A consumer of messages from RabbitMQ based on exchange and routing key matching patterns.
6
+ # @abstract Subclass and override #perform to define your customized topic worker class.
7
+ #
8
+ # @example
9
+ # class SpainTennisListener < RabbitMQ::Actors::TopicConsumer
10
+ # def initialize
11
+ # super(topic_name: 'sports',
12
+ # binding_keys: '#.tennis.#.spain.#',
13
+ # logger: Rails.logger,
14
+ # on_cancellation: ->{ ActiveRecord::Base.connection.close })
15
+ # end
16
+ #
17
+ # private
18
+ #
19
+ # def perform(**task)
20
+ # match_data = JSON.parse(task[:body])
21
+ # process_tennis_match(match_data)
22
+ # end
23
+ #
24
+ # def process_tennis_match(data)
25
+ # ...
26
+ # end
27
+ # end
28
+ #
29
+ # class AmericaSoccerListener < RabbitMQ::Actors::TopicConsumer
30
+ # def initialize
31
+ # super(exchange_name: 'sports',
32
+ # binding_keys: '#.soccer.#.america.#',
33
+ # logger: Rails.logger,
34
+ # on_cancellation: ->{ ActiveRecord::Base.connection.close })
35
+ # end
36
+ #
37
+ # private
38
+ #
39
+ # def perform(**task)
40
+ # match_data = JSON.parse(task[:body])
41
+ # process_soccer_match(match_data)
42
+ # end
43
+ #
44
+ # def process_soccer_match(data)
45
+ # ...
46
+ # end
47
+ # end
48
+ #
49
+ # RabbitMQ::Server.url = 'amqp://localhost'
50
+ #
51
+ # SpainTennisListener.new.start!
52
+ # AmericaSoccerListener.new.start!
53
+ #
54
+ class TopicConsumer < Base::Consumer
55
+ # @!attribute [r] topic_name
56
+ # @return [Bunny::Exchange] the topic exchange where to get messages from.
57
+ attr_reader :topic_name
58
+
59
+ # @!attribute [r] binding_keys
60
+ # @return [String, Array] the routing key patterns this worker is interested in.
61
+ attr_reader :binding_keys
62
+
63
+ # @param :topic_name [String] name of the topic exchange this worker will receive messages.
64
+ # @param :binding_keys [String, Array] routing key patterns this worker is interested in.
65
+ # Default to all: '#'
66
+ # @option opts [Proc] :on_cancellation to be executed before the worker is terminated
67
+ # @option opts [Logger] :logger the logger where to output info about this agent's activity.
68
+ # Rest of options required by your subclass.
69
+ def initialize(topic_name:, binding_keys: '#', **opts)
70
+ super(opts.merge(topic_name: topic_name, binding_keys: binding_keys))
71
+ end
72
+
73
+ private
74
+
75
+ # Set topic exchange_name and binding_keys this worker is bound to.
76
+ # @see #initialize for the list of options that can be received.
77
+ def pre_initialize(**opts)
78
+ @topic_name = opts[:topic_name]
79
+ @binding_keys = Array(opts[:binding_keys])
80
+ super
81
+ end
82
+
83
+ # Bind this worker's queue to the topic exchange and to the given binding_key patterns
84
+ # @see #initialize for the list of options that can be received.
85
+ def post_initialize(**opts)
86
+ bind_queue_to_exchange_routing_keys
87
+ super
88
+ end
89
+
90
+ # The durable RabbitMQ topic exchange from where messages are received
91
+ # @return [Bunny::Exchange]
92
+ def exchange
93
+ @exchange ||= channel.topic(topic_name, durable: true)
94
+ end
95
+
96
+ # Bind this worker's listening queue to the topic exchange and receive only messages with routing key
97
+ # matching the patterns in binding_keys.
98
+ def bind_queue_to_exchange_routing_keys
99
+ binding_keys.each do |key|
100
+ queue.bind(exchange, routing_key: key)
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,64 @@
1
+ require_relative '../../base/producer'
2
+
3
+ module RabbitMQ
4
+ module Actors
5
+ # A producer of messages routed to all the queues bound to the message's routing_key via matching patterns
6
+ #
7
+ # @example
8
+ # RabbitMQ::Server.url = 'amqp://localhost'
9
+ #
10
+ # publisher = RabbitMQ::Actors::TopicProducer.new(topic_name: 'weather', logger: Rails.logger)
11
+ # message = { temperature: 20, rain: 30%, wind: 'NorthEast' }.to_json
12
+ # publisher.publish(message, message_id: '1234837633', content_type: "application/json", routing_key: 'Europe.Spain.Madrid')
13
+ #
14
+ class TopicProducer < Base::Producer
15
+ # @!attribute [r] topic_name
16
+ # @return [Bunny::Exchange] the topic exchange where to publish messages.
17
+ attr_reader :topic_name
18
+
19
+ # @param :topic_name [String] name of the topic exchange where to send messages to.
20
+ # @option opts [String] :reply_queue_name the name of the queue where a consumer should reply.
21
+ # @option opts [Logger] :logger the logger where to output info about this agent's activity.
22
+ def initialize(topic_name:, **opts)
23
+ super(opts.merge(topic_name: topic_name))
24
+ end
25
+
26
+ # Send a message to the RabbitMQ server.
27
+ # @param message [String] the message body to be sent.
28
+ # @param :message_id [String] user-defined id for replies to refer to this message using :correlation_id
29
+ # @param :routing_key [String] send the message only to queues bound to this exchange and matching this routing_key
30
+ # @see Bunny::Exchange#publish for extra options:
31
+ # @option opts [Boolean] :persistent Should the message be persisted to disk?. Default true.
32
+ # @option opts [Boolean] :mandatory Should the message be returned if it cannot be routed to any queue?
33
+ # @option opts [Integer] :timestamp A timestamp associated with this message
34
+ # @option opts [Integer] :expiration Expiration time after which the message will be deleted
35
+ # @option opts [String] :type Message type, e.g. what type of event or command this message represents. Can be any string
36
+ # @option opts [String] :reply_to Queue name other apps should send the response to. Default to
37
+ # replay_queue_name if it was defined at creation time.
38
+ # @option opts [String] :content_type Message content type (e.g. application/json)
39
+ # @option opts [String] :content_encoding Message content encoding (e.g. gzip)
40
+ # @option opts [String] :correlation_id Message correlated to this one, e.g. what request this message is a reply for
41
+ # @option opts [Integer] :priority Message priority, 0 to 9. Not used by RabbitMQ, only applications
42
+ # @option opts [String] :user_id Optional user ID. Verified by RabbitMQ against the actual connection username
43
+ # @option opts [String] :app_id Optional application ID
44
+ def publish(message, message_id:, routing_key:, **opts)
45
+ super(message, opts.merge(message_id: message_id, routing_key: routing_key))
46
+ end
47
+
48
+ private
49
+
50
+ # Sets the exchange name to connect to.
51
+ # @see #initialize for the list of options that can be received.
52
+ def pre_initialize(**opts)
53
+ @topic_name = opts[:topic_name]
54
+ super
55
+ end
56
+
57
+ # The durable RabbitMQ topic exchange where to publish messages.
58
+ # @return [Bunny::Exchange]
59
+ def exchange
60
+ @exchange ||= channel.topic(topic_name, durable: true)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,8 @@
1
+ module RabbitMQ
2
+ module Actors
3
+ module Testing
4
+ end
5
+ end
6
+ end
7
+
8
+ require_relative 'testing/rspec' if defined?(RSpec)
@@ -0,0 +1,8 @@
1
+ if RSpec.respond_to?(:configure)
2
+ require_relative 'rspec/stub'
3
+
4
+ RSpec.configure do |config|
5
+ config.extend RabbitMQ::Actors::Testing::Rspec::Stub
6
+ config.include RabbitMQ::Actors::Testing::Rspec::Stub
7
+ end
8
+ end
@@ -0,0 +1,52 @@
1
+ module RabbitMQ
2
+ module Actors
3
+ module Testing
4
+ module Rspec
5
+
6
+ module Stub
7
+ # This is a macro available in your rspec tests to stub the RabbitMQ server
8
+ # and Bunny objects associated to it.
9
+ #
10
+ # describe MyListener do
11
+ # stub_rabbitmq!
12
+ #
13
+ # context "..." do
14
+ # ...
15
+ # end
16
+ # end
17
+ def stub_rabbitmq!
18
+ let(:default_exchange) { double(publish: :published, on_return: :on_returned, type: :direct) }
19
+ let(:channel) { double(prefetch: true, ack: true, default_exchange: default_exchange, close: true) }
20
+ let(:connection) { double(create_channel: channel).as_null_object }
21
+
22
+ before do
23
+ allow(channel).to receive(:queue) do |name, durable:, auto_delete:, exclusive:|
24
+ double(name: name, durable: durable.present?, durable?: durable.present?,
25
+ auto_delete: auto_delete.present?, auto_delete?: auto_delete.present?,
26
+ exclusive: exclusive.present?, exclusive?: exclusive.present?, bind: :bound)
27
+ end
28
+
29
+ allow(channel).to receive(:direct) do |name|
30
+ double(name: name, publish: :published, type: :direct)
31
+ end
32
+
33
+ allow(channel).to receive(:fanout) do |name|
34
+ double(name: name, publish: :published, type: :fanout)
35
+ end
36
+
37
+ allow(channel).to receive(:topic) do |name|
38
+ double(name: name, publish: :published, type: :topic)
39
+ end
40
+
41
+ allow(channel).to receive(:headers) do |name|
42
+ double(name: name, publish: :published, type: :headers)
43
+ end
44
+
45
+ allow(RabbitMQ::Server).to receive(:connection) { connection }
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,5 @@
1
+ module RabbitMQ
2
+ module Actors
3
+ VERSION = "2.0.0"
4
+ end
5
+ end
@@ -0,0 +1,18 @@
1
+ module RabbitMQ
2
+ module Server
3
+
4
+ # The url to the server
5
+ mattr_reader :url, instance_accessor: false
6
+
7
+ # The bunny connection object to the server
8
+ mattr_reader :connection, instance_accessor: false
9
+
10
+ class << self
11
+ # Set the url to the server and open a new connection
12
+ def url=(url)
13
+ @@url = url
14
+ @@connection = Bunny.new(url).start
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rabbitmq/actors/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rabbitmq-actors"
8
+ spec.version = RabbitMQ::Actors::VERSION
9
+ spec.authors = ["Lorenzo Tello"]
10
+ spec.email = ["ltello8a@gmail.com"]
11
+
12
+ spec.summary = "Ruby client agents implementing different RabbitMQ producer/consumer patterns."
13
+ spec.description = "High level classes (consumers, producers, publishers...) for a Ruby application to use RabbitMQ."
14
+ spec.homepage = "https://github.com/ltello/rabbitmq-actors"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.13"
24
+ spec.add_development_dependency "rake" # Run scripted tasks
25
+ spec.add_development_dependency "rspec", "~> 3.4" # Test framework
26
+ spec.add_development_dependency "factory_girl", "~> 4.5" # Data factories to be used in tests
27
+ spec.add_development_dependency "byebug", "~> 5.0" # Debugger
28
+ spec.add_development_dependency "simplecov" # Code coverage analyzer
29
+
30
+ spec.add_runtime_dependency "activesupport", "~> 4.2" # Rails ActiveSupport
31
+ spec.add_runtime_dependency "bunny", "~> 2.0" # Ruby RabbitMQ client.
32
+ end
metadata ADDED
@@ -0,0 +1,185 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rabbitmq-actors
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Lorenzo Tello
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-05-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
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: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.4'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: factory_girl
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
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: activesupport
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '4.2'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '4.2'
111
+ - !ruby/object:Gem::Dependency
112
+ name: bunny
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '2.0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '2.0'
125
+ description: High level classes (consumers, producers, publishers...) for a Ruby application
126
+ to use RabbitMQ.
127
+ email:
128
+ - ltello8a@gmail.com
129
+ executables: []
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - ".gitignore"
134
+ - ".rspec"
135
+ - ".travis.yml"
136
+ - Gemfile
137
+ - README.md
138
+ - Rakefile
139
+ - bin/console
140
+ - bin/setup
141
+ - lib/rabbitmq/actors.rb
142
+ - lib/rabbitmq/actors/base/agent.rb
143
+ - lib/rabbitmq/actors/base/consumer.rb
144
+ - lib/rabbitmq/actors/base/producer.rb
145
+ - lib/rabbitmq/actors/patterns.rb
146
+ - lib/rabbitmq/actors/patterns/headers/headers_consumer.rb
147
+ - lib/rabbitmq/actors/patterns/headers/headers_producer.rb
148
+ - lib/rabbitmq/actors/patterns/master_workers/master_producer.rb
149
+ - lib/rabbitmq/actors/patterns/master_workers/worker.rb
150
+ - lib/rabbitmq/actors/patterns/publish_subscribe/publisher.rb
151
+ - lib/rabbitmq/actors/patterns/publish_subscribe/subscriber.rb
152
+ - lib/rabbitmq/actors/patterns/routing/routing_consumer.rb
153
+ - lib/rabbitmq/actors/patterns/routing/routing_producer.rb
154
+ - lib/rabbitmq/actors/patterns/topics/topic_consumer.rb
155
+ - lib/rabbitmq/actors/patterns/topics/topic_producer.rb
156
+ - lib/rabbitmq/actors/testing.rb
157
+ - lib/rabbitmq/actors/testing/rspec.rb
158
+ - lib/rabbitmq/actors/testing/rspec/stub.rb
159
+ - lib/rabbitmq/actors/version.rb
160
+ - lib/rabbitmq/server.rb
161
+ - rabbitmq-actors.gemspec
162
+ homepage: https://github.com/ltello/rabbitmq-actors
163
+ licenses: []
164
+ metadata: {}
165
+ post_install_message:
166
+ rdoc_options: []
167
+ require_paths:
168
+ - lib
169
+ required_ruby_version: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ required_rubygems_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ requirements: []
180
+ rubyforge_project:
181
+ rubygems_version: 2.6.8
182
+ signing_key:
183
+ specification_version: 4
184
+ summary: Ruby client agents implementing different RabbitMQ producer/consumer patterns.
185
+ test_files: []