bug_bunny 4.6.1 → 4.8.0

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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.agents/skills/rabbitmq-expert/SKILL.md +1555 -0
  3. data/.claude/commands/gem-ai-setup.md +174 -0
  4. data/.claude/commands/pr.md +53 -0
  5. data/.claude/commands/release.md +52 -0
  6. data/.claude/commands/rubocop.md +22 -0
  7. data/.claude/commands/service-ai-setup.md +168 -0
  8. data/.claude/commands/test.md +28 -0
  9. data/.claude/commands/yard.md +46 -0
  10. data/CHANGELOG.md +50 -15
  11. data/CLAUDE.md +240 -0
  12. data/README.md +154 -221
  13. data/Rakefile +19 -3
  14. data/docs/_index.md +50 -0
  15. data/docs/ai/_index.md +56 -0
  16. data/docs/ai/antipatterns.md +166 -0
  17. data/docs/ai/api.md +251 -0
  18. data/docs/ai/architecture.md +92 -0
  19. data/docs/ai/errors.md +158 -0
  20. data/docs/ai/faq_external.md +133 -0
  21. data/docs/ai/faq_internal.md +86 -0
  22. data/docs/ai/glossary.md +45 -0
  23. data/docs/concepts.md +140 -0
  24. data/docs/howto/controller.md +194 -0
  25. data/docs/howto/middleware_client.md +119 -0
  26. data/docs/howto/middleware_consumer.md +127 -0
  27. data/docs/howto/rails.md +214 -0
  28. data/docs/howto/resource.md +200 -0
  29. data/docs/howto/routing.md +133 -0
  30. data/docs/howto/testing.md +259 -0
  31. data/docs/howto/tracing.md +119 -0
  32. data/lib/bug_bunny/client.rb +45 -21
  33. data/lib/bug_bunny/configuration.rb +63 -0
  34. data/lib/bug_bunny/consumer.rb +51 -37
  35. data/lib/bug_bunny/consumer_middleware.rb +14 -5
  36. data/lib/bug_bunny/controller.rb +39 -18
  37. data/lib/bug_bunny/exception.rb +5 -1
  38. data/lib/bug_bunny/middleware/raise_error.rb +3 -3
  39. data/lib/bug_bunny/observability.rb +28 -6
  40. data/lib/bug_bunny/producer.rb +11 -13
  41. data/lib/bug_bunny/railtie.rb +8 -7
  42. data/lib/bug_bunny/request.rb +3 -11
  43. data/lib/bug_bunny/resource.rb +81 -41
  44. data/lib/bug_bunny/routing/route.rb +6 -1
  45. data/lib/bug_bunny/routing/route_set.rb +60 -22
  46. data/lib/bug_bunny/session.rb +18 -11
  47. data/lib/bug_bunny/version.rb +1 -1
  48. data/lib/bug_bunny.rb +4 -2
  49. data/lib/generators/bug_bunny/install/install_generator.rb +45 -5
  50. data/lib/tasks/bug_bunny.rake +50 -0
  51. data/plan_test.txt +63 -0
  52. data/skills-lock.json +10 -0
  53. data/spec/integration/client_spec.rb +117 -0
  54. data/spec/integration/consumer_middleware_spec.rb +86 -0
  55. data/spec/integration/controller_spec.rb +140 -0
  56. data/spec/integration/error_handling_spec.rb +57 -0
  57. data/spec/integration/infrastructure_spec.rb +52 -0
  58. data/spec/integration/resource_spec.rb +113 -0
  59. data/spec/spec_helper.rb +70 -0
  60. data/spec/support/bunny_mocks.rb +18 -0
  61. data/spec/support/integration_helper.rb +87 -0
  62. data/spec/unit/client_session_pool_spec.rb +159 -0
  63. data/spec/unit/configuration_spec.rb +164 -0
  64. data/spec/unit/consumer_middleware_spec.rb +129 -0
  65. data/spec/unit/consumer_spec.rb +90 -0
  66. data/spec/unit/controller_after_action_spec.rb +155 -0
  67. data/spec/unit/observability_spec.rb +167 -0
  68. data/spec/unit/resource_attributes_spec.rb +69 -0
  69. data/spec/unit/session_spec.rb +98 -0
  70. metadata +50 -3
  71. data/sig/bug_bunny.rbs +0 -4
