bug_bunny 3.1.5 → 3.1.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: a96d4e81fd57e9a1a524aa479bbb85d6fe3a99bd654da95014f633d2fb0cb2d2
4
- data.tar.gz: cb49f5e1789c1737572fb98b2d21428cf02c129c33e3049de1a0de4f6270a9d3
3
+ metadata.gz: 7fec9f487076682121e6d8586e44fe9d70f380bd809fb46014265105421a0d8f
4
+ data.tar.gz: e847fc6f1943f9921f2250a46a20bd6f76b20736812fcbf6bfc437ab49a8a30c
5
5
  SHA512:
6
- metadata.gz: ea0ae037590a852734607ad7e46fabff4cfab87aef068a724da42d506fc79ccda376d0db85f0d4ebd1f516ac2aef963fcdd18604a6d808efb3e4e2de0ca32391
7
- data.tar.gz: 6930cc1f181bc7d6968cb4fe3ed82f25cb59650522f45c5ad5e6f9328cc33125bc41d197de35f05f16e1126014ea7ada86cb2d6a348e7c4e0b6a4c8e56a61a7a
6
+ metadata.gz: 199faaa596f52cc1c192259e8fe3e7da95152d117db9ded079051bebdff985793e63acde11cb8ceed5e0e216d6934ff35056993947609f2818d8429f524a789a
7
+ data.tar.gz: dfefdd78b4597f90dc059ef07d7423dda86d2630cdab409c514941708cc539d174a763d1a6e18c918dee7058d25c0a996aac0bb52f1b440573b42480e723e284
data/CHANGELOG.md CHANGED
@@ -1,4 +1,12 @@
1
1
  # Changelog
2
+ ## [3.1.6] - 2026-02-27
3
+
4
+ ### 🐛 Bug Fixes & Router Improvements
5
+ * **Enhanced Heuristic Router (ID Detection):** Mejoras críticas en `Consumer#router_dispatch` para soportar una gama mucho más amplia de formatos de identificadores y evitar colisiones con namespaces:
6
+ * **Soporte para Swarm/NanoID:** Se amplió la expresión regular de detección de IDs para capturar hashes alfanuméricos de 20 o más caracteres (`[a-zA-Z0-9_-]{20,}`), permitiendo el correcto ruteo de IDs generados por Docker Swarm (25 caracteres) o NanoID.
7
+ * **Escaneo Inverso (Right-to-Left):** Se modificó la búsqueda del ID para que escanee los segmentos de la URL desde el final hacia el principio (`rindex`). Esto evita falsos positivos donde namespaces cortos como `v1` (ej. `api/v1/...`) eran confundidos accidentalmente con un ID.
8
+ * **Fallback Semántico Posicional:** Se introdujo una red de seguridad (fallback) que infiere la posición del ID basándose en el Verbo HTTP. Si el ID no coincide con ningún patrón Regex (ej. es un ID corto como `node-1`), pero el método es `PUT`, `PATCH` o `DELETE`, el enrutador ahora asume inteligentemente que el penúltimo/último segmento corresponde al ID del recurso.
9
+
2
10
  ## [3.1.5] - 2026-02-25
3
11
 
4
12
  ### ✨ New Features & Improvements
@@ -180,7 +180,7 @@ module BugBunny
180
180
  # Interpreta la URL y el verbo para decidir qué controlador ejecutar.
181
181
  #
182
182
  # Implementa un Router Heurístico que soporta namespaces y acciones custom
183
- # buscando dinámicamente el ID en la ruta.
183
+ # buscando dinámicamente el ID en la ruta mediante Regex y Fallback Semántico.
184
184
  #
185
185
  # @param method [String] Verbo HTTP (GET, POST, etc).
186
186
  # @param path [String] URL virtual del recurso (ej: 'foo/bar/algo/13/test').
@@ -196,30 +196,49 @@ module BugBunny
196
196
 
197
197
  # 1. Acción Built-in: Health Check Global (/up o /api/up)
198
198
  if segments.last == 'up' && method.to_s.upcase == 'GET'
199
- # Si la ruta es solo 'up', usamos un controlador genérico 'application'
200
199
  ctrl = segments.size > 1 ? segments[0...-1].join('/') : 'application'
