harmoniser 0.12.0 → 0.14.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9feb1146437db2d23bb56ef1a72e47ef0912dbbc02b776c2ef026db44dc687e
4
- data.tar.gz: 79dfe110ea228ba5ae58062c575768c8cc5747228a7a4a5432c36a686da7113d
3
+ metadata.gz: 3a7c3ac5b97ffe50dee2438808e152133893642092fee47a74e49f31e5440686
4
+ data.tar.gz: 156dbce5e8ba12711085e55d2a2b6f823259946d0df42d6cccd7bd29aeba0139
5
5
  SHA512:
6
- metadata.gz: 47e94a951a8200b1e2ce2287f4fad5d4dda4ac6d4bf4d4e21a7f0bb03a51c8d95a949f0ef8dccc1f873558741ece21a0de89198d0d4325228869b0ad985d098a
7
- data.tar.gz: d7d11b42621ce764702a408a3c1f789326cef84dfa15ad24d6f8c84ba5b2cd28ce4f4ca1496fd38bfc1aedb1a0d89381adf19825c76ef6d310389173782a03bd
6
+ metadata.gz: 3c14b5e163b0d3863ae1e25db3dc7aaec5ac6debacfe6cb0148066b5154156619b3692a7ea1e72d03d27e407a6366ad93c879476e04e606f864522ba56ffec59
7
+ data.tar.gz: 546204550ed925fac2e177240ec3266a25d61d81d9034019f76653588af7141c797be2ed42d576000cb7de72699843333b59cfafdb3d50a80a7a31a9596e1172
data/CHANGELOG.md CHANGED
@@ -1,5 +1,54 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.14.0] - 2025-11-25
4
+
5
+ ### Added
6
+ - Introduce Mock functionality for testing without RabbitMQ dependency. Usage:
7
+
8
+ ```ruby
9
+ require "harmoniser/mock"
10
+ Harmoniser::Mock.mock!
11
+
12
+ # Your Publisher/Subscriber code works the same but without RabbitMQ
13
+ class TestPublisher
14
+ include Harmoniser::Publisher
15
+ harmoniser_publisher exchange_name: "my_exchange"
16
+ end
17
+
18
+ exchange = TestPublisher.publish("message", routing_key: "test")
19
+ published_messages = exchange.published_messages # Access captured messages
20
+ ```
21
+
22
+ - Add examples for mock publishing and topology under `examples/` folder
23
+ - Create Channel wrapper (`Harmoniser::Channel`) to isolate RabbitMQ dependencies
24
+
25
+ ### Changed
26
+ - Remove explicit dependencies to Bunny classes except for Consumer, enabling better RabbitMQ isolation through a single, well-defined interface
27
+ - Use Channel wrapper everywhere except for subscriptions (which still require actual Channel instance for Bunny::Consumer)
28
+
29
+
30
+ ## [0.13.0] - 2025-05-19
31
+
32
+ ### Added
33
+ - Introduce Harmoniser.configuration#on_error callback to handle errors that occur while running the application, (e.g. when a subscriber raises an error, or when a connection cannot be established to RabbitMQ). This method permits appending as many handlers as needed. Usage:
34
+
35
+ ```ruby
36
+ Harmoniser.configuration do |config|
37
+ config.on_error do |error, ctx|
38
+ # error is the exception, i.e. StandardError class raised by the application whereas ctx is the context of the error passed as Hash. The ctx hash contains at least the key `description` which is a string describing the error.
39
+
40
+ puts "An error occurred: exception = `#{error.detailed_message}`; description = `#{ctx[:description]}`"
41
+ end
42
+ end
43
+ ```
44
+
45
+ ### Changed
46
+ - Update Bunny to the latest version, i.e., 2.24
47
+ - Delegate into Bunny::Channel to cancel consumers and close channel through
48
+ Channel#cancel_consumers_before_closing!
49
+ - Change severity level from error to warning for errors occurring at channel level
50
+
51
+
3
52
  ## [0.12.0] - 2024-12-22
4
53
 
5
54
  ### Changed
data/harmoniser.gemspec CHANGED
@@ -24,5 +24,5 @@ Gem::Specification.new do |spec|
24
24
  spec.executables = ["harmoniser"]
25
25
  spec.require_paths = ["lib"]
26
26
 
27
- spec.add_runtime_dependency "bunny", "~> 2.23"
27
+ spec.add_runtime_dependency "bunny", "~> 2.24"
28
28
  end
@@ -0,0 +1,66 @@
1
+ require "forwardable"
2
+
3
+ module Harmoniser
4
+ class Channel
5
+ extend Forwardable
6
+
7
+ def_delegators :@bunny_channel,
8
+ :exchange,
9
+ :queue,
10
+ :queue_bind
11
+
12
+ attr_reader :bunny_channel
13
+
14
+ def initialize(bunny_channel)
15
+ @bunny_channel = bunny_channel
16
+ after_initialize
17
+ end
18
+
19
+ private
20
+
21
+ def after_initialize
22
+ bunny_channel.cancel_consumers_before_closing!
23
+ attach_callbacks
24
+ end
25
+
26
+ def attach_callbacks
27
+ bunny_channel.on_error(&method(:on_error_callback).to_proc)
28
+ bunny_channel.on_uncaught_exception(&method(:on_uncaught_exception_callback).to_proc)
29
+ end
30
+
31
+ def on_error_callback(channel, amq_method)
32
+ attributes = {
33
+ amq_method: amq_method,
34
+ exchanges: channel.exchanges.keys,
35
+ queues: channel.consumers.values.map(&:queue)
36
+ }
37
+
38
+ if amq_method.is_a?(AMQ::Protocol::Channel::Close)
39
+ attributes[:reply_code] = amq_method.reply_code
40
+ attributes[:reply_text] = amq_method.reply_text
41
+ end
42
+
43
+ stringified_attributes = attributes.map { |k, v| "#{k} = `#{v}`" }.join(", ")
44
+ Harmoniser.logger.warn("Default on_error handler executed for channel: #{stringified_attributes}")
45
+ maybe_kill_process(amq_method)
46
+ end
47
+
48
+ def on_uncaught_exception_callback(error, consumer)
49
+ handle_error(error, {description: "Uncaught exception from consumer", arguments: consumer.arguments, channel_id: consumer.channel.id, consumer_tag: consumer.consumer_tag, exclusive: consumer.exclusive, no_ack: consumer.no_ack, queue: consumer.queue})
50
+ end
51
+
52
+ def maybe_kill_process(amq_method)
53
+ Process.kill("USR1", Process.pid) if ack_timed_out?(amq_method) && Harmoniser.server?
54
+ end
55
+
56
+ def ack_timed_out?(amq_method)
57
+ return false unless amq_method.is_a?(AMQ::Protocol::Channel::Close)
58
+
59
+ amq_method.reply_text =~ /delivery acknowledgement on channel \d+ timed out/
60
+ end
61
+
62
+ def handle_error(exception, ctx)
63
+ Harmoniser.configuration.handle_error(exception, ctx)
64
+ end
65
+ end
66
+ end
@@ -1,5 +1,6 @@
1
1
  require "forwardable"
