@spree/docs 0.1.254 → 0.1.255
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.
- package/dist/developer/cli/quickstart.md +1 -1
- package/dist/developer/contributing/creating-an-extension.md +7 -17
- package/dist/developer/dashboard/plugins/publishing.md +4 -4
- package/dist/developer/dashboard/plugins/scaffolding.md +3 -3
- package/dist/developer/deployment/background_jobs.md +46 -1
- package/dist/developer/tutorial/model-and-api.md +6 -2
- package/dist/developer/upgrades/5.6-to-6.0.md +1 -1
- package/package.json +1 -1
|
@@ -233,7 +233,7 @@ spree generate subscriber OmsOrderSync order.placed # runs spree:su
|
|
|
233
233
|
spree generate migration AddPositionToSpreeBrands position:integer # Rails built-in, forwarded as-is
|
|
234
234
|
```
|
|
235
235
|
|
|
236
|
-
`spree:api_resource` scaffolds the full v3 surface: model, migration, Store + Admin controllers and serializers, factory, controller specs, and
|
|
236
|
+
`spree:api_resource` scaffolds the full v3 surface: model, migration, Store + Admin controllers and serializers, factory, controller specs, routes, and the `read_<resources>` / `write_<resources>` permissions that let staff roles and secret API keys reach the Admin API.
|
|
237
237
|
|
|
238
238
|
### `spree migrate`
|
|
239
239
|
|
|
@@ -23,10 +23,10 @@ In this guide we build `spree_reviews`: customers read product reviews through t
|
|
|
23
23
|
|
|
24
24
|
## Generate the extension
|
|
25
25
|
|
|
26
|
-
Install the Spree extension generator:
|
|
26
|
+
Install the Spree extension generator. Spree 6 needs version 2.0 or newer — earlier versions generate a Spree 5 scaffold built around the Rails admin:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
gem install spree_extension
|
|
29
|
+
gem install spree_extension -v '>= 2.0'
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
Run the following command from a directory outside your Spree application:
|
|
@@ -35,14 +35,12 @@ Run the following command from a directory outside your Spree application:
|
|
|
35
35
|
spree-extension create reviews
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
This creates a `spree_reviews` directory containing the engine, a gemspec, a test setup and continuous integration configuration. Change to its directory:
|
|
38
|
+
This creates a `spree_reviews` directory containing the engine, a gemspec, a test setup and continuous integration configuration. Its `config/routes.rb` and `config/initializers/spree.rb` come with commented-out Spree 6 examples — API routes, permission scopes, workflow hooks and event subscribers — that the steps below fill in. Change to its directory:
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
41
|
cd spree_reviews
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
> **WARNING:** The generated scaffold still declares a dependency on `spree_admin`, the Spree 5 Rails admin, which does not exist in Spree 6. Remove the `spree_admin` lines from both the `.gemspec` and the `Gemfile` before running `bundle install`. The JavaScript, importmap and asset setup in the scaffold is only used by the Spree 5 admin, so you can delete it too.
|
|
45
|
-
|
|
46
44
|
## Add a model
|
|
47
45
|
|
|
48
46
|
Create the migration in `db/migrate/`:
|
|
@@ -288,10 +286,10 @@ module SpreeReviews
|
|
|
288
286
|
end
|
|
289
287
|
```
|
|
290
288
|
|
|
291
|
-
Subscribers are not discovered automatically. Register them
|
|
289
|
+
Subscribers are not discovered automatically. Register them in the extension's initializer — `bin/rails g spree:subscriber` adds the line for you:
|
|
292
290
|
|
|
293
|
-
```ruby
|
|
294
|
-
config.after_initialize do
|
|
291
|
+
```ruby config/initializers/spree.rb
|
|
292
|
+
Rails.application.config.after_initialize do
|
|
295
293
|
Spree.subscribers << SpreeReviews::ReviewRequestSubscriber
|
|
296
294
|
end
|
|
297
295
|
```
|
|
@@ -312,15 +310,7 @@ end
|
|
|
312
310
|
Spree::Product.prepend SpreeReviews::ProductDecorator
|
|
313
311
|
```
|
|
314
312
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
```ruby lib/spree_reviews/engine.rb
|
|
318
|
-
config.to_prepare do
|
|
319
|
-
Dir.glob(root.join('app/**/*_decorator*.rb')) do |decorator|
|
|
320
|
-
Rails.configuration.cache_classes ? require(decorator) : load(decorator)
|
|
321
|
-
end
|
|
322
|
-
end
|
|
323
|
-
```
|
|
313
|
+
Rails does not load decorators on its own, so the scaffold's engine does it for you: `lib/spree_reviews/engine.rb` loads every `app/**/*_decorator*.rb` file at boot and again on each code reload in development. Name the file with a `_decorator` suffix and there is nothing else to wire up.
|
|
324
314
|
|
|
325
315
|
## Add admin screens
|
|
326
316
|
|
|
@@ -27,13 +27,13 @@ Your plugin imports React, the dashboard, Tailwind, i18next, etc., but **you don
|
|
|
27
27
|
}
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
Use ranges, not exact versions — pin too tightly and consumers can't upgrade the dashboard without you cutting a release. One pre-release caveat: a range like `^1.0.0-beta.3` accepts later `1.0.0` betas and every stable `1.x` release, but not betas of other versions — so keep your `@spree/dashboard-*` peers in step with the dashboard until 1.0 ships.
|
|
30
|
+
Use ranges, not exact versions — pin too tightly and consumers can't upgrade the dashboard without you cutting a release. One pre-release caveat: a range like `^1.0.0-beta.3` accepts later `1.0.0` betas and every stable `1.x` release, but not betas of other versions — so keep your `@spree/dashboard-*` peers in step with the dashboard until 1.0 ships. `spree plugin new` scaffolds ranges starting at the admin SDK and dashboard versions released alongside your `@spree/cli`.
|
|
31
31
|
|
|
32
32
|
`dependencies` is for things your plugin *uses* that the host *doesn't* — small utilities (e.g., `clsx`, `date-fns` if you need a specific version, your own helper packages). When in doubt, peer-dep it. The trade-off is "user has to install one more thing" vs. "user has two copies of React" — always pay the first cost.
|
|
33
33
|
|
|
34
34
|
## Choose what to publish
|
|
35
35
|
|
|
36
|
-
The scaffold's `package.json` ships TS source by default. Two options:
|
|
36
|
+
The scaffold's `package.json` ships TS source by default, and its `build` script type-checks that source. Two options:
|
|
37
37
|
|
|
38
38
|
### Option A — Ship pre-built JS
|
|
39
39
|
|
|
@@ -70,7 +70,7 @@ Pick A if you're going to npm in earnest. Pick B for internal-only packages used
|
|
|
70
70
|
|
|
71
71
|
## Side-effects flag
|
|
72
72
|
|
|
73
|
-
The plugin's whole purpose is side effects (`defineDashboardPlugin` registers stuff at module-load time). **Tell bundlers not to tree-shake it
|
|
73
|
+
The plugin's whole purpose is side effects (`defineDashboardPlugin` registers stuff at module-load time). **Tell bundlers not to tree-shake it.** The scaffold lists its source entry, `./src/index.tsx`; add the `dist/` entries if you ship pre-built JS:
|
|
74
74
|
|
|
75
75
|
```json
|
|
76
76
|
{
|
|
@@ -133,7 +133,7 @@ Set `"private": false` and remove the `"publishConfig.access": "restricted"` if
|
|
|
133
133
|
|
|
134
134
|
## Avoiding lock-step releases
|
|
135
135
|
|
|
136
|
-
Your plugin should compile against a **range** of dashboard versions. Run the build against the lowest
|
|
136
|
+
Your plugin should compile against a **range** of dashboard versions. Run the build against the lowest version your peer range allows (for example `pnpm add -D @spree/dashboard-core@1.0.0-beta.3`) and add a CI matrix job that bumps to the next minor and rebuilds. That's the cheapest way to catch "I depended on something the oldest supported dashboard doesn't have".
|
|
137
137
|
|
|
138
138
|
## Reference
|
|
139
139
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Scaffolding
|
|
3
3
|
sidebarTitle: Scaffolding
|
|
4
|
-
description: Generate a new dashboard plugin monorepo with `@spree/cli` in one command. Includes TypeScript, biome,
|
|
4
|
+
description: Generate a new dashboard plugin monorepo with `@spree/cli` in one command. Includes TypeScript, biome, a working example entry-point, and translations.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
`@spree/cli` ships a `plugin new` command that scaffolds the whole plugin monorepo for you. Generated layout:
|
|
@@ -68,14 +68,14 @@ For CI and scripted use, `-y` / `--yes` accepts the default for anything the fla
|
|
|
68
68
|
npx @spree/cli plugin new my-plugin -y
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
`--force` overwrites an existing directory. `--no-dashboard`
|
|
71
|
+
`--force` overwrites an existing directory. `--no-dashboard` and `--no-engine` are reserved for when engine scaffolding lands — today the dashboard package is the only half the CLI generates, so `--no-dashboard` leaves nothing to scaffold.
|
|
72
72
|
|
|
73
73
|
## After scaffolding
|
|
74
74
|
|
|
75
75
|
```bash
|
|
76
76
|
cd my-plugin
|
|
77
77
|
pnpm install # runs by default unless --no-install
|
|
78
|
-
pnpm build
|
|
78
|
+
pnpm build # type-checks the plugin — it ships TypeScript source
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
You now have a buildable plugin. The next page covers what to put in `package.json` for publishing — but if you're using it internally and importing by path, you can stop here and skip [Publishing](publishing.md).
|
|
@@ -23,7 +23,21 @@ Every deployment ships [Mission Control](https://github.com/rails/mission_contro
|
|
|
23
23
|
|
|
24
24
|
## Recurring Jobs
|
|
25
25
|
|
|
26
|
-
Scheduled work
|
|
26
|
+
Scheduled work is defined in `config/recurring.yml`, Solid Queue's built-in cron. A new project already schedules everything Spree needs:
|
|
27
|
+
|
|
28
|
+
| Schedule | Job | What it does |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| Every minute | `Spree::StockReservations::ExpireJob` | Releases checkout stock reservations whose hold expired |
|
|
31
|
+
| Every 5 minutes | `Spree::Orders::FinalizeStaleDraftsJob` | Finishes orders whose payment succeeded but whose completion never committed; never refunds |
|
|
32
|
+
| Hourly | `Spree::Carts::ReapExpiredJob` | Deletes abandoned carts past their expiry; carts with a live payment are kept |
|
|
33
|
+
| Hourly | `Spree::Collections::RegenerateTimeBasedJob` | Refreshes automatic collections with time-based rules, such as "available on" |
|
|
34
|
+
| Daily | `spree:price_history:prune` task | Deletes price history older than each store's retention period (EU Omnibus) |
|
|
35
|
+
| Daily | `Spree::SellerPayouts::SweepDueJob` | Settles marketplace sellers who are due a payout (see below) |
|
|
36
|
+
| Hourly | `Spree::SellerTransfers::ExecutePendingDueJob` | Retries seller earnings a payout provider refused |
|
|
37
|
+
|
|
38
|
+
The marketplace jobs do nothing on a store with no sellers, so keep them enabled. If your project predates any of these, copy the missing entries from the [starter's `config/recurring.yml`](https://github.com/spree/spree-starter/blob/main/config/recurring.yml).
|
|
39
|
+
|
|
40
|
+
Add your own:
|
|
27
41
|
|
|
28
42
|
```yaml config/recurring.yml
|
|
29
43
|
production:
|
|
@@ -110,6 +124,37 @@ Move the `config/recurring.yml` schedules to sidekiq-cron (it auto-loads `config
|
|
|
110
124
|
expire_stock_reservations:
|
|
111
125
|
cron: "* * * * *"
|
|
112
126
|
class: Spree::StockReservations::ExpireJob
|
|
127
|
+
finalize_stale_draft_orders:
|
|
128
|
+
cron: "*/5 * * * *"
|
|
129
|
+
class: Spree::Orders::FinalizeStaleDraftsJob
|
|
130
|
+
reap_expired_carts:
|
|
131
|
+
cron: "7 * * * *"
|
|
132
|
+
class: Spree::Carts::ReapExpiredJob
|
|
133
|
+
regenerate_time_based_collections:
|
|
134
|
+
cron: "17 * * * *"
|
|
135
|
+
class: Spree::Collections::RegenerateTimeBasedJob
|
|
136
|
+
prune_price_history:
|
|
137
|
+
cron: "0 4 * * *"
|
|
138
|
+
class: PrunePriceHistoryJob
|
|
139
|
+
sweep_due_seller_payouts:
|
|
140
|
+
cron: "0 3 * * *"
|
|
141
|
+
class: Spree::SellerPayouts::SweepDueJob
|
|
142
|
+
execute_pending_seller_transfers:
|
|
143
|
+
cron: "27 * * * *"
|
|
144
|
+
class: Spree::SellerTransfers::ExecutePendingDueJob
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
sidekiq-cron schedules jobs, not commands, so price history pruning needs a small job that runs the task:
|
|
148
|
+
|
|
149
|
+
```ruby app/jobs/prune_price_history_job.rb
|
|
150
|
+
require 'rake'
|
|
151
|
+
|
|
152
|
+
class PrunePriceHistoryJob < ApplicationJob
|
|
153
|
+
def perform
|
|
154
|
+
Rails.application.load_tasks unless Rake::Task.task_defined?('spree:price_history:prune')
|
|
155
|
+
Rake::Task['spree:price_history:prune'].tap(&:reenable).invoke
|
|
156
|
+
end
|
|
157
|
+
end
|
|
113
158
|
```
|
|
114
159
|
|
|
115
160
|
The `/jobs` dashboard is Solid Queue-specific — mount [Sidekiq's Web UI](https://github.com/sidekiq/sidekiq/wiki/Monitoring) instead:
|
|
@@ -28,6 +28,8 @@ The generator reports what it created:
|
|
|
28
28
|
create spec/factories/spree/brand_factory.rb
|
|
29
29
|
create spec/controllers/spree/api/v3/store/brands_controller_spec.rb
|
|
30
30
|
create spec/controllers/spree/api/v3/admin/brands_controller_spec.rb
|
|
31
|
+
append config/initializers/spree.rb
|
|
32
|
+
create config/locales/spree_brands.en.yml
|
|
31
33
|
|
|
32
34
|
✓ Generated Spree::Brand API resource
|
|
33
35
|
|
|
@@ -55,7 +57,7 @@ Attributes follow the familiar `name:type:index` form, with the Spree convention
|
|
|
55
57
|
| `active:boolean` | A boolean column with a default |
|
|
56
58
|
| `user:belongs_to` | A reference with the class name resolved |
|
|
57
59
|
|
|
58
|
-
Useful flags: `--paranoid` for soft delete, `--custom-fields` for [custom fields](../core-concepts/metafields.md) support, `--writable` to give the Store API create, update and destroy as well, and `--no-store` or `--no-admin` to generate only one side. Run the generator with `--help` for the full list.
|
|
60
|
+
Useful flags: `--paranoid` for soft delete, `--custom-fields` for [custom fields](../core-concepts/metafields.md) support, `--writable` to give the Store API create, update and destroy as well, `--permission-group` to choose where the brand permissions appear in the role editor (`catalog` by default), and `--no-store` or `--no-admin` to generate only one side. Run the generator with `--help` for the full list.
|
|
59
61
|
|
|
60
62
|
## Step 2: What you got
|
|
61
63
|
|
|
@@ -76,7 +78,9 @@ The Store API is read-only by default — a shopper cannot create a brand. Pass
|
|
|
76
78
|
|
|
77
79
|
**Brands belong to a store.** Every resource you generate is scoped to one, because commerce data — catalog, configuration, orders — is always per-store. A new record picks up its store automatically, and a field marked unique is unique *within* a store, so two stores can each have a brand called `wilson`. Only genuinely global reference data, like countries, opts out with `--no-store-scoped`.
|
|
78
80
|
|
|
79
|
-
|
|
81
|
+
**Access to the Admin API is a permission.** The generator registers `read_brands` and `write_brands` in `config/initializers/spree.rb`, so you can grant them to a staff role in **Settings → Roles** or to a secret API key, and labels them for the role editor in `config/locales/spree_brands.en.yml`. Until you grant them, only store admins and full-access keys reach the endpoint. See [Permissions](../customization/permissions.md).
|
|
82
|
+
|
|
83
|
+
> **WARNING:** Read lookups through the store (`current_store.brands`) rather than the whole table. Scoping by association is the cheapest defence against one store reading another's records — a permission decides *whether* someone may read brands, not *which* ones.
|
|
80
84
|
|
|
81
85
|
## Step 3: Try it
|
|
82
86
|
|
|
@@ -726,7 +726,7 @@ Old initializers fail loudly at boot: the set constants raise `NameError`, and `
|
|
|
726
726
|
|
|
727
727
|
```ruby
|
|
728
728
|
# db/seeds.rb — "roles as code" is plain ActiveRecord now
|
|
729
|
-
Spree::
|
|
729
|
+
Spree::Store.default.roles.find_or_create_by!(name: 'support')
|
|
730
730
|
.update!(permissions: %w[read_orders read_customers])
|
|
731
731
|
```
|
|
732
732
|
|