bug_bunny 3.0.1 → 3.0.3
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 +27 -0
- data/README.md +166 -197
- data/bin_suite.rb +41 -12
- data/lib/bug_bunny/config.rb +10 -0
- data/lib/bug_bunny/consumer.rb +116 -50
- data/lib/bug_bunny/controller.rb +131 -57
- data/lib/bug_bunny/producer.rb +10 -2
- data/lib/bug_bunny/resource.rb +140 -38
- data/lib/bug_bunny/version.rb +1 -1
- data/lib/bug_bunny.rb +1 -1
- data/test_resource.rb +10 -13
- metadata +21 -19
data/lib/bug_bunny/resource.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
require 'active_model'
|
|
3
3
|
require 'active_support/core_ext/string/inflections'
|
|
4
4
|
require 'uri'
|
|
5
|
+
require 'rack/utils'
|
|
5
6
|
|
|
6
7
|
module BugBunny
|
|
7
8
|
# Clase base para modelos remotos que implementan **Active Record over AMQP (RESTful)**.
|
|
@@ -9,15 +10,21 @@ module BugBunny
|
|
|
9
10
|
# Esta clase transforma operaciones CRUD estándar en peticiones RPC utilizando
|
|
10
11
|
# verbos HTTP semánticos (GET, POST, PUT, DELETE) transportados sobre headers AMQP.
|
|
11
12
|
#
|
|
12
|
-
#
|
|
13
|
+
# También gestiona la serialización automática de parámetros ("wrapping") para
|
|
14
|
+
# compatibilidad con Strong Parameters de Rails.
|
|
15
|
+
#
|
|
16
|
+
# @example Definición de un recurso
|
|
13
17
|
# class User < BugBunny::Resource
|
|
14
18
|
# self.exchange = 'app.topic'
|
|
15
19
|
# self.resource_name = 'users'
|
|
20
|
+
# # Opcional: Personalizar la clave raíz del JSON
|
|
21
|
+
# self.param_key = 'user_data'
|
|
16
22
|
# end
|
|
17
23
|
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
24
|
+
# @example Uso con contexto temporal
|
|
25
|
+
# # La instancia 'user' recordará que debe usar la routing_key 'urgent'
|
|
26
|
+
# user = User.with(routing_key: 'urgent').new(name: 'Gaby')
|
|
27
|
+
# user.save # Enviará a la cola 'urgent' aunque estemos fuera del bloque .with
|
|
21
28
|
class Resource
|
|
22
29
|
include ActiveModel::API
|
|
23
30
|
include ActiveModel::Dirty
|
|
@@ -26,17 +33,39 @@ module BugBunny
|
|
|
26
33
|
|
|
27
34
|
define_model_callbacks :save, :create, :update, :destroy
|
|
28
35
|
|
|
36
|
+
# @return [HashWithIndifferentAccess] Contenedor de los atributos remotos (JSON crudo).
|
|
29
37
|
attr_reader :remote_attributes
|
|
38
|
+
|
|
39
|
+
# @return [Boolean] Indica si el objeto ha sido guardado en el servicio remoto.
|
|
30
40
|
attr_accessor :persisted
|
|
31
41
|
|
|
42
|
+
# @return [String, nil] Routing Key capturada en el momento de la instanciación.
|
|
43
|
+
attr_accessor :routing_key
|
|
44
|
+
|
|
45
|
+
# @return [String, nil] Exchange capturado en el momento de la instanciación.
|
|
46
|
+
attr_accessor :exchange
|
|
47
|
+
|
|
48
|
+
# @return [String, nil] Tipo de Exchange capturado en el momento de la instanciación.
|
|
49
|
+
attr_accessor :exchange_type
|
|
50
|
+
|
|
32
51
|
class << self
|
|
33
|
-
|
|
52
|
+
# Configuración heredable
|
|
53
|
+
attr_writer :connection_pool, :exchange, :exchange_type, :resource_name, :routing_key, :param_key
|
|
34
54
|
|
|
35
|
-
#
|
|
55
|
+
# Lee la configuración del Thread actual (usado por el scope .with).
|
|
56
|
+
# @api private
|
|
57
|
+
def thread_config(key)
|
|
58
|
+
Thread.current["bb_#{object_id}_#{key}"]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Resuelve la configuración buscando en: 1. Thread (Scope), 2. Clase, 3. Herencia.
|
|
36
62
|
# @api private
|
|
37
63
|
def resolve_config(key, instance_var)
|
|
38
|
-
|
|
39
|
-
|
|
64
|
+
# 1. Prioridad: Contexto de hilo (.with)
|
|
65
|
+
val = thread_config(key)
|
|
66
|
+
return val if val
|
|
67
|
+
|
|
68
|
+
# 2. Prioridad: Jerarquía de clases
|
|
40
69
|
target = self
|
|
41
70
|
while target <= BugBunny::Resource
|
|
42
71
|
value = target.instance_variable_get(instance_var)
|
|
@@ -46,15 +75,32 @@ module BugBunny
|
|
|
46
75
|
nil
|
|
47
76
|
end
|
|
48
77
|
|
|
78
|
+
# @return [ConnectionPool] El pool de conexiones asignado.
|
|
49
79
|
def connection_pool; resolve_config(:pool, :@connection_pool); end
|
|
80
|
+
|
|
81
|
+
# @return [String] El exchange configurado.
|
|
82
|
+
# @raise [ArgumentError] Si no se ha definido un exchange.
|
|
50
83
|
def current_exchange; resolve_config(:exchange, :@exchange) || raise(ArgumentError, "Exchange not defined"); end
|
|
84
|
+
|
|
85
|
+
# @return [String] El tipo de exchange (default: direct).
|
|
51
86
|
def current_exchange_type; resolve_config(:exchange_type, :@exchange_type) || 'direct'; end
|
|
52
87
|
|
|
88
|
+
# @return [String] El nombre del recurso (ej: 'users'). Se infiere del nombre de la clase si no existe.
|
|
53
89
|
def resource_name
|
|
54
90
|
resolve_config(:resource_name, :@resource_name) || name.demodulize.underscore.pluralize
|
|
55
91
|
end
|
|
56
92
|
|
|
57
|
-
# Define
|
|
93
|
+
# Define la clave raíz para envolver el payload JSON (Wrapping).
|
|
94
|
+
#
|
|
95
|
+
# Por defecto utiliza `model_name.element`, lo que elimina los namespaces.
|
|
96
|
+
# Ej: `Manager::Service` -> `'service'`.
|
|
97
|
+
#
|
|
98
|
+
# @return [String] La clave paramétrica.
|
|
99
|
+
def param_key
|
|
100
|
+
resolve_config(:param_key, :@param_key) || model_name.element
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Define un middleware para el cliente HTTP/AMQP de este recurso.
|
|
58
104
|
def client_middleware(&block)
|
|
59
105
|
@client_middleware_stack ||= []
|
|
60
106
|
@client_middleware_stack << block
|
|
@@ -72,56 +118,72 @@ module BugBunny
|
|
|
72
118
|
stack
|
|
73
119
|
end
|
|
74
120
|
|
|
121
|
+
# Instancia un cliente configurado con el pool y middlewares del recurso.
|
|
122
|
+
# @return [BugBunny::Client]
|
|
75
123
|
def bug_bunny_client
|
|
76
124
|
pool = connection_pool
|
|
77
125
|
raise BugBunny::Error, "Connection pool missing for #{name}" unless pool
|
|
78
|
-
|
|
126
|
+
|
|
79
127
|
BugBunny::Client.new(pool: pool) do |conn|
|
|
80
128
|
resolve_middleware_stack.each { |block| block.call(conn) }
|
|
81
129
|
end
|
|
82
130
|
end
|
|
83
131
|
|
|
132
|
+
# Ejecuta un bloque (o retorna un Proxy) con una configuración temporal.
|
|
133
|
+
# Útil para cambiar de exchange o routing_key para una operación específica.
|
|
134
|
+
#
|
|
135
|
+
# @example
|
|
136
|
+
# User.with(routing_key: 'urgent').create(params)
|
|
84
137
|
def with(exchange: nil, routing_key: nil, exchange_type: nil, pool: nil)
|
|
85
138
|
keys = { exchange: "bb_#{object_id}_exchange", exchange_type: "bb_#{object_id}_exchange_type", pool: "bb_#{object_id}_pool", routing_key: "bb_#{object_id}_routing_key" }
|
|
86
139
|
old_values = {}
|
|
87
140
|
keys.each { |k, v| old_values[k] = Thread.current[v] }
|
|
141
|
+
|
|
142
|
+
# Seteamos valores temporales
|
|
88
143
|
Thread.current[keys[:exchange]] = exchange if exchange
|
|
89
144
|
Thread.current[keys[:exchange_type]] = exchange_type if exchange_type
|
|
90
145
|
Thread.current[keys[:pool]] = pool if pool
|
|
91
146
|
Thread.current[keys[:routing_key]] = routing_key if routing_key
|
|
147
|
+
|
|
92
148
|
if block_given?
|
|
93
149
|
begin; yield; ensure; keys.each { |k, v| Thread.current[v] = old_values[k] }; end
|
|
94
150
|
else
|
|
95
151
|
ScopeProxy.new(self, keys, old_values)
|
|
96
152
|
end
|
|
97
153
|
end
|
|
98
|
-
|
|
154
|
+
|
|
155
|
+
# Proxy para permitir encadenamiento: User.with(...).find(1)
|
|
99
156
|
class ScopeProxy < BasicObject
|
|
100
157
|
def initialize(target, keys, old_values); @target = target; @keys = keys; @old_values = old_values; end
|
|
101
158
|
def method_missing(method, *args, &block); @target.public_send(method, *args, &block); ensure; @keys.each { |k, v| ::Thread.current[v] = @old_values[k] }; end
|
|
102
159
|
end
|
|
103
160
|
|
|
104
161
|
# Calcula la Routing Key.
|
|
105
|
-
# @
|
|
162
|
+
# @return [String]
|
|
106
163
|
def calculate_routing_key(id = nil)
|
|
107
|
-
|
|
164
|
+
# 1. Contexto .with
|
|
165
|
+
manual_rk = thread_config(:routing_key)
|
|
108
166
|
return manual_rk if manual_rk
|
|
167
|
+
|
|
168
|
+
# 2. Configuración estática
|
|
109
169
|
static_rk = resolve_config(:routing_key, :@routing_key)
|
|
110
170
|
return static_rk if static_rk.present?
|
|
171
|
+
|
|
172
|
+
# 3. Default: Resource name
|
|
111
173
|
resource_name
|
|
112
174
|
end
|
|
113
175
|
|
|
114
|
-
# @!group Acciones CRUD RESTful
|
|
176
|
+
# @!group Acciones CRUD RESTful (Clase)
|
|
115
177
|
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
# @param filters [Hash] Parámetros de consulta (Query params).
|
|
178
|
+
# Busca recursos que coincidan con los filtros.
|
|
179
|
+
# Envía: GET resource?query
|
|
119
180
|
def where(filters = {})
|
|
120
181
|
rk = calculate_routing_key
|
|
121
182
|
path = resource_name
|
|
122
|
-
path += "?#{URI.encode_www_form(filters)}" if filters.present?
|
|
123
183
|
|
|
124
|
-
#
|
|
184
|
+
# Usamos Rack para serializar anidamiento (q[service]=val)
|
|
185
|
+
path += "?#{Rack::Utils.build_nested_query(filters)}" if filters.present?
|
|
186
|
+
|
|
125
187
|
response = bug_bunny_client.request(path, method: :get, exchange: current_exchange, exchange_type: current_exchange_type, routing_key: rk)
|
|
126
188
|
|
|
127
189
|
return [] unless response['body'].is_a?(Array)
|
|
@@ -135,18 +197,16 @@ module BugBunny
|
|
|
135
197
|
|
|
136
198
|
def all; where({}); end
|
|
137
199
|
|
|
138
|
-
#
|
|
139
|
-
#
|
|
140
|
-
# @param id [String, Integer] ID del recurso.
|
|
200
|
+
# Busca un recurso por ID.
|
|
201
|
+
# Envía: GET resource/id
|
|
141
202
|
def find(id)
|
|
142
203
|
rk = calculate_routing_key(id)
|
|
143
204
|
path = "#{resource_name}/#{id}"
|
|
144
205
|
|
|
145
|
-
# REST: GET member
|
|
146
206
|
response = bug_bunny_client.request(path, method: :get, exchange: current_exchange, exchange_type: current_exchange_type, routing_key: rk)
|
|
147
207
|
|
|
148
208
|
return nil if response.nil? || response['status'] == 404
|
|
149
|
-
|
|
209
|
+
|
|
150
210
|
attributes = response['body']
|
|
151
211
|
return nil unless attributes.is_a?(Hash)
|
|
152
212
|
|
|
@@ -156,6 +216,7 @@ module BugBunny
|
|
|
156
216
|
instance
|
|
157
217
|
end
|
|
158
218
|
|
|
219
|
+
# Crea un nuevo recurso.
|
|
159
220
|
def create(payload)
|
|
160
221
|
instance = new(payload)
|
|
161
222
|
instance.save
|
|
@@ -165,36 +226,64 @@ module BugBunny
|
|
|
165
226
|
|
|
166
227
|
# @!group Instancia
|
|
167
228
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
229
|
+
# Inicializa una nueva instancia del recurso.
|
|
230
|
+
#
|
|
231
|
+
# **IMPORTANTE:** Captura la configuración del contexto actual (`.with`)
|
|
232
|
+
# y la guarda en la instancia. Esto permite que objetos creados dentro de un bloque `with`
|
|
233
|
+
# mantengan esa configuración (routing_key, exchange) durante todo su ciclo de vida,
|
|
234
|
+
# incluso si `save` se llama fuera del bloque.
|
|
235
|
+
#
|
|
236
|
+
# @param attributes [Hash] Atributos iniciales.
|
|
173
237
|
def initialize(attributes = {})
|
|
174
238
|
@remote_attributes = {}.with_indifferent_access
|
|
175
239
|
@persisted = false
|
|
240
|
+
|
|
241
|
+
# === CAPTURA DE CONTEXTO ===
|
|
242
|
+
@routing_key = self.class.thread_config(:routing_key)
|
|
243
|
+
@exchange = self.class.thread_config(:exchange)
|
|
244
|
+
@exchange_type = self.class.thread_config(:exchange_type)
|
|
245
|
+
|
|
176
246
|
assign_attributes(attributes)
|
|
177
247
|
super()
|
|
178
248
|
end
|
|
179
249
|
|
|
250
|
+
# Prioridad Routing Key: 1. Instancia (Capturada), 2. Clase
|
|
251
|
+
def calculate_routing_key(id=nil)
|
|
252
|
+
return @routing_key if @routing_key
|
|
253
|
+
self.class.calculate_routing_key(id)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Prioridad Exchange: 1. Instancia (Capturada), 2. Clase
|
|
257
|
+
def current_exchange
|
|
258
|
+
@exchange || self.class.current_exchange
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# Prioridad Exchange Type: 1. Instancia (Capturada), 2. Clase
|
|
262
|
+
def current_exchange_type
|
|
263
|
+
@exchange_type || self.class.current_exchange_type
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def bug_bunny_client; self.class.bug_bunny_client; end
|
|
267
|
+
|
|
180
268
|
def persisted?; !!@persisted; end
|
|
181
269
|
|
|
182
270
|
def assign_attributes(new_attributes)
|
|
183
271
|
return if new_attributes.nil?
|
|
184
272
|
new_attributes.each { |k, v| public_send("#{k}=", v) }
|
|
185
273
|
end
|
|
186
|
-
|
|
274
|
+
|
|
187
275
|
def update(attributes)
|
|
188
276
|
assign_attributes(attributes)
|
|
189
277
|
save
|
|
190
278
|
end
|
|
191
279
|
|
|
280
|
+
# Retorna solo los atributos que han cambiado.
|
|
192
281
|
def changes_to_send
|
|
193
282
|
return changes.transform_values(&:last) unless changes.empty?
|
|
194
283
|
@remote_attributes.except('id', 'ID', 'Id', '_id')
|
|
195
284
|
end
|
|
196
285
|
|
|
197
|
-
#
|
|
286
|
+
# Métodos mágicos para atributos.
|
|
198
287
|
def method_missing(method_name, *args, &block)
|
|
199
288
|
attribute_name = method_name.to_s
|
|
200
289
|
if attribute_name.end_with?('=')
|
|
@@ -225,23 +314,36 @@ module BugBunny
|
|
|
225
314
|
|
|
226
315
|
# @!group Persistencia RESTful
|
|
227
316
|
|
|
228
|
-
# Guarda el registro
|
|
317
|
+
# Guarda el registro.
|
|
318
|
+
# Envía POST si es nuevo, PUT si ya existe.
|
|
319
|
+
#
|
|
320
|
+
# **AUTOMÁTICO:** Envuelve los parámetros en la clave del modelo (`param_key`).
|
|
321
|
+
# Ej: Manager::Service -> "service". Esto facilita `params.require(:service)`.
|
|
322
|
+
#
|
|
323
|
+
# @return [Boolean] true si se guardó correctamente.
|
|
229
324
|
def save
|
|
230
325
|
return false unless valid?
|
|
231
326
|
|
|
232
327
|
run_callbacks(:save) do
|
|
233
328
|
is_new = !persisted?
|
|
234
329
|
rk = calculate_routing_key(id)
|
|
235
|
-
|
|
236
|
-
#
|
|
330
|
+
|
|
331
|
+
# 1. Obtenemos el payload plano (atributos modificados)
|
|
332
|
+
flat_payload = changes_to_send
|
|
333
|
+
|
|
334
|
+
# 2. Wrappeamos automáticamente en la clave del modelo
|
|
335
|
+
key = self.class.param_key
|
|
336
|
+
wrapped_payload = { key => flat_payload }
|
|
337
|
+
|
|
338
|
+
# Mapeo a verbos HTTP
|
|
237
339
|
if is_new
|
|
238
340
|
# REST: POST resource (Create)
|
|
239
341
|
path = self.class.resource_name
|
|
240
|
-
response = bug_bunny_client.request(path, method: :post, exchange: current_exchange, exchange_type: current_exchange_type, routing_key: rk, body:
|
|
342
|
+
response = bug_bunny_client.request(path, method: :post, exchange: current_exchange, exchange_type: current_exchange_type, routing_key: rk, body: wrapped_payload)
|
|
241
343
|
else
|
|
242
344
|
# REST: PUT resource/id (Update)
|
|
243
345
|
path = "#{self.class.resource_name}/#{id}"
|
|
244
|
-
response = bug_bunny_client.request(path, method: :put, exchange: current_exchange, exchange_type: current_exchange_type, routing_key: rk, body:
|
|
346
|
+
response = bug_bunny_client.request(path, method: :put, exchange: current_exchange, exchange_type: current_exchange_type, routing_key: rk, body: wrapped_payload)
|
|
245
347
|
end
|
|
246
348
|
|
|
247
349
|
handle_save_response(response)
|
|
@@ -251,12 +353,12 @@ module BugBunny
|
|
|
251
353
|
false
|
|
252
354
|
end
|
|
253
355
|
|
|
254
|
-
# Elimina el registro
|
|
356
|
+
# Elimina el registro.
|
|
357
|
+
# Envía DELETE resource/id.
|
|
255
358
|
def destroy
|
|
256
359
|
return false unless persisted?
|
|
257
360
|
|
|
258
361
|
run_callbacks(:destroy) do
|
|
259
|
-
# REST: DELETE resource/id
|
|
260
362
|
path = "#{self.class.resource_name}/#{id}"
|
|
261
363
|
rk = calculate_routing_key(id)
|
|
262
364
|
|
data/lib/bug_bunny/version.rb
CHANGED
data/lib/bug_bunny.rb
CHANGED
|
@@ -95,7 +95,7 @@ module BugBunny
|
|
|
95
95
|
username: options[:username] || default.username,
|
|
96
96
|
password: options[:password] || default.password,
|
|
97
97
|
vhost: options[:vhost] || default.vhost,
|
|
98
|
-
logger: options[:logger] || default.
|
|
98
|
+
logger: options[:logger] || default.bunny_logger,
|
|
99
99
|
automatically_recover: options[:automatically_recover] || default.automatically_recover,
|
|
100
100
|
network_recovery_interval: options[:network_recovery_interval] || default.network_recovery_interval,
|
|
101
101
|
connection_timeout: options[:connection_timeout] || default.connection_timeout,
|
data/test_resource.rb
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
require_relative 'test_helper'
|
|
2
2
|
|
|
3
3
|
class TestUser < BugBunny::Resource
|
|
4
|
-
#
|
|
5
|
-
self.connection_pool = -> {
|
|
6
|
-
nil || TEST_POOL
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
# El exchange cambia según el entorno
|
|
10
|
-
self.exchange = -> {
|
|
11
|
-
ENV['IS_STAGING'] ? 'test_exchange' : 'test_exchange'
|
|
12
|
-
}
|
|
4
|
+
# Configuración del Pool
|
|
5
|
+
self.connection_pool = -> { nil || TEST_POOL }
|
|
13
6
|
|
|
7
|
+
# Configuración del Exchange
|
|
8
|
+
self.exchange = -> { ENV['IS_STAGING'] ? 'test_exchange' : 'test_exchange' }
|
|
14
9
|
self.exchange_type = 'topic'
|
|
15
|
-
self.routing_key_prefix = 'test_user'
|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
# ACTUALIZADO v3.0: Usamos resource_name
|
|
12
|
+
self.resource_name = 'test_users'
|
|
13
|
+
|
|
14
|
+
# ELIMINADO: attribute :id, :integer (Causa crash en v3)
|
|
15
|
+
# ELIMINADO: attribute :name, :string (Causa crash en v3)
|
|
20
16
|
|
|
17
|
+
# Las validaciones siguen funcionando igual
|
|
21
18
|
validates :name, presence: true
|
|
22
19
|
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.0.
|
|
4
|
+
version: 3.0.3
|
|
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-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bunny
|
|
@@ -100,14 +100,14 @@ dependencies:
|
|
|
100
100
|
requirements:
|
|
101
101
|
- - ">="
|
|
102
102
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: '0'
|
|
103
|
+
version: '2.0'
|
|
104
104
|
type: :runtime
|
|
105
105
|
prerelease: false
|
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
107
|
requirements:
|
|
108
108
|
- - ">="
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
|
-
version: '0'
|
|
110
|
+
version: '2.0'
|
|
111
111
|
- !ruby/object:Gem::Dependency
|
|
112
112
|
name: ostruct
|
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -128,57 +128,59 @@ dependencies:
|
|
|
128
128
|
requirements:
|
|
129
129
|
- - ">="
|
|
130
130
|
- !ruby/object:Gem::Version
|
|
131
|
-
version: '0'
|
|
131
|
+
version: '2.0'
|
|
132
132
|
type: :development
|
|
133
133
|
prerelease: false
|
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
|
135
135
|
requirements:
|
|
136
136
|
- - ">="
|
|
137
137
|
- !ruby/object:Gem::Version
|
|
138
|
-
version: '0'
|
|
138
|
+
version: '2.0'
|
|
139
139
|
- !ruby/object:Gem::Dependency
|
|
140
140
|
name: rake
|
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
|
142
142
|
requirements:
|
|
143
|
-
- - "
|
|
143
|
+
- - "~>"
|
|
144
144
|
- !ruby/object:Gem::Version
|
|
145
|
-
version: '0'
|
|
145
|
+
version: '13.0'
|
|
146
146
|
type: :development
|
|
147
147
|
prerelease: false
|
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
|
149
149
|
requirements:
|
|
150
|
-
- - "
|
|
150
|
+
- - "~>"
|
|
151
151
|
- !ruby/object:Gem::Version
|
|
152
|
-
version: '0'
|
|
152
|
+
version: '13.0'
|
|
153
153
|
- !ruby/object:Gem::Dependency
|
|
154
154
|
name: rspec
|
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
|
156
156
|
requirements:
|
|
157
|
-
- - "
|
|
157
|
+
- - "~>"
|
|
158
158
|
- !ruby/object:Gem::Version
|
|
159
|
-
version: '0'
|
|
159
|
+
version: '3.0'
|
|
160
160
|
type: :development
|
|
161
161
|
prerelease: false
|
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
|
163
163
|
requirements:
|
|
164
|
-
- - "
|
|
164
|
+
- - "~>"
|
|
165
165
|
- !ruby/object:Gem::Version
|
|
166
|
-
version: '0'
|
|
166
|
+
version: '3.0'
|
|
167
167
|
- !ruby/object:Gem::Dependency
|
|
168
168
|
name: yard
|
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
|
170
170
|
requirements:
|
|
171
|
-
- - "
|
|
171
|
+
- - "~>"
|
|
172
172
|
- !ruby/object:Gem::Version
|
|
173
|
-
version: '0'
|
|
173
|
+
version: '0.9'
|
|
174
174
|
type: :development
|
|
175
175
|
prerelease: false
|
|
176
176
|
version_requirements: !ruby/object:Gem::Requirement
|
|
177
177
|
requirements:
|
|
178
|
-
- - "
|
|
178
|
+
- - "~>"
|
|
179
179
|
- !ruby/object:Gem::Version
|
|
180
|
-
version: '0'
|
|
181
|
-
description:
|
|
180
|
+
version: '0.9'
|
|
181
|
+
description: BugBunny is a lightweight RPC framework for Ruby on Rails over RabbitMQ.
|
|
182
|
+
It simulates a RESTful architecture with an intelligent router, Active Record-like
|
|
183
|
+
resources, and middleware support.
|
|
182
184
|
email:
|
|
183
185
|
- gab.edera@gmail.com
|
|
184
186
|
executables: []
|