rabbitmq_client 0.0.0.pre → 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.
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