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 +4 -4
- data/CHANGELOG.md +17 -0
- data/bin_worker.rb +16 -10
- data/lib/bug_bunny/{config.rb → configuration.rb} +26 -28
- data/lib/bug_bunny/middleware/base.rb +46 -0
- data/lib/bug_bunny/middleware/json_response.rb +3 -3
- data/lib/bug_bunny/middleware/raise_error.rb +3 -3
- data/lib/bug_bunny/railtie.rb +14 -19
- data/lib/bug_bunny/version.rb +1 -1
- data/lib/bug_bunny.rb +76 -86
- metadata +4 -5
- data/lib/bug_bunny/middleware.rb +0 -44
- data/lib/bug_bunny/rabbit.rb +0 -82
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0a8a31414070c751adbf073b7588e31d866ed7f1047abfbadad6de2ae75e411e
|
|
4
|
+
data.tar.gz: 438a1f9d2a2d3cd6f012f16a5c1349c42f20c87be1cb76212a841c528004bdfd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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)
|
|
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
|
|
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á
|
|
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
|
|
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
|
|
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
|
-
|
|
66
|
-
@
|
|
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
|
-
|
|
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.
|
data/lib/bug_bunny/railtie.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
#
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
45
|
+
BugBunny.disconnect
|
|
51
46
|
end
|
|
52
47
|
end
|
|
53
48
|
end
|
data/lib/bug_bunny/version.rb
CHANGED
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/
|
|
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/
|
|
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
|
-
#
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
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
|
-
# @
|
|
49
|
-
# @
|
|
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 ||=
|
|
38
|
+
self.configuration ||= Configuration.new
|
|
53
39
|
yield(configuration)
|
|
54
40
|
end
|
|
55
41
|
|
|
56
|
-
#
|
|
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 [
|
|
59
|
-
|
|
60
|
-
|
|
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
|
|
64
|
-
#
|
|
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
|
-
|
|
70
|
-
end
|
|
74
|
+
return unless @global_connection
|
|
71
75
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
+
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-
|
|
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/
|
|
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
|
data/lib/bug_bunny/middleware.rb
DELETED
|
@@ -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
|
data/lib/bug_bunny/rabbit.rb
DELETED
|
@@ -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
|