bug_bunny 5.0.0 → 5.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1076566b6a3b8c25f6f00fc624c92681e8db881ffc3b8d6640da3bb7158333b
4
- data.tar.gz: 2510fe871ec6f32804dba3faa7f1110408656e8cff30d2c0d537c7d0b30e1686
3
+ metadata.gz: 877e5f5961f53d587b2bc67e6a8c39d247b996e1272ddad076236633df65f267
4
+ data.tar.gz: 6928594ae30624f1dccd2841594bee4b0faed40ee0cd238a69387399559a757b
5
5
  SHA512:
6
- metadata.gz: 13525491f89b99f1e1be12663f64990109e17b9da22e034ecea463a3fda5ff68abfb3ab10fdf274d43df5460b3c74eabd14e1699e2af7f8c38e608a78e22cb69
7
- data.tar.gz: 9f31c6ed9fbfff650e2770f970ab94e49979f8eec8b254c212817ca410106b4f4293cc9b6cde87f382145a2483f639aabe0998134c5656b7ec6b053aebcb7c73
6
+ metadata.gz: fea6d7206396aae6ff2b35277e651ed944bee590f74200a392f2fb962af8757f56057b6d637434a91603d272b59b6215b459b330e98999d37f72d1c2ad6af763
7
+ data.tar.gz: b9d531b234ec094ab7127ea5921506d92a32e57961463904e6a62e158ce8a8e3020368ff1c6006cb30ae62a5beea942f3c004ce33e91362c910de2e87165aa6e
data/AGENTS.md CHANGED
@@ -17,7 +17,7 @@ proyecto y convenciones de equipo, ver `CLAUDE.md`. Para la entrada humana, ver
17
17
  | Errores | `docs/errors/errors.md` | completo (§a/b/d estructura + §c política inferida) | jerarquía de excepciones públicas, mapeo `status→excepción`, shape del payload, política retry |
18
18
  | Configuración | `docs/config/configuracion.md` | completo (estructura + enrich §f/g/h) | opciones de `Configuration`, defaults, failure-mode/threading, inyecciones del `Railtie`, ENV sugeridas |
19
19
  | Dependencias consumidas | `docs/consumed/rabbitmq.md` | completo (estructura + enrich §c/e) | qué consume del broker RabbitMQ vía `bunny`, mapeo error-Bunny→excepción, retry/degradación |
