bug_bunny 4.0.1 → 4.1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/README.md +20 -0
- data/lib/bug_bunny/client.rb +27 -4
- data/lib/bug_bunny/consumer.rb +8 -2
- data/lib/bug_bunny/controller.rb +7 -12
- data/lib/bug_bunny/request.rb +3 -1
- data/lib/bug_bunny/resource.rb +17 -2
- data/lib/bug_bunny/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 599004cdcb498ca746612f60202301ce9783a67ac37d14ecd4c29f51ccaf48bb
|
|
4
|
+
data.tar.gz: f748ba9a55e91f96a5571f671afa524f7e330cceede0f12c5c7cef6d25d2d2ff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7df1503a55cb62d9f87168a84b8e92fe773b4c9525b143d6f848227e66fe63e5ab75f6191a408b00bb9c8840b44c0f8fdbc36cd0a8d2eb56e02018d09654d269
|
|
7
|
+
data.tar.gz: d5433d839c45ad95a2d0399535ff629112458e90d0e4e1ec89f97afa450b139bcb7855c64c27042c22bce0bde87b2bfa7bd6f8bae5b879dd7eb0c71ff1d20d3c
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
|
+
|
|
3
|
+
## [4.1.1] - 2026-03-22
|
|
4
|
+
|
|
5
|
+
### 🐛 Bug Fixes
|
|
6
|
+
* **Consumer:** Previene memory leak al detener el `TimerTask` de health check previo antes de realizar una reconexión.
|
|
7
|
+
* **Controller:** Corrige la mutación accidental de \`log_tags\` globales al usar una lógica de herencia no destructiva en \`compute_tags\`.
|
|
8
|
+
|
|
9
|
+
### ✨ Improvements
|
|
10
|
+
* **Controller:** Ahora lanza una excepción \`BugBunny::BadRequest\` (400) si el cuerpo de la petición contiene un JSON inválido, mejorando la depuración en el cliente.
|
|
11
|
+
* **Resource:** Se añadió una protección a \`.with\` (\`ScopeProxy\`) para asegurar que el contexto sea de un solo uso, evitando efectos secundarios en llamadas encadenadas.
|
|
12
|
+
|
|
13
|
+
## [4.1.0] - 2026-03-22
|
|
14
|
+
|
|
15
|
+
### 🚀 New Features & Improvements
|
|
16
|
+
* **Faraday-style Client API:** Se introdujo el método \`Client#send\` como punto de entrada genérico, permitiendo una sintaxis más familiar y flexible.
|
|
17
|
+
* **Flexible Delivery Modes:** Introducción del atributo \`delivery_mode\` (:rpc o :publish). Ahora es posible configurar la estrategia de envÃo a nivel de cliente o por cada petición individual.
|
|
18
|
+
* **Smart Request Defaults:** Los métodos \`request\` y \`publish\` ahora delegan internamente en \`send\`, manteniendo la compatibilidad pero beneficiándose de la nueva arquitectura de peticiones.
|
|
19
|
+
|
|
2
20
|
## [4.0.1] - 2026-03-13
|
|
3
21
|
|
|
4
22
|
### 🐛 Bug Fixes
|
data/README.md
CHANGED
|
@@ -203,6 +203,26 @@ client = BugBunny::Client.new(pool: BUG_BUNNY_POOL) do |stack|
|
|
|
203
203
|
stack.use BugBunny::Middleware::JsonResponse
|
|
204
204
|
end
|
|
205
205
|
|
|
206
|
+
# 1. Método genérico 'send' (Estilo Faraday)
|
|
207
|
+
# El comportamiento (RPC o Fire-and-forget) depende de 'delivery_mode'
|
|
208
|
+
client.delivery_mode = :rpc # Default
|
|
209
|
+
client.send('users/1', method: :get)
|
|
210
|
+
|
|
211
|
+
# 2. Configuración flexible del modo de entrega
|
|
212
|
+
# Por cada petición
|
|
213
|
+
client.send('logs', method: :post, body: { msg: 'system_up' }, delivery_mode: :publish)
|
|
214
|
+
|
|
215
|
+
# O mediante un bloque para configuración avanzada
|
|
216
|
+
client.send('users/1') do |req|
|
|
217
|
+
req.method = :get
|
|
218
|
+
req.delivery_mode = :rpc
|
|
219
|
+
req.timeout = 5
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# 3. Métodos de conveniencia (Atajos)
|
|
223
|
+
client.request('users/1') # Siempre :rpc
|
|
224
|
+
client.publish('events', body: { type: 'click' }) # Siempre :publish
|
|
225
|
+
|
|
206
226
|
# Ahora el cliente devolverá Hashes y lanzará errores si el worker falla
|
|
207
227
|
response = client.request('users/1', method: :get)
|
|
208
228
|
```
|
data/lib/bug_bunny/client.rb
CHANGED
|
@@ -21,6 +21,9 @@ module BugBunny
|
|
|
21
21
|
# @return [BugBunny::Middleware::Stack] La pila de middlewares configurada.
|
|
22
22
|
attr_reader :stack
|
|
23
23
|
|
|
24
|
+
# @return [Symbol] El modo de entrega por defecto para este cliente (:rpc o :publish).
|
|
25
|
+
attr_accessor :delivery_mode
|
|
26
|
+
|
|
24
27
|
# Inicializa un nuevo cliente.
|
|
25
28
|
#
|
|
26
29
|
# @param pool [ConnectionPool] Pool de conexiones a RabbitMQ configurado previamente.
|
|
@@ -30,9 +33,22 @@ module BugBunny
|
|
|
30
33
|
raise ArgumentError, "BugBunny::Client requiere un 'pool:'" if pool.nil?
|
|
31
34
|
@pool = pool
|
|
32
35
|
@stack = BugBunny::Middleware::Stack.new
|
|
36
|
+
@delivery_mode = :rpc
|
|
33
37
|
yield(@stack) if block_given?
|
|
34
38
|
end
|
|
35
39
|
|
|
40
|
+
# Realiza una petición general al estilo Faraday.
|
|
41
|
+
# El comportamiento (RPC o Fire-and-forget) depende de {#delivery_mode}.
|
|
42
|
+
#
|
|
43
|
+
# @param url [String] La ruta del recurso.
|
|
44
|
+
# @param args [Hash] Opciones de configuración.
|
|
45
|
+
# @yield [req] Bloque para configurar el objeto Request directamente.
|
|
46
|
+
def send(url, **args)
|
|
47
|
+
run_in_pool(url, args) do |req|
|
|
48
|
+
yield req if block_given?
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
36
52
|
# Realiza una petición Síncrona (RPC / Request-Response).
|
|
37
53
|
#
|
|
38
54
|
# Envía un mensaje y bloquea la ejecución del hilo actual hasta recibir respuesta.
|
|
@@ -48,7 +64,8 @@ module BugBunny
|
|
|
48
64
|
# @yield [req] Bloque para configurar el objeto Request directamente.
|
|
49
65
|
# @return [Hash] La respuesta del servidor.
|
|
50
66
|
def request(url, **args)
|
|
51
|
-
|
|
67
|
+
send(url, **args) do |req|
|
|
68
|
+
req.delivery_mode = :rpc
|
|
52
69
|
yield req if block_given?
|
|
53
70
|
end
|
|
54
71
|
end
|
|
@@ -60,7 +77,8 @@ module BugBunny
|
|
|
60
77
|
# @yield [req] Bloque para configurar el objeto Request.
|
|
61
78
|
# @return [void]
|
|
62
79
|
def publish(url, **args)
|
|
63
|
-
|
|
80
|
+
send(url, **args) do |req|
|
|
81
|
+
req.delivery_mode = :publish
|
|
64
82
|
yield req if block_given?
|
|
65
83
|
end
|
|
66
84
|
end
|
|
@@ -70,15 +88,16 @@ module BugBunny
|
|
|
70
88
|
# Ejecuta la lógica de envío dentro del contexto del Pool.
|
|
71
89
|
# Mapea los argumentos al objeto Request y ejecuta la cadena de middlewares.
|
|
72
90
|
#
|
|
73
|
-
# @param method_name [Symbol] El método del productor a llamar (:rpc o :fire).
|
|
74
91
|
# @param url [String] La ruta destino.
|
|
75
92
|
# @param args [Hash] Argumentos pasados a los métodos públicos.
|
|
76
93
|
# @yield [req] Bloque para configuración adicional del Request.
|
|
77
|
-
def run_in_pool(
|
|
94
|
+
def run_in_pool(url, args)
|
|
78
95
|
# 1. Builder del Request
|
|
79
96
|
req = BugBunny::Request.new(url)
|
|
80
97
|
|
|
81
98
|
# 2. Syntactic Sugar: Mapeo de argumentos a atributos del Request
|
|
99
|
+
req.delivery_mode = delivery_mode # Default del cliente
|
|
100
|
+
req.delivery_mode = args[:delivery_mode] if args[:delivery_mode]
|
|
82
101
|
req.method = args[:method] if args[:method]
|
|
83
102
|
req.body = args[:body] if args[:body]
|
|
84
103
|
req.exchange = args[:exchange] if args[:exchange]
|
|
@@ -101,6 +120,10 @@ module BugBunny
|
|
|
101
120
|
producer = BugBunny::Producer.new(session)
|
|
102
121
|
|
|
103
122
|
begin
|
|
123
|
+
# Mapeo de delivery_mode al método del productor (:rpc o :fire)
|
|
124
|
+
# :publish se mapea a :fire por consistencia interna.
|
|
125
|
+
method_name = req.delivery_mode == :publish ? :fire : :rpc
|
|
126
|
+
|
|
104
127
|
# Onion Architecture: La acción final es llamar al Producer real.
|
|
105
128
|
final_action = ->(env) { producer.send(method_name, env) }
|
|
106
129
|
|
data/lib/bug_bunny/consumer.rb
CHANGED
|
@@ -44,6 +44,7 @@ module BugBunny
|
|
|
44
44
|
# @param connection [Bunny::Session] Conexión nativa de Bunny.
|
|
45
45
|
def initialize(connection)
|
|
46
46
|
@session = BugBunny::Session.new(connection)
|
|
47
|
+
@health_timer = nil
|
|
47
48
|
end
|
|
48
49
|
|
|
49
50
|
# Inicia la suscripción a la cola y comienza el bucle de procesamiento.
|
|
@@ -244,12 +245,16 @@ module BugBunny
|
|
|
244
245
|
# @param q_name [String] Nombre de la cola a monitorear.
|
|
245
246
|
# @return [void]
|
|
246
247
|
def start_health_check(q_name)
|
|
248
|
+
# Detener el timer anterior antes de crear uno nuevo (evita leak en cada retry)
|
|
249
|
+
@health_timer&.shutdown
|
|
250
|
+
@health_timer = nil
|
|
251
|
+
|
|
247
252
|
file_path = BugBunny.configuration.health_check_file
|
|
248
253
|
|
|
249
254
|
# Toque inicial para indicar al orquestador que el worker arrancó correctamente
|
|
250
255
|
touch_health_file(file_path) if file_path
|
|
251
256
|
|
|
252
|
-
Concurrent::TimerTask.new(execution_interval: BugBunny.configuration.health_check_interval) do
|
|
257
|
+
@health_timer = Concurrent::TimerTask.new(execution_interval: BugBunny.configuration.health_check_interval) do
|
|
253
258
|
# 1. Verificamos la salud de RabbitMQ (si falla, levanta un error y corta la ejecución del bloque)
|
|
254
259
|
session.channel.queue_declare(q_name, passive: true)
|
|
255
260
|
|
|
@@ -258,7 +263,8 @@ module BugBunny
|
|
|
258
263
|
rescue StandardError => e
|
|
259
264
|
BugBunny.configuration.logger.warn("[BugBunny::Consumer] ⚠️ Queue check failed: #{e.message}. Reconnecting session...")
|
|
260
265
|
session.close
|
|
261
|
-
end
|
|
266
|
+
end
|
|
267
|
+
@health_timer.execute
|
|
262
268
|
end
|
|
263
269
|
|
|
264
270
|
# Actualiza la fecha de modificación del archivo de health check (touchfile).
|
data/lib/bug_bunny/controller.rb
CHANGED
|
@@ -146,11 +146,6 @@ module BugBunny
|
|
|
146
146
|
def process(body)
|
|
147
147
|
prepare_params(body)
|
|
148
148
|
|
|
149
|
-
# Inyección de configuración global de logs si el controlador no define propios
|
|
150
|
-
if self.class.log_tags.empty? && BugBunny.configuration.log_tags.any?
|
|
151
|
-
self.class.log_tags = BugBunny.configuration.log_tags
|
|
152
|
-
end
|
|
153
|
-
|
|
154
149
|
action_name = headers[:action].to_sym
|
|
155
150
|
current_arounds = resolve_callbacks(self.class.around_actions, action_name)
|
|
156
151
|
|
|
@@ -245,12 +240,11 @@ module BugBunny
|
|
|
245
240
|
if body.is_a?(Hash)
|
|
246
241
|
params.merge!(body)
|
|
247
242
|
elsif body.is_a?(String) && headers[:content_type].to_s.include?('json')
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
params.merge!(parsed) if parsed
|
|
243
|
+
begin
|
|
244
|
+
params.merge!(JSON.parse(body))
|
|
245
|
+
rescue JSON::ParserError => e
|
|
246
|
+
raise BugBunny::BadRequest, "Invalid JSON in request body: #{e.message}"
|
|
247
|
+
end
|
|
254
248
|
else
|
|
255
249
|
self.raw_string = body
|
|
256
250
|
end
|
|
@@ -284,7 +278,8 @@ module BugBunny
|
|
|
284
278
|
end
|
|
285
279
|
|
|
286
280
|
def compute_tags
|
|
287
|
-
self.class.log_tags.
|
|
281
|
+
tags = self.class.log_tags.presence || BugBunny.configuration.log_tags
|
|
282
|
+
tags.map do |tag|
|
|
288
283
|
case tag
|
|
289
284
|
when Proc
|
|
290
285
|
tag.call(self)
|
data/lib/bug_bunny/request.rb
CHANGED
|
@@ -16,6 +16,7 @@ module BugBunny
|
|
|
16
16
|
# @attr routing_key [String] La routing key específica. Si es nil, se usará {#path}.
|
|
17
17
|
# @attr timeout [Integer] Tiempo máximo en segundos para timeout RPC.
|
|
18
18
|
#
|
|
19
|
+
# @attr delivery_mode [Symbol] El modo de entrega (:rpc o :publish).
|
|
19
20
|
# @attr exchange_options [Hash] Opciones específicas para la declaración del Exchange en esta petición.
|
|
20
21
|
# @attr queue_options [Hash] Opciones específicas para la declaración de la Cola en esta petición.
|
|
21
22
|
class Request
|
|
@@ -27,6 +28,7 @@ module BugBunny
|
|
|
27
28
|
attr_accessor :exchange_type
|
|
28
29
|
attr_accessor :routing_key
|
|
29
30
|
attr_accessor :timeout
|
|
31
|
+
attr_accessor :delivery_mode
|
|
30
32
|
|
|
31
33
|
# Configuración de Infraestructura Específica
|
|
32
34
|
attr_accessor :exchange_options
|
|
@@ -48,12 +50,12 @@ module BugBunny
|
|
|
48
50
|
@timestamp = Time.now.to_i
|
|
49
51
|
@persistent = false
|
|
50
52
|
@exchange_type = 'direct'
|
|
53
|
+
@delivery_mode = :rpc
|
|
51
54
|
|
|
52
55
|
# Inicialización de opciones de infraestructura para evitar errores de nil durante el merge.
|
|
53
56
|
@exchange_options = {}
|
|
54
57
|
@queue_options = {}
|
|
55
58
|
end
|
|
56
|
-
|
|
57
59
|
# Calcula la Routing Key final que se usará en RabbitMQ.
|
|
58
60
|
#
|
|
59
61
|
# Principio: "Convention over Configuration".
|
data/lib/bug_bunny/resource.rb
CHANGED
|
@@ -154,9 +154,24 @@ module BugBunny
|
|
|
154
154
|
end
|
|
155
155
|
|
|
156
156
|
# Proxy para el encadenamiento del método `.with`.
|
|
157
|
+
# Solo puede usarse para UNA llamada de método: el contexto se restaura al finalizar.
|
|
157
158
|
class ScopeProxy < BasicObject
|
|
158
|
-
def initialize(target, keys, old_values)
|
|
159
|
-
|
|
159
|
+
def initialize(target, keys, old_values)
|
|
160
|
+
@target = target
|
|
161
|
+
@keys = keys
|
|
162
|
+
@old_values = old_values
|
|
163
|
+
@used = false
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def method_missing(method, *args, &block)
|
|
167
|
+
if @used
|
|
168
|
+
::Kernel.raise ::BugBunny::Error, "ScopeProxy is single-use. Call .with again for a new context."
|
|
169
|
+
end
|
|
170
|
+
@used = true
|
|
171
|
+
@target.public_send(method, *args, &block)
|
|
172
|
+
ensure
|
|
173
|
+
@keys.each { |k, v| ::Thread.current[v] = @old_values[k] }
|
|
174
|
+
end
|
|
160
175
|
end
|
|
161
176
|
|
|
162
177
|
# Calcula la routing key final.
|
data/lib/bug_bunny/version.rb
CHANGED
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: 4.
|
|
4
|
+
version: 4.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- gabix
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-03-
|
|
11
|
+
date: 2026-03-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bunny
|