bug_bunny 3.0.4 → 3.0.6

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: 4752202964e5f4382a685a356a31264db3ae51049c42a49d3e5a6602fda10ed1
4
- data.tar.gz: 1cb2f414e1fb52560d9a4310fd0679c1a59f86dc072fce6dd385b17006ecc6a0
3
+ metadata.gz: 0a8a31414070c751adbf073b7588e31d866ed7f1047abfbadad6de2ae75e411e
4
+ data.tar.gz: 438a1f9d2a2d3cd6f012f16a5c1349c42f20c87be1cb76212a841c528004bdfd
5
5
  SHA512:
6
- metadata.gz: 74af2e635f79d182cfe0fc336a67bf5175ad02729e39b5ddb77fb6bfdde4e3e733f863c78e453160ca6e03ff381dc81f1169a1c9d7be39a116d971638f61d846
7
- data.tar.gz: c7da63dcce1cc2f0b77faa253327ddb4eec88866328429e84383172e07be402ff9d9c2ca6a2fbe16e9c0f7762bd8119c17496caa37a4ad35d0be07867e8aa78c
6
+ metadata.gz: 130ae01d89317ad8ecf2c9bb832d4ce235f6eb23df903e256ea47a5669bc2b300239b41beabcbb6a2e8dc44a674ac3eaa90cb8fab0bc44037c377e1503317022
7
+ data.tar.gz: 603106d7e627b086624d88914601b6cdeb465162116cb6821ca519b5a75c20b7700a0a7ee578e44ef3b311784f69bc17efea1e73a7d32403e5c19efb6b21836a
data/CHANGELOG.md CHANGED
@@ -1,4 +1,21 @@
1
1
  # Changelog
2
+ ## [3.0.6] - 2026-02-17
3
+
4
+ ### ♻️ Refactor & Standards
5
+ * **Architectural Cleanup:** Removed the `BugBunny::Rabbit` intermediate class. Connection management logic (`disconnect`) has been moved directly to the main `BugBunny` module for simplicity.
6
+ * **Rails Standardization:** Renamed `lib/bug_bunny/config.rb` to `lib/bug_bunny/configuration.rb` and the class from `Config` to `Configuration`. This ensures full compliance with Zeitwerk autoloading standards.
7
+
8
+ ### 🛡 Stability
9
+ * **Fork Safety:** Enhanced `Railtie` to robustly handle process forking. Added support for `ActiveSupport::ForkTracker` (Rails 7.1+) and guarded Puma event hooks to prevent `NoMethodError` on newer Puma versions.
10
+
11
+ ## [3.0.5] - 2026-02-17
12
+
13
+ ### 🐛 Bug Fixes
14
+ * **Load Error Resolution:** Fixed `TypeError: Middleware is not a module` by converting `BugBunny::Middleware` into a proper module and introducing `BugBunny::Middleware::Base`.
15
+
16
+ ### 🛠 Improvements
17
+ * **Standardized Middleware Architecture:** Consolidated the Template Method pattern across all internal interceptors (`RaiseError`, `JsonResponse`).
18
+
2
19
  ## [3.0.4] - 2026-02-16
3
20
 
4
21
  ### ♻️ Refactoring & Architecture
data/bin_worker.rb CHANGED
@@ -1,20 +1,26 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # bin_worker.rb
2
- require_relative 'test_helper'
3
- require_relative 'test_controller'
4
+ require 'bundler/setup'
5
+ require 'bug_bunny'
6
+ require_relative 'test_controller' # Cargamos los controladores
7
+
8
+ puts '🐰 WORKER INICIADO (Exchange: Topic)...'
4
9
 
5
- puts "🐰 WORKER INICIADO (Exchange: Topic)..."
10
+ # Configuración básica
11
+ BugBunny.configure do |config|
12
+ config.logger = Logger.new($stdout)
13
+ config.logger.level = Logger::DEBUG
14
+ end
6
15
 
7
- # Creamos la conexión (o usamos una del pool si quisieras)
16
+ # Iniciar el Consumidor
17
+ # Escucha en la cola 'bug_bunny_queue', atada al exchange 'test_exchange' con routing key '#' (todo)
8
18
  connection = BugBunny.create_connection
9
19
 