2
2
  require "harmoniser/connection"
3
+ require "harmoniser/error_handler"
3
4
  require "harmoniser/topology"
4
5
  require "harmoniser/options"
5
6
 
@@ -7,14 +8,16 @@ module Harmoniser
7
8
  class Configuration
8
9
  extend Forwardable
9
10
 
10
- attr_reader :logger, :options
11
+ attr_reader :error_handler, :logger, :options
11
12
  def_delegators :options, :concurrency, :environment, :require, :verbose, :timeout
13
+ def_delegators :error_handler, :handle_error, :on_error
12
14
 
13
15
  def initialize
14
16
  @logger = Harmoniser.logger
15
- @options = Options.new(**default_options)
17
+ @options = Options.new
16
18
  set_logger_severity
17
19
  @topology = Topology.new
20
+ @error_handler = ErrorHandler.default
18
21
  end
19
22
 
20
23
  def connection_opts
@@ -40,16 +43,6 @@ module Harmoniser
40
43
 
41
44
  private
42
45
 
43
- def default_options
44
- {
45
- concurrency: Float::INFINITY,
46
- environment: ENV.fetch("RAILS_ENV", ENV.fetch("RACK_ENV", "production")),
47
- require: ".",
48
- timeout: 25,
49
- verbose: false
50
- }
51
- end
52
-
53
46
  def set_logger_severity