201
200
  return { controller: ctrl, action: 'up', id: nil, params: query_params }
202
201
  end
203
202
 
204
- # 2. Búsqueda dinámica del ID (Heurística)
205
- # Patrón: Números enteros, UUIDs, o hashes alfanuméricos largos (MongoDB/Snowflake)
206
- id_pattern = /^(?:\d+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|[0-9a-fA-F]{24})$/
207
- id_index = segments.find_index { |s| s.match?(id_pattern) }
203
+ # 2. Búsqueda dinámica del ID (Heurística por Regex)
204
+ # Patrón: Enteros, UUIDs, o Hashes largos (Docker Swarm 25 chars, Mongo 24 chars)
205
+ id_pattern = /^(?:\d+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|[a-zA-Z0-9_-]{20,})$/
208
206
 
207
+ # FIX: Usamos rindex (de derecha a izquierda) para evitar falsos positivos con namespaces como 'v1'
208
+ id_index = segments.rindex { |s| s.match?(id_pattern) }
209
+
210
+ # 3. Fallback Semántico Posicional
211
+ # Si el regex no detectó el ID (ej: ID corto como "node-1"), pero la semántica HTTP
212
+ # indica que es una operación singular (PUT/DELETE/GET), asumimos que el último segmento es el ID.
213
+ if id_index.nil? && segments.size >= 2
214
+ last_segment = segments.last
215
+ method_up = method.to_s.upcase
216
+
217
+ is_member_verb = %w[PUT PATCH DELETE].include?(method_up)
218
+ # En GET, nos aseguramos que la última palabra no sea una acción estándar de REST
219
+ is_get_member = method_up == 'GET' && !%w[index new edit up action].include?(last_segment)
220
+
221
+ if is_member_verb || is_get_member
222
+ # Si tiene 3 o más segmentos (ej. nodes/node-1/stats), el ID no está al final.
223
+ # Este fallback asume que para IDs raros, el formato clásico es recurso/id
224
+ id_index = segments.size - 1
225
+ end
226
+ end
227
+
228
+ # 4. Asignación de variables según escenario
209
229
  if id_index
210
- # ESCENARIO A: Ruta Miembro (ej. foo/bar/algo/13/test)
211
- # Todo lo que está antes del ID es el namespace/controlador
212
- controller_name = segments[0...id_index].join('/') # "foo/bar/algo"
213
- id = segments[id_index] # "13"
214
- action = segments[id_index + 1] # "test" (puede ser nil)
230
+ # ESCENARIO A: Ruta Miembro (ej. nodes/4bv445vgc158hk4twlxmdjo0v/stats)
231
+ controller_name = segments[0...id_index].join('/')
232
+ id = segments[id_index]
233
+ action = segments[id_index + 1] # Puede ser nil si no hay acción extra al final
215
234
  else
216
- # ESCENARIO B: Ruta Colección (ej. foo/bar/algo o api/v1/users)
235
+ # ESCENARIO B: Ruta Colección (ej. api/v1/nodes)
217
236
  controller_name = segments.join('/')
218
237
  id = nil
219
238
  action = nil
220
239
  end
221
240
 
222
- # 3. Inferimos la acción si no hay una explícita en la ruta
241
+ # 5. Inferimos la acción clásica de Rails si no hay una explícita
223
242
  unless action
224
243
  action = case method.to_s.upcase
225
244
  when 'GET' then id ? 'show' : 'index'
@@ -230,7 +249,7 @@ module BugBunny
230
249
  end
231
250
  end
232
251
 
233
- # 4. Inyectamos el ID en los parámetros para que el Controller lo tenga fácil
252
+ # 6. Inyectamos el ID en los parámetros para fácil acceso en el Controlador
234
253
  query_params['id'] = id if id
235
254
 
236
255
  { controller: controller_name, action: action, id: id, params: query_params }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BugBunny
4
- VERSION = "3.1.5"
4
+ VERSION = "3.1.6"
5
5
  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.1.5
4
+ version: 3.1.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-25 00:00:00.000000000 Z
11
+ date: 2026-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bunny