Files
summercms/modules/backpack/README.md

4.1 KiB

backpack

Per-instance application container that holds the configuration, a typed service registry, the event bus and the set of activated plugins.

import "git.golem15.com/golem15/summercms/modules/backpack"

Overview

backpack plays the role of the Laravel service container that WinterCMS plugins reach through App::make and singleton bindings, without any process-global state: every backpack.App is independent, so tests and multiple instances in one process do not interfere. The generated main of an application loads configuration with compass, creates the container with backpack.New, and hands it to party, which passes it to every plugin's Register and Boot. backpack deliberately does not import party, which keeps the dependency graph acyclic.

Features

  • backpack.New wires a container around a loaded *compass.Config: backpack.App.Config, a fresh service registry in backpack.App.Services and a new festival bus in backpack.App.Events.
  • Typed services keyed by the type argument: backpack.App.Publish stores a value under its type T and backpack.App.Lookup returns it. Publishing the same T twice or publishing nil is an error, so two plugins cannot silently replace each other's service. Framework modules share their infrastructure this way (for example the database handles published by lagoon, or the translator from phrasebook).
  • Plugin presence checks: backpack.App.SetPlugins records the complete activated set before any plugin boots, and backpack.App.HasPlugin answers whether an optional integration partner is part of this build (the equivalent of WinterCMS's PluginManager::exists).
  • backpack.Registry can be used on its own through backpack.NewRegistry; it is safe for concurrent use.
  • Nil-safe methods: calls on a nil container or registry return an error or a zero value instead of panicking.

Usage

package blog

import (
	"git.golem15.com/golem15/summercms/modules/backpack"
	"git.golem15.com/golem15/summercms/modules/compass"
)

// Feed is a service the blog plugin offers to other plugins.
type Feed interface {
	Latest(n int) []string
}

type staticFeed struct{}

func (staticFeed) Latest(n int) []string { return []string{"hello-world"} }

func setup() error {
	cfg, err := compass.Load("config")
	if err != nil {
		return err
	}
	app := backpack.New(cfg)
	app.SetPlugins([]string{"acme.blog", "acme.search"})

	// Provider side, usually in the plugin's Register step.
	var feed Feed = staticFeed{}
	if err := app.Publish(feed); err != nil { // published under Feed, not staticFeed
		return err
	}

	// Consumer side, usually in another plugin's Boot step.
	if app.HasPlugin("acme.blog") {
		if found, ok := app.Lookup[Feed](); ok {
			_ = found.Latest(5)
		}
	}
	return nil
}

Publish under an interface type when consumers should not depend on the concrete implementation; the lookup must use exactly the same type argument.

API reference

Identifier Description
backpack.App The application container: configuration, service registry, event bus and the activated plugin set.
backpack.New Creates a backpack.App around a loaded configuration, with an empty registry and a new event bus.
backpack.App.Publish / backpack.App.Lookup Store and retrieve an app-scoped service by type.
backpack.App.SetPlugins / backpack.App.HasPlugin Record the activated plugin IDs and check whether one is present.
backpack.Registry Concurrency-safe typed service catalog behind backpack.App.Services.
backpack.NewRegistry Returns an empty registry.
backpack.Registry.Publish / backpack.Registry.Lookup Registry-level publish and lookup behind the backpack.App methods.

Dependencies

  • SummerCMS modules: compass, festival.
  • Third-party: none.
  • Standard library: fmt, reflect, sync.

Testing

go test ./modules/backpack/...

The tests build containers in memory and need no external services.