54
47
  @logger.level = @options.verbose? ? Logger::DEBUG : Logger::INFO
55
48
  end
@@ -1,4 +1,5 @@
1
1
  require "harmoniser/connection"
2
+ require "harmoniser/channel"
2
3
 
3
4
  module Harmoniser
4
5
  module Connectable
@@ -7,7 +8,7 @@ module Harmoniser
7
8
  module ClassMethods
8
9
  def connection(configuration = Harmoniser.configuration)
9
10
  MUTEX.synchronize do
10
- @connection ||= Connection.new(configuration.connection_opts)
11
+ @connection ||= Connection.new(configuration.connection_opts, error_handler: configuration.error_handler)
11
12
  @connection.start unless @connection.open? || @connection.recovering_from_network_failure?
12
13
  @connection
13
14
  end
@@ -20,47 +21,7 @@ module Harmoniser
20
21
  def create_channel(consumer_pool_size: 1, consumer_pool_shutdown_timeout: 60)
21
22
  connection
22
23
  .create_channel(nil, consumer_pool_size, false, consumer_pool_shutdown_timeout)
23
- .tap do |channel|
24
- attach_callbacks(channel)
25
- end
26
- end
27
-
28
- private
29
-
30
- def attach_callbacks(channel)
31
- channel.on_error(&method(:on_error).to_proc)
32
- channel.on_uncaught_exception(&method(:on_uncaught_exception).to_proc)
33
- end
34
-
35
- def on_error(channel, amq_method)
36
- attributes = {
37
- amq_method: amq_method,
38
- exchanges: channel.exchanges.keys,
39
- queues: channel.consumers.values.map(&:queue)
40
- }
41
-
42
- if amq_method.is_a?(AMQ::Protocol::Channel::Close)
43
- attributes[:reply_code] = amq_method.reply_code
44
- attributes[:reply_text] = amq_method.reply_text
45
- end
46
-
47
- stringified_attributes = attributes.map { |k, v| "#{k} = `#{v}`" }.join(", ")
48
- Harmoniser.logger.error("Default on_error handler executed for channel: #{stringified_attributes}")
49
- maybe_kill_process(amq_method)
50
- end
51
-
52
- def on_uncaught_exception(error, consumer)
53
- Harmoniser.logger.error("Default on_uncaught_exception handler executed for channel: error_class = `#{error.class}`, error_message = `#{error.message}`, error_backtrace = `#{error.backtrace&.first(5)}, queue = `#{consumer.queue}`")
54
- end
55
-
56
- def maybe_kill_process(amq_method)
57
- Process.kill("USR1", Process.pid) if ack_timed_out?(amq_method) && Harmoniser.server?
58
- end
59
-
60
- def ack_timed_out?(amq_method)
61
- return false unless amq_method.is_a?(AMQ::Protocol::Channel::Close)
62
-
63
- amq_method.reply_text =~ /delivery acknowledgement on channel \d+ timed out/
24
+ .yield_self { |bunny_channel| Channel.new(bunny_channel) }
64
25
  end
65
26
  end
66
27
 
@@ -1,5 +1,6 @@
1
1
  require "forwardable"
2
2
  require "bunny"
