Codegen builders / setters / child validators#16
Conversation
|
Nice! Will take a look in a bit Fwiw though I wouldn't worry too much about benchmarks here
|
|
Yeah - you could argue that benchmarks are premature optimization in this case. But - they are really easy to write in go and benchmarking is pretty prevalent in the community as a result. |
toptobes
left a comment
There was a problem hiding this comment.
I see nothing egregious, and it's really satisfying seeing all of the now-old boilerplate being removed 🙂
tools/gen-options/main.go
Outdated
| merged, _ := MergeOptions(v...) | ||
| o.{{ .Field }} = merged |
There was a problem hiding this comment.
| merged, _ := MergeOptions(v...) | |
| o.{{ .Field }} = merged | |
| o.{{ .Field }} = MergeOptions(v...) |
I thinking pulling out MergeOptions into
MergeOptions<T> => T(sets defaults + merges options; used inside builders)MergeAndValidateOptions<T> => T, error(calls the prev function + validates; used inside commands)
could be a bit clearer + save on a few CPU cycles since the error isn't being used here anyways
There was a problem hiding this comment.
Not a bad thought! Also - now that I'm thinking about this, we could probably shorten that to Merge since, outside the package, we are using it like this:
// Current Options is redundant
merged, err := options.MergeOptions(opts...)
// Updated
merged, err := options.Merge(opts...)Which make options.MergeAndValidate not seem as verbose.
options/options.go
Outdated
| // for composable options. | ||
| type Builder[T Validator] interface { | ||
| type Builder[T any] interface { | ||
| List() []func(*T) |
There was a problem hiding this comment.
doesn't matter that much maybe "Setters" or "ListSetters" may be the most clear name
There was a problem hiding this comment.
Yeah - naming things is hard. I think "Builder" makes sense for the overarching idea that it "builds" options. But the funcs themselves being called "Setters" might make more sense. More than anything, I want to think about how it looks/feels to consumers of the library. Consider this godoc link for create keyspace:
https://pkg.go.dev/github.com/datastax/astra-db-go#AstraDbAdmin.CreateKeyspace
I don't love that the method signature is:
CreateKeyspace(ctx context.Context, keyspace string, opts ...options.Builder[options.CreateKeyspaceOptions]) errorIt's not obvious to the caller that they can use the fluent builder / pass options directly without more comments. And I HAVE covered that in comments / examples but I am iffy about this in the godoc. So one option would be aliasing them:
// CreateKeyspaceOption configures a CreateKeyspace operation.
// Use the fluent builder:
//
// options.CreateKeyspace().SetBlocking(false)
//
// Or pass a struct directly:
//
// &options.CreateKeyspaceOptions{Blocking: ptr.To(false)}
type CreateKeyspaceOption = Builder[CreateKeyspaceOptions]Then we change our CreateKeyspace method to:
CreateKeyspace(ctx context.Context, keyspace string, opts ...options.CreateKeyspaceOption) errorOr something along those lines. But basically, I want to get builder out of that method signature altogether if possible.
| // {{ .Method }} sets the {{ .Field }} option. | ||
| // {{ .Comment }} |
There was a problem hiding this comment.
major nit but leaving a gap between the boilerplate comment and the actual comment may be more readable in IDEs, especially when the lines are not split
| // {{ .Method }} sets the {{ .Field }} option. | |
| // {{ .Comment }} | |
| // {{ .Method }} sets the {{ .Field }} option. | |
| // | |
| // {{ .Comment }} |
There was a problem hiding this comment.
There was a problem hiding this comment.
Hm you know what fair enough
|
Hey @toptobes I made some more changes. Renamed builder, added those aliases to codegen, and then I realized we can just NOT export the builders at all for decreased surface area. It cleans up the godoc to not have those comments codegen copy-pasted 2 places. Only thing I need to do still is: fix the example for the aliases. Right now it's: // CreateKeyspaceOption is a convenience alias for Builder[CreateKeyspaceOptions].
type CreateKeyspaceOption = Builder[CreateKeyspaceOptions]... which I did just to vet the idea and get the whole thing building. But need to change it to be more descriptive like we talked about: // CreateKeyspaceOption configures a CreateKeyspace operation.
// Use the fluent builder:
//
// options.CreateKeyspace().SetBlocking(false)
//
// Or pass a struct directly:
//
// &options.CreateKeyspaceOptions{Blocking: ptr.To(false)}
type CreateKeyspaceOption = Builder[CreateKeyspaceOptions]Once I figure that out, we will be in real good shape. |
|
Hey @toptobes I figured out the last piece. I started moving towards stringers instead of everything being in one giant template. I think I might refactor some of the other things to be a little more self-contained as well. Also makes them easier to test (I added some tests as well). Here's what the godoc looks like:
Hyperlinks referencing related types everywhere. Code examples you can use. And the struct defs are kept close to each other. Etc. I think it's pretty good. Ready to move on to getting more coverage and keep using this for now and see when I run up against any rough edges in the future and fix them. |





Hey @toptobes I took a stab at integrating your idea into the repo. I decided to just make
Validate()optional and check for it. I avoided using reflection and wrote a benchmark to verify that whatever we do, performance doesn't suffer.Still need to figure out what we are going to do about comments. Working on that.