goose: use marker functions instead of comments

To avoid making this CL too large, I did not migrate the existing goose
comments through the repository.  This will be addressed in a subsequent
CL.

Reviewed-by: Tuo Shan <shantuo@google.com>
This commit is contained in:
Ross Light
2018-04-27 13:44:54 -04:00
parent 13698e656a
commit f8e446fa17
59 changed files with 713 additions and 983 deletions

113
README.md
View File

@@ -13,34 +13,27 @@ might have hand-written.
### Defining Providers
The primary mechanism in goose is the **provider**: a function that can
produce a value, annotated with the special `goose:provide` directive. These
functions are otherwise ordinary Go code.
produce a value. These functions are ordinary Go code.
```go
package foobarbaz
type Foo int
// goose:provide
// ProvideFoo returns a Foo.
func ProvideFoo() Foo {
return 42
}
```
Providers are always part of a **provider set**: if there is no provider set
named on the `//goose:provide` line, then the provider is added to the provider
set with the same name as the function (`ProvideFoo`, in this case).
Providers can specify dependencies with parameters:
```go
package foobarbaz
type Bar int
// ...
// goose:provide SuperSet
type Bar int
// ProvideBar returns a Bar: a negative Foo.
func ProvideBar(foo Foo) Bar {
@@ -58,9 +51,9 @@ import (
"errors"
)
type Baz int
// ...
// goose:provide SuperSet
type Baz int
// ProvideBaz returns a value if Bar is not zero.
func ProvideBaz(ctx context.Context, bar Bar) (Baz, error) {
@@ -71,23 +64,36 @@ func ProvideBaz(ctx context.Context, bar Bar) (Baz, error) {
}
```
Provider sets can import other provider sets. To add the `ProvideFoo` set to
`SuperSet`:
Providers can be grouped in **provider sets**. To add these providers to a new
set called `SuperSet`, use the `goose.NewSet` function:
```go
// goose:import SuperSet ProvideFoo
package foobarbaz
import (
// ...
"codename/goose"
)
// ...
var SuperSet = goose.NewSet(ProvideFoo, ProvideBar, ProvideBaz)
```
You can also import provider sets in another package, provided that you have a
Go import for the package:
You can also add other provider sets into a provider set.
```go
// goose:import SuperSet "example.com/some/other/pkg".OtherSet
```
package foobarbaz
A provider set reference is an optional import qualifier (either a package name
or a quoted import path, as seen above) ending with a dot, followed by the
provider set name.
import (
// ...
"example.com/some/other/pkg"
)
// ...
var MegaSet = goose.NewSet(SuperSet, pkg.OtherSet)
```
### Injectors
@@ -95,32 +101,34 @@ An application wires up these providers with an **injector**: a function that
calls providers in dependency order. With goose, you write the injector's
signature, then goose generates the function's body.
An injector is declared by writing a function declaration without a body in a
file guarded by a `gooseinject` build tag. Let's say that the above providers
were defined in a package called `example.com/foobarbaz`. The following would
declare an injector to obtain a `Baz`:
An injector is declared by writing a function declaration whose body is a call
to `panic()` with a call to `goose.Use` as its argument. Let's say that the
above providers were defined in a package called `example.com/foobarbaz`. The
following would declare an injector to obtain a `Baz`:
```go
//+build gooseinject
// +build gooseinject
// ^ build tag makes sure the stub is not built in the final build
package main
import (
"context"
"codename/goose"
"example.com/foobarbaz"
)
// goose:use foobarbaz.SuperSet
func initializeApp(ctx context.Context) (foobarbaz.Baz, error)
func initializeApp(ctx context.Context) (foobarbaz.Baz, error) {
panic(goose.Use(foobarbaz.MegaSet))
}
```
Like providers, injectors can be parameterized on inputs (which then get sent to
providers) and can return errors. Each `goose:use` directive specifies a
provider set to use in the injection. An injector can have one or more
`goose:use` directives. `goose:use` directives use the same syntax as
`goose:import` to reference provider sets.
providers) and can return errors. Arguments to `goose.Use` are the same as
`goose.NewSet`: they form a provider set. This is the provider set that gets
used during code generation for that injector.
You can generate the injector by invoking goose in the package directory:
@@ -164,7 +172,7 @@ func initializeApp(ctx context.Context) (foobarbaz.Baz, error) {
```
As you can see, the output is very close to what a developer would write
themselves. Further, there is no dependency on goose at runtime: all of the
themselves. Further, there is little dependency on goose at runtime: all of the
written code is just normal Go code, and can be used without goose.
[`go generate`]: https://blog.golang.org/generate
@@ -228,19 +236,21 @@ func (b *Bar) Foo() string {
return string(*b)
}
//goose:provide BarFooer
func provideBar() *Bar {
func ProvideBar() *Bar {
b := new(Bar)
*b = "Hello, World!"
return b
}
//goose:bind BarFooer Fooer *Bar
var BarFooer = goose.NewSet(
ProvideBar,
goose.Bind(Fooer(nil), (*Bar)(nil)))
```
The syntax is provider set name, interface type, and finally the concrete type.
An interface binding does not necessarily need to have a provider in the same
set that provides the concrete type.
The first argument to `goose.Bind` is a nil value for the interface type and the
second argument is a zero value of the concrete type. An interface binding does
not necessarily need to have a provider in the same set that provides the
concrete type.
[type identity]: https://golang.org/ref/spec#Type_identity
[return concrete types]: https://github.com/golang/go/wiki/CodeReviewComments#interfaces
@@ -256,32 +266,31 @@ following providers:
type Foo int
type Bar int
//goose:provide Foo
func provideFoo() Foo {
func ProvideFoo() Foo {
// ...
}
//goose:provide Bar
func provideBar() Bar {
func ProvideBar() Bar {
// ...
}
//goose:provide
type FooBar struct {
Foo Foo
Bar Bar
}
var Set = goose.NewSet(
ProvideFoo,
ProvideBar,
FooBar{})
```
A generated injector for `FooBar` would look like this:
```go
func injectFooBar() FooBar {
foo := provideFoo()
bar := provideBar()
foo := ProvideFoo()
bar := ProvideBar()
fooBar := FooBar{
Foo: foo,
Bar: bar,
@@ -300,8 +309,6 @@ this to either return an aggregated cleanup function to the caller or to clean
up the resource if a later provider returns an error.
```go
//goose:provide
func provideFile(log Logger, path Path) (*os.File, func(), error) {
f, err := os.Open(string(path))
if err != nil {