10
- # Usamos el método de clase directo.
11
- # Al no pasar 'block: false', esto bloqueará la ejecución aquí mismo eternamente.
12
20
  BugBunny::Consumer.subscribe(
13
21
  connection: connection,
14
- queue_name: 'test_users_queue',
15
22
  exchange_name: 'test_exchange',
16
23
  exchange_type: 'topic',
17
- routing_key: 'test_user.#'
24
+ queue_name: 'bug_bunny_test_queue',
25
+ routing_key: '#' # Wildcard para recibir todo en este test
18
26
  )
19
-
20
- # ¡Ya no necesitas el loop! El subscribe mantiene vivo el proceso.
@@ -1,19 +1,17 @@
1
- # lib/bug_bunny/config.rb
1
+ # frozen_string_literal: true
2
+
2
3
  require 'logger'
3
4
 
4
5
  module BugBunny
5
6
  # Clase de configuración global para la gema BugBunny.
6
7
  # Almacena las credenciales de conexión, timeouts y parámetros de ajuste de RabbitMQ.
7
- #
8
- # @example Configuración típica
9
- # BugBunny.configure do |config|
10
- # config.host = 'rabbit.local'
11
- # config.rpc_timeout = 5
12
- # end
13
- class Config
8
+ class Configuration
14
9
  # @return [String] Host o IP del servidor RabbitMQ (ej: 'localhost').
15
10
  attr_accessor :host
16
11
 
12
+ # @return [Integer] Puerto del servidor RabbitMQ (default: 5672).
13
+ attr_accessor :port
14
+
17
15
  # @return [String] Usuario para la autenticación (default: 'guest').
18
16
  attr_accessor :username
19
17
 
@@ -26,48 +24,51 @@ module BugBunny
26
24
  # @return [Logger] Instancia del logger para depuración (default: Logger a STDOUT).
27
25
  attr_accessor :logger
28
26
 
29
- # @return [Logger] Logger específico para el driver Bunny (Conexión, Heartbeats, Frames).
30
- # Se recomienda nivel WARN para evitar ruido.
27
+ # @return [Logger] Logger específico para el driver Bunny.
31
28
  attr_accessor :bunny_logger
32
29
 
33
- # @return [Boolean] Si `true`, Bunny intentará reconectar automáticamente ante fallos de red (default: true).
30
+ # @return [Boolean] Si `true`, Bunny intentará reconectar automáticamente.
34
31
  attr_accessor :automatically_recover
35
32
 
36
- # @return [Integer] Tiempo en segundos a esperar antes de intentar reconectar (default: 5).
33
+ # @return [Integer] Tiempo en segundos a esperar antes de intentar reconectar.
37
34
  attr_accessor :network_recovery_interval
38
35
 
39
- # @return [Integer] Timeout en segundos para establecer la conexión TCP inicial (default: 10).
36
+ # @return [Integer] Timeout en segundos para establecer la conexión TCP inicial.
40
37
  attr_accessor :connection_timeout
41
38
 
42
- # @return [Integer] Timeout en segundos para leer datos del socket TCP (default: 30).
39
+ # @return [Integer] Timeout en segundos para leer datos del socket TCP.
43
40
  attr_accessor :read_timeout
44
41
 
45
- # @return [Integer] Timeout en segundos para escribir datos en el socket TCP (default: 30).
42
+ # @return [Integer] Timeout en segundos para escribir datos en el socket TCP.
46
43
  attr_accessor :write_timeout
47
44
 
48
- # @return [Integer] Intervalo en segundos para enviar latidos (heartbeats) y mantener la conexión viva (default: 15).
45
+ # @return [Integer] Intervalo en segundos para enviar latidos (heartbeats).
49
46
  attr_accessor :heartbeat
50
47
 
51
- # @return [Integer] Timeout en milisegundos para operaciones de continuación RPC internas de Bunny (default: 15_000).
48
+ # @return [Integer] Timeout en milisegundos para operaciones de continuación RPC internas.
52
49
  attr_accessor :continuation_timeout
53
50
 
54
- # @return [Integer] Cantidad de mensajes que el consumidor pre-cargará antes de procesarlos (QoS) (default: 1).
51
+ # @return [Integer] Cantidad de mensajes que el consumidor pre-cargará (QoS).
55
52
  attr_accessor :channel_prefetch
56
53
 
