Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc: Expand and organize Decorate documentation #858

Merged
merged 2 commits into from Mar 10, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
126 changes: 108 additions & 18 deletions decorate.go
Expand Up @@ -29,41 +29,82 @@ import (
)

// Decorate specifies one or more decorator functions to an Fx application.
// Decorator functions let users augment objects in the graph. They can take in
// zero or more dependencies that must be provided to the application with fx.Provide,
// and produce one or more values that can be used by other invoked values.
//
// An example decorator is the following function which accepts a value, augments that value,
// and returns the replacement value.
// Decorator functions
//
// Decorator functions let users augment objects in the graph.
// They can take in zero or more dependencies that must be provided to the
// application with fx.Provide, and produce one or more values that can be used
// by other fx.Provide and fx.Invoke calls.
//
// fx.Decorate(func(log *zap.Logger) *zap.Logger {
// return log.Named("myapp")
// })
// fx.Invoke(func(log *zap.Logger) {
// log.Info("hello")
// // Output:
// // {"level": "info","logger":"myapp","msg":"hello"}
// })
//
// The following decorator accepts multiple dependencies from the graph, augments and returns
// one of them.
// The following decorator accepts multiple dependencies from the graph,
// augments and returns one of them.
//
// fx.Decorate(func(log *zap.Logger, cfg *Config) *zap.Logger {
// return log.Named(cfg.Name)
// })
//
// Similar to fx.Provide, functions passed to fx.Decorate may optionally return an error
// as their last result. If a decorator returns a non-nil error, it will halt application startup.
// Similar to fx.Provide, functions passed to fx.Decorate may optionally return
// an error as their last result.
// If a decorator returns a non-nil error, it will halt application startup.
//
// fx.Decorate(func(conn *sql.DB, cfg *Config) (*sql.DB, error) {
// if err := conn.Ping(); err != nil {
// return sql.Open("driver-name", cfg.FallbackDB)
// }
// return conn, nil
// })
//
// Decorators support both, fx.In and fx.Out structs, similar to fx.Provide and
// fx.Invoke.
//
// type Params struct {
// fx.In
//
// Client usersvc.Client `name:"readOnly"`
// }
//
// All modifications in the object graph due to a decorator are scoped to the fx.Module it was
// specified from. Decorations specified in the top-level fx.New call apply across the application.
// type Result struct {
// fx.Out
//
// Client usersvc.Client `name:"readOnly"`
// }
//
// Decorators can be annotated using fx.Annotate, but not with fx.Annotated. Refer to documentation
// on fx.Annotate() to learn how to use it for annotating functions.
// fx.Decorate(func(p Params) Result {
// ...
// })
//
// Decorators support fx.In and fx.Out structs, similar to fx.Provide and fx.Invoke.
// Decorators can be annotated with the fx.Annotate function, but not with the
// fx.Annotated type. Refer to documentation on fx.Annotate() to learn how to
// use it for annotating functions.
//
// fx.Decorate(
// fx.Annotate(
// func(client usersvc.Client) usersvc.Client {
// // ...
// },
// fx.ParamTags(`name:"readOnly"`),
// fx.ResultTags(`name:"readOnly"`),
// ),
// )
//
// Decorators support value groups as well. For example, the following code shows a decorator
// which takes in a value group using fx.In struct, and returns another value group.
// Decorators support augmenting, filtering, or replacing value groups.
// To decorate a value group, expect the entire value group slice and produce
// the new slice.
//
// type HandlerParam struct {
// fx.In
//
// Log *zap.Logger
// Handlers []Handler `group:"server"
// }
//
Expand All @@ -73,11 +114,60 @@ import (
// Handlers []Handler `group:"server"
// }
//
// fx.Decorate(func(p HandlerParam) HandlerResult {
// var r HandlerResult
// for _, handler := range p.Handlers {
// r.Handlers = append(r.Handlers, wrapWithLogger(p.Log, handler))
// }
// return r
// }),
//
// Decorator scope
//
// Modifications made to the Fx graph with fx.Decorate are scoped to the
// deepest fx.Module inside which the decorator was specified.
//
// fx.Module("mymodule",
// fx.Decorate(func(log *zap.Logger) *zap.Logger {
// return log.Named("myapp")
// }),
// fx.Invoke(func(log *zap.Logger) {
// log.Info("decorated logger")
// // Output:
// // {"level": "info","logger":"myapp","msg":"decorated logger"}
// }),
// ),
// fx.Invoke(func(log *zap.Logger) {
// log.Info("plain logger")
// // Output:
// // {"level": "info","msg":"plain logger"}
// }),
//
// Decorations specified in the top-level fx.New call apply across the
// application and chain with module-specific decorators.
//
// fx.New(
// // ...
// fx.Decorate(func(p HandlerParam) HandlerResult {
// // ...
// fx.Decorate(func(log *zap.Logger) *zap.Logger {
// return log.With(zap.Field("service", "myservice"))
// }),
// // ...
// fx.Invoke(func(log *zap.Logger) {
// log.Info("outer decorator")
// // Output:
// // {"level": "info","service":"myservice","msg":"outer decorator"}
// }),
// // ...
// fx.Module("mymodule",
// fx.Decorate(func(log *zap.Logger) *zap.Logger {
// return log.Named("myapp")
// }),
// fx.Invoke(func(log *zap.Logger) {
// log.Info("inner decorator")
// // Output:
// // {"level": "info","logger":"myapp","service":"myservice","msg":"inner decorator"}
// }),
// ),
// )
func Decorate(decorators ...interface{}) Option {
return decorateOption{
Expand Down