@spree/docs 0.1.168 → 0.1.169

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.
@@ -384,10 +384,121 @@ The `compare_at_price` **reader** (which resolves against `cost_currency`) is un
384
384
  Also note:
385
385
 
386
386
  - **Ransack:** `default_price` is no longer a searchable association on `Variant`; query `prices` instead.
387
- - **`Spree::PermittedAttributes`:** the dead `:price` and `:compare_at_price` entries are dropped from `product_attributes` and `variant_attributes`. Prices were already written as nested `prices: [{ amount:, currency: }]` under variants — the top-level keys had no writer behind them.
387
+ - **Prices in permitted params:** the dead `:price` and `:compare_at_price` entries are gone. Prices were already written as nested `prices: [{ amount:, currency: }]` under variants — the top-level keys had no writer behind them. (`Spree::PermittedAttributes` itself is removed in 6.0 — see below.)
388
388
  - Localized number parsing still happens: `Spree::Price#amount=` runs `Spree::LocalizedNumber.parse`, so `set_price(currency, '1,599.99')` works as `price=` did.
389
389
  - The variant validation that inferred a missing price from the product's default variant is gone. Set prices explicitly (the product and variant factories already do).
390
390
 
391
+ ### `Spree::PermittedAttributes` is removed
392
+
393
+ The global permitted-attributes registry is gone, with no deprecation bridge. It
394
+ existed so the Rails admin and storefront could share one allowlist; both are
395
+ removed in 6.0, and API v3 declares its attributes in the controller.
396
+
397
+ Removed alongside it: `Spree::Core::ControllerHelpers::StrongParameters` (the
398
+ `permitted_*_attributes` helper methods it mixed into controllers) and the
399
+ fallback that inferred an attribute list from the model name.
400
+
401
+ Attributes you pushed from an initializer are now declared on the model, and
402
+ standard resource endpoints append them to their own allowlist — so one
403
+ declaration still covers the model's create and update endpoints.
404
+
405
+ #### How to migrate
406
+
407
+ **1. Find every call site.** The constant and the helper methods are both gone,
408
+ so a missed reference raises `NameError` or `NoMethodError` the first time that
409
+ code runs — loudly, but not necessarily at boot:
410
+
411
+ ```bash
412
+ grep -rn "PermittedAttributes" app config lib
413
+ grep -rnE "permitted_[[:alnum:]_]+_attributes" app config lib
414
+ ```
415
+
416
+ The second pattern is deliberately broad: the helper module generated one
417
+ `permitted_*_attributes` method per registry key, so there were dozens of them.
418
+
419
+ **2. Decide what each attribute actually is.** Most fall into one of three
420
+ buckets, and only the last needs this hook:
421
+
422
+ | What you were adding | Where it goes in 6.0 |
423
+ | --- | --- |
424
+ | A merchant-managed field (text, number, dropdown) | [Custom Fields](../core-concepts/custom-fields.md) — no code, and filterable/sortable |
425
+ | Config for an STI type you register (promotion rule, delivery method rule, …) | `additional_permitted_attributes` on that subclass, as before — unchanged |
426
+ | A real database column your extension added to a core model | `additional_permitted_attributes` on the model |
427
+
428
+ **3. Point each declaration at the model.** It stays in your initializer — only
429
+ the receiver changes, from the global registry to the model itself:
430
+
431
+ ```ruby config/initializers/spree.rb
432
+ # Before
433
+ Spree::PermittedAttributes.product_attributes << :brand_id
434
+
435
+ # After
436
+ Spree::Product.additional_permitted_attributes += [:brand_id]
437
+ ```
438
+
439
+ Use `+=`, not `=` — the list is per model, and assigning replaces whatever
440
+ another extension already added. `<<` raises a `FrozenError`: the default is a
441
+ frozen shared array, so mutating it in place would leak your attribute onto every
442
+ other model.
443
+
444
+ Declare only attributes of your own. Redeclaring a key the controller already
445
+ permits (`metadata`, `prices`) does not widen it — strong parameters keeps the
446
+ last filter for that key, so the controller's own would be replaced by yours.
447
+
448
+ Entries are `params.permit` fragments, so collections and nested structures keep
449
+ the shapes you already know: `[:brand_id, { region_ids: [] }]`.
450
+
451
+ **4. Fix your own controllers.** If you subclassed a Spree v3 resource
452
+ controller and relied on the attribute list being inferred from the model name,
453
+ declare it now:
454
+
455
+ ```ruby
456
+ class BrandsController < Spree::Api::V3::Admin::ResourceController
457
+ protected
458
+
459
+ def model_class
460
+ Spree::Brand
461
+ end
462
+
463
+ # Before: no such method — the base class inferred `brand_attributes`
464
+ # from the model name. Now you say what you accept.
465
+ def resource_permitted_attributes
466
+ [:name, :slug, :description]
467
+ end
468
+ end
469
+ ```
470
+
471
+ Declaring neither `resource_permitted_attributes` nor `permitted_params` raises
472
+ `NotImplementedError` on the first write, so this surfaces in your test suite
473
+ rather than silently permitting a stale list.
474
+
475
+ **5. Verify a write actually persists.** A declaration that never reaches a
476
+ controller fails silently — strong parameters drop the unpermitted key, the
477
+ request still returns 200, and the column keeps its old value. Assert on the
478
+ saved record, not the response status:
479
+
480
+ ```ruby
481
+ patch "/api/v3/admin/products/#{product.prefixed_id}",
482
+ params: { brand_id: brand.id }, headers: headers
483
+
484
+ expect(product.reload.brand_id).to eq(brand.id)
485
+ ```
486
+
487
+ If that assertion fails, the endpoint is not consulting your declaration. Check
488
+ whether the controller overrides `permitted_attributes` — that method is where
489
+ the extension attributes are appended, so overriding it replaces them. Override
490
+ `resource_permitted_attributes` instead.
491
+
492
+ > **WARNING:** Two endpoints deliberately ignore the hook because their parameters are
493
+ > authorization data rather than resource data: API keys (`scopes`, `key_type`)
494
+ > and invitations (`role_id`). Adding attributes there needs a controller
495
+ > decorator, not a model declaration.
496
+
497
+ STI types registered through a Spree registry (promotion rules and actions,
498
+ delivery method rules, commission rules) already used
499
+ `additional_permitted_attributes` and need no changes — the hook simply moved up
500
+ to `Spree::Base`.
501
+
391
502
  ### `StateChange` and `LogEntry` are gone
392
503
 
393
504
  `Spree::StateChange` and `Spree::LogEntry` are removed — the models, the `state_changes` associations on `Order`, `Payment` and `Fulfillment`, the `log_entries` associations on `Payment` and `Refund`, and everything that wrote to them. Both were write-only: nothing in Spree read the rows back, and the admin screens that displayed them are gone.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spree/docs",
3
- "version": "0.1.168",
3
+ "version": "0.1.169",
4
4
  "description": "Spree Commerce developer documentation for AI agents and local reference",
5
5
  "type": "module",
6
6
  "license": "CC-BY-4.0",