57
- # @return [Integer] Tiempo máximo en segundos que el cliente esperará una respuesta RPC antes de lanzar {BugBunny::RequestTimeout} (default: 10).
54
+ # @return [Integer] Tiempo máximo en segundos que el cliente esperará una respuesta RPC.
58
55
  attr_accessor :rpc_timeout
59
56
 
60
- # @return [Integer] Intervalo en segundos para verificar que la cola del consumidor sigue existiendo (default: 60).
57
+ # @return [Integer] Intervalo en segundos para verificar la salud de la cola.
61
58
  attr_accessor :health_check_interval
62
59
 
63
60
  # Inicializa la configuración con valores por defecto seguros.
64
61
  def initialize
65
- # Logger de la Aplicación (BugBunny) -> INFO (Ves tus requests)
66
- @logger = Logger.new(STDOUT)
62
+ @host = '127.0.0.1' # Valor por defecto explícito
63
+ @port = 5672 # <--- AGREGADO (Default RabbitMQ Port)
64
+ @username = 'guest'
65
+ @password = 'guest'
66
+ @vhost = '/'
67
+
68
+ @logger = Logger.new($stdout)
67
69
  @logger.level = Logger::INFO
68
70
 
69
- # Logger del Driver (Bunny) -> WARN (Oculta el ruido de "handle_frame")
70
- @bunny_logger = Logger.new(STDOUT)
71
+ @bunny_logger = Logger.new($stdout)
71
72
  @bunny_logger.level = Logger::WARN
72
73
  @automatically_recover = true
73
74
  @network_recovery_interval = 5
@@ -82,11 +83,8 @@ module BugBunny
82
83
  end
83
84
 
84
85
  # Construye la URL de conexión AMQP basada en los atributos configurados.
85
- # Útil para logs o herramientas externas.
86
- #
87
- # @return [String] La cadena de conexión en formato `amqp://user:pass@host/vhost`.
88
86
  def url
89
- "amqp://#{username}:#{password}@#{host}/#{vhost}"
87
+ "amqp://#{username}:#{password}@#{host}:#{port}/#{vhost}"
90
88
  end
91
89
  end
92
90
  end
@@ -0,0 +1,46 @@
1
+ # lib/bug_bunny/middleware.rb
2
+ # frozen_string_literal: true
3
+
4
+ module BugBunny
5
+ module Middleware
6
+ # Clase base para todos los middlewares de BugBunny.
7
+ #
8
+ # Implementa el patrón "Template Method" para estandarizar el flujo de ejecución
9
+ # de la cadena de responsabilidades (Ida y Vuelta).
10
+ #
11
+ # Las subclases deben implementar:
12
+ # * {#on_request} para modificar la petición antes de enviarla.
13
+ # * {#on_complete} para modificar la respuesta después de recibirla.
14
+ #
15
+ # @abstract Subclase y anula {#on_request} o {#on_complete} para inyectar lógica.
16
+ class Base
17
+ # @return [Object] El siguiente middleware en la pila o el adaptador final.
18
+ attr_reader :app
19
+
20
+ # Inicializa el middleware.
21
+ #
22
+ # @param app [Object] El siguiente eslabón de la cadena.
23
+ def initialize(app)
24
+ @app = app
25
+ end
26
+
27
+ # Ejecuta el middleware orquestando los hooks de ciclo de vida.
28
+ #
29
+ # 1. Llama a {#on_request} (Ida).
30
+ # 2. Llama al siguiente eslabón (`@app.call`).
31
+ # 3. Llama a {#on_complete} (Vuelta).
32
+ #
33
+ # @param env [BugBunny::Request] El objeto request (entorno).
34
+ # @return [Hash] La respuesta final procesada.
35
+ def call(env)
36
+ on_request(env) if respond_to?(:on_request)
37
+
38
+ response = @app.call(env)
39
+
40
+ on_complete(response) if respond_to?(:on_complete)
41
+
42
+ response
43
+ end
44
+ end
45
+ end
46
+ end
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'json'
5
- require_relative '../middleware'
5
+ require_relative '../middleware/base'
6
6
 
7
7
  module BugBunny
8
8
  module Middleware
@@ -11,8 +11,8 @@ module BugBunny
11
11
  # Convierte strings JSON en Hashes de Ruby. Si está disponible ActiveSupport,