3
+ require "harmoniser/error_handler"
3
4
 
4
5
  module Harmoniser
5
6
  class Connection
@@ -21,7 +22,8 @@ module Harmoniser
21
22
 
22
23
  def_delegators :@bunny, :create_channel, :open?, :recovering_from_network_failure?
23
24
 
24
- def initialize(opts, logger: Harmoniser.logger)
25
+ def initialize(opts, error_handler: ErrorHandler.default, logger: Harmoniser.logger)
26
+ @error_handler = error_handler
25
27
  @logger = logger
26
28
  @bunny = Bunny.new(maybe_dynamic_opts(opts)).tap do |bunny|
27
29
  attach_callbacks(bunny)
@@ -43,7 +45,7 @@ module Harmoniser
43
45
  begin
44
46
  @bunny.start
45
47
  rescue => e
46
- @logger.error("Connection attempt failed: retries = `#{retries}`, error_class = `#{e.class}`, error_message = `#{e.message}`")
48
+ handle_error(e, {description: "Connection attempt failed", retries: retries})
47
49
  sleep(1)
48
50
  retries += 1
49
51
  retry
@@ -56,7 +58,7 @@ module Harmoniser
56
58
  @logger.info("Connection closed: connection = `#{self}`")
57
59
  end
58
60
  rescue => e
59
- @logger.error("Connection#close failed: error_class = `#{e.class}`, error_message = `#{e.message}`")
61
+ handle_error(e, {description: "Connection close failed"})
60
62
  false
61
63
  end
62
64
 
@@ -102,5 +104,9 @@ module Harmoniser
102
104
  def vhost
103
105
  @bunny.vhost
104
106
  end
107
+
108
+ def handle_error(exception, ctx)
109
+ @error_handler.handle_error(exception, ctx)
110
+ end
105
111
  end
106
112
  end
@@ -0,0 +1,51 @@
1
+ module Harmoniser
2
+ class ErrorHandler
3
+ TIMEOUT = 25
4
+ DEFAULT_ERROR_HANDLER = ->(exception, ctx) do
5
+ Harmoniser.logger.error("Error handler called: exception = `#{exception.detailed_message}`, context = `#{ctx}`")
6
+ end
7
+
8
+ class << self
9
+ def default
10
+ @default ||= new([DEFAULT_ERROR_HANDLER])
11
+ end
12
+ end
13
+
14
+ def initialize(handlers = [])
15
+ @handlers = handlers
16
+ end
17
+
18
+ def on_error(handler = nil, &block)
19
+ h = handler || block
20
+ raise ArgumentError, "Please, provide a handler or a block" if h.nil?
21
+ raise TypeError, "Handler must respond to call" unless h.respond_to?(:call)
22
+
23
+ @handlers << h
24
+ self
25
+ end
26
+
27
+ def handle_error(exception, ctx = {})
28
+ coordinator = Thread::Queue.new
29
+
30
+ Thread.new do
31
+ handlers
32
+ .each { |handler| trigger_handler(handler, exception, ctx) }
33
+ .tap { coordinator.push(true) }
34
+ end.tap do |thread|
35
+ thread.report_on_exception = true
36
+ end
37
+
38
+ !!coordinator.pop(timeout: TIMEOUT)
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :handlers
44
+
45
+ def trigger_handler(handler, exception, ctx)
46
+ handler.call(exception, ctx)
47
+ rescue => e
48
+ warn "An error occurred while handling a previous error: #{e.detailed_message}"
49
+ end
50
+ end
51
+ end
@@ -61,15 +61,13 @@ module Harmoniser
61
61
  def maybe_close_subscriber
62
62
  return unless Subscriber.connection?
63
63
 
64
- maybe_cancel_subscribers
64
+ report_subscribers_to_cancel
65
65
  report_work_pool
66
66
  Subscriber.connection.close
67
67
  end
68
68
 
