bug_bunny 3.0.3 → 3.0.4

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: 6d012f3384230432e8f94c365d2a19ea05179f362f0433482c8d9967c3838102
4
- data.tar.gz: ae306fdebe3c63b08a4980337294e46830a0277c9deea509a485d9978b2a9760
3
+ metadata.gz: 4752202964e5f4382a685a356a31264db3ae51049c42a49d3e5a6602fda10ed1
4
+ data.tar.gz: 1cb2f414e1fb52560d9a4310fd0679c1a59f86dc072fce6dd385b17006ecc6a0
5
5
  SHA512:
6
- metadata.gz: fdee3c4b42d2e852649985f962fe81ca0cf3b152357d484e3e37310bbacd07889c82408c92b3b99936c0f81d2cf60e22ca57bae24f54e30efb0a36040649382b
7
- data.tar.gz: 4c47cde21b912b1cad5cda132af21cb7ea5fcb0a7cefd8827f572695fbe50d6945b6083738e81fa8cc3f40b03344314f28655586e7c298925b2bf9482034acaa
6
+ metadata.gz: 74af2e635f79d182cfe0fc336a67bf5175ad02729e39b5ddb77fb6bfdde4e3e733f863c78e453160ca6e03ff381dc81f1169a1c9d7be39a116d971638f61d846
7
+ data.tar.gz: c7da63dcce1cc2f0b77faa253327ddb4eec88866328429e84383172e07be402ff9d9c2ca6a2fbe16e9c0f7762bd8119c17496caa37a4ad35d0be07867e8aa78c
data/CHANGELOG.md CHANGED
@@ -1,4 +1,13 @@
1
1
  # Changelog
2
+ ## [3.0.4] - 2026-02-16
3
+
4
+ ### ♻️ Refactoring & Architecture
5
+ * **Middleware Architecture Overhaul:** Refactored the internal middleware stack to follow the **Template Method** pattern (Faraday-style).
6
+ * **New Base Class:** Introduced `BugBunny::Middleware` to standardize the execution flow (`call`, `app.call`).
7
+ * **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.
8
+ * **Core Middlewares:** Refactored `RaiseError` and `JsonResponse` to use this new pattern, resulting in cleaner and more maintainable code.
9
+ * This change is **fully backward compatible** and paves the way for future middlewares (Loggers, Tracing, Headers injection).
10
+
2
11
  ## [3.0.3] - 2026-02-13
3
12
 
4
13
  ### 🐛 Bug Fixes
@@ -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'
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
- # Este middleware intercepta la respuesta proveniente del servicio remoto. Si el `body`
9
- # es un String JSON válido, lo convierte a un Hash o Array de Ruby.
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
- # @example Uso en la configuración del cliente
18
- # client = BugBunny::Client.new(pool: POOL) do |conn|
19
- # # Se recomienda ponerlo después de RaiseError para tener el body parseado en las excepciones
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
15
+ class JsonResponse < BugBunny::Middleware
16
+ # Hook de ciclo de vida: Ejecutado después de recibir la respuesta.
32
17
  #
33
- # Invoca al siguiente eslabón (`@app.call`) y espera su retorno.
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 env [BugBunny::Request] El objeto request actual (el entorno).
37
- # @return [Hash] La respuesta con el campo 'body' transformado (si era JSON).
38
- def call(env)
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 de la respuesta.
48
- # @return [Object] El cuerpo parseado (Hash/Array) o el objeto original si falla el parseo.
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: tests o mocks), lo dejamos pasar, si es String intentamos parsear.
54
- parsed = body.is_a?(String) ? JSON.parse(body) : body
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
- # Si estamos en un entorno Rails, aplicamos la conversión para UX del desarrollador.
58
- if defined?(ActiveSupport)
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
- parsed
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
- # Si el body no es un JSON válido (ej: texto plano o error del servidor),
69
- # devolvemos el string original sin lanzar excepción.
70
- body
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'
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
- # Mapea los códigos de estado HTTP/AMQP a excepciones específicas de Ruby para facilitar
8
- # el manejo de errores mediante `rescue`.
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
12
+ class RaiseError < BugBunny::Middleware
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'] # Nota: Puede ser String o Hash dependiendo de JsonResponse
23
+ body = response['body']
55
24
 
56
25
  case status
57
26
  when 200..299
58
- # OK: No action needed
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
- raise BugBunny::ClientError, "Unknown error: #{status}" if status >= 400
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
@@ -0,0 +1,44 @@
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,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BugBunny
4
- VERSION = "3.0.3"
4
+ VERSION = "3.0.4"
5
5
  end
data/lib/bug_bunny.rb CHANGED
@@ -13,6 +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
+ require_relative 'bug_bunny/middleware'
16
17
 
17
18
  require_relative 'bug_bunny/middleware/stack'
18
19
  require_relative 'bug_bunny/middleware/raise_error'
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.3
4
+ version: 3.0.4
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-13 00:00:00.000000000 Z
11
+ date: 2026-02-16 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.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