Skip to content

Commit

Permalink
doc: Expand and organize Decorate documentation (#858)
Browse files Browse the repository at this point in the history
This elaborates on the documentation for fx.Decorate with more examples,
and organizes it into two sections:

- Decorator functions, enumerating the various signatures a decorator
  function can have
- Decorator scope, explaining how decorator changes are scoped

Added demonstrative examples for everything.
In the future, we can turn some of these into example tests.

Refs GO-1254
  • Loading branch information
abhinav committed Mar 10, 2022
1 parent 6bc0d21 commit 65e6e7f
Showing 1 changed file with 108 additions and 18 deletions.
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

0 comments on commit 65e6e7f

Please sign in to comment.