12
12
  # aplica HashWithIndifferentAccess.
13
13
  #
14
- # @see BugBunny::Middleware
15
- class JsonResponse < BugBunny::Middleware
14
+ # @see BugBunny::Middleware::Base
15
+ class JsonResponse < BugBunny::Middleware::Base
16
16
  # Hook de ciclo de vida: Ejecutado después de recibir la respuesta.
17
17
  #
18
18
  # Intercepta el body y lo reemplaza por su versión parseada.
@@ -1,15 +1,15 @@
1
1
  # lib/bug_bunny/middleware/raise_error.rb
2
2
  # frozen_string_literal: true
3
3
 
4
- require_relative '../middleware'
4
+ require_relative '../middleware/base'
5
5
 
6
6
  module BugBunny
7
7
  module Middleware
8
8
  # Middleware que inspecciona el status de la respuesta y lanza excepciones
9
9
  # si se encuentran errores (4xx o 5xx).
10
10
  #
11
- # @see BugBunny::Middleware
12
- class RaiseError < BugBunny::Middleware
11
+ # @see BugBunny::Middleware::Base
12
+ class RaiseError < BugBunny::Middleware::Base
13
13
  # Hook de ciclo de vida: Ejecutado después de recibir la respuesta.
14
14
  #
15
15
  # Verifica el código de estado y lanza la excepción correspondiente.
@@ -1,4 +1,5 @@
1
- # lib/bug_bunny/railtie.rb
1
+ # frozen_string_literal: true
2
+
2
3
  require 'rails'
3
4
 
4
5
  module BugBunny
@@ -11,11 +12,7 @@ module BugBunny
11
12
  # @see https://guides.rubyonrails.org/engines.html#railtie
12
13
  class Railtie < ::Rails::Railtie
13
14
  # 1. Configuración de Autoload
14
- #
15
- # Agrega el directorio `app/rabbit` a los paths de carga automática.
16
- # Esto permite que Rails encuentre automáticamente los controladores definidos por el usuario
17
- # (ej: `Rabbit::Controllers::Users`) sin necesidad de `require` manuales.
18
- initializer "bug_bunny.add_autoload_paths" do |app|
15
+ initializer 'bug_bunny.add_autoload_paths' do |app|
19
16
  rabbit_path = File.join(app.root, 'app', 'rabbit')
20
17
  if Dir.exist?(rabbit_path)
21
18
  app.config.autoload_paths << rabbit_path
@@ -23,31 +20,29 @@ module BugBunny
23
20
  end
24
21
  end
25
22
 
26
- # 2. Hook de Puma (Servidor Web)
23
+ # 2. Gestión de Forks (Puma / Spring / otros)
27
24
  #
28
- # Detecta cuando Puma arranca un nuevo "worker" en modo clúster.
29
25
  # Es vital cerrar la conexión heredada del proceso padre (Master) antes de que
30
26
  # el hijo empiece a trabajar, para evitar compartir el mismo socket TCP.
31
- #
32
- # La nueva conexión se creará perezosamente (Lazy) cuando el worker la necesite.
33
27
  config.after_initialize do
34
- if defined?(Puma)
28
+ # Estrategia 1: Rails 7.1+ ForkTracker (La forma estándar moderna)
29
+ if defined?(ActiveSupport::ForkTracker)
30
+ ActiveSupport::ForkTracker.after_fork { BugBunny.disconnect }
31
+ end
32
+
33
+ # Estrategia 2: Hook específico de Puma (Legacy)
34
+ # Solo intentamos usarlo si la API 'events' está disponible (Puma < 5).
35
+ if defined?(Puma) && Puma.respond_to?(:events)
35
36
  Puma.events.on_worker_boot do
36
- BugBunny::Rabbit.disconnect
37
+ BugBunny.disconnect
37
38
  end
38
39
  end
39
40
  end
40
41
 
41
42
  # 3. Hook de Spring (Preloader)
42
- #
43
- # Spring mantiene una instancia de la aplicación en memoria y hace `fork` para
44
- # ejecutar comandos (rails c, rspec, etc) rápidamente.
45
- #
46
- # Al igual que con Puma, debemos desconectar la conexión al RabbitMQ justo después
47
- # del fork para asegurar que el nuevo proceso tenga su propio socket limpio.
48
43
  if defined?(Spring)
