bug_bunny 3.0.0 → 3.0.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 +15 -0
- data/README.md +142 -100
- data/lib/bug_bunny/client.rb +22 -39
- data/lib/bug_bunny/consumer.rb +65 -55
- data/lib/bug_bunny/request.rb +30 -70
- data/lib/bug_bunny/resource.rb +82 -229
- 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: 730ae48562650742b536036de0db8b811efa48855790ef08bffc15713867654e
|
|
4
|
+
data.tar.gz: c4d16c6b17307831dd9cfc3be7ef2f78ecc52254034ded5187b2a19f00490aa3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5551a1bff89d318faf4155dead6e042ac991b57a30459efa2bfcad9029191b12391e6e058e275dda39bdaa4ed7cd2413e46ececa048905836009e4294b8d85e2
|
|
7
|
+
data.tar.gz: dd6af676b128f77d01c885d1107b773080d3ebd80b0a49477f3f6d4db511f91acdf8d2775281fa4cae529e17161cf7180caf41c983aece183112f6ab93fedf69
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.0.1] - 2026-02-10
|
|
4
|
+
|
|
5
|
+
### 🚀 Features: RESTful Architecture
|
|
6
|
+
* **HTTP Verbs over AMQP:** Implemented support for semantic HTTP verbs (`GET`, `POST`, `PUT`, `DELETE`) within AMQP headers (`x-http-method`). This enables a true RESTful design over RabbitMQ.
|
|
7
|
+
* **Smart Router:** The `BugBunny::Consumer` now behaves like a Rails Router. It automatically infers the controller action based on the combination of the **Verb** and the **URL Path** (e.g., `GET users/1` dispatches to `show`, `POST users` to `create`).
|
|
8
|
+
* **Resource CRUD Mapping:** `BugBunny::Resource` now maps Ruby operations to their specific REST verbs:
|
|
9
|
+
* `create` -> `POST`
|
|
10
|
+
* `update` -> `PUT`
|
|
11
|
+
* `destroy` -> `DELETE`
|
|
12
|
+
* `find/where` -> `GET`.
|
|
13
|
+
|
|
14
|
+
### 🛠 Improvements
|
|
15
|
+
* **Client API:** Updated `BugBunny::Client#request` and `#publish` to accept a `method:` argument (e.g., `client.request('users', method: :post)`), giving developers full control over the request semantics without changing the method signature.
|
|
16
|
+
* **Request Metadata:** `BugBunny::Request` now handles the `method` attribute and ensures it is properly injected into the AMQP headers for the consumer to read.
|
|
17
|
+
|
|
3
18
|
## [3.0.0] - 2026-02-05
|
|
4
19
|
|
|
5
20
|
### ⚠ Breaking Changes
|
data/README.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
**BugBunny** es un framework RPC para Ruby on Rails sobre **RabbitMQ**.
|
|
4
4
|
|
|
5
|
-
Su filosofía es **"Active Record over AMQP"**. Abstrae la complejidad de colas y exchanges transformando patrones de mensajería en una arquitectura **RESTful simulada
|
|
5
|
+
Su filosofía es **"Active Record over AMQP"**. Abstrae la complejidad de colas y exchanges transformando patrones de mensajería en una arquitectura **RESTful simulada**.
|
|
6
|
+
|
|
7
|
+
A diferencia de otros clientes de RabbitMQ, BugBunny viaja con **Verbos HTTP** (`GET`, `POST`, `PUT`, `DELETE`) inyectados en los headers AMQP. Esto permite construir una API semántica donde un **Router Inteligente** despacha los mensajes a controladores Rails estándar.
|
|
6
8
|
|
|
7
9
|
---
|
|
8
10
|
|
|
@@ -20,7 +22,7 @@ Ejecuta el bundle:
|
|
|
20
22
|
bundle install
|
|
21
23
|
```
|
|
22
24
|
|
|
23
|
-
Corre el instalador para generar la configuración:
|
|
25
|
+
Corre el instalador para generar la configuración inicial:
|
|
24
26
|
|
|
25
27
|
```bash
|
|
26
28
|
rails g bug_bunny:install
|
|
@@ -30,11 +32,9 @@ rails g bug_bunny:install
|
|
|
30
32
|
|
|
31
33
|
## ⚙️ Configuración
|
|
32
34
|
|
|
33
|
-
Configura tus credenciales y el Pool de conexiones en el inicializador.
|
|
35
|
+
Configura tus credenciales y el Pool de conexiones en el inicializador `config/initializers/bug_bunny.rb`.
|
|
34
36
|
|
|
35
37
|
```ruby
|
|
36
|
-
# config/initializers/bug_bunny.rb
|
|
37
|
-
|
|
38
38
|
BugBunny.configure do |config|
|
|
39
39
|
config.host = ENV.fetch('RABBITMQ_HOST', 'localhost')
|
|
40
40
|
config.username = ENV.fetch('RABBITMQ_USER', 'guest')
|
|
@@ -46,7 +46,9 @@ BugBunny.configure do |config|
|
|
|
46
46
|
config.network_recovery_interval = 5
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
# Definimos el Pool Global
|
|
49
|
+
# ⚠️ CRÍTICO: Definimos el Pool Global
|
|
50
|
+
# Es vital usar ConnectionPool para garantizar la seguridad en entornos
|
|
51
|
+
# multi-hilo como Puma o Sidekiq.
|
|
50
52
|
BUG_BUNNY_POOL = ConnectionPool.new(size: ENV.fetch('RAILS_MAX_THREADS', 5).to_i, timeout: 5) do
|
|
51
53
|
BugBunny.create_connection
|
|
52
54
|
end
|
|
@@ -59,128 +61,152 @@ BugBunny::Resource.connection_pool = BUG_BUNNY_POOL
|
|
|
59
61
|
|
|
60
62
|
## 🚀 Modo Resource (ORM / Active Record)
|
|
61
63
|
|
|
62
|
-
Define modelos que actúan como
|
|
64
|
+
Define modelos que actúan como proxies de recursos remotos. BugBunny separa la **Lógica de Transporte** (RabbitMQ) de la **Lógica de Aplicación** (Controladores).
|
|
63
65
|
|
|
64
|
-
###
|
|
65
|
-
Ideal cuando quieres enrutar por acción. La Routing Key se genera automáticamente usando `resource_name.action`.
|
|
66
|
+
### Definición del Modelo
|
|
66
67
|
|
|
67
68
|
```ruby
|
|
68
69
|
class RemoteUser < BugBunny::Resource
|
|
69
|
-
#
|
|
70
|
+
# 1. Configuración de Transporte
|
|
70
71
|
self.exchange = 'app.topic'
|
|
71
72
|
self.exchange_type = 'topic'
|
|
72
73
|
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
74
|
+
# 2. Configuración Lógica (Routing)
|
|
75
|
+
# Define el nombre del recurso. Se usa para:
|
|
76
|
+
# - Routing Key automática: 'users' (Topic)
|
|
77
|
+
# - URL Base: 'users'
|
|
76
78
|
self.resource_name = 'users'
|
|
77
79
|
|
|
78
|
-
# No necesitas definir atributos
|
|
79
|
-
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Escenario B: Routing Estático (Direct / Cola Dedicada)
|
|
83
|
-
Ideal cuando quieres enviar todo a una cola específica (ej: un Manager), independientemente de la acción.
|
|
84
|
-
|
|
85
|
-
```ruby
|
|
86
|
-
class BoxManager < BugBunny::Resource
|
|
87
|
-
# --- Configuración ---
|
|
88
|
-
self.exchange = 'warehouse.direct'
|
|
89
|
-
self.exchange_type = 'direct'
|
|
90
|
-
|
|
91
|
-
# FORZAMOS LA ROUTING KEY.
|
|
92
|
-
# Todo viaja con esta key, sin importar la acción.
|
|
93
|
-
self.routing_key = 'manager_queue'
|
|
94
|
-
|
|
95
|
-
# Define el nombre lógico para el Controlador.
|
|
96
|
-
# Header Type: 'box_manager/create', 'box_manager/show/12'
|
|
97
|
-
self.resource_name = 'box_manager'
|
|
80
|
+
# Nota: BugBunny es Schema-less. No necesitas definir atributos.
|
|
81
|
+
# Soporta acceso dinámico: user.Name, user.email, etc.
|
|
98
82
|
end
|
|
99
83
|
```
|
|
100
84
|
|
|
101
|
-
### Consumiendo el Servicio (CRUD)
|
|
85
|
+
### Consumiendo el Servicio (CRUD RESTful)
|
|
102
86
|
|
|
103
|
-
|
|
87
|
+
BugBunny traduce automáticamente las llamadas de Ruby a peticiones HTTP simuladas.
|
|
104
88
|
|
|
105
89
|
```ruby
|
|
106
|
-
# --- READ (
|
|
107
|
-
#
|
|
108
|
-
# Routing Key: "users
|
|
90
|
+
# --- READ COLLECTION (Index) ---
|
|
91
|
+
# Envia: GET users?active=true
|
|
92
|
+
# Routing Key: "users"
|
|
109
93
|
users = RemoteUser.where(active: true)
|
|
110
94
|
|
|
111
|
-
# --- READ (
|
|
112
|
-
#
|
|
113
|
-
# Routing Key: "users
|
|
95
|
+
# --- READ MEMBER (Show) ---
|
|
96
|
+
# Envia: GET users/123
|
|
97
|
+
# Routing Key: "users"
|
|
114
98
|
user = RemoteUser.find(123)
|
|
115
|
-
puts user.
|
|
99
|
+
puts user.email
|
|
116
100
|
|
|
117
101
|
# --- CREATE ---
|
|
118
|
-
#
|
|
119
|
-
|
|
102
|
+
# Envia: POST users
|
|
103
|
+
# Routing Key: "users"
|
|
104
|
+
# Body: { "email": "test@test.com", "role": "admin" }
|
|
105
|
+
user = RemoteUser.create(email: "test@test.com", role: "admin")
|
|
120
106
|
|
|
121
107
|
# --- UPDATE ---
|
|
122
|
-
#
|
|
108
|
+
# Envia: PUT users/123
|
|
109
|
+
# Routing Key: "users"
|
|
123
110
|
user.update(email: "edit@test.com")
|
|
124
|
-
# Dirty Tracking: Solo se envían los
|
|
111
|
+
# Dirty Tracking: Solo se envían los campos modificados.
|
|
125
112
|
|
|
126
113
|
# --- DESTROY ---
|
|
127
|
-
#
|
|
114
|
+
# Envia: DELETE users/123
|
|
115
|
+
# Routing Key: "users"
|
|
128
116
|
user.destroy
|
|
129
117
|
```
|
|
130
118
|
|
|
119
|
+
### Estrategias de Routing
|
|
120
|
+
|
|
121
|
+
Tienes 3 formas de controlar la `routing_key` hacia donde se envían los mensajes:
|
|
122
|
+
|
|
123
|
+
| Nivel | Método | Descripción | Ejemplo Config |
|
|
124
|
+
| :--- | :--- | :--- | :--- |
|
|
125
|
+
| **1. Dinámico** | `resource_name` | (Por defecto) Usa el nombre del recurso. | `self.resource_name = 'users'` -> Key `users` |
|
|
126
|
+
| **2. Estático** | `routing_key` | Fuerza TODO a una sola cola. | `self.routing_key = 'cola_manager'` |
|
|
127
|
+
| **3. Temporal** | `.with(...)` | Override solo para esa petición. | `User.with(routing_key: 'urgent').create` |
|
|
128
|
+
|
|
131
129
|
---
|
|
132
130
|
|
|
133
131
|
## 🔌 Modo Publisher (Cliente Manual)
|
|
134
132
|
|
|
135
|
-
Si no necesitas mapear un recurso o quieres enviar mensajes crudos
|
|
133
|
+
Si no necesitas mapear un recurso o quieres enviar mensajes crudos, utiliza `BugBunny::Client`. Soporta semántica REST pasando el argumento `method:`.
|
|
136
134
|
|
|
137
135
|
### 1. Instanciar el Cliente
|
|
138
136
|
|
|
139
137
|
```ruby
|
|
140
|
-
# Puedes inyectar middlewares personalizados aquí si lo deseas
|
|
141
138
|
client = BugBunny::Client.new(pool: BUG_BUNNY_POOL) do |conn|
|
|
139
|
+
# Puedes inyectar middlewares aquí
|
|
142
140
|
conn.use BugBunny::Middleware::JsonResponse
|
|
143
141
|
end
|
|
144
142
|
```
|
|
145
143
|
|
|
146
|
-
### 2.
|
|
147
|
-
|
|
144
|
+
### 2. Request (RPC Síncrono)
|
|
145
|
+
|
|
146
|
+
Envía el mensaje, **bloquea el hilo** y espera la respuesta JSON. Ideal para obtener datos.
|
|
148
147
|
|
|
149
148
|
```ruby
|
|
150
|
-
#
|
|
151
|
-
client.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
149
|
+
# GET (Leer)
|
|
150
|
+
response = client.request('users/123', method: :get)
|
|
151
|
+
puts response['body']
|
|
152
|
+
|
|
153
|
+
# POST (Crear / Ejecutar)
|
|
154
|
+
response = client.request('math/calc', method: :post, body: { a: 10, b: 20 })
|
|
155
|
+
|
|
156
|
+
# PUT (Actualizar)
|
|
157
|
+
client.request('users/123', method: :put, body: { active: true })
|
|
158
|
+
|
|
159
|
+
# DELETE (Borrar)
|
|
160
|
+
client.request('users/123', method: :delete)
|
|
157
161
|
```
|
|
158
162
|
|
|
159
|
-
### 3.
|
|
160
|
-
|
|
163
|
+
### 3. Publish (Asíncrono / Fire-and-Forget)
|
|
164
|
+
|
|
165
|
+
Envía el mensaje y retorna inmediatamente. No espera respuesta. Por defecto usa `method: :post` si no se especifica.
|
|
161
166
|
|
|
162
167
|
```ruby
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
exchange: 'rpc.direct',
|
|
167
|
-
routing_key: 'calculator',
|
|
168
|
-
body: { a: 10, b: 20 },
|
|
169
|
-
timeout: 5 # Segundos de espera máxima
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
puts response['body'] # => { "result": 30 }
|
|
168
|
+
# Enviar log o evento
|
|
169
|
+
client.publish('logs/error', method: :post, body: { msg: 'Disk full' })
|
|
170
|
+
```
|
|
173
171
|
|
|
174
|
-
|
|
175
|
-
|
|
172
|
+
### 4. Configuración Avanzada (Bloques)
|
|
173
|
+
|
|
174
|
+
Puedes usar un bloque para configurar opciones de bajo nivel de AMQP (prioridad, expiración, headers, app_id).
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
client.publish('jobs/process') do |req|
|
|
178
|
+
req.method = :post
|
|
179
|
+
req.body = { image_id: 99 }
|
|
180
|
+
|
|
181
|
+
# Metadatos AMQP
|
|
182
|
+
req.priority = 9 # Alta prioridad (0-9)
|
|
183
|
+
req.expiration = '5000' # TTL 5 segundos (ms)
|
|
184
|
+
req.app_id = 'web-frontend'
|
|
185
|
+
req.headers['X-Trace-Id'] = 'abc-123'
|
|
176
186
|
end
|
|
177
187
|
```
|
|
178
188
|
|
|
189
|
+
### 5. Referencia de Opciones
|
|
190
|
+
|
|
191
|
+
Estas opciones pueden pasarse como argumentos (`client.request(key: val)`) o dentro del bloque (`req.key = val`).
|
|
192
|
+
|
|
193
|
+
| Opción / Atributo | Tipo | Descripción | Default |
|
|
194
|
+
| :--- | :--- | :--- | :--- |
|
|
195
|
+
| `body` | `Hash/String` | El contenido del mensaje. | `nil` |
|
|
196
|
+
| `method` | `Symbol` | Verbo HTTP (`:get`, `:post`, `:put`, `:delete`). | `:get` (en request) |
|
|
197
|
+
| `exchange` | `String` | Nombre del Exchange destino. | `''` (Default Ex) |
|
|
198
|
+
| `routing_key` | `String` | Clave de ruteo. Si falta, usa el `path`. | `path` |
|
|
199
|
+
| `headers` | `Hash` | Headers personalizados. | `{}` |
|
|
200
|
+
| `timeout` | `Integer` | (Solo RPC) Segundos máx de espera. | Config global |
|
|
201
|
+
| `app_id` | `String` | ID de la aplicación origen. | `nil` |
|
|
202
|
+
| `priority` | `Integer` | Prioridad del mensaje (0-9). | `0` |
|
|
203
|
+
| `expiration` | `String` | TTL del mensaje en ms. | `nil` |
|
|
204
|
+
|
|
179
205
|
---
|
|
180
206
|
|
|
181
207
|
## 📡 Modo Servidor (El Worker)
|
|
182
208
|
|
|
183
|
-
BugBunny incluye un **Router Inteligente** que
|
|
209
|
+
BugBunny incluye un **Router Inteligente** que funciona igual que el `config/routes.rb` de Rails. Infiere la acción basándose en el **Verbo HTTP** y la estructura de la **URL**.
|
|
184
210
|
|
|
185
211
|
### 1. Definir Controladores
|
|
186
212
|
|
|
@@ -190,26 +216,19 @@ Crea tus controladores en `app/rabbit/controllers/`. Heredan de `BugBunny::Contr
|
|
|
190
216
|
# app/rabbit/controllers/users_controller.rb
|
|
191
217
|
class UsersController < BugBunny::Controller
|
|
192
218
|
|
|
193
|
-
#
|
|
219
|
+
# GET users
|
|
194
220
|
def index
|
|
195
|
-
# params fusiona Query Params y Body
|
|
196
221
|
users = User.where(active: params[:active])
|
|
197
222
|
render status: 200, json: users
|
|
198
223
|
end
|
|
199
224
|
|
|
200
|
-
#
|
|
225
|
+
# GET users/123
|
|
201
226
|
def show
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if user
|
|
206
|
-
render status: 200, json: user
|
|
207
|
-
else
|
|
208
|
-
render status: 404, json: { error: 'Not Found' }
|
|
209
|
-
end
|
|
227
|
+
user = User.find(params[:id])
|
|
228
|
+
render status: 200, json: user
|
|
210
229
|
end
|
|
211
230
|
|
|
212
|
-
#
|
|
231
|
+
# POST users
|
|
213
232
|
def create
|
|
214
233
|
user = User.new(params)
|
|
215
234
|
if user.save
|
|
@@ -219,10 +238,33 @@ class UsersController < BugBunny::Controller
|
|
|
219
238
|
render status: 422, json: { errors: user.errors }
|
|
220
239
|
end
|
|
221
240
|
end
|
|
241
|
+
|
|
242
|
+
# PUT users/123
|
|
243
|
+
def update
|
|
244
|
+
# ...
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# DELETE users/123
|
|
248
|
+
def destroy
|
|
249
|
+
# ...
|
|
250
|
+
end
|
|
222
251
|
end
|
|
223
252
|
```
|
|
224
253
|
|
|
225
|
-
### 2.
|
|
254
|
+
### 2. Tabla de Ruteo (Convención)
|
|
255
|
+
|
|
256
|
+
El Router despacha automáticamente según esta tabla:
|
|
257
|
+
|
|
258
|
+
| Header `x-http-method` | Header `type` (URL) | Controlador | Acción |
|
|
259
|
+
| :--- | :--- | :--- | :--- |
|
|
260
|
+
| `GET` | `users` | `UsersController` | `index` |
|
|
261
|
+
| `GET` | `users/12` | `UsersController` | `show` |
|
|
262
|
+
| `POST` | `users` | `UsersController` | `create` |
|
|
263
|
+
| `PUT` | `users/12` | `UsersController` | `update` |
|
|
264
|
+
| `DELETE` | `users/12` | `UsersController` | `destroy` |
|
|
265
|
+
| `POST` | `users/12/promote` | `UsersController` | `promote` (Custom) |
|
|
266
|
+
|
|
267
|
+
### 3. Ejecutar el Worker
|
|
226
268
|
|
|
227
269
|
```bash
|
|
228
270
|
bundle exec rake bug_bunny:work
|
|
@@ -232,21 +274,20 @@ bundle exec rake bug_bunny:work
|
|
|
232
274
|
|
|
233
275
|
## 🏗 Arquitectura REST-over-AMQP
|
|
234
276
|
|
|
235
|
-
BugBunny desacopla el transporte de la lógica usando headers.
|
|
277
|
+
BugBunny desacopla el transporte de la lógica usando headers AMQP estándar.
|
|
236
278
|
|
|
237
|
-
| Concepto | REST (HTTP) | BugBunny (AMQP) |
|
|
238
|
-
| :--- | :--- | :--- |
|
|
239
|
-
| **
|
|
240
|
-
| **
|
|
241
|
-
| **Destino
|
|
242
|
-
| **
|
|
243
|
-
| **Status** | HTTP Code (200, 404) | JSON Response `status` | N/A |
|
|
279
|
+
| Concepto | REST (HTTP) | BugBunny (AMQP) |
|
|
280
|
+
| :--- | :--- | :--- |
|
|
281
|
+
| **Recurso** | `POST /users` | Header `type`: `users` + Header `x-http-method`: `POST` |
|
|
282
|
+
| **Parametros** | Query String / Body | Header `type` (Query) + Body (Payload) |
|
|
283
|
+
| **Destino** | DNS / IP | Routing Key (ej: `users`) |
|
|
284
|
+
| **Status** | HTTP Code (200, 404) | JSON Response `status` |
|
|
244
285
|
|
|
245
286
|
---
|
|
246
287
|
|
|
247
288
|
## 🛠 Middlewares
|
|
248
289
|
|
|
249
|
-
BugBunny usa una pila de middlewares para procesar respuestas.
|
|
290
|
+
BugBunny usa una pila de middlewares para procesar peticiones y respuestas, permitiendo logging, manejo de errores y transformación de datos.
|
|
250
291
|
|
|
251
292
|
```ruby
|
|
252
293
|
# Configuración global en el Resource
|
|
@@ -259,12 +300,13 @@ BugBunny::Resource.client_middleware do |conn|
|
|
|
259
300
|
end
|
|
260
301
|
```
|
|
261
302
|
|
|
262
|
-
### Excepciones
|
|
303
|
+
### Excepciones Soportadas
|
|
263
304
|
|
|
264
|
-
* `BugBunny::
|
|
265
|
-
* `BugBunny::NotFound` (404)
|
|
266
|
-
* `BugBunny::RequestTimeout
|
|
267
|
-
* `BugBunny::
|
|
305
|
+
* `BugBunny::BadRequest` (400)
|
|
306
|
+
* `BugBunny::NotFound` (404)
|
|
307
|
+
* `BugBunny::RequestTimeout` (408)
|
|
308
|
+
* `BugBunny::UnprocessableEntity` (422) - Incluye errores de validación.
|
|
309
|
+
* `BugBunny::InternalServerError` (500)
|
|
268
310
|
|
|
269
311
|
---
|
|
270
312
|
|
data/lib/bug_bunny/client.rb
CHANGED
|
@@ -4,31 +4,25 @@ require_relative 'middleware/stack'
|
|
|
4
4
|
module BugBunny
|
|
5
5
|
# Cliente principal para realizar peticiones a RabbitMQ.
|
|
6
6
|
#
|
|
7
|
-
# Implementa el patrón "Onion Middleware" (Arquitectura de Cebolla) similar a Faraday
|
|
8
|
-
#
|
|
9
|
-
# como las respuestas entrantes mediante una pila de middlewares.
|
|
7
|
+
# Implementa el patrón "Onion Middleware" (Arquitectura de Cebolla) similar a Faraday.
|
|
8
|
+
# Mantiene una interfaz flexible donde el verbo HTTP se pasa como opción.
|
|
10
9
|
#
|
|
11
|
-
# @example
|
|
12
|
-
# client
|
|
10
|
+
# @example Petición RPC (GET)
|
|
11
|
+
# client.request('users/123', method: :get)
|
|
13
12
|
#
|
|
14
|
-
# @example
|
|
15
|
-
# client
|
|
16
|
-
# conn.use BugBunny::Middleware::RaiseError
|
|
17
|
-
# conn.use BugBunny::Middleware::JsonResponse
|
|
18
|
-
# conn.use BugBunny::Middleware::Logger, Rails.logger
|
|
19
|
-
# end
|
|
13
|
+
# @example Publicación Fire-and-Forget (POST)
|
|
14
|
+
# client.publish('logs', method: :post, body: { msg: 'Error' })
|
|
20
15
|
class Client
|
|
21
16
|
# @return [ConnectionPool] El pool de conexiones subyacente a RabbitMQ.
|
|
22
17
|
attr_reader :pool
|
|
23
18
|
|
|
24
|
-
# @return [BugBunny::Middleware::Stack] La pila de middlewares configurada
|
|
19
|
+
# @return [BugBunny::Middleware::Stack] La pila de middlewares configurada.
|
|
25
20
|
attr_reader :stack
|
|
26
21
|
|
|
27
22
|
# Inicializa un nuevo cliente.
|
|
28
23
|
#
|
|
29
24
|
# @param pool [ConnectionPool] Pool de conexiones a RabbitMQ configurado previamente.
|
|
30
25
|
# @yield [stack] Bloque opcional para configurar la pila de middlewares.
|
|
31
|
-
# @yieldparam stack [BugBunny::Middleware::Stack] El objeto stack para registrar middlewares con {#use}.
|
|
32
26
|
# @raise [ArgumentError] Si no se proporciona un `pool`.
|
|
33
27
|
def initialize(pool:)
|
|
34
28
|
raise ArgumentError, "BugBunny::Client requiere un 'pool:'" if pool.nil?
|
|
@@ -39,21 +33,16 @@ module BugBunny
|
|
|
39
33
|
|
|
40
34
|
# Realiza una petición Síncrona (RPC / Request-Response).
|
|
41
35
|
#
|
|
42
|
-
# Envía un mensaje y bloquea la ejecución del hilo actual hasta recibir
|
|
43
|
-
# correlacionada del servidor o hasta que se supere el tiempo de espera (timeout).
|
|
36
|
+
# Envía un mensaje y bloquea la ejecución del hilo actual hasta recibir respuesta.
|
|
44
37
|
#
|
|
45
|
-
# @param url [String] La ruta
|
|
46
|
-
# @param args [Hash] Opciones de configuración
|
|
47
|
-
# @option args [
|
|
48
|
-
# @option args [
|
|
49
|
-
# @option args [
|
|
50
|
-
# @option args [
|
|
51
|
-
# @
|
|
52
|
-
# @
|
|
53
|
-
# @yield [req] Bloque opcional para configurar el objeto Request directamente.
|
|
54
|
-
# @yieldparam req [BugBunny::Request] Objeto request configurable.
|
|
55
|
-
# @return [Hash] La respuesta del servidor, conteniendo habitualmente `status` y `body`.
|
|
56
|
-
# @raise [BugBunny::RequestTimeout] Si no se recibe respuesta en el tiempo límite.
|
|
38
|
+
# @param url [String] La ruta del recurso (ej: 'users/1').
|
|
39
|
+
# @param args [Hash] Opciones de configuración.
|
|
40
|
+
# @option args [Symbol] :method El verbo HTTP (:get, :post, :put, :delete). Default: :get.
|
|
41
|
+
# @option args [Object] :body El cuerpo del mensaje.
|
|
42
|
+
# @option args [Hash] :headers Headers AMQP adicionales.
|
|
43
|
+
# @option args [Integer] :timeout Tiempo máximo de espera.
|
|
44
|
+
# @yield [req] Bloque para configurar el objeto Request directamente.
|
|
45
|
+
# @return [Hash] La respuesta del servidor.
|
|
57
46
|
def request(url, **args)
|
|
58
47
|
run_in_pool(:rpc, url, args) do |req|
|
|
59
48
|
yield req if block_given?
|
|
@@ -62,13 +51,9 @@ module BugBunny
|
|
|
62
51
|
|
|
63
52
|
# Realiza una publicación Asíncrona (Fire-and-Forget).
|
|
64
53
|
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
# @param url [String] La ruta o acción del mensaje.
|
|
69
|
-
# @param args [Hash] Mismas opciones que {#request}, excepto `:timeout` (no aplica).
|
|
70
|
-
# @yield [req] Bloque opcional para configurar el objeto Request.
|
|
71
|
-
# @yieldparam req [BugBunny::Request] Objeto request configurable.
|
|
54
|
+
# @param url [String] La ruta del evento/recurso.
|
|
55
|
+
# @param args [Hash] Mismas opciones que {#request}, excepto `:timeout`.
|
|
56
|
+
# @yield [req] Bloque para configurar el objeto Request.
|
|
72
57
|
# @return [void]
|
|
73
58
|
def publish(url, **args)
|
|
74
59
|
run_in_pool(:fire, url, args) do |req|
|
|
@@ -78,16 +63,14 @@ module BugBunny
|
|
|
78
63
|
|
|
79
64
|
private
|
|
80
65
|
|
|
81
|
-
# Ejecuta la lógica de envío dentro del contexto del Pool
|
|
82
|
-
#
|
|
83
|
-
# @param method_name [Symbol] El método a invocar en el Producer (:rpc o :fire).
|
|
84
|
-
# @param url [String] La URL/Acción del request.
|
|
85
|
-
# @param args [Hash] Argumentos pasados al método público.
|
|
66
|
+
# Ejecuta la lógica de envío dentro del contexto del Pool.
|
|
67
|
+
# Mapea los argumentos al objeto Request y ejecuta la cadena de middlewares.
|
|
86
68
|
def run_in_pool(method_name, url, args)
|
|
87
69
|
# 1. Builder del Request
|
|
88
70
|
req = BugBunny::Request.new(url)
|
|
89
71
|
|
|
90
72
|
# 2. Syntactic Sugar: Mapeo de argumentos a atributos del Request
|
|
73
|
+
req.method = args[:method] if args[:method]
|
|
91
74
|
req.body = args[:body] if args[:body]
|
|
92
75
|
req.exchange = args[:exchange] if args[:exchange]
|
|
93
76
|
req.exchange_type = args[:exchange_type] if args[:exchange_type]
|