M web/beads.go => web/beads.go +0 -2
@@ 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" }
M web/memory.go => web/memory.go +0 -17
@@ 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" }
M web/milestones.go => web/milestones.go +0 -2
@@ 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" }
M web/views.go => web/views.go +20 -4
@@ 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() {