49
44
  Spring.after_fork do
50
- BugBunny::Rabbit.disconnect
45
+ BugBunny.disconnect
51
46
  end
52
47
  end
53
48
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BugBunny
4
- VERSION = "3.0.4"
4
+ VERSION = "3.0.6"
5
5
  end
data/lib/bug_bunny.rb CHANGED
@@ -1,114 +1,104 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bunny'
2
4
  require 'logger'
3
- require 'connection_pool'
4
-
5
5
  require_relative 'bug_bunny/version'
6
- require_relative 'bug_bunny/config'
7
6
  require_relative 'bug_bunny/exception'
8
- require_relative 'bug_bunny/request'
7
+ require_relative 'bug_bunny/configuration'
8
+ require_relative 'bug_bunny/middleware/base'
9
+ require_relative 'bug_bunny/middleware/stack'
10
+ require_relative 'bug_bunny/middleware/raise_error'
11
+ require_relative 'bug_bunny/middleware/json_response'
12
+ require_relative 'bug_bunny/client'
9
13
  require_relative 'bug_bunny/session'
14
+ require_relative 'bug_bunny/consumer'
15
+ require_relative 'bug_bunny/request'
10
16
  require_relative 'bug_bunny/producer'
11
- require_relative 'bug_bunny/client'
12
17
  require_relative 'bug_bunny/resource'
13
- require_relative 'bug_bunny/rabbit'
14
- require_relative 'bug_bunny/consumer'
15
18
  require_relative 'bug_bunny/controller'
16
- require_relative 'bug_bunny/middleware'
17
-
18
- require_relative 'bug_bunny/middleware/stack'
19
- require_relative 'bug_bunny/middleware/raise_error'
20
- require_relative 'bug_bunny/middleware/json_response'
19
+ require_relative 'bug_bunny/railtie' if defined?(Rails)
21
20
 
22
- # Punto de entrada principal y Namespace de la gema BugBunny.
23
- #
24
- # BugBunny es un framework ligero sobre RabbitMQ diseñado para simplificar
25
- # patrones de mensajería (RPC y Fire-and-Forget) en aplicaciones Ruby on Rails.
26
- #
27
- # @see BugBunny::Client Para enviar mensajes.
28
- # @see BugBunny::Resource Para mapear modelos remotos.
29
- # @see BugBunny::Consumer Para procesar mensajes entrantes.
21
+ # Módulo principal de la gema BugBunny.
22
+ # Actúa como espacio de nombres y punto de configuración global.
30
23
  module BugBunny
31
- # Factory method (Alias) para instanciar un nuevo Cliente.
32
- #
33
- # @param args [Hash] Argumentos pasados al constructor de {BugBunny::Client}.
34
- # @return [BugBunny::Client] Una nueva instancia del cliente.
35
- def self.new(**args)
36
- BugBunny::Client.new(**args)
24
+ class << self
25
+ # @return [BugBunny::Configuration] La configuración global actual.
26
+ attr_accessor :configuration
27
+
28
+ # @return [Bunny::Session, nil] La conexión global (Singleton) usada por procesos Rails.
29
+ attr_accessor :global_connection
37
30
  end
38
31
 
39
- # Configura la librería globalmente.
40
- #
41
- # @example Configuración típica en un initializer
42
- # BugBunny.configure do |config|
43
- # config.host = 'localhost'
44
- # config.username = 'guest'
45
- # config.rpc_timeout = 5
46
- # end
32
+ # Configura la librería BugBunny.
33
+ # Si no se ha configurado previamente, inicializa una nueva configuración por defecto.
47
34
  #
48
- # @yield [config] Bloque de configuración.
49
- # @yieldparam config [BugBunny::Config] Objeto de configuración global.
50
- # @return [BugBunny::Config] La configuración actualizada.
35
+ # @yieldparam config [BugBunny::Configuration] El objeto de configuración para modificar.
36
+ # @return [void]
51
37
  def self.configure
52
- self.configuration ||= Config.new
38
+ self.configuration ||= Configuration.new
53
39
  yield(configuration)
54
40
  end
55
41
 
