rabbitmq-actors 2.0.0

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