feat(11.1-03): add the Architecture and Plugins docs sections
- architecture: introduction, Go modules and workspaces, application lifecycle, request lifecycle - plugins: registration, scheduling, extending, testing - verified Examples for backpack services, towel request context, pact schedules and festival events - TestDocsRequiredPages lists the eight new pages
This commit is contained in:
42
modules/backpack/example_test.go
Normal file
42
modules/backpack/example_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package backpack_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.golem15.com/golem15/summercms/modules/backpack"
|
||||
"git.golem15.com/golem15/summercms/modules/compass"
|
||||
)
|
||||
|
||||
// Greeter is a service the acme.greeter plugin offers to other plugins.
|
||||
type Greeter interface {
|
||||
Greet(name string) string
|
||||
}
|
||||
|
||||
type englishGreeter struct{}
|
||||
|
||||
func (englishGreeter) Greet(name string) string { return "Hello, " + name }
|
||||
|
||||
func ExampleApp_Publish() {
|
||||
app := backpack.New(&compass.Config{})
|
||||
app.SetPlugins([]string{"acme.greeter", "acme.blog"})
|
||||
|
||||
// acme.greeter, in its Register step: publish under the interface type.
|
||||
var greeter Greeter = englishGreeter{}
|
||||
if err := app.Publish(greeter); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// acme.blog, in its Boot step: look the service up by the same type.
|
||||
if app.HasPlugin("acme.greeter") {
|
||||
if found, ok := app.Lookup[Greeter](); ok {
|
||||
fmt.Println(found.Greet("blog"))
|
||||
}
|
||||
}
|
||||
|
||||
// A second Publish under the same type is refused.
|
||||
fmt.Println(app.Publish(greeter) != nil)
|
||||
// Output:
|
||||
// Hello, blog
|
||||
// true
|
||||
}
|
||||
35
modules/festival/example_test.go
Normal file
35
modules/festival/example_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package festival_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.golem15.com/golem15/summercms/modules/festival"
|
||||
)
|
||||
|
||||
// PostPublished is the event the acme.blog plugin fires after a post goes live.
|
||||
type PostPublished struct {
|
||||
Title string
|
||||
}
|
||||
|
||||
func ExampleBus_Fire() {
|
||||
bus := festival.New() // in a plugin, use app.Events
|
||||
|
||||
// acme.search and acme.notify extend acme.blog from their Boot steps.
|
||||
bus.Listen("acme.search", func(ctx context.Context, e PostPublished) error {
|
||||
fmt.Println("index", e.Title)
|
||||
return nil
|
||||
})
|
||||
bus.ListenPriority("acme.notify", 10, func(ctx context.Context, e PostPublished) error {
|
||||
fmt.Println("notify subscribers of", e.Title)
|
||||
return nil
|
||||
})
|
||||
|
||||
// acme.blog fires the event; higher priorities run first.
|
||||
if err := bus.Fire(context.Background(), PostPublished{Title: "Hello"}); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// Output:
|
||||
// notify subscribers of Hello
|
||||
// index Hello
|
||||
}
|
||||
37
modules/pact/example_test.go
Normal file
37
modules/pact/example_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package pact_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.golem15.com/golem15/summercms/modules/pact"
|
||||
)
|
||||
|
||||
// BlogPlugin is the acme.blog plugin; only its schedule is shown here.
|
||||
type BlogPlugin struct{}
|
||||
|
||||
var _ pact.HasSchedule = (*BlogPlugin)(nil)
|
||||
|
||||
// Schedule runs three of the plugin's registered console commands.
|
||||
func (p *BlogPlugin) Schedule() []pact.ScheduledCommand {
|
||||
return []pact.ScheduledCommand{
|
||||
{Command: "blog:prune-drafts", Cadence: pact.Daily()},
|
||||
{Command: "blog:send-digest", Cadence: pact.DailyAt(7, 30)},
|
||||
{Command: "blog:sync-feed", Args: []string{"--quiet"}, Cadence: pact.Every(15 * time.Minute)},
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleHasSchedule() {
|
||||
var plugin pact.HasSchedule = &BlogPlugin{}
|
||||
for _, entry := range plugin.Schedule() {
|
||||
if hour, minute, daily := entry.Cadence.At(); daily {
|
||||
fmt.Printf("%s %v: daily at %02d:%02d\n", entry.Command, entry.Args, hour, minute)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("%s %v: every %s\n", entry.Command, entry.Args, entry.Cadence.Interval())
|
||||
}
|
||||
// Output:
|
||||
// blog:prune-drafts []: daily at 00:00
|
||||
// blog:send-digest []: daily at 07:30
|
||||
// blog:sync-feed [--quiet]: every 15m0s
|
||||
}
|
||||
37
modules/towel/example_test.go
Normal file
37
modules/towel/example_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package towel_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
"git.golem15.com/golem15/summercms/modules/towel"
|
||||
)
|
||||
|
||||
func ExampleWithLocale() {
|
||||
// A middleware stores the organization once for the whole request.
|
||||
withAcme := func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := towel.WithOrganization(r.Context(), "acme")
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
// The handler reads the values back from its request context.
|
||||
listPosts := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
org, _ := towel.Organization(r.Context())
|
||||
locale, ok := towel.Locale(r.Context())
|
||||
if !ok {
|
||||
locale = "en"
|
||||
}
|
||||
fmt.Fprintf(w, "posts for %s in %s", org, locale)
|
||||
})
|
||||
|
||||
// surf sets the locale from Accept-Language; here the test sets it.
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/blog/posts", nil)
|
||||
req = req.WithContext(towel.WithLocale(req.Context(), "pl"))
|
||||
rec := httptest.NewRecorder()
|
||||
withAcme(listPosts).ServeHTTP(rec, req)
|
||||
fmt.Println(rec.Body.String())
|
||||
// Output: posts for acme in pl
|
||||
}
|
||||
Reference in New Issue
Block a user