Comments (11)
Discussed with @vangent offline. For this and #406, we want to develop a way of specifying a subset of fields. (Off-hand, I think this could be done by specifying the types of fields, since we're guaranteeing they have to be unique.) Once that's in place, you could imagine:
package wire
// FieldsOf specifies that the given field types will be provided by extracting them
// from a struct type, which must also be provided in the same provider set. Each
// of the arguments must be a pointer to the type they wish to reference.
//
// For example:
//
// type S struct {
// MyFoo *Foo
// }
// func NewStruct() *S { /* ... */ }
// var Set = wire.NewSet(wire.FieldsOf(new(S), new(*Foo)))
func FieldsOf(structType interface{}, fieldTypes ...interface{}) StructFields
(The semantics of struct versus pointer-to-struct likely have to be worked out.)
from wire.
Thanks for checking out wire!
Think of a struct with fields as being created by a function that takes all of the fields' types;
type FooBar struct {
foo *Foo
bar *Bar
}
requires the same injection data to create FooBar as
func newFooBar(foo *Foo, bar *Bar) FooBar {...}
In either case, the values can be injected using wire.Value, but also by other kinds of providers. Here is an example of fields of a struct (Foo and Bar in struct FooBar) being injected via provider functions (provideFoo and provideBar) instead of via wire.Value.
But yes, each field in the struct / each argument to the function needs to be provided.
I'm not sure wire.FieldValues() is necessary, since wire automatically matches up the provided types from all of the providers given to wire.Build to the required injection data. If want to group the providers, you could use wire.ProviderSet to do that; here is an example of that.
Hope that helps!
from wire.
Maybe I should provide a bit more context about what I'm trying to achieve.
There is an existing project which uses dependency injection, but the injection code was written manually. I wanted to see if it can be replaced with Wire.
In this project each component is inside its own package and has its own Config type.
For instance there is package mongo which contains a service that stores some data inside mongo (simplified snippet):
// mongo.go
package mongo
type Config struct {
URL string
User string
Password string
}
type Service struct {
cfg *Config
}
func NewService(cfg *Config) {
return &Service {
cfg: cfg,
}
}
// ... service methods omitted for brevity
There are also a bunch of other packages, containing other components, also having their own Config types.
Inside our main.go, we also have a Config type, which is composed of all Configs from the components:
// main.go
package main
type Config struct {
Mongo *mongo.Config
Redis *redis.Config
// ......
}
type MainService struct {
mongo *mongo.Service
redis *redis.Service
//....
}
func main() {
var cfg *Config
// ... load cfg from somewhere
svc := InitializeMainService(cfg)
// ... use svc for something
}
func InitializeMainService(cfg *Config) *MainService {
mongoService := mongo.NewService(cfg.Mongo)
redisService := redis.NewService(cfg.Redis)
return &MainService {
mongo: mongoService,
redis: redisService,
}
}
So here, each component's constructor is called with only the config relevant to it. With wire, it seems like I would have to make a provider function for each sub-config which takes the main config and returns the relevant sub-config:
// injectors.go
package main
func ProvideMongoConfig(cfg *Config) *mongo.Config {
return cfg.Mongo
}
func ProvideRedisConfig(cfg *Config) *redis.Config {
return cfg.Redis
}
func InitializeMainService(cfg *Config) *MainService {
panic(wire.Build(ProvideMongoConfig, mongo.NewService, ProvideRedisConfig, redis.NewService))
}
In our case we have ~15 such separate components, some of which depend on others, so it would be nice to have the wiring auto-generated, but each of them has its own Config type and having to write a separate provider for each sub-config from the main config seems like a lot of repetition. Is there a better way that I am missing? If not, I think it would be nice if I could somehow tell wire to use the sub-fields of the Config struct as injectors instead of the whole config itself:
// injectors.go
package main
func InitializeMainService(cfg *Config) *MainService {
panic(wire.Build(wire.FieldsOf(cfg), mongo.NewService, redis.NewService))
}
Would something like that be possible and does what I am trying to achieve make sense?
from wire.
No, I don't think there's a better way right now. Maybe @zombiezen knows of one, and could comment on whether wire.FieldsOf() is a reasonable feature request.
I thought that something like this would work:
func InitializeMainService(cfg *Config) *MainService {
wire.Build(
mongo.NewService,
wire.Value(cfg.Foo),
redis.NewService,
wire.Value(cfg.Redis),
...
)
}
which doesn't seem too bad, but I'm getting an error. I filed #378 to look into it.
from wire.
I also tried this and got the same error, but I assumed that I had misunderstood the purpose of wire.Value. It seemed that wire.Value only accepts values that are known at compile time and not at runtime.
from wire.
For now I ended up doing something like this:
//main.go
package main
func main() {
var cfg *Config
// ... load cfg from somewhere
svc := InitializeMainService(transformArgs(cfg))
// ... use svc for something
}
// injectors.go
package main
func transformArgs(cfg *Config) (*mongo.Config, *redis.Config) {
return cfg,
cfg.Mongo,
cfg.Redis,
}
func InitializeMainService(cfg *Config, mongoCFG *mongo.Config, redisCFG *redis.Config) *MainService {
panic(wire.Build(mongo.NewService, redis.NewService))
}
This way the workaround logic is all inside the transformArgs function rather than having to create a bunch of providers.
from wire.
Thanks, that looks reasonable. If nothing else we could add that to the Best Practices section of the wire documentation for this use case.
from wire.
(Just returned from Gophercon, thank you for your patience.)
@e-nikolov Your understanding is correct: struct providers in wire allow you to group a bunch of related types, but there is no corresponding way to "ungroup" in Wire right now. I feel that this would be a good feature, but we would need to iron out a way to be selective about what fields are being extracted. Ideas for syntax here would be welcome.
@vangent Yes, documentation in the interim would be good. gcp.CredentialsTokenSource
is a function that exists purely for this reason.
from wire.
(This is also tangentially related to #406)
from wire.
@zombiezen per your request, assigning to you to think about. Let me know when you are ready to discuss.
from wire.
Discussed with @zombiezen offline a bit more. We could add a little bit more of flexibility by matching the "names" of the fields, not types. But the uniqueness of the types that can be used in FieldsOf
remains unchanged.
package wire
// FieldsOf extracts the structType into a list of types specified by fieldNames.
// All types should be provided in the same provider set. The structType argument
// must be a pointer to the struct it wishes to reference.
//
// Example:
//
// type S struct {
// MyFoo *Foo
// MyBar *Bar
// }
// func NewStruct() *S { /* ... */ }
// var Set = wire.NewSet(wire.FieldsOf(new(S), "Foo", "Bar"))
func FieldsOf(structType interface{}, fieldNames ...string) StructFields {
return StructFields{}
}
from wire.
Related Issues (20)
- wire `sync.Map` throw error HOT 2
- I has bean tried for two days, but failed. I define four struct and it's constructor, I define a init function in wire.go, but I can
- I can't use the init function which from my wire.go in my main.go HOT 3
- wire.NewSet support generics HOT 1
- internal/wire: update the path of wire which go generate run wire cmd
- Wire: not enough arguments in call to xxx
- is wire.FieldsOf support any field ? HOT 2
- Support for request scoped DI code generation HOT 2
- Injecting the subpackage into the parent package results in the error "no provider found for invalid type". HOT 3
- How to use wire.Bind() if concrete implementation is resolved at runtime? HOT 2
- please create new tag for 0675cdc
- Crashing with Go 1.22 HOT 3
- go generate ./... no longer works with go 1.22.0 and wire 0.6.0 HOT 3
- go build -mod=vendor ./cmd/wire doesn't work after update to 0.6.0
- go: -mod may only be set to readonly or vendor when in workspace mode, but it is set to "mod" HOT 2
- Sometimes it succeeds, sometimes it fails HOT 1
- Cleanup functions should return erors
- wire does not work with go 1.23.0
- TestWire/UnexportedStruct fails because of the error wording
- The alias defined using the `type` keyword is not used during code generation; instead, the original type is used. HOT 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from wire.