rabbitmq_client 0.0.0.pre → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.reek.yml +21 -0
  4. data/.travis.yml +12 -1
  5. data/README.md +129 -17
  6. data/bin/setup +4 -0
  7. data/lib/rabbitmq_client/callback.rb +47 -0
  8. data/lib/rabbitmq_client/exchange.rb +25 -0
  9. data/lib/rabbitmq_client/exchange_registry.rb +33 -0
  10. data/lib/rabbitmq_client/json_formatter.rb +29 -0
  11. data/lib/rabbitmq_client/json_log_subscriber.rb +90 -0
  12. data/lib/rabbitmq_client/lifecycle.rb +53 -0
  13. data/lib/rabbitmq_client/log_subscriber_base.rb +16 -0
  14. data/lib/rabbitmq_client/logger_builder.rb +35 -0
  15. data/lib/rabbitmq_client/message_publisher.rb +57 -0
  16. data/lib/rabbitmq_client/plain_log_subscriber.rb +64 -0
  17. data/lib/rabbitmq_client/plugin.rb +31 -0
  18. data/lib/rabbitmq_client/publisher.rb +79 -0
  19. data/lib/rabbitmq_client/tags_filter.rb +16 -0
  20. data/lib/rabbitmq_client/text_formatter.rb +42 -0
  21. data/lib/rabbitmq_client/version.rb +2 -1
  22. data/lib/rabbitmq_client.rb +99 -2
  23. data/rabbitmq_client.gemspec +1 -0
  24. data/script/travis.sh +2 -0
  25. data/spec/callback_spec.rb +31 -0
  26. data/spec/exchange_registry_spec.rb +32 -0
  27. data/spec/json_formatter_spec.rb +43 -0
  28. data/spec/json_log_subscriber_spec.rb +145 -0
  29. data/spec/lifecycle_spec.rb +78 -0
  30. data/spec/plain_log_subscriber_spec.rb +115 -0
  31. data/spec/plugin_spec.rb +12 -0
  32. data/spec/publisher_spec.rb +150 -0
  33. data/spec/rabbitmq_client_spec.rb +83 -0
  34. data/spec/support/dummy_rabbitmq_client_plugin.rb +13 -0
  35. data/spec/tags_filter_spec.rb +37 -0
  36. data/spec/text_formatter_spec.rb +45 -0
  37. metadata +57 -5
  38. data/Gemfile.lock +0 -156
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 919915c1cb4d6658ccdea2d77ebde498edb6f2bdb4888be57263a895a8e1951a
4
- data.tar.gz: c3f2fb6e0b6afa7f8a818de32db7bdb014c604901cf749327f4630b066b11ee7
3
+ metadata.gz: a2b060865b600b3377cbb16799ecdc82fedbd874b8b9f158c58a57604925c7b8
4
+ data.tar.gz: 211d0b536583982f7248a188daab96c77ec2a37765f840bdf85b4dfb9bf27930
5
5
  SHA512:
6
- metadata.gz: 8ceb8db083e03339f8603dcadb54ca4a13025dbf6e40f98cf743067a883fdef3132a07ab9bd8b63f6bde93a61991161042b08fb0df6b91b985ab2c489d7f145b
7
- data.tar.gz: 201de069eaf129c7054ba2370fd9c9053c454f3f043296d25dc4201a0d9f9ae4f71ee7bd9494b3813ecd9a54b135a66d838d4c1ec861ea513a7d3f32d6e8dd88
6
+ metadata.gz: 7ca283805742bb671e6f43818ba3c9ad4f2ad8587f708c1d41fd472ccfaefa6e50f8d615feffeb4699a7a5f1b8957ebe5eda770f77229cab60a8283c066640b5
7
+ data.tar.gz: 5431a7e2bfe7a6ef70ab1bdcdb9e79069178b1071f09a481c656c431d7e7174291efbf4861d6e4ee35fc7cfc6d0d3620e1fff7a188b5d83a6bc3c3765cbb2224
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ badges/
1
2
  .bundle/