69
- def maybe_cancel_subscribers
69
+ def report_subscribers_to_cancel
70
70
  @logger.info("Subscribers will be cancelled from queues: klasses = `#{@subscribers}`")
71
- @subscribers.each(&:harmoniser_subscriber_stop)
72
- @logger.info("Subscribers cancelled: klasses = `#{@subscribers}`")
73
71
  end
74
72
 
75
73
  def report_work_pool
@@ -12,7 +12,7 @@ module Harmoniser
12
12
  end
13
13
 
14
14
  def channel
15
- @channel ||= Subscriber.create_channel(consumer_pool_size: @configuration.concurrency, consumer_pool_shutdown_timeout: @configuration.timeout)
15
+ @channel ||= Subscriber.create_channel(consumer_pool_size: @configuration.concurrency, consumer_pool_shutdown_timeout: @configuration.timeout).bunny_channel
16
16
  end
17
17
  end
18
18
  end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Harmoniser
4
+ module Mock
5
+ class Channel
6
+ class MockExchange
7
+ attr_reader :name, :type, :opts
8
+
9
+ def initialize(name, opts = {})
10
+ @name = name
11
+ @type = opts[:type]
12
+ @opts = opts.except(:type)
13
+ @published_messages = []
14
+ @return_handler = nil
15
+ end
16
+
17
+ def publish(payload, opts = {})
18
+ @published_messages << {payload: payload, opts: opts}
19
+ true
20
+ end
21
+
22
+ def on_return(&block)
23
+ @return_handler = block
24
+ self
25
+ end
26
+
27
+ def published_messages
28
+ @published_messages.dup
29
+ end
30
+
31
+ def reset!
32
+ @published_messages.clear
33
+ @return_handler = nil
34
+ end
35
+ end
36
+
37
+ class MockQueue
38
+ attr_reader :name, :opts
39
+
40
+ def initialize(name, opts = {})
41
+ @name = name
42
+ @opts = opts
43
+ end
44
+ end
45
+
46
+ def initialize
47
+ @exchanges = {}
48
+ @queues = {}
49
+ @bindings = []
50
+ end
51
+
52
+ def exchange(name, opts = {})
53
+ @exchanges[name] ||= MockExchange.new(name, opts)
54
+ end
55
+
56
+ def queue(name, opts = {})
57
+ @queues[name] ||= MockQueue.new(name, opts)
58
+ end
59
+
60
+ def queue_bind(destination_name, exchange_name, opts = {})
61
+ @bindings << {
62
+ destination_name: destination_name,
63
+ exchange_name: exchange_name,
64
+ opts: opts
65
+ }
66
+ true
67
+ end
68
+
69
+ def exchanges
70
+ @exchanges.dup
71
+ end
72
+
73
+ def queues
74
+ @queues.dup
75
+ end
76
+
77
+ def bindings
78
+ @bindings.dup
79
+ end
80
+
81
+ def reset!
82
+ @exchanges.clear
83
+ @queues.clear
84
+ @bindings.clear
85
+ end
86
+
87
+ def bunny_channel
88
+ raise "Cannot access bunny_channel in mock mode. Mock mode is intended for testing only and cannot be used when running Harmoniser as a server process."
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "harmoniser/mock/channel"
4
+
5
+ module Harmoniser
6
+ module Mock
7
+ class Connection
8
+ def initialize(opts = {}, error_handler: nil, logger: nil)
9
+ @opts = opts
10
+ @error_handler = error_handler
11
+ @logger = logger
12
+ @open = false
13
+ end
14
+
15
+ def create_channel(id = nil, consumer_pool_size = 1, consumer_pool_ack = false, consumer_pool_shutdown_timeout = 60)
16
+ Channel.new
17
+ end
18
+
19
+ def open?
20
+ @open
21
+ end
22
+
23
+ def recovering_from_network_failure?
24
+ false
25
+ end
26
+
27
+ def start
28
+ @open = true
29
+ self
30
+ end
31
+
32
+ def close
33
+ @open = false
34
+ true
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "harmoniser/mock/connection"
4
+ require "harmoniser/mock/channel"
5
+ require "harmoniser/connectable"
6
+
7
+ module Harmoniser
8
+ module Mock
9
+ @mocked = false
10
+ @prepended = false
11
+
12
+ class << self
13
+ def mock!
14
+ unless @prepended
15
+ @prepended = true
16
+ Harmoniser::Connectable::ClassMethods.prepend(MockConnectableMethods)
17
+ end
18
+ @mocked = true
19
+ end
20
+
21
+ def disable!
22
+ @mocked = false
23
+ end
24
+
25
+ def mocked?
26
+ @mocked
27
+ end
28
+
29
+ def disabled?
30
+ !@mocked
31
+ end
32
+ end
33
+
34
+ module MockConnectableMethods
35
+ def connection(configuration = Harmoniser.configuration)
36
+ return super unless Harmoniser::Mock.mocked?
37
+
38
+ Harmoniser::Connectable::MUTEX.synchronize do
39
+ @mock_connection ||= Harmoniser::Mock::Connection.new(configuration.connection_opts, error_handler: configuration.error_handler)
40
+ @mock_connection.start unless @mock_connection.open? || @mock_connection.recovering_from_network_failure?
41
+ @mock_connection
42
+ end
43
+ end
44
+
45
+ def connection?
46
+ return super unless Harmoniser::Mock.mocked?
47
+ !!defined?(@mock_connection)
48
+ end
49
+
50
+ def create_channel(consumer_pool_size: 1, consumer_pool_shutdown_timeout: 60)
51
+ return super unless Harmoniser::Mock.mocked?
52
+ connection.create_channel(nil, consumer_pool_size, false, consumer_pool_shutdown_timeout)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,5 +1,21 @@
1
1
  module Harmoniser
