bug_bunny 3.0.3 → 3.0.5
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/lib/bug_bunny/middleware/base.rb +46 -0
- data/lib/bug_bunny/middleware/json_response.rb +38 -49
- data/lib/bug_bunny/middleware/raise_error.rb +21 -43
- data/lib/bug_bunny/version.rb +1 -1
- data/lib/bug_bunny.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 65b3c20095ebcd609f8b077d5b9775491e88d5a79162c9a55538a21380b5cfe7
|
|
4
|
+
data.tar.gz: 387dc4fe344456068bc00a600d3ec861fdbf28785b6ea6138ad0461a57cc9041
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 64e39a7c76216cd27abd2308d1e926922aff6e5f57d15b188308ab583297b706aa6c85cd3e3302ab174f79fc653a07c08ff61b9da87f7661337db1d4f2842db9
|
|
7
|
+
data.tar.gz: b2e836747a841b870b27d13cb096484d2c812fb74f6a3ac44316a923686bcff6589ec9bca90086254394bbf365ed6ba6cc3a9b4520f48bd216cfea0928573f57
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
|
+
## [3.0.5] - 2026-02-17
|
|
3
|
+
|
|
4
|
+
### 🐛 Bug Fixes
|
|
5
|
+
* **Load Error Resolution:** Fixed `TypeError: Middleware is not a module` by converting `BugBunny::Middleware` into a proper module and introducing `BugBunny::Middleware::Base`.
|
|
6
|
+
|
|
7
|
+
### 🛠 Improvements
|
|
8
|
+
* **Standardized Middleware Architecture:** Consolidated the Template Method pattern across all internal interceptors (`RaiseError`, `JsonResponse`).
|
|
9
|
+
|
|
10
|
+
## [3.0.4] - 2026-02-16
|
|
11
|
+
|
|
12
|
+
### ♻️ Refactoring & Architecture
|
|
13
|
+
* **Middleware Architecture Overhaul:** Refactored the internal middleware stack to follow the **Template Method** pattern (Faraday-style).
|
|
14
|
+
* **New Base Class:** Introduced `BugBunny::Middleware` to standardize the execution flow (`call`, `app.call`).
|
|
15
|
+
* **Lifecycle Hooks:** Middlewares can now simply implement `on_request(env)` and/or `on_complete(response)` methods, eliminating the need to manually manage the execution chain.
|
|
16
|
+
* **Core Middlewares:** Refactored `RaiseError` and `JsonResponse` to use this new pattern, resulting in cleaner and more maintainable code.
|
|
17
|
+
* This change is **fully backward compatible** and paves the way for future middlewares (Loggers, Tracing, Headers injection).
|
|
18
|
+
|
|
2
19
|
## [3.0.3] - 2026-02-13
|
|
3
20
|
|
|
4
21
|
### 🐛 Bug Fixes
|
|
@@ -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
|
|
@@ -1,73 +1,62 @@
|
|
|
1
1
|
# lib/bug_bunny/middleware/json_response.rb
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
2
4
|
require 'json'
|
|
5
|
+
require_relative '../middleware/base'
|
|
3
6
|
|
|
4
7
|
module BugBunny
|
|
5
8
|
module Middleware
|
|
6
9
|
# Middleware encargado de parsear automáticamente el cuerpo de la respuesta.
|
|
7
10
|
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# **Integración con Rails:**
|
|
12
|
-
# Si `ActiveSupport` está cargado en el entorno, convierte los Hashes resultantes
|
|
13
|
-
# a `HashWithIndifferentAccess`. Esto permite a los desarrolladores acceder a las claves
|
|
14
|
-
# usando símbolos o strings indistintamente (ej: `body[:id]` o `body['id']`),
|
|
15
|
-
# comportamiento estándar en Rails.
|
|
11
|
+
# Convierte strings JSON en Hashes de Ruby. Si está disponible ActiveSupport,
|
|
12
|
+
# aplica HashWithIndifferentAccess.
|
|
16
13
|
#
|
|
17
|
-
# @
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
# conn.use BugBunny::Middleware::RaiseError
|
|
21
|
-
# conn.use BugBunny::Middleware::JsonResponse
|
|
22
|
-
# end
|
|
23
|
-
class JsonResponse
|
|
24
|
-
# Inicializa el middleware.
|
|
25
|
-
#
|
|
26
|
-
# @param app [Object] El siguiente middleware o el productor final en el stack.
|
|
27
|
-
def initialize(app)
|
|
28
|
-
@app = app
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Ejecuta el middleware.
|
|
14
|
+
# @see BugBunny::Middleware::Base
|
|
15
|
+
class JsonResponse < BugBunny::Middleware::Base
|
|
16
|
+
# Hook de ciclo de vida: Ejecutado después de recibir la respuesta.
|
|
32
17
|
#
|
|
33
|
-
#
|
|
34
|
-
# Una vez recibida la respuesta, procesa el `body` antes de devolverla hacia arriba en la cadena.
|
|
18
|
+
# Intercepta el body y lo reemplaza por su versión parseada.
|
|
35
19
|
#
|
|
36
|
-
# @param
|
|
37
|
-
# @return [
|
|
38
|
-
def
|
|
39
|
-
response = @app.call(env)
|
|
40
|
-
# Parseamos el body DESPUÉS de recibir la respuesta (Post-processing)
|
|
20
|
+
# @param response [Hash] La respuesta cruda.
|
|
21
|
+
# @return [void]
|
|
22
|
+
def on_complete(response)
|
|
41
23
|
response['body'] = parse_body(response['body'])
|
|
42
|
-
response
|
|
43
24
|
end
|
|
44
25
|
|
|
26
|
+
private
|
|
27
|
+
|
|
45
28
|
# Intenta convertir el cuerpo de la respuesta a una estructura Ruby nativa.
|
|
46
29
|
#
|
|
47
|
-
# @param body [String, Hash, Array, nil] El cuerpo original
|
|
48
|
-
# @return [Object] El cuerpo parseado
|
|
49
|
-
# @api private
|
|
30
|
+
# @param body [String, Hash, Array, nil] El cuerpo original.
|
|
31
|
+
# @return [Object] El cuerpo parseado o el original si falla.
|
|
50
32
|
def parse_body(body)
|
|
51
|
-
return nil if body.nil? || body.empty?
|
|
33
|
+
return nil if body.nil? || (body.respond_to?(:empty?) && body.empty?)
|
|
52
34
|
|
|
53
|
-
# Si ya es un objeto (ej:
|
|
54
|
-
parsed = body.is_a?(String) ?
|
|
35
|
+
# Si ya es un objeto (ej: mocks), lo dejamos pasar; si es String, parseamos.
|
|
36
|
+
parsed = body.is_a?(String) ? safe_json_parse(body) : body
|
|
55
37
|
|
|
56
38
|
# Rails Magic: Indifferent Access
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if parsed.is_a?(Array)
|
|
60
|
-
parsed.map! { |e| e.try(:with_indifferent_access) || e }
|
|
61
|
-
elsif parsed.is_a?(Hash)
|
|
62
|
-
parsed = parsed.with_indifferent_access
|
|
63
|
-
end
|
|
64
|
-
end
|
|
39
|
+
apply_indifferent_access(parsed)
|
|
40
|
+
end
|
|
65
41
|
|
|
66
|
-
|
|
42
|
+
# Parsea JSON de forma segura, retornando el original si falla.
|
|
43
|
+
def safe_json_parse(json_string)
|
|
44
|
+
JSON.parse(json_string)
|
|
67
45
|
rescue JSON::ParserError
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
46
|
+
json_string
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Aplica ActiveSupport::HashWithIndifferentAccess si es posible.
|
|
50
|
+
def apply_indifferent_access(data)
|
|
51
|
+
return data unless defined?(ActiveSupport)
|
|
52
|
+
|
|
53
|
+
if data.is_a?(Array)
|
|
54
|
+
data.map! { |e| e.try(:with_indifferent_access) || e }
|
|
55
|
+
elsif data.is_a?(Hash)
|
|
56
|
+
data.with_indifferent_access
|
|
57
|
+
else
|
|
58
|
+
data
|
|
59
|
+
end
|
|
71
60
|
end
|
|
72
61
|
end
|
|
73
62
|
end
|
|
@@ -1,61 +1,30 @@
|
|
|
1
1
|
# lib/bug_bunny/middleware/raise_error.rb
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative '../middleware/base'
|
|
5
|
+
|
|
2
6
|
module BugBunny
|
|
3
7
|
module Middleware
|
|
4
8
|
# Middleware que inspecciona el status de la respuesta y lanza excepciones
|
|
5
9
|
# si se encuentran errores (4xx o 5xx).
|
|
6
10
|
#
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
# @note Orden de Middlewares:
|
|
11
|
-
# Se recomienda usar este middleware **antes** de `JsonResponse` si deseas que
|
|
12
|
-
# la excepción contenga el cuerpo ya parseado (Hash).
|
|
13
|
-
#
|
|
14
|
-
# @example Configuración recomendada
|
|
15
|
-
# client = BugBunny::Client.new(pool: POOL) do |conn|
|
|
16
|
-
# conn.use BugBunny::Middleware::RaiseError # 1. Verifica errores primero (al salir)
|
|
17
|
-
# conn.use BugBunny::Middleware::JsonResponse # 2. Parsea JSON
|
|
18
|
-
# end
|
|
19
|
-
class RaiseError
|
|
20
|
-
# Inicializa el middleware.
|
|
21
|
-
# @param app [Object] El siguiente middleware o la aplicación final.
|
|
22
|
-
def initialize(app)
|
|
23
|
-
@app = app
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Ejecuta el middleware.
|
|
27
|
-
# Realiza la petición y, al retornar, verifica el estado de la respuesta.
|
|
11
|
+
# @see BugBunny::Middleware::Base
|
|
12
|
+
class RaiseError < BugBunny::Middleware::Base
|
|
13
|
+
# Hook de ciclo de vida: Ejecutado después de recibir la respuesta.
|
|
28
14
|
#
|
|
29
|
-
# @param env [BugBunny::Request] El objeto request actual.
|
|
30
|
-
# @return [Hash] La respuesta si el status es exitoso (2xx).
|
|
31
|
-
# @raise [BugBunny::ClientError] Si el status es 4xx.
|
|
32
|
-
# @raise [BugBunny::ServerError] Si el status es 5xx.
|
|
33
|
-
def call(env)
|
|
34
|
-
response = @app.call(env)
|
|
35
|
-
on_complete(response)
|
|
36
|
-
response
|
|
37
|
-
end
|
|
38
|
-
|
|
39
15
|
# Verifica el código de estado y lanza la excepción correspondiente.
|
|
40
16
|
#
|
|
41
|
-
# Mapeo de errores:
|
|
42
|
-
# * 400 -> {BugBunny::BadRequest}
|
|
43
|
-
# * 404 -> {BugBunny::NotFound}
|
|
44
|
-
# * 406 -> {BugBunny::NotAcceptable}
|
|
45
|
-
# * 408 -> {BugBunny::RequestTimeout}
|
|
46
|
-
# * 422 -> {BugBunny::UnprocessableEntity}
|
|
47
|
-
# * 500 -> {BugBunny::InternalServerError}
|
|
48
|
-
# * Otros 4xx -> {BugBunny::ClientError}
|
|
49
|
-
#
|
|
50
17
|
# @param response [Hash] El hash de respuesta conteniendo 'status' y 'body'.
|
|
18
|
+
# @raise [BugBunny::ClientError] Si el status es 4xx.
|
|
19
|
+
# @raise [BugBunny::ServerError] Si el status es 5xx.
|
|
51
20
|
# @return [void]
|
|
52
21
|
def on_complete(response)
|
|
53
22
|
status = response['status'].to_i
|
|
54
|
-
body = response['body']
|
|
23
|
+
body = response['body']
|
|
55
24
|
|
|
56
25
|
case status
|
|
57
26
|
when 200..299
|
|
58
|
-
# OK
|
|
27
|
+
nil # OK
|
|
59
28
|
when 400 then raise BugBunny::BadRequest, body
|
|
60
29
|
when 404 then raise BugBunny::NotFound
|
|
61
30
|
when 406 then raise BugBunny::NotAcceptable
|
|
@@ -63,9 +32,18 @@ module BugBunny
|
|
|
63
32
|
when 422 then raise BugBunny::UnprocessableEntity, body
|
|
64
33
|
when 500 then raise BugBunny::InternalServerError, body
|
|
65
34
|
else
|
|
66
|
-
|
|
35
|
+
handle_unknown_error(status)
|
|
67
36
|
end
|
|
68
37
|
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
# Maneja errores 4xx genéricos no mapeados explícitamente.
|
|
42
|
+
# @param status [Integer] El código de estado HTTP.
|
|
43
|
+
# @raise [BugBunny::ClientError] Siempre lanza esta excepción.
|
|
44
|
+
def handle_unknown_error(status)
|
|
45
|
+
raise BugBunny::ClientError, "Unknown error: #{status}" if status >= 400
|
|
46
|
+
end
|
|
69
47
|
end
|
|
70
48
|
end
|
|
71
49
|
end
|
data/lib/bug_bunny/version.rb
CHANGED
data/lib/bug_bunny.rb
CHANGED
|
@@ -13,7 +13,7 @@ require_relative 'bug_bunny/resource'
|
|
|
13
13
|
require_relative 'bug_bunny/rabbit'
|
|
14
14
|
require_relative 'bug_bunny/consumer'
|
|
15
15
|
require_relative 'bug_bunny/controller'
|
|
16
|
-
|
|
16
|
+
require_relative 'bug_bunny/middleware/base'
|
|
17
17
|
require_relative 'bug_bunny/middleware/stack'
|
|
18
18
|
require_relative 'bug_bunny/middleware/raise_error'
|
|
19
19
|
require_relative 'bug_bunny/middleware/json_response'
|
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.5
|
|
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
|
|
@@ -200,6 +200,7 @@ files:
|
|
|
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/base.rb
|
|
203
204
|
- lib/bug_bunny/middleware/json_response.rb
|
|
204
205
|
- lib/bug_bunny/middleware/raise_error.rb
|
|
205
206
|
- lib/bug_bunny/middleware/stack.rb
|