56
- # Accesor al objeto de configuración global (Singleton).
42
+ # Crea e inicia una nueva conexión a RabbitMQ utilizando la gema Bunny.
43
+ # Mezcla las opciones pasadas explícitamente con la configuración global por defecto.
44
+ #
45
+ # @param options [Hash] Opciones de conexión que sobrescriben la configuración global.
46
+ # @option options [String] :host ('127.0.0.1') Host del servidor RabbitMQ.
47
+ # @option options [Integer] :port (5672) Puerto del servidor.
48
+ # @option options [String] :username ('guest') Usuario de conexión.
49
+ # @option options [String] :password ('guest') Contraseña.
50
+ # @option options [String] :vhost ('/') Virtual Host.
51
+ # @option options [Logger] :logger Logger para la conexión interna de Bunny.
52
+ # @option options [Boolean] :automatically_recover (true) Si debe reconectar automáticamente.
53
+ # @option options [Integer] :connection_timeout (10) Tiempo de espera para conectar.
54
+ # @option options [Integer] :read_timeout (10) Tiempo de espera para lectura.
55
+ # @option options [Integer] :write_timeout (10) Tiempo de espera para escritura.
56
+ # @option options [Integer] :heartbeat (15) Intervalo de latidos en segundos.
57
+ # @option options [Integer] :continuation_timeout (15000) Timeout para operaciones RPC internas.
57
58
  #
58
- # @return [BugBunny::Config] La instancia de configuración actual.
59
- def self.configuration
60
- @configuration ||= Config.new
59
+ # @return [Bunny::Session] Una sesión de Bunny ya iniciada (`start` ya invocado).
60
+ # @raise [Bunny::TCPConnectionFailed] Si no se puede conectar al servidor.
61
+ def self.create_connection(**options)
62
+ conn_options = merge_connection_options(options)
63
+ Bunny.new(conn_options).tap(&:start)
61
64
  end
62
65
 
63
- # Cierra la conexión global mantenida por {BugBunny::Rabbit}.
64
- # Útil para liberar recursos en scripts o tareas Rake al finalizar.
66
+ # Cierra la conexión global si existe.
67
+ #
68
+ # Este método es utilizado principalmente por el Railtie para asegurar que
69
+ # los procesos hijos (forks) de servidores como Puma o Spring no hereden
70
+ # la conexión TCP del proceso padre, forzando una reconexión limpia ("Lazy").
65
71
  #
66
- # @see BugBunny::Rabbit.disconnect
67
72
  # @return [void]
68
73
  def self.disconnect
69
- BugBunny::Rabbit.disconnect
70
- end
74
+ return unless @global_connection
71
75
 
72
- # Crea una nueva conexión a RabbitMQ (Bunny Session).
73
- #
74
- # Este método fusiona la configuración global por defecto con las opciones
75
- # pasadas explícitamente como argumentos, dando prioridad a estas últimas.
76
- #
77
- # Maneja automáticamente el inicio de la conexión (`start`) y captura errores
78
- # de red comunes envolviéndolos en excepciones de BugBunny.
79
- #
80
- # @param options [Hash] Opciones de conexión que sobrescriben la configuración global.
81
- # @option options [String] :host Host de RabbitMQ.
82
- # @option options [String] :vhost Virtual Host.
83
- # @option options [String] :username Usuario.
84
- # @option options [String] :password Contraseña.
85
- # @option options [Logger] :logger Logger personalizado.
86
- # @option options [Boolean] :automatically_recover (true/false).
87
- # @option options [Integer] :network_recovery_interval Intervalo de reconexión.
88
- # @option options [Integer] :connection_timeout Timeout de conexión TCP.
89
- # @return [Bunny::Session] Una sesión de Bunny iniciada y lista para usar.
90
- # @raise [BugBunny::CommunicationError] Si no se puede establecer la conexión TCP.
91
- def self.create_connection(**options)
92
- default = configuration
76
+ @global_connection.close if @global_connection.open?
77
+ @global_connection = nil
78
+ configuration.logger.info('[BugBunny] Global connection closed.')
79
+ end
93
80
 