2
- Options = Data.define(:concurrency, :environment, :require, :verbose, :timeout) do
2
+ class Options < Data.define(:concurrency, :environment, :require, :verbose, :timeout)
3
+ DEFAULT = {
4
+ concurrency: -> { Float::INFINITY },
5
+ environment: -> { ENV.fetch("RAILS_ENV", ENV.fetch("RACK_ENV", "production")) },
6
+ require: -> { "." },
7
+ timeout: -> { 25 },
8
+ verbose: -> { false }
9
+ }.freeze
10
+
11
+ def initialize(**kwargs)
12
+ options = DEFAULT
13
+ .map { |k, v| [k, v.call] }
14
+ .to_h
15
+ .merge(kwargs)
16
+ super(**options)
17
+ end
18
+
3
19
  def production?
4
20
  environment == "production"
5
21
  end
@@ -33,12 +33,12 @@ module Harmoniser
33
33
  end
34
34
 
35
35
  def create_exchange
36
- exchange = Bunny::Exchange.new(
37
- Publisher.create_channel,
38
- @harmoniser_exchange_definition.type,
39
- @harmoniser_exchange_definition.name,
40
- @harmoniser_exchange_definition.opts
41
- )
36
+ exchange = Publisher
37
+ .create_channel
38
+ .exchange(
39
+ @harmoniser_exchange_definition.name,
40
+ {type: @harmoniser_exchange_definition.type}.merge(@harmoniser_exchange_definition.opts)
41
+ )
42
42
  handle_return(exchange)
43
43
  exchange
44
44
  end
@@ -41,7 +41,7 @@ module Harmoniser
41
41
  def create_consumer(channel)
42
42
  raise_missing_consumer_definition unless @harmoniser_consumer_definition
43
43
 