2
3
  .yardoc
3
4
  _yardoc/
@@ -6,4 +7,5 @@ doc/
6
7
  pkg/
7
8
  spec/reports/
8
9
  tmp/
9
-
10
+ Gemfile.lock
11
+ testing.log
data/.reek.yml ADDED
@@ -0,0 +1,21 @@
1
+ ---
2
+ detectors:
3
+ LongParameterList:
4
+ max_params: 5
5
+ TooManyStatements:
6
+ enabled: true
7
+ max_statements: 10
8
+ UncommunicativeVariableName:
9
+ accept:
10
+ - e
11
+ Attribute:
12
+ exclude:
13
+ - "RabbitmqClient::Plugin#callback_block"
14
+ UtilityFunction:
15
+ exclude:
16
+ - 'RabbitmqClient::Publisher#notify'
17
+ - 'LogSubscriberBase#logger'
18
+ - 'RabbitmqClient::JsonLogSubscriber'
19
+ FeatureEnvy:
20
+ exclude:
21
+ - 'RabbitmqClient::PlainLogSubscriber'
data/.travis.yml CHANGED
@@ -6,12 +6,23 @@ branches:
6
6
 
7
7
  rvm:
8
8
  - 2.6.4
9
+
9
10
  cache: bundler
10
11
  bundler_args: "--jobs=3 --retry=3 --without production"
12
+
11
13
  matrix:
12
14
  fast_finish: true
15
+
13
16
  script:
14
17
  - sh script/travis.sh
18
+
19
+ notifications:
20
+ email: false # default notification method
21
+ slack:
22
+ if: branch = master
23
+ rooms:
24
+ secure: S5yfvL5slwkKqkIkUSEz3jcdMUGcaZ6tSlAL3h59p+Mlj40ezvKPvG3ZsSYw170+BQK0sxouxm1BpIGZ/aPu3ou6cK28eaaHrtS/CIVDM/6exobruWFAoidkjyyXyolzXAn/htAqOt3S21mCLfYDafUL2LeAnsheGf+TfPdKoBVwXzjn14RBsxW9qMOZ9rOgwxAikBtKELZyEr9MhBnLiJCSWFG/+sCvdYfpmAPvIlobZS65/943jbmUDMt0gu/AaW3De/97CaYF0rcxiiJVw/foDCJ5mX2UbzfGearxPzyF6F0ZjxGKHBEDQ+mqOE+cSPEKnkCc+YsBT4RhiyCWJupoEiCKbk3jy06n+8sT/PKvv+wDy4CUtYnqSXxSTVrUTWyqItxVEXUjK/+H8A7YosTC3FtVfYyZlVnmur0VitOCF9SKVUi+RtIrExGu5lI/Jvy6gUw6J2+tjWcDIoUuJ6iAKe84cRf1DUY4um3/M8QFI1QH21s9DT2bBfi5ombKeB4GkVqF7r26qbt18cDrYJwdUCVdRlYsXVyocHbrsBydBicUoTdbLLJlFI1PY4EiXNVlgVQKpnWdsodCu73xnLKxazqWX77gucXkvfIAKcu3br/onIU8IRGSPWKl7Bxo64/sNym4ZHFUYUS+ODFXUwiyykQcnyc25YnlgmHSDSo=
25
+
15
26
  deploy:
16
27
  provider: pages
17
28
  skip-cleanup: true
@@ -19,4 +30,4 @@ deploy:
19
30
  keep-history: true
20
31
  verbose: true
21
32
  on:
