A store can be listed or not

Soft deletion already said "this store is gone". What was missing is "not yet" —
a catalog still being set up, or one pulled from the picker for a while without
losing the row and its history. `GET /api/stores` now answers with the active
ones only.

The client is deliberately told nothing about the flag. It has no state for
"there but switched off", and giving it one would mean every client release
having an opinion about it; an inactive store is simply absent, which is a case
the client already handles because it is the same as never having existed. The
payload stays two fields, and a spec holds it there.

Default true, so the migration lists every store that exists today. One that
silently emptied the registry would be a client with nothing to install from.
No index: a handful of rows, read once per client on first run.

In the admin the flag is what the page is *for*, so it is not just a checkbox on
the form: Active is the default scope, the index shows listed/hidden as a status
tag with a one-click toggle beside Edit, and the two batch actions do it in bulk.
A request spec covers all of it, because none of it is reachable from a model
spec — the pipelines resource shipped without `permit_params` and every edit
raised, which is the same layer and the same lesson. The last example toggles in
the admin and then reads /api/stores, since a change here that the registry does
not reflect is the only failure that actually matters.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-19 14:23:20 +02:00
co-authored by Claude Opus 5
parent 23f4c43da8
commit bcb95424c3
8 changed files with 184 additions and 6 deletions
@@ -0,0 +1,17 @@
class AddActiveToStores < ActiveRecord::Migration[8.1]
# A store that exists but should not be offered yet.
#
# Soft deletion already covered "this store is gone"; what was missing is "not yet" —
# a catalog being set up, or one taken out of the picker for a while without losing
# the row and its history. The client has no notion of either: it lists whatever
# /api/stores hands it, so the filtering has to happen here.
#
# Default true, so every store that exists today keeps being listed. A migration that
# silently emptied the registry would be a client with nothing to install from.
#
# No index: this table holds a handful of rows and is read once per client on first
# run. An index on a two-valued column would be ceremony.
def change
add_column :stores, :active, :boolean, null: false, default: true
end
end
+2 -1
View File
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.1].define(version: 2026_08_19_120000) do
ActiveRecord::Schema[8.1].define(version: 2026_08_19_121824) do
create_table "admin_users", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "deleted_at", precision: 3
@@ -300,6 +300,7 @@ ActiveRecord::Schema[8.1].define(version: 2026_08_19_120000) do
end
create_table "stores", id: { type: :bigint, unsigned: true }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.boolean "active", default: true, null: false
t.string "catalog_url", null: false
t.datetime "created_at", precision: 3, null: false
t.datetime "deleted_at", precision: 3