data/CLAUDE.md ADDED
@@ -0,0 +1,240 @@
1
+ # BugBunny — Project Intelligence
2
+
3
+ ## ¿Qué es BugBunny?
4
+
5
+ BugBunny es una gema Ruby que implementa una capa de enrutamiento RESTful sobre AMQP (RabbitMQ). Permite que microservicios se comuniquen via RabbitMQ usando patrones familiares de HTTP: verbos (GET, POST, PUT, DELETE), controladores, rutas declarativas, RPC síncrono y fire-and-forget.
6
+
7
+ **Problema que resuelve:** Eliminar el acoplamiento directo entre microservicios via HTTP, usando RabbitMQ como bus de mensajes con la misma ergonomía de un framework web.
8
+
9
+ ## Knowledge Base
10
+
11
+ - **Docs AI:** `docs/ai/` — conocimiento estructurado para agentes
12
+ - **Index:** `docs/ai/_index.md` — manifest con versión, audiencias y archivos
13
+ - **Docs humanos:** `docs/_index.md` — manifest de toda la documentación (`howto/`, `concepts.md`)
14
+
15
+ ## Skills disponibles
16
+
17
+ - `.agents/skills/rabbitmq-expert/` — arquitectura AMQP, exchanges, quorum queues, DLX, HA, clustering
18
+
19
+ ---
20
+
21
+ ## Arquitectura
22
+
23
+ ```
24
+ Publisher (Client/Resource)
25
+ └─ Producer → Session → Bunny → RabbitMQ Exchange
26
+
27
+ RabbitMQ Queue
28
+
29
+ Consumer (subscribe loop)
30
+ └─ ConsumerMiddleware::Stack
31
+ └─ process_message
32
+ └─ Router → Controller → Action
33
+ └─ reply (RPC)
34
+ ```
35
+
36
+ ### Componentes clave
37
+
38
+ | Clase | Responsabilidad |
39
+ |---|---|
40
+ | `BugBunny::Session` | Wrapper de canal Bunny. Declara exchanges y queues. |
41
+ | `BugBunny::Consumer` | Subscribe loop. Rutea mensajes a controladores via `BugBunny.routes`. |
42
+ | `BugBunny::ConsumerMiddleware::Stack` | Pipeline de middlewares antes de `process_message`. |
43
+ | `BugBunny::Producer` | Publica mensajes. Implementa RPC con `Concurrent::IVar`. |
44
+ | `BugBunny::Client` | API de alto nivel para el publicador. Pool de conexiones. |
45
+ | `BugBunny::Controller` | Base class tipo Rails. `around_action`, `before_action`, `render`. |
46
+ | `BugBunny::Resource` | ActiveRecord-like sobre AMQP. `find`, `where`, `create`, etc. |
47
+ | `BugBunny::Request` | Value object del mensaje saliente (path, method, params, headers). |
48
+ | `BugBunny::Observability` | Mixin de logging estructurado. `safe_log`, `exception_metadata`. |
49
+ | `BugBunny::Configuration` | Configuración global. Logger, timeouts, middleware hooks. |
50
+
51
+ ### Flujo RPC completo
52
+
53
+ 1. `Resource.find(id)` → `Client#request` → `Producer#rpc`
54
+ 2. Producer publica en exchange con `reply_to: 'amq.rabbitmq.reply-to'`
55
+ 3. `Concurrent::IVar` bloquea el thread principal (`future.value(timeout)`)
56
+ 4. Consumer recibe → middleware stack → controller → `reply(response)`
57
+ 5. Reply listener thread setea `future.set({ body:, headers: })`
58
+ 6. Thread principal: `on_rpc_reply&.call(headers)` → `parse_response(body)`
59
+
60
+ ## Hooks de extensión
61
+
62
+ ```ruby
63
+ # Middleware antes de process_message (ej: tracing, auth)
64
+ BugBunny.consumer_middlewares.use MyMiddleware
65
+
66
+ # Headers a inyectar en el reply RPC (ej: trace context actualizado)
67
+ config.rpc_reply_headers = -> { { 'X-Amzn-Trace-Id' => Tracer.header } }
68
+
69
+ # Callback en el thread principal al recibir el reply (ej: hidratar tracer)
70
+ config.on_rpc_reply = ->(headers) { Tracer.hydrate(headers['X-Amzn-Trace-Id']) }
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Dominio y Expertise
76
+
77
+ Al trabajar en esta gema aplicá expertise en:
78
+
79
+ - **Ruby idiomático**: módulos, mixins, metaprogramación, `class_attribute`, `Concurrent::*`
80
+ - **RabbitMQ / AMQP**: exchanges (direct/topic/fanout), queues, bindings, `reply_to`, `correlation_id`, `properties.headers`, publisher confirms, manual ack
81
+ - **Bunny**: la gema Ruby que wrappea AMQP. `channel`, `basic_consume`, `basic_publish`, `IVar`
82
+ - **Rails patterns**: `ActiveModel`, `ActiveSupport`, `class_attribute`, `concerns`, `constantize`
83
+ - **Rack**: `Rack::Utils.parse_nested_query`, `build_nested_query`
84
+
85
+ ---
86
+
87
+ ## Observability — Estándar de Logging
88
+
89
+ Esta gema implementa su propio patrón de observability via `BugBunny::Observability`.
90
+
91
+ ### Reglas fundamentales
92
+
93
+ - **Formato**: `component=x event=clase.evento [key=value ...]` — todo en una línea
94
+ - **Nunca** llamar al logger directamente. Siempre usar `safe_log`
95
+ - **Nunca** `Kernel#warn`, `$stderr`, `puts`
96
+ - **Niveles**: `ERROR`=excepción, `WARN`=inesperado+continuó, `INFO`=normal, `DEBUG`=detalle
97
+ - `DEBUG` siempre en bloque: `logger.debug { "k=#{v}" }` — `safe_log` lo maneja internamente
98
+ - Duraciones: `Process.clock_gettime(Process::CLOCK_MONOTONIC)`, nunca `Time.now`
99
+ - Logger failures **nunca** interrumpen el flujo — `safe_log` tiene `rescue StandardError`
100
+
101
+ ### Uso en clases nuevas
102
+
103
+ ```ruby
104
+ class BugBunny::MiClase
105
+ include BugBunny::Observability
106
+
107
+ def initialize
108
+ @logger = BugBunny.configuration.logger
109
+ end
110
+
111
+ def mi_metodo
112
+ start = monotonic_now
113
+ # ...
114
+ safe_log(:info, "mi_clase.mi_evento", campo: valor, duration_s: duration_s(start))
115
+ rescue StandardError => e
116
+ safe_log(:error, "mi_clase.error", **exception_metadata(e))
117
+ end
118
+ end
119
+ ```
120
+
121
+ ### Naming de eventos
122
+
123
+ Formato estricto: `"clase.evento"` (string, nunca symbol)
124
+
125
+ | Evento | Nivel | Cuándo |
126
+ |---|---|---|
127
+ | `consumer.start` | INFO | Consumer inicia subscribe |
128
+ | `consumer.bound` | INFO | Queue bindeada al exchange |
129
+ | `consumer.message_received` | INFO | Mensaje recibido, antes del routing |
130
+ | `consumer.route_matched` | DEBUG | Ruta encontrada |
131
+ | `consumer.message_processed` | INFO | Procesamiento exitoso con duración |
132
+ | `consumer.execution_error` | ERROR | Excepción en el procesamiento |
133
+ | `producer.publish` | INFO | Mensaje publicado |
134
+ | `producer.rpc_waiting` | DEBUG | Bloqueando esperando respuesta |
135
+ | `producer.rpc_response_received` | DEBUG | Reply recibido (thread principal) |
136
+
137
+ ### Campos estándar
138
+
139
+ ```ruby
140
+ safe_log(:error, "clase.error", **exception_metadata(e))
141
+ # => error_class: "RuntimeError", error_message: "..."
142
+
143
+ safe_log(:info, "clase.evento", duration_s: duration_s(start_time))
144
+ # => duration_s: 0.001234
145
+
146
+ # Valores sensibles se filtran automáticamente:
147
+ # password, token, secret, api_key, auth → [FILTERED]
148
+ ```
149
+
150
+ ---
151
+
152
+ ## Standards de Código
153
+
154
+ ### RuboCop
155
+
156
+ Esta gema usa **rubocop-rails-omakase**. Todo código nuevo o modificado debe cumplir.
157
+
158
+ ```bash
159
+ source /opt/homebrew/opt/chruby/share/chruby/chruby.sh && chruby ruby-3.3.8
160
+ bundle exec rubocop
161
+ bundle exec rubocop -a # autocorrect
162
+ ```
163
+
164
+ **No corregir código existente no tocado.** Solo el código nuevo o modificado en el PR.
165
+
166
+ ### YARD
167
+
168
+ Todo método público nuevo o modificado lleva documentación YARD:
169
+
170
+ ```ruby
171
+ # Descripción breve.
172
+ #
173
+ # Descripción extendida si es necesario.
174
+ #
175
+ # @param name [Type] Descripción
176
+ # @return [Type] Descripción
177
+ # @raise [ErrorClass] Cuándo se lanza
178
+ # @example
179
+ # resultado = mi_metodo(arg)
180
+ def mi_metodo(name)
181
+ ```
182
+
183
+ ```bash
184
+ bundle exec yard doc
185
+ bundle exec yard stats --list-undoc
186
+ ```
187
+
188
+ ### RSpec
189
+
190
+ Tests en `spec/`. Sin mocks de dependencias externas reales (RabbitMQ se mockea con doubles de Bunny).
191
+
192
+ ```bash
193
+ source /opt/homebrew/opt/chruby/share/chruby/chruby.sh && chruby ruby-3.3.8
194
+ bundle exec rspec
195
+ bundle exec rspec spec/bug_bunny/consumer_spec.rb # archivo específico
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Entorno de Desarrollo
201
+
202
+ ### Ruby
203
+
204
+ ```bash
205
+ source /opt/homebrew/opt/chruby/share/chruby/chruby.sh && chruby ruby-3.3.8
206
+ ```
207
+
208
+ Nunca usar `bundle exec ruby` con el Ruby del sistema (2.6). Siempre sourcear chruby primero.
209
+
210
+ ### Worktrees
211
+
212
+ - **Main**: `/Users/gabriel/src/gems/bug_bunny` (rama `main`)
213
+ - **Work**: `/Users/gabriel/src/gems/worktrees/current-5n3` (ramas de feature)
214
+ - `main` está checkeado en otro worktree — no se puede hacer `git checkout main` desde el worktree de trabajo
215
+
216
+ ### Push a remoto
217
+
218
+ SSH está roto en este entorno. Para push siempre:
219
+
220
+ ```bash
221
+ git remote set-url origin https://github.com/gedera/bug_bunny.git
222
+ git push origin main
223
+ git remote set-url origin git@github.com:gedera/bug_bunny.git # restaurar
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Release Workflow
229
+
230
+ Usá el comando `/release` para el flujo completo. Manualmente:
231
+
232
+ 1. Determinar tipo: `patch`=bugfix, `minor`=feature nueva, `major`=breaking change
233
+ 2. Actualizar `lib/bug_bunny/version.rb`
234
+ 3. Agregar entrada al tope de `CHANGELOG.md`
235
+ 4. Commit con mensaje convencional
236
+ 5. Desde `/Users/gabriel/src/gems/bug_bunny`: `git merge --ff-only <branch>`
237
+ 6. Push via HTTPS + restaurar SSH
238
+ 7. `git tag vX.Y.Z && git push origin vX.Y.Z`
239
+
240
+ **Nunca commitear ni pushear sin permiso explícito del usuario.**