94
- bunny = Bunny.new(
95
- host: options[:host] || default.host,
96
- username: options[:username] || default.username,
97
- password: options[:password] || default.password,
98
- vhost: options[:vhost] || default.vhost,
99
- logger: options[:logger] || default.bunny_logger,
100
- automatically_recover: options[:automatically_recover] || default.automatically_recover,
101
- network_recovery_interval: options[:network_recovery_interval] || default.network_recovery_interval,
102
- connection_timeout: options[:connection_timeout] || default.connection_timeout,
103
- read_timeout: options[:read_timeout] || default.read_timeout,
104
- write_timeout: options[:write_timeout] || default.write_timeout,
105
- heartbeat: options[:heartbeat] || default.heartbeat,
106
- continuation_timeout: options[:continuation_timeout] || default.continuation_timeout
107
- )
81
+ # @api private
82
+ # Fusiona las opciones del usuario con los valores por defecto de la configuración.
83
+ def self.merge_connection_options(options)
84
+ # .compact elimina los valores nil de options para no sobrescribir los defaults
85
+ default_connection_options.merge(options.compact)
86
+ end
108
87
 
109
- bunny.start
110
- bunny
111
- rescue Timeout::Error, Bunny::ConnectionError => e
112
- raise BugBunny::CommunicationError, e.message
88
+ # @api private
89
+ # Genera el hash de opciones por defecto basado en la configuración global.
90
+ # Extraído para reducir la métrica AbcSize de merge_connection_options.
91
+ def self.default_connection_options
92
+ cfg = configuration || Configuration.new
93
+ {
94
+ host: cfg.host, port: cfg.port,
95
+ username: cfg.username, password: cfg.password, vhost: cfg.vhost,
96
+ logger: cfg.bunny_logger, automatically_recover: cfg.automatically_recover,
97
+ connection_timeout: cfg.connection_timeout, read_timeout: cfg.read_timeout,
98
+ write_timeout: cfg.write_timeout, heartbeat: cfg.heartbeat,
99
+ continuation_timeout: cfg.continuation_timeout
100
+ }
113
101
  end
102
+
103
+ private_class_method :merge_connection_options, :default_connection_options
114
104
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bug_bunny
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.4
4
+ version: 3.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - gabix
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-02-16 00:00:00.000000000 Z
11
+ date: 2026-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bunny
@@ -196,16 +196,15 @@ files:
196
196
  - initializer_example.rb
197
197
  - lib/bug_bunny.rb
198
198
  - lib/bug_bunny/client.rb
199
- - lib/bug_bunny/config.rb
199
+ - lib/bug_bunny/configuration.rb
200
200
  - lib/bug_bunny/consumer.rb
201
201
  - lib/bug_bunny/controller.rb
202
202
  - lib/bug_bunny/exception.rb
203
- - lib/bug_bunny/middleware.rb
203
+ - lib/bug_bunny/middleware/base.rb
204
204
  - lib/bug_bunny/middleware/json_response.rb
205
205
  - lib/bug_bunny/middleware/raise_error.rb
206
206
  - lib/bug_bunny/middleware/stack.rb
207
207
  - lib/bug_bunny/producer.rb
208
- - lib/bug_bunny/rabbit.rb
209
208
  - lib/bug_bunny/railtie.rb
210
209
  - lib/bug_bunny/request.rb
211
210
  - lib/bug_bunny/resource.rb