22
- branch: master
33
+ branch: master
data/README.md CHANGED
@@ -1,15 +1,44 @@
1
- # RabbitmqClient
1
+
2
+ ```sh
3
+ ____ _ _ _ _ __ __ ___ ____ _ _ _
4
+ | _ \ __ _| |__ | |__ (_) |_| \/ |/ _ \ / ___| (_) ___ _ __ | |_
5
+ | |_) / _` | '_ \| '_ \| | __| |\/| | | | | | | | | |/ _ \ '_ \| __|
6
+ | _ < (_| | |_) | |_) | | |_| | | | |_| | | |___| | | __/ | | | |_
7
+ |_| \_\__,_|_.__/|_.__/|_|\__|_| |_|\__\_\ \____|_|_|\___|_| |_|\__|
8
+
9
+ ```
10
+
11
+ [![Gem Version](https://badge.fury.io/rb/rabbitmq_client.svg)](https://badge.fury.io/rb/rabbitmq_client)
12
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](http://opensource.org/licenses/MIT)
2
13
  [![Build Status](https://travis-ci.com/wshihadeh/rabbitmq_client.svg?branch=master)](https://travis-ci.com/wshihadeh/rabbitmq_client)
3
14
  [![Depfu](https://badges.depfu.com/badges/b7ffc2788d24431bf85864706c5f9fb2/count.svg)](https://depfu.com/github/wshihadeh/rabbitmq_client?project_id=9862)
4
- [![Coverage](https://wshihadeh.github.io/rabbitmq_client/badges/coverage_badge_total.svg)](https://wshihadeh.github.io/rabbitmq_client/coverage/index.html)
5
- [![RubyCritic](https://wshihadeh.github.io/rabbitmq_client/badges/rubycritic_badge_score.svg)](https://wshihadeh.github.io/rabbitmq_client/tmp/rubycritic/overview.html)
15
+ [![Coverage](https://wshihadeh.github.io/rabbitmq_client/badges/coverage_badge_total.svg)](https://wshihadeh.github.io/rabbitmq_client/coverage_info/index.html)
16
+ [![RubyCritic](https://wshihadeh.github.io/rabbitmq_client/badges/rubycritic_badge_score.svg)](https://wshihadeh.github.io/rabbitmq_client/rubycritic/overview.html)
17
+
18
+ Simplifying RabbitMQ for Ruby apps!
19
+
20
+ RabbitMQ Client is a ruby client library for applications dealing with [RabbitMQ](https://www.rabbitmq.com/).
21
+ - It wraps common behaviors needed by publishers and subscribers in an easy and convenient API.
22
+ - It uses both `bunny` and `connection_pool` to manage RabbitMQ communications.
23
+ - It is extendable using plugins.
24
+
25
+ Why RabbitMQ Client? Why not `bunny` and `connection_pool` directly? Well, gems are just a clients. You still need to write a lot of code to manage proper subscribing and publishing of messages. You need to do error handling, passing request headers to RabbitMQ and maybe logging/instrumenting the message management process. Finally, you also need to consider how to deploy your app and how to start it.
26
+
27
+ With RabbitMQ Client by your side, all this becomes smooth and easy.
6
28
 
29
+ ## Table of Contents
7
30
 
8
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/rabbitmq_client`. To experiment with that code, run `bin/console` for an interactive prompt.
31
+ 1. [Installation](#installation)
32
+ 1. [Usage](#usage)
33
+ 1. [Configurations](#usage-configuration)
34
+ 1. [Publishing messages to RabbitMQ](#usage-publish-messages-to-rmq)
35
+ 1. [Plugins](#plugins)
36
+ 1. [Development and Testing](#development)
37
+ 1. [Contributing](#contributing)
38
+ 1. [License](#license)
9
39
 
10
- TODO: Delete this and the text above, and describe your gem
11
40
 
12
- ## Installation
41
+ ## <a name="installation"></a> Installation
13
42
 
14
43
  Add this line to your application's Gemfile:
15
44
 
@@ -25,24 +54,107 @@ Or install it yourself as:
25
54
 
26
55
  $ gem install rabbitmq_client
27
56
 
28
- ## Usage
57
+ ## <a name="usage"></a> Usage
58
+ RabbitMQ Client can be used to publsih and subscribe RabbitMQ messages.
29
59
 
30
- TODO: Write usage instructions here
60
+ ### <a name="usage-configuration"></a> Configurations
61
+ RabbitMQ Client support the following configurations
31
62
 
32
- ## Development
63
+ | Configuration | Description | Default Value |
64
+ |------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------|
65
+ | rabbitmq_url | RabbitMQ server URL | amqp://guest:guest@127.0.0.1:5672 |
66
+ | logger_configs.logs_format | RabbitmqClient logs format.<br>values can be either json or plain | plain |
67
+ | logger_configs.logs_level | RabbitmqClient logs level.<br>values can be one of :debug, :info, :error | info |
68
+ | logger_configs.logs_filename | Logs file name, if nil STOUT will be used | nil |
69
+ | logger_configs.logger | Logger object, if nil STOUT logger will created | nil |
70
+ | session_params.heartbeat_publisher | Heartbeat interval for publisher sessions.<br>0 means no heartbeat | 0 |
71
+ | session_params.session_pool | Number of sessions with rabbitmq | 1 |
72
+ | plugins | Array of used plugins | [] |
73
+ | global_store | Global Store used to store tags and headers: <br>[RequestHeaderMiddleware](https://github.com/fidor/request_headers_middleware)<br>[RequestStore](https://github.com/steveklabnik/request_store) | nil |
74
+ | whitelist | List of whitelisted headers | ['x-request-id'.to_sym] |
33
75
 
34
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
76
+ #### Full Configuration Example
35
77
 
36
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
78
+ ```ruby
79
+ RabbitmqClient.configure do |config|
80
+ config.rabbitmq_url = "${rabbitmq_url}"
81
+ config.logger_configs = {
82
+ logs_format: 'plain',
83
+ logs_level: :info,
84
+ logs_filename: nil,
85
+ logger: nil
86
+ }
87
+ config.session_params = {
88
+ heartbeat_publisher: 0,
89
+ session_pool: 1
90
+ }
91
+ config.plugins = []
92
+ config.global_store = RequestHeaderMiddleware
93
+ config.whitelist = ['x-request-id'.to_sym]
94
+ end
95
+ ```
37
96
 
38
- ## Contributing
97
+ #### Append plungins or whitelist items
39
98
 
40
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rabbitmq_client. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
99
+ ```ruby
100
+ RabbitmqClient.plugins << MYRabbitmqClientPlugin
101
+ RabbitmqClient.whitelist << :white_listed_header_key
102
+ ```
41
103
 
42
- ## License
104
+ ### <a name="usage-publish-messages-to-rmq"></a> Publishing messages to RabbitMQ
105
+ - Publish messages using RabbitmqClient singleton publisher
43
106
 
44
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
107
+ ```ruby
108
+ # RabbitmqClient.add_exchange(exchange, type, options)
109
+ RabbitmqClient.add_exchange('default.rabbitmq_client', :topic, {})
110
+ # RabbitmqClient.publish(payload, options)
111
+ RabbitmqClient.publish({id: 10, name: 'rabbitmq_client'}, { exchange_name: 'default.rabbitmq_client' })
112
+ ```
113
+
114
+ - Create a publisher and use it
45
115
 
46
- ## Code of Conduct
116
+ ```ruby
117
+ config = {
118
+ rabbitmq_url: val,
119
+ exchange_registry: val,
120
+ session_params: { heartbeat_publisher: val }
121
+ }
47
122
 
48
- Everyone interacting in the RabbitmqClient project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/rabbitmq_client/blob/master/CODE_OF_CONDUCT.md).
123
+ publisher = RabbitmqClient::Publisher.new(config)
124
+ publisher.publish(payload, options)
125
+ ```
126
+
127
+ ## <a name="plugins"></a> Plugins
128
+ RabbitmqClient plugins are classes that define a callbacks that can be executed before or after events that occuers during RabbitmqClient lifecycle.
129
+ Current supprted lifecycle events are:
130
+ - publish: occure when publisgin a massage is triggered.
131
+
132
+ Here is an example where we define a plugin to add some headers to message options before publishing the message.
133
+
134
+ ```ruby
135
+ class MQTestPlugin < RabbitmqClient::Plugin
136
+ callbacks do |lifecycle|
137
+ lifecycle.before(:publish) do |_message, options|
138
+ options[:headers] = { test_key: 'test'}
139
+ end
140
+ end
141
+ end
142
+ RabbitmqClient.plugins << MQTestPlugin
143
+ ```
144
+ ## <a name="development"></a> Development and Testing
145
+
146
+ After checking out the repo:
147
+ * run `bin/setup` to install dependencies
148
+ * run `script/travis.sh` to run the tests.
149
+
150
+ You can also run `bin/console` for an interactive prompt that will allow you to experiment.
151
+
152
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
153
+
154
+ ## <a name="contributing"></a> Contributing
155
+
156
+ Bug reports and pull requests are welcome on GitHub at https://github.com/wshihadeh/rabbitmq_client.
157
+
158
+ ## <a name="license"></a> License
159
+
160
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/bin/setup ADDED
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+
3
+ gem install bundler
4
+ bundle install
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RabbitmqClient
4
+ # Custom error thrown when an unsupported callback type is used
5
+ class InvalidCallback < RuntimeError
6
+ def initialize(name)
7
+ super("The Callback '#{name}' is an invalid callback and cannot be used")
8
+ end
9
+ end
10
+
11
+ # Callback Object Store all plugins clallbacks
12
+ # Supported callback types are before and adter
13
+ class Callback
14
+ def initialize
15
+ @before = []
16
+ @after = []
17
+ end
18
+
19
+ def execute(*args, &block)
20
+ execute_before_callbacks(*args)
21
+ result = block.call(*args)
22
+ execute_after_callbacks(*args)
23
+ result
24
+ end
25
+
26
+ def add(type, &callback)
27
+ case type
28
+ when :before
29
+ @before << callback
30
+ when :after
31
+ @after << callback
32
+ else
33
+ raise InvalidCallback, "Invalid callback type: #{type}"
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def execute_before_callbacks(*args)
40
+ @before.each { |callback| callback.call(*args) }
41
+ end
42
+
43
+ def execute_after_callbacks(*args)
44
+ @after.each { |callback| callback.call(*args) }
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RabbitmqClient
4
+ # ExchangeRegistry is a store for all managed exchanges and their details
5
+ class Exchange
6
+ attr_reader :name, :type, :options
7
+
8
+ def initialize(name, type, options)
9
+ @name = name
10
+ @type = type
11
+ @options = options
12
+ end
13
+
14
+ def create(channel)
15
+ exhange_obj = Bunny::Exchange.new(channel, @type, @name,
16
+ @options)
17
+ ActiveSupport::Notifications.instrument(
18
+ 'created_exhange.rabbitmq_client',
19
+ name: @name,
20
+ type: @type
21
+ )
22
+ exhange_obj
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'exchange'
4
+
5
+ module RabbitmqClient
6
+ # ExchangeRegistry is a store for all managed exchanges and their details
7
+ class ExchangeRegistry
8
+ # Custom Eroor thrown when trying to find unkown exchange
9
+ class ExchangeNotFound < StandardError
10
+ def initialize(name)
11
+ super("The Exchange '#{name}' cannot be found")
12
+ end
13
+ end
14
+
15
+ def initialize
16
+ @exchanges = {}
17
+ end
18
+
19
+ def add(name, type, options = {})
20
+ @exchanges[name] = Exchange.new(name, type, options)
21
+ end
22
+
23
+ def find(name)
24
+ @exchanges.fetch(name) do
25
+ ActiveSupport::Notifications.instrument(
26
+ 'exhange_not_found.rabbitmq_client',
27
+ name: name
28
+ )
29
+ raise ExchangeNotFound, name
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module RabbitmqClient
6
+ # Formatter for json log messages
7
+ class JsonFormatter
8
+ TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%6N '
9
+ def initialize
10
+ @json = {}
11
+ @msg = ''
12
+ end
13
+
14
+ def call(severity, timestamp, progname, msg)
15
+ @json = build_new_json(msg)
16
+ @json = @json.merge(progname: progname.to_s, level: severity,
17
+ timestamp: timestamp.strftime(TIME_FORMAT))
18
+ @json = @json.reject { |_key, value| value.to_s.empty? }
19
+
20
+ @json.to_json + "\n"
21
+ end
22
+
23
+ def build_new_json(msg)
24
+ @msg = msg
25
+ @json = @msg.is_a?(Hash) ? @msg : { message: @msg.strip }
26
+ @json.merge(TagsFilter.tags || {})
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'log_subscriber_base'
4
+
5
+ module RabbitmqClient
6
+ # Manage RabbitmqClient plain text logs
7
+ class JsonLogSubscriber < LogSubscriberBase
8
+ def publisher_created(event)
9
+ debug(action: 'publisher_created',
10
+ message: 'The RabbitmqClient publisher is created',
11
+ publisher_configs: event.payload)
12
+ end
13
+
14
+ def network_error(event)
15
+ payload = event.payload
16
+ error({ action: 'network_error',
17
+ message: 'Failed to publish a message',
18
+ error_message: payload.fetch(:error).message }.merge(
19
+ process_payload(payload)
20
+ ))
21
+ end
22
+
23
+ def overriding_configs(event)
24
+ debug(action: 'overriding_configs',
25
+ message: 'Overriding publisher configs',
26
+ publisher_configs: event.payload)
27
+ end
28
+
29
+ def publishing_message(event)
30
+ debug({ action: 'publishing_message',
31
+ message: 'Publishing a new message' }.merge(
32
+ process_payload(event.payload)
33
+ ))
34
+ end
35
+
36
+ def published_message(event)
37
+ info({ action: 'published_message',
38
+ message: 'Published a message' }.merge(
39
+ process_payload(event.payload)
40
+ ))
41
+ end
42
+
43
+ def confirming_message(event)
44
+ debug({ action: 'confirming_message',
45
+ message: 'Confirming a message' }.merge(
46
+ process_payload(event.payload)
47
+ ))
48
+ end
49
+
50
+ def message_confirmed(event)
51
+ debug({ action: 'message_confirmed',
52
+ message: 'Confirmed a message' }.merge(
53
+ process_payload(event.payload)
54
+ ))
55
+ end
56
+
57
+ def exhange_not_found(event)
58
+ error(action: 'exhange_not_found',
59
+ message: 'Exhange Not Found',
60
+ exchange_name: event.payload.fetch(:name))
61
+ end
62
+
63
+ def created_exhange(event)
64
+ debug(action: 'created_exhange',
65
+ message: 'Exhange is created successfuly',
66
+ exchange_name: event.payload.fetch(:name))
67
+ end
68
+
69
+ private
70
+
71
+ %w[info debug warn error fatal unknown].each do |level|
72
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
73
+ def #{level}(progname = nil, &block)
74
+ logger.#{level} rabbitmq_client_event(progname, &block) if logger
75
+ end
76
+ METHOD
77
+ end
78
+
79
+ def rabbitmq_client_event(event)
80
+ { source: 'rabbitmq_client' }.merge(event)
81
+ end
82
+
83
+ def process_payload(payload)
84
+ {
85
+ exchange_name: payload.fetch(:exchange, 'undefined'),
86
+ message_id: payload.fetch(:message_id, 'undefined')
87
+ }
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'callback'
4
+
5
+ module RabbitmqClient
6
+ # Lifecycle defines the rabbitmq_client lifecycle events,
7
+ # callbacks and manage the execution of these callbacks
8
+ class Lifecycle
9
+ EVENTS = {
10
+ publish: %i[message options]
11
+ }.freeze
12
+
13
+ attr_reader :callbacks
14
+
15
+ def initialize
16
+ @callbacks = EVENTS.keys.each_with_object({}) do |key, hash|
17
+ hash[key] = Callback.new
18
+ end
19
+ end
20
+
21
+ def before(event, &block)
22
+ add(:before, event, &block)
23
+ end
24
+
25
+ def after(event, &block)
26
+ add(:after, event, &block)
27
+ end
28
+
29
+ def run_callbacks(event, *args, &block)
30
+ missing_callback(event) unless @callbacks.key?(event)
31
+ event_obj = EVENTS[event]
32
+ event_size = event_obj.size
33
+
34
+ unless event_size == args.size
35
+ raise ArgumentError, "Callback #{event} expects\
36
+ #{event_size} parameter(s): #{event_obj.join(', ')}"
37
+ end
38
+
39
+ @callbacks[event].execute(*args, &block)
40
+ end
41
+
42
+ private
43
+
44
+ def add(type, event, &block)
45
+ missing_callback(event) unless @callbacks.key?(event)
46
+ @callbacks[event].add(type, &block)
47
+ end
48
+
49
+ def missing_callback(event)
50
+ raise InvalidCallback, "Unknown callback event: #{event}"
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RabbitmqClient
4
+ # Log Subscriber base class
5
+ class LogSubscriberBase < ActiveSupport::LogSubscriber
6
+ class << self
7
+ def logger
8
+ @logger ||= RabbitmqClient.logger
9
+ end
10
+ end
11
+
12
+ def logger
13
+ LogSubscriberBase.logger
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RabbitmqClient
4
+ # ExchangeRegistry is a store for all managed exchanges and their details
5
+ class LoggerBuilder
6
+ def initialize(config)
7
+ @logger = config[:logger].clone
8
+ @format = config[:logs_format]
9
+ @level = config[:logs_level].to_sym
10
+ @filename = config[:logs_filename]
11
+ end
12
+
13
+ def build_logger
14
+ @logger ||= ::Logger.new(@filename || STDOUT)
15
+ @logger.level = @level
16
+ @logger.formatter = create_logger_formatter
17
+ log_subscriber.attach_to(:rabbitmq_client)
18
+ @logger
19
+ end
20
+
21
+ private
22
+
23
+ def create_logger_formatter
24
+ json? ? JsonFormatter.new : TextFormatter.new
25
+ end
26
+
27
+ def log_subscriber
28
+ json? ? JsonLogSubscriber : PlainLogSubscriber
29
+ end
30
+
31
+ def json?
32
+ __method__.to_s == "#{@format}?"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RabbitmqClient
4
+ # ExchangeRegistry is a store for all managed exchanges and their details
5
+ class MessagePublisher
6
+ # Custom error is thrown when rabbitmq do not confirm publishing an event
7
+ class ConfirmationFailed < StandardError
8
+ def initialize(exchange, nacked, unconfirmed)
9
+ msg = 'Message confirmation on the exchange ' \
10
+ "#{exchange} has failed (#{nacked}/#{unconfirmed})."
11
+ super(msg)
12
+ end
13
+ end
14
+
15
+ def initialize(data, exchange, channel, options)
16
+ @data = data.to_json
17
+ @exchange = exchange
18
+ @channel = channel
19
+ @options = { headers: {} }.merge(options)
20
+ @options[:headers][:tags] = TagsFilter.tags
21
+ end
22
+
23
+ def publish
24
+ exchange = @exchange.create(@channel)
25
+
26
+ notify('publishing_message')
27
+ exchange.publish(@data, **@options)
28
+ notify('published_message')
29
+ end
30
+
31
+ def wait_for_confirms
32
+ notify('confirming_message')
33
+ if @channel.wait_for_confirms
34
+ notify('message_confirmed')
35
+ return
36
+ end
37
+ raise ConfirmationFailed.new(@exchange.name, @channel.nacked_set,
38
+ @channel.unconfirmed_set)
39
+ end
40
+
41
+ private
42
+
43
+ def notify(event)
44
+ ActiveSupport::Notifications.instrument(
45
+ "#{event}.rabbitmq_client",
46
+ message_payload
47
+ )
48
+ end
49
+
50
+ def message_payload
51
+ {
52
+ exchange: @exchange.name,
53
+ message_id: @options[:message_id]
54
+ }
55
+ end
56
+ end
57
+ end