From 0190aab3497d337504118a6eb3f90001a37e3b92 Mon Sep 17 00:00:00 2001 From: Eugene Blikh Date: Thu, 13 Aug 2026 08:53:09 +0300 Subject: [PATCH] web: register the views in one explicit list --- web/beads.go | 2 -- web/memory.go | 17 ----------------- web/milestones.go | 2 -- web/views.go | 24 ++++++++++++++++++++---- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/web/beads.go b/web/beads.go index 1fee93c07628eb3597a1ed1da4027d7bc3315532..8d40584f7d4c9ff807491ecdae0c29084b5fd770 100644 --- a/web/beads.go +++ b/web/beads.go @@ -23,8 +23,6 @@ import ( // hand-off of the request's ref and query. type beadsView struct{} -func init() { RegisterView(&beadsView{}) } - func (*beadsView) Name() string { return "beads" } func (*beadsView) Label() string { return "Beads" } func (*beadsView) Template() string { return "beads.html" } diff --git a/web/memory.go b/web/memory.go index db55b145967be3dae5d5ce4957894057698645d2..b8272e401f7f4826815b30c8af4b38a48e700b76 100644 --- a/web/memory.go +++ b/web/memory.go @@ -19,23 +19,6 @@ import ( // label, template, and the hand-off of the request's ref and query. type memoryView struct{} -// init registers the Memory view, and registers it *after* Milestones, which is -// where the design puts the tab: registration order is tab order (views.go). -// -// Getting that order is not free here, and the extra line is the whole reason -// this comment exists. A package's init functions run in lexical file-name -// order, and "memory.go" sorts before "milestones.go" — so registering only -// memoryView would put this tab between Beads and Milestones. Registering -// milestonesView first claims the slot ahead of it; milestones.go's own init -// then re-registers the same view, which RegisterView resolves in place (same -// Name), leaving the order alone. Both calls name real views, so a rename or a -// removal on either side is a compile error rather than a silently reordered -// tab bar. -func init() { - RegisterView(&milestonesView{}) - RegisterView(&memoryView{}) -} - func (*memoryView) Name() string { return "memory" } func (*memoryView) Label() string { return "Memory" } func (*memoryView) Template() string { return "memory.html" } diff --git a/web/milestones.go b/web/milestones.go index a86adb9b389ca9b86de2df18171938ce07bdcb12..567000cf0a5777f0a2cb50a9b9a07fa516bbf3cc 100644 --- a/web/milestones.go +++ b/web/milestones.go @@ -15,8 +15,6 @@ import ( // The grouping itself lives in the beads package; this type is the View adapter. type milestonesView struct{} -func init() { RegisterView(&milestonesView{}) } - func (*milestonesView) Name() string { return "milestones" } func (*milestonesView) Label() string { return "Milestones" } func (*milestonesView) Template() string { return "milestones.html" } diff --git a/web/views.go b/web/views.go index b7e5a908b0628d05cbb299544e2f45473dee3404..705d3e602a981412f639c11d811213523c990e09 100644 --- a/web/views.go +++ b/web/views.go @@ -38,10 +38,26 @@ type View interface { // is what it is. var registeredViews []View -// RegisterView adds v to the global registry. It is meant to be called from an -// init() in the file that defines a concrete view. If a view with the same -// Name() is already registered it is replaced, so double registration (e.g. -// from a duplicated init) is safe. +// init registers every view this service ships, in the order their tabs appear. +// +// The registration lives here, in one list, rather than in an init() beside each +// concrete view — which is where it used to be, and which made the tab order a +// consequence of file names. A package's init functions run in lexical file +// order, so "memory.go" registering itself would have put Memory between Beads +// and Milestones, and the only way to restore the intended order from inside +// memory.go was to register a view belonging to another file first. Tab order is +// a decision, and a decision that reads as a list of three lines cannot be +// changed by renaming a file. +func init() { + RegisterView(&beadsView{}) + RegisterView(&milestonesView{}) + RegisterView(&memoryView{}) +} + +// RegisterView adds v to the global registry, appending it after everything +// registered before it: registration order is tab order. If a view with the same +// Name() is already registered it is replaced in place, so re-registering (a +// test swapping an implementation, say) never reorders the bar. func RegisterView(v View) { for i, existing := range registeredViews { if existing.Name() == v.Name() {