package cooler_test import ( "context" "encoding/json" "fmt" "io" "net/http" "net/http/httptest" "os" "time" "git.golem15.com/golem15/summercms/modules/backpack" "git.golem15.com/golem15/summercms/modules/cabana" "git.golem15.com/golem15/summercms/modules/compass" "git.golem15.com/golem15/summercms/modules/cooler" "git.golem15.com/golem15/summercms/modules/pact" ) func ExampleFromApp() { // serve publishes the Cache at start-up; a job or console command asks // FromApp, which builds the configured driver (memory here) once. app := backpack.New(nil) cache, err := cooler.FromApp(app) if err != nil { fmt.Println(err) return } ctx := context.Background() loads := 0 popular := func(context.Context) ([]byte, error) { loads++ return json.Marshal([]string{"Hello", "Summer"}) } var titles []string for range 2 { raw, err := cache.Remember(ctx, "blog:popular", 10*time.Minute, popular) if err != nil { fmt.Println(err) return } if err := json.Unmarshal(raw, &titles); err != nil { fmt.Println(err) return } } fmt.Println(titles, loads) // Output: [Hello Summer] 1 } // Article is a blog's article model. type Article struct { Slug string } // articlesController is a blog's admin controller (its pact.AdminController // methods are left out here). type articlesController struct { app func() *backpack.App } // purge registers one site-cache purge for the saved article, sent after the // save transaction commits (at once outside one). Without a purge configured // the purger is nil and nothing is sent. func (c articlesController) purge(ctx context.Context, model any) error { article, ok := model.(*Article) if !ok { return nil } tx, _ := cabana.TxFromContext(ctx) cooler.PurgeSiteCacheAfterCommit(ctx, tx, cooler.SiteCacheFromApp(c.app()), "article", article.Slug) return nil } // FormAfterCreate runs inside the create's transaction. func (c articlesController) FormAfterCreate(ctx context.Context, model any) error { return c.purge(ctx, model) } // FormAfterUpdate runs inside the update's transaction. func (c articlesController) FormAfterUpdate(ctx context.Context, model any) error { return c.purge(ctx, model) } // FormAfterDelete runs inside the delete's transaction. func (c articlesController) FormAfterDelete(ctx context.Context, model any) error { return c.purge(ctx, model) } var ( _ pact.FormAfterCreate = articlesController{} _ pact.FormAfterUpdate = articlesController{} _ pact.FormAfterDelete = articlesController{} ) func ExampleSiteCacheFromApp() { // The host site's purge route, standing in on loopback. site := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { body, _ := io.ReadAll(r.Body) fmt.Println(r.Method, r.URL.Path, string(body)) _, _ = w.Write([]byte(`{"purged":3}`)) })) defer site.Close() dir, err := os.MkdirTemp("", "cooler-example") if err != nil { fmt.Println(err) return } defer os.RemoveAll(dir) cfg, err := compass.Open(compass.Options{Dir: dir, Environ: []string{ "SUMMER_SITE_CACHE__PURGE_URL=" + site.URL + "/api/__site__/cache/purge", "SUMMER_SITE_CACHE__PURGE_TOKEN=example-purge-token-0123456789abcdef", }}) if err != nil { fmt.Println(err) return } app := backpack.New(cfg) c := articlesController{app: func() *backpack.App { return app }} if err := c.FormAfterUpdate(context.Background(), &Article{Slug: "hello-summer"}); err != nil { fmt.Println(err) } // Output: POST /api/__site__/cache/purge {"kind":"article","slug":"hello-summer"} }