@spree/docs 0.1.169 → 0.1.170

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.
@@ -279,6 +279,40 @@ is reported through `Rails.error` so it's visible rather than mysterious.
279
279
  | `products.update` | `after_update` | lifecycle | After the product is saved (in transaction) |
280
280
  | `products.destroy` | `validate` | validate | Before the product is soft-deleted — refuse a deletion your store shouldn't allow |
281
281
  | `products.destroy` | `after_destroy` | lifecycle | After the soft-delete, for host cleanup (in transaction) |
282
+ | `products.activate` | `validate` | validate | Before the product goes on sale — the place to require an image, a price or a category |
283
+ | `products.activate` | `after_activate` | lifecycle | After the status is written (in transaction) |
284
+ | `products.archive` | `validate` | validate | Before the product is taken off sale |
285
+ | `products.archive` | `after_archive` | lifecycle | After the status is written (in transaction) |
286
+ | `products.draft` | `validate` | validate | Before the product returns to draft |
287
+ | `products.draft` | `after_draft` | lifecycle | After the status is written (in transaction) |
288
+ | `gift_cards.apply` | `validate` | validate | Before a card is drawn against — who may spend a card, on what, up to how much |
289
+ | `gift_cards.apply` | `after_apply` | lifecycle | After the store credit and payment exist (in transaction) |
290
+ | `gift_cards.remove` | `validate` | validate | Before a card is taken back off an order |
291
+ | `gift_cards.remove` | `after_remove` | lifecycle | After the balance is returned to the card (in transaction) |
292
+ | `gift_cards.redeem` | `validate` | validate | Before the card is recorded as spent |
293
+ | `gift_cards.redeem` | `after_redeem` | lifecycle | After the status is written (in transaction) |
294
+ | `gift_cards.cancel` | `validate` | validate | Before a card is voided |
295
+ | `gift_cards.cancel` | `after_cancel` | lifecycle | After the card is voided (in transaction) |
296
+ | `price_lists.create` | `validate` | validate | After the list is built, before it is saved |
297
+ | `price_lists.create` | `after_create` | lifecycle | After the list and its product membership exist (in transaction) |
298
+ | `price_lists.update` | `validate` | validate | With the pending attributes assigned, before anything is written |
299
+ | `price_lists.update` | `after_update` | lifecycle | After membership and price overrides are applied (in transaction) |
300
+ | `price_lists.activate` | `validate` | validate | Before a price list takes effect |
301
+ | `price_lists.activate` | `after_activate` | lifecycle | After the list goes live or is scheduled (in transaction) |
302
+ | `price_lists.deactivate` | `validate` | validate | Before a price list stops applying |
303
+ | `price_lists.deactivate` | `after_deactivate` | lifecycle | After the list is switched off (in transaction) |
304
+ | `invitations.accept` | `validate` | validate | After the expiry and invitee checks pass, before any access is granted |
305
+ | `invitations.accept` | `after_accept` | lifecycle | After the role is granted and the invitation marked accepted (in transaction) |
306
+ | `imports.start_mapping` | `validate` | validate | Before the uploaded file is read |
307
+ | `imports.start_mapping` | `after_start_mapping` | lifecycle | After the column mappings are built (in transaction) |
308
+ | `imports.complete_mapping` | `validate` | validate | Before the mapping is accepted — refuse a mapping your store considers incomplete |
309
+ | `imports.complete_mapping` | `after_complete_mapping` | lifecycle | After the mapping is accepted, before row creation is dispatched (in transaction) |
310
+ | `imports.start_processing` | `validate` | validate | Before the import starts working through its rows |
311
+ | `imports.start_processing` | `after_start_processing` | lifecycle | After the status is written (in transaction) |
312
+ | `imports.complete` | `validate` | validate | Before the import is closed out |
313
+ | `imports.complete` | `after_complete` | lifecycle | After the import is completed and the store touched (in transaction) |
314
+ | `imports.retry_failed_rows` | `validate` | validate | Before failed rows are queued again |
315
+ | `imports.retry_failed_rows` | `after_retry` | lifecycle | After the import returns to processing, before re-dispatch (in transaction) |
282
316
 
283
317
  `before_cancel` and `before_resume` accept `reject!` like a `validate` hook.
284
318
 
@@ -396,11 +430,66 @@ Two rules worth internalising:
396
430
  connection open across a network round trip, and a timeout leaves your database
397
431
  and the payment processor disagreeing about what happened.
398
432
 
399
- **New models get a plain `status` string, not a state machine.** Transitions are
433
+ **Models get a plain `status` string, not a state machine.** Transitions are
400
434
  workflows: `MyStore::Subscriptions::Cancel.call(...)`, not `subscription.cancel!`.
401
435
  Transition callbacks hide side effects inside a save, cannot take arguments, and
402
436
  have no compensation story.
403
437
 
438
+ As of 6.0 this is not just advice for new models — Spree has no state machines
439
+ left. Every status a record can hold is declared with `Spree::HasStatus`, and
440
+ every move between two of them is a workflow you can hook.
441
+
442
+ ## Statuses
443
+
444
+ `Spree::HasStatus` declares the statuses a model can hold:
445
+
446
+ ```ruby
447
+ class MyStore::Subscription < Spree.base_class
448
+ include Spree::HasStatus
449
+ has_status :trialing, :active, :paused, :canceled, default: :trialing
450
+ end
451
+ ```
452
+
453
+ That gives you an inclusion validation, a predicate per value
454
+ (`subscription.paused?`), a scope per value (`Subscription.paused`) and
455
+ `with_status(:active, :trialing)` for several at once. It deliberately does
456
+ *not* give you a transition graph — deciding which moves are legal is the
457
+ workflow's job, which is what lets a transition take arguments, call out to a
458
+ gateway outside a transaction, and undo itself when a later step fails.
459
+
460
+ Statuses are additive, so an extension can add its own without reopening the
461
+ model:
462
+
463
+ ```ruby
464
+ Spree::GiftCard.add_status(:on_hold, after: :active)
465
+ ```
466
+
467
+ A custom status needs a custom workflow to move records into it. That is the
468
+ design, not a gap: a central place validating transitions would be a state
469
+ machine again.
470
+
471
+ > **NOTE:** `has_status` never overwrites something the model already defines. Where a
472
+ > status name means more than the column value — `Spree::GiftCard#active?` also
473
+ > requires the card not to have expired, and `Spree::GiftCard.active` includes
474
+ > partially redeemed cards — the model's own definition wins and the generated
475
+ > one is skipped.
476
+
477
+ ### Moving a record between statuses
478
+
479
+ Call the workflow, not the model:
480
+
481
+ ```ruby
482
+ result = Spree.product_archive_workflow.call(product: product)
483
+ result.success?
484
+ ```
485
+
486
+ Spree ships one workflow per transition — `Spree::Products::Activate`,
487
+ `Spree::GiftCards::Redeem`, `Spree::Imports::Complete`, and so on — each with
488
+ its own `validate` and `after_*` hooks, all in
489
+ [Available hooks](#available-hooks) above. Because the write and the event it
490
+ publishes happen in the same place, registering against a hook is enough to
491
+ see every transition, wherever it was triggered from.
492
+
404
493
  ## Observability
405
494
 
406
495
  Every step emits an `ActiveSupport::Notifications` event, so your APM sees the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spree/docs",
3
- "version": "0.1.169",
3
+ "version": "0.1.170",
4
4
  "description": "Spree Commerce developer documentation for AI agents and local reference",
5
5
  "type": "module",
6
6
  "license": "CC-BY-4.0",