@@ -1,44 +0,0 @@
1
- # lib/bug_bunny/middleware.rb
2
- # frozen_string_literal: true
3
-
4
- module BugBunny
5
- # Clase base para todos los middlewares de BugBunny.
6
- #
7
- # Implementa el patrón "Template Method" para estandarizar el flujo de ejecución
8
- # de la cadena de responsabilidades (Ida y Vuelta).
9
- #
10
- # Las subclases deben implementar:
11
- # * {#on_request} para modificar la petición antes de enviarla.
12
- # * {#on_complete} para modificar la respuesta después de recibirla.
13
- #
14
- # @abstract Subclase y anula {#on_request} o {#on_complete} para inyectar lógica.
15
- class Middleware
16
- # @return [Object] El siguiente middleware en la pila o el adaptador final.
17
- attr_reader :app
18
-
19
- # Inicializa el middleware.
20
- #
21
- # @param app [Object] El siguiente eslabón de la cadena.
22
- def initialize(app)
23
- @app = app
24
- end
25
-
26
- # Ejecuta el middleware orquestando los hooks de ciclo de vida.
27
- #
28
- # 1. Llama a {#on_request} (Ida).
29
- # 2. Llama al siguiente eslabón (`@app.call`).
30
- # 3. Llama a {#on_complete} (Vuelta).
31
- #
32
- # @param env [BugBunny::Request] El objeto request (entorno).
33
- # @return [Hash] La respuesta final procesada.
34
- def call(env)
35
- on_request(env) if respond_to?(:on_request)
36
-
37
- response = @app.call(env)
38
-
39
- on_complete(response) if respond_to?(:on_complete)
40
-
41
- response
42
- end
43
- end
44
- end
@@ -1,82 +0,0 @@
1
- # lib/bug_bunny/rabbit.rb
2
-
3
- module BugBunny
4
- # Clase de soporte e infraestructura para gestionar la conexión global de la aplicación.
5
- #
6
- # Actúa como un Singleton que mantiene una instancia de `Bunny::Session`.
7
- # Es utilizada principalmente por tareas administrativas (Rake tasks), inicializadores
8
- # y para lanzar consumidores (Workers).
9
- #
10
- # @note ¡IMPORTANTE!
11
- # No utilizar esta clase para publicar mensajes desde controladores o servicios web.
12
- # Esta clase mantiene una sola conexión TCP. Para publicar en entornos concurrentes (Puma/Sidekiq),
13
- # se debe utilizar siempre {BugBunny::Client} inyectándole un `ConnectionPool`.
14
- class Rabbit
15
- class << self
16
- # Permite inyectar una conexión manualmente (útil para tests).
17
- # @api private
18
- attr_writer :connection
19
-
20
- # Obtiene la conexión global actual (Singleton).
21
- #
22
- # Implementa "Lazy Initialization": si la conexión no existe o está cerrada,
23
- # crea una nueva automáticamente.
24
- #
25
- # @return [Bunny::Session] La sesión cruda de Bunny.
26
- def connection
27
- return @connection if @connection&.open?
28
-
29
- @connection = create_connection
30
- end
31
-
32
- # Crea una nueva conexión utilizando la configuración global de la gema.
33
- # Delega la creación al factory principal.
34
- #
35
- # @see BugBunny.create_connection
36
- # @return [Bunny::Session] Una nueva sesión iniciada.
37
- def create_connection
38
- BugBunny.create_connection
39
- end
40
-
41
- # Cierra la conexión global de forma segura.
42
- #
43
- # Es un método idempotente: verifica si la conexión existe y está abierta antes de intentar cerrarla.
44
- # Utilizado comúnmente en los hooks de `at_exit`, o por Railties al recargar código en Spring/Puma.
45
- #
46
- # @return [void]
47
- def disconnect
48
- return unless @connection
49
-
50
- @connection.close if @connection.open?
51
- @connection = nil
52
- BugBunny.configuration.logger.info("[BugBunny] Global connection closed.")
53
- end
54
-
55
- # Helper de conveniencia para instanciar y ejecutar un Consumidor (Worker).
56
- #
57
- # Simplifica el arranque de workers desde tareas Rake, encapsulando la creación
58
- # de la instancia {BugBunny::Consumer} y la suscripción.
59
- #
60
- # @param connection [Bunny::Session] Conexión dedicada para el consumidor (no usar la compartida si es posible).
61
- # @param exchange [String] Nombre del exchange.
62
- # @param exchange_type [String] Tipo de exchange ('topic', 'direct', etc).
63
- # @param queue_name [String] Nombre de la cola.
64
- # @param routing_key [String] Routing key para el binding.
65
- # @param queue_opts [Hash] Opciones adicionales para la cola (:durable, etc).
66
- # @return [void] Este método suele bloquear la ejecución (loop infinito del consumidor).
67
- def run_consumer(connection:, exchange:, exchange_type:, queue_name:, routing_key:, queue_opts: {})
68
- # 1. Instanciamos el Consumidor (que crea su propia Session interna)
69
- consumer = BugBunny::Consumer.new(connection)
70
-
71
- # 2. Iniciamos la suscripción (Bloqueante por defecto)
72
- consumer.subscribe(
73
- queue_name: queue_name,
74
- exchange_name: exchange,
75
- exchange_type: exchange_type,
76
- routing_key: routing_key,
77
- queue_opts: queue_opts
78
- )
79
- end
80
- end
81
- end
82
- end