44
- ch = channel || Subscriber.create_channel
44
+ ch = channel || Subscriber.create_channel.bunny_channel
45
45
  consumer = Bunny::Consumer.new(
46
46
  ch,
47
47
  @harmoniser_consumer_definition.queue_name,
@@ -5,7 +5,7 @@ module Harmoniser
5
5
  class Topology
6
6
  include Connectable
7
7
 
8
- attr_reader :bindings, :exchanges, :queues
8
+ attr_reader :bindings, :exchanges, :queues, :declared_channel
9
9
 
10
10
  def initialize
11
11
  @bindings = Set.new
@@ -41,25 +41,26 @@ module Harmoniser
41
41
  end
42
42
 
43
43
  def declare
44
- self.class.create_channel.tap do |ch|
44
+ @declared_channel = self.class.create_channel.tap do |ch|
45
45
  declare_exchanges(ch)
46
46
  declare_queues(ch)
47
47
  declare_bindings(ch)
48
- ch.connection.close
49
48
  end
49
+ self.class.connection.close
50
+ self
50
51
  end
51
52
 
52
53
  private
53
54
 
54
55
  def declare_exchanges(channel)
55
56
  exchanges.each do |exchange|
56
- Bunny::Exchange.new(channel, exchange.type, exchange.name, exchange.opts)
57
+ channel.exchange(exchange.name, {type: exchange.type}.merge(exchange.opts))
57
58
  end
58
59
  end
59
60
 
60
61
  def declare_queues(channel)
61
62
  queues.each do |queue|
62
- Bunny::Queue.new(channel, queue.name, queue.opts)
63
+ channel.queue(queue.name, queue.opts)
63
64
  end
64
65
  end
65
66
 
@@ -1,3 +1,3 @@
1
1
  module Harmoniser
2
- VERSION = "0.12.0"
2
+ VERSION = "0.14.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: harmoniser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jose Lloret
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-22 00:00:00.000000000 Z
11
+ date: 2025-11-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bunny
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.23'
19
+ version: '2.24'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.23'
26
+ version: '2.24'
27
27
  description: A declarative approach to communication with RabbitMQ that simplifies
28
28
  the integration of publishing and consuming messages.
29
29
  email:
@@ -42,17 +42,22 @@ files:
42
42
  - bin/setup
43
43
  - harmoniser.gemspec
44
44
  - lib/harmoniser.rb
45
+ - lib/harmoniser/channel.rb
45
46
  - lib/harmoniser/cli.rb
46
47
  - lib/harmoniser/configurable.rb
47
48
  - lib/harmoniser/configuration.rb
48
49
  - lib/harmoniser/connectable.rb
49
50
  - lib/harmoniser/connection.rb
50
51
  - lib/harmoniser/definition.rb
52
+ - lib/harmoniser/error_handler.rb
51
53
  - lib/harmoniser/launcher.rb
52
54
  - lib/harmoniser/launcher/base.rb
53
55
  - lib/harmoniser/launcher/bounded.rb
54
56
  - lib/harmoniser/launcher/unbounded.rb
55
57
  - lib/harmoniser/loggable.rb
58
+ - lib/harmoniser/mock.rb
59
+ - lib/harmoniser/mock/channel.rb
60
+ - lib/harmoniser/mock/connection.rb
56
61
  - lib/harmoniser/options.rb
57
62
  - lib/harmoniser/parser.rb
58
63
  - lib/harmoniser/publisher.rb
@@ -68,7 +73,7 @@ metadata:
68
73
  homepage_uri: https://github.com/jollopre/harmoniser
69
74
  source_code_uri: https://github.com/jollopre/harmoniser
70
75
  changelog_uri: https://github.com/jollopre/harmoniser/blob/master/CHANGELOG.md
71
- post_install_message:
76
+ post_install_message:
72
77
  rdoc_options: []
73
78
  require_paths:
74
79
  - lib
@@ -84,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
89
  version: '0'
85
90
  requirements: []
86
91
  rubygems_version: 3.4.10
87
- signing_key:
92
+ signing_key:
88
93
  specification_version: 4
89
94
  summary: A minimalistic approach to interact with RabbitMQ
90
95
  test_files: []