20
- | Test | `docs/test/test.md` | completo (estructura + enrich §e-h) | suites RSpec/Minitest, CI, contract-assessment, link a incidentes (#49/#52) |
20
+ | Test | `docs/test/testing.md` | completo (estructura + enrich §e-h) | suites RSpec/Minitest, CI, contract-assessment, link a incidentes (#49/#52) |
21
21
  | Release | `docs/release/release.md` | completo | patrón gema-tag, versionado, publish a RubyGems |
22
22
  | Datos | — | n/a | gema sin DB |
23
23
  | Operaciones / Interfaz / Topología | — | dev-structure F2 no implementado | contrato embebido en `README.md`/`skill/SKILL.md` (interim RFC-008 §2) |
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [5.1.0] - 2026-07-22
4
+
5
+ ### Nuevas funcionalidades
6
+ - **Hook `extra_top_level_params` en `Resource#save`:** una subclase puede sobrescribirlo (default `{}`) para mergear datos TOP-LEVEL hermanos del recurso en el body del `save` —fuera del wrapper `param_key`— sin que sean atributos del recurso ni se persistan en el modelo. Caso de uso: un dato de transporte (p. ej. una credencial) que el servidor lee como `params[:x]`. No-breaking (el default `{}` deja el body idéntico para los recursos que no lo sobrescriben). — @Pslp
7
+
8
+ ### Documentación
9
+ - `docs/test/` incrementado (spec `extra_top_level_params`, conteos 16 unit / 23 total) y renombrado a `docs/test/testing.md` (filename canónico RFC-013). Hook documentado en el contrato interino `skill/references/resource.md`. — @Pslp
10
+
3
11
  ## [5.0.0] - 2026-07-01
4
12
 
5
13
  > **BREAKING.** Se elimina la constante pública `BugBunny::SecurityError`. Aunque la excepción nunca se *levantaba*, su **ausencia rompe en evaluación**: un `rescue BugBunny::SecurityError` en un consumidor resuelve la constante cuando *cualquier* excepción entra a ese bloque → `NameError` que enmascara la excepción original. Por eso es breaking real, no inerte.
@@ -1,14 +1,14 @@
1
1
  # Test — bug_bunny
2
2
 
3
3
  > meta: artefacto test · RFC-013 · generado `arch-structure` (§a-§d) +
4
- > `arch-enrich` (§e-§h) · anclado a `24ea397`, `Rakefile`, `bug_bunny.gemspec`,
4
+ > `arch-enrich` (§e-§h) · anclado a `7bf1da7`, `Rakefile`, `bug_bunny.gemspec`,
5
5
  > `spec/spec_helper.rb`, `spec/support/integration_helper.rb`,
6
- > `.github/workflows/main.yml`, `CHANGELOG.md` · fecha 2026-06-30 · cobertura:
6
+ > `.github/workflows/main.yml`, `CHANGELOG.md` · fecha 2026-07-22 · cobertura:
7
7
  > §a-§d (estructura) + §e-§h (enrich, anclado a specs/CHANGELOG) completas.
8
8
 
9
9
  ## 1. Resumen
10
10
 
11
- Suite principal **RSpec** (`spec/`, 22 specs: 15 unit + 7 integration). Tarea
11
+ Suite principal **RSpec** (`spec/`, 23 specs: 16 unit + 7 integration). Tarea
12
12
  `:test` legacy de **Minitest** (`test/`, 2 archivos) fuera del default y del CI.
13
13
  CI corre `bundle exec rake` (= `:spec`) en Ruby 3.4.4. Sin coverage tool
14
14
  configurado.
@@ -19,7 +19,7 @@ configurado.
19
19
 
20
20
  | framework | dir | nivel | nº | propósito |
21
21
  |---|---|---|---|---|
22
- | **RSpec** `~> 3.0` | `spec/unit/` | unit | 15 | client/session pool, configuration, consumer, producer, controller, raise_error, remote_error, request, route, observability, otel, resource, middleware |
22
+ | **RSpec** `~> 3.0` | `spec/unit/` | unit | 16 | client/session pool, configuration, consumer, producer, controller, raise_error, remote_error, request, route, observability, otel, resource, middleware, `extra_top_level_params` (hook de params hermanos en `Resource#save`) |
23
23
  | **RSpec** | `spec/integration/` | integration | 7 | client, consumer_middleware, controller, error_handling, infrastructure, publisher_confirms, resource — **requieren RabbitMQ real** (usan `BugBunny.create_connection` + pool) |
24
24
  | **Minitest** `~> 5.0` (+ `mocha`, `minitest-reporters`) | `test/integration/` | integration (legacy) | 2 | `manual_client_test.rb`, `infrastructure_test.rb` — tarea `:test`, **no** en default ni CI |
25
25
 
@@ -60,7 +60,7 @@ umbral de coverage declarado.
60
60
  - **Integration specs no corren en CI:** `main.yml` no declara servicio RabbitMQ;
61
61
  las 7 integration specs **se skipean** vía `rabbitmq_available?`
62
62
  (`spec/support/integration_helper.rb:14`, ver `publisher_confirms_spec.rb:10`).
63
- En CI solo se ejercitan las **15 unit specs** → el contrato AMQP real (publish/
63
+ En CI solo se ejercitan las **16 unit specs** → el contrato AMQP real (publish/
64
64
  consume/confirms contra broker) **no se valida en pipeline**, solo localmente
65
65
  con broker. Gap relevante.
66
66
  - **Sin medición de cobertura:** no hay SimpleCov ni umbral → la cobertura no está
@@ -440,7 +440,7 @@ module BugBunny
440
440
  rk = calculate_routing_key(id)
441
441
  flat_payload = changes_to_send
442
442
  key = self.class.param_key
443
- wrapped_payload = { key => flat_payload }
443
+ wrapped_payload = { key => flat_payload }.merge(extra_top_level_params)
444
444
 
445
445
  path = is_new ? self.class.resource_name : "#{self.class.resource_name}/#{id}"
446
446
  method = is_new ? :post : :put
@@ -467,6 +467,20 @@ module BugBunny
467
467
  false
468
468
  end
469
469
 
470
+ # Params top-level extra a incluir en el body del `save`, JUNTO al recurso
471
+ # envuelto en `param_key` (no dentro de él). Default: ninguno.
472
+ #
473
+ # Las subclases lo sobrescriben para enviar datos HERMANOS del recurso que no
474
+ # forman parte de sus atributos ni deben persistir en el modelo — p. ej. una
475
+ # credencial de transporte que el servidor lee como `params[:x]` y que no se
476
+ # serializa dentro del recurso. Se mergea sobre `{ param_key => attrs }`, así
477
+ # que no debe usar `param_key` como clave (colisionaría con el recurso).
478
+ #
479
+ # @return [Hash]
480
+ def extra_top_level_params
481
+ {}
482
+ end
483
+
470
484
  # Elimina el recurso del servidor remoto (DELETE).
471
485
  #
472
486
  # @return [Boolean]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BugBunny
4
- VERSION = '5.0.0'
4
+ VERSION = '5.1.0'
5
5
  end
@@ -65,6 +65,24 @@ order.errors # ActiveModel::Errors
65
65
  - **Existente** (`persisted? == true`): Envía PUT solo con atributos cambiados (`changes_to_send`).
66
66
  - Captura `BugBunny::UnprocessableEntity` (422) y carga `resource.errors`. Retorna `false`.
67
67
 
68
+ ### Params top-level hermanos: `extra_top_level_params`
69
+
70
+ Por default `save` envía el body `{ param_key => attrs }`. Una subclase puede sobrescribir `extra_top_level_params` (default `{}`) para mergear datos **top-level hermanos** del recurso —fuera del wrapper `param_key`— sin que sean atributos del recurso ni se persistan en el modelo. Caso de uso: un dato de transporte (p. ej. una credencial) que el servidor lee como `params[:x]`.
71
+
72
+ ```ruby
73
+ class Service < BugBunny::Resource
74
+ self.param_key = 'service'
75
+ attr_accessor :registry_auth # dato transiente, NO atributo del recurso
76
+
77
+ def extra_top_level_params
78
+ registry_auth ? { registry_auth: registry_auth } : {}
79
+ end
80
+ end
81
+ # save envía: { 'service' => { ...attrs }, registry_auth: '...' }
82
+ ```
83
+
84
+ No debe usar `param_key` como clave (colisionaría con el wrapper del recurso).
85
+
68
86
  ## Contexto Dinámico (.with)
69
87
 
70
88
  ### Forma de bloque (recomendada)
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ # Hook `extra_top_level_params`: permite a una subclase mandar params HERMANOS del
6
+ # recurso en el body del `save` (top-level, junto al `param_key`), sin que formen
7
+ # parte de los atributos del recurso ni se persistan en el modelo. Caso de uso:
8
+ # una credencial de transporte que el servidor lee como `params[:x]`.
9
+ module ExtraTopLevelParamsSpec
10
+ # Recurso sin override: usa el hook por default (no manda extras).
11
+ class Plain < BugBunny::Resource
12
+ self.resource_name = 'widget'
13
+ self.param_key = 'widget'
14
+ self.exchange = 'etlp_ex'
15
+ self.exchange_type = 'direct'
16
+ attribute :name, :string
17
+ end
18
+
19
+ # Recurso que sobrescribe el hook para mandar un sibling transiente (no atributo).
20
+ class WithSibling < BugBunny::Resource
21
+ self.resource_name = 'service'
22
+ self.param_key = 'service'
23
+ self.exchange = 'etlp_ex'
24
+ self.exchange_type = 'direct'
25
+ attribute :name, :string
26
+
27
+ attr_accessor :registry_auth
28
+
29
+ def extra_top_level_params
30
+ registry_auth ? { registry_auth: registry_auth } : {}
31
+ end
32
+ end
33
+ end
34
+
35
+ RSpec.describe 'BugBunny::Resource#extra_top_level_params' do
36
+ let(:client) { instance_double(BugBunny::Client) }
37
+
38
+ before do
39
+ allow(client).to receive(:request) do |_path, **args|
40
+ @sent_body = args[:body]
41
+ { 'body' => {} }
42
+ end
43
+ end
44
+
45
+ def save_with_stub(resource)
46
+ allow(resource).to receive(:bug_bunny_client).and_return(client)
47
+ resource.save
48
+ end
49
+
50
+ it 'default: el body solo envuelve el recurso en param_key (sin extras)' do
51
+ save_with_stub(ExtraTopLevelParamsSpec::Plain.new(name: 'w'))
52
+
53
+ expect(@sent_body.keys).to eq(['widget'])
54
+ end
55
+
56
+ it 'override: agrega el sibling top-level JUNTO al recurso envuelto, no adentro' do
57
+ resource = ExtraTopLevelParamsSpec::WithSibling.new(name: 's').tap { |r| r.registry_auth = 'b64cred' }
58
+
59
+ save_with_stub(resource)
60
+
61
+ expect(@sent_body[:registry_auth]).to eq('b64cred')
62
+ expect(@sent_body['service']).not_to have_key('registry_auth')
63
+ end
64
+
65
+ it 'override sin valor: no agrega el sibling (hook devuelve {})' do
66
+ save_with_stub(ExtraTopLevelParamsSpec::WithSibling.new(name: 's'))
67
+
68
+ expect(@sent_body.keys).to eq(['service'])
69
+ end
70
+ 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: 5.0.0
4
+ version: 5.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - gabix
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-01 00:00:00.000000000 Z
11
+ date: 2026-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bunny
@@ -240,7 +240,7 @@ files:
240
240
  - docs/errors/errors.md
241
241
  - docs/glossary/glossary.md
242
242
  - docs/release/release.md
243
- - docs/test/test.md
243
+ - docs/test/testing.md
244
244
  - initializer_example.rb
245
245
  - lib/bug_bunny.rb
246
246
  - lib/bug_bunny/client.rb
@@ -294,6 +294,7 @@ files:
294
294
  - spec/unit/consumer_middleware_spec.rb
295
295
  - spec/unit/consumer_spec.rb
296
296
  - spec/unit/controller_after_action_spec.rb
297
+ - spec/unit/extra_top_level_params_spec.rb
297
298
  - spec/unit/observability_spec.rb
298
299
  - spec/unit/otel_spec.rb
299
300
  - spec/unit/producer_spec.rb
@@ -313,7 +314,7 @@ metadata:
313
314
  homepage_uri: https://github.com/gedera/bug_bunny
314
315
  source_code_uri: https://github.com/gedera/bug_bunny
315
316
  changelog_uri: https://github.com/gedera/bug_bunny/blob/main/CHANGELOG.md
316
- documentation_uri: https://github.com/gedera/bug_bunny/blob/v5.0.0/skill
317
+ documentation_uri: https://github.com/gedera/bug_bunny/blob/v5.1.0/skill
317
318
  post_install_message:
318
319
  rdoc_options: []
319
320
  require_paths: