-
Notifications
You must be signed in to change notification settings - Fork 206
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
Implementing a dig.As ProvideOption #233
Conversation
still need to add a test for the |
Codecov Report
@@ Coverage Diff @@
## master #233 +/- ##
==========================================
+ Coverage 98.4% 98.47% +0.06%
==========================================
Files 10 10
Lines 1130 1179 +49
==========================================
+ Hits 1112 1161 +49
Misses 13 13
Partials 5 5
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the contribution! Looks good overall - I only have a minor suggestion with respect to the implementation. Can I get another pair of eyes, @akshayjshah @glibsm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can arguably mimic this behavior in the current design by explicitly providing constructors for the interfaces you want to provide:
type Foo struct {}
func (f Foo) String() string {
return "foo"
}
...
Provide(func () Foo { return Foo{} })
Provide(func (f Foo) fmt.Stringer { return f })
Regardless, the As
semantic is nice so I think it's something to consider. I'm curious to hear others' thoughts here.
Yeah that was the suggestion on the issue too. The problem is that (at iZettle) we opted for explicitly declare the function providers that you've put in one line. This means that some of our fx modules where we do the DI wiring are quite large. Having the As(..) makes the code much nicer and decreases the amount of functions we need to declare. |
This adds a dig.As option which allows the caller to specify a constructor and a list of interfaces that the constructed struct implements. Closes uber-go#197
@amckinney, @glibsm, @prashantv I have applied the changes requested and is ready for a re-review |
This rewords the error messages for `dig.As` validation to specify the type encountered rather than the Kind.
The dig.As functionality shouldn't introduce its own result type because this isn't a new leaf kind. It's another option for the case when a single result is being provided. This change merges resultSingle and resultAs. In the process, the conversion of the dig.As arguments to reflect.Types is done once and re-used throughout.
Thanks for the change @alessandrozucca! Besides minor nits around the error I went ahead and made the required changes to avoid a bunch of back-and-forth. |
@abhinav thank you, looks better now It did cross my mind, didn't do it for two reasons:
Having said that, reusing resultSingle now makes sense to me 👍 |
dig.go
Outdated
@@ -127,6 +151,28 @@ func Group(group string) ProvideOption { | |||
}) | |||
} | |||
|
|||
// As is a ProvideOption that specifies that the struct produced by the constructor | |||
// implements the given interface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicate doc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Derp. Forgot to delete.
dig.go
Outdated
@@ -598,9 +659,10 @@ type node struct { | |||
|
|||
type nodeOptions struct { | |||
// If specified, all values produced by this node have the provided name | |||
// or belong to the specified value group | |||
// belong to the specified value group or implement any of the interfaces |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Missing period.
This adds a test for one of the missing invalid cases and verifies that dig.As plays nicely with graph visualization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change looks good to me -- a couple of additional unit tests would be good, expectially for As
and Named
being combined.
Is it clear that the Name
applies to the As
as well? E.g.,
c.Provide(NewBuffer, dig.Name("foo"), dig.As(new(io.Writer)))
It's not clear to me whether the writer is also named, or whether we can do dig.As(new(io.Writer), dig.Name("foo"))
. If we're not sure, we could disallow it too.
dig.go
Outdated
case resultGrouped: | ||
// we don't really care about the path for this since conflicts are | ||
// okay for group results. We'll track it for the sake of having a | ||
// value there. | ||
k := key{group: r.Group, t: r.Type} | ||
cv.keyPaths[k] = path | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can remove blank
@@ -1905,6 +2024,46 @@ func TestProvideFailures(t *testing.T) { | |||
`dig.out embeds \*dig.Out`, | |||
) | |||
}) | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
couple of additional tests:
- use
Named
andAs
together (and verify the behaviour -- ensure both the function's return type and theAs
type are added to the container with the name). - provide a function that returns an interface, and then use
As
. If theAs
is for the same type, it should fail, but it should work if it's a different interface type
This adds a test to verify that dig.As is compatible with dig.Name and updates the documentation to clarify this.
If dig.As is called with the same type the constructor is producing, ignore that As entry rather than erroring out.
Thanks for the contribution @alessandrozucca! We'll try to release this soon. |
This brings back #233. Per #235 (review), the issues we need to resolve are, 1. `dig.As` seems to indicate that it's a total override of the provided type. The way it currently works is it appends other interfaces on top of whatever the constructor already returns 2. semantics of `dig.Provide(func New() (Foo, io.Reader, error), dig.As(new(io.Writer)`. It currently fails due to inability to case reader to writer, but we'd want some extra validation here. Perhaps `dig.As` is only supported for a single type, error tuple? Closes #197
This brings back #233. Per #235 (review), the issues we need to resolve are, 1. `dig.As` seems to indicate that it's a total override of the provided type. The way it currently works is it appends other interfaces on top of whatever the constructor already returns 2. semantics of `dig.Provide(func New() (Foo, io.Reader, error), dig.As(new(io.Writer)`. It currently fails due to inability to case reader to writer, but we'd want some extra validation here. Perhaps `dig.As` is only supported for a single type, error tuple? Closes #197
This brings back uber-go#233. Per uber-go#235 (review), the issues we need to resolve are, 1. `dig.As` seems to indicate that it's a total override of the provided type. The way it currently works is it appends other interfaces on top of whatever the constructor already returns 2. semantics of `dig.Provide(func New() (Foo, io.Reader, error), dig.As(new(io.Writer)`. It currently fails due to inability to case reader to writer, but we'd want some extra validation here. Perhaps `dig.As` is only supported for a single type, error tuple? Closes uber-go#197
This brings back Dig.As from #233 with some changes. Specifically, Dig.Provide(t1, As(t2))used to provide type t2 as well as type t1 but that is no longer the case. For example, c.Provide(newBuffer, dig.As(new(io.Reader), new(io.Writer))) will provide io.Reader and io.Writer, but not buffer. Refs: GO-451
This adds a dig.As option which allows the caller to specify a
constructor and a list of interfaces that the constructed struct
implements.
Closes #197