-
Notifications
You must be signed in to change notification settings - Fork 436
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
contrib/internal/httputil: generate all possible http interfaces for the response writer wrapper #278
Conversation
contrib/internal/httputil/gen/gen.go
Outdated
// This code is generated because we have to account for all the permutations | ||
// of the interfaces. | ||
func wrapResponseWriter(w http.ResponseWriter, span ddtrace.Span) http.ResponseWriter { | ||
`) |
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.
Would you mind using a template here? This is too cryptic and hard to follow.
This is a great solution. I love it. But please use a template so it's easy to see and work with the generated output. |
@@ -0,0 +1,119 @@ | |||
// Code generated by ./gen do not modify |
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.
Let's respect the standard comment heading for generated files outlined here: golang/go#13560 (comment)
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.
This is a fantastic solution
contrib/internal/httputil/gen/gen.go
Outdated
}) | ||
} | ||
|
||
func combinations(strs []string, pick int) (all [][]string) { |
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.
I've spent quite a lot of time here trying to make sense of this algorithm. Even though it looks short and witty, and possibly a well known algorithm for computer science aficiandos, I'd expect a large percentage of people visiting this code to be like me and not understand what's going on without spending time.
Would it be possible to augment it with documentation describing in detail how it works? Or, if it's a common algorithm that has a Wiki page, simply to link to it?
I was also wondering if we could rename strs
to list
. "strs" makes me think "strings" which is unclear when reading. But maybe it's just me.
contrib/internal/httputil/gen/gen.go
Outdated
|
||
func combinations(strs []string, pick int) (all [][]string) { | ||
switch pick { | ||
case 0: |
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.
I usually like to put a comment in switch cases like this. For example:
case 0:
// nothing to do
contrib/internal/httputil/gen/gen.go
Outdated
combos = append(combos, combinations(interfaces, pick)) | ||
} | ||
|
||
template.Must(template.ParseFiles("./gen.gohtml")).Execute(os.Stdout, map[string]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.
Thanks for being patient again. And I'm sorry for being unclear. I think it would be nicer, easier to handle and make sense of this code, if both the template text and the Go code would be in the same file. Basically, at the end of the Go code we'd put a const template=...
with a backtick, like here or here.
Just like in those two examples, we could also just keep this in the httputil
folder using the // +build !ignore
flag. All in one file: make_responsewriter.go
(or similar). I found this worked well in the past, avoiding the need for a new package.
w = newResponseWriter(w, span) | ||
switch { | ||
{{- range .Combinations }} | ||
{{- range . }} |
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.
Could we possibly indent all these {{ - range . }}
and {{- end }}
commands? It's slightly difficult to see where each starts and ends.
Also, what is this -
prefix? I've not used it before.
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.
The -
prefix removes whitespace before the block.
contrib/internal/httputil/gen/gen.go
Outdated
@@ -0,0 +1,36 @@ | |||
package main |
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.
Let's document this package and explain what it does. It shouldn't be a package level documentation, it should be the documentation of this utility, something like "This program generates the implementation of all possible combinations of the http.ResponseWriter's interface implementations [...]" and so on. To make it clear that it's not package level documentation we should add a space between the documentation and package declaration.
@gbbr thanks for the review. I renamed the code generated and added some additional documentation. Sorry that |
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 adding the explanation. It makes a huge difference. I am wondering if it would be fun to generate the tests for this code too, using the same combinations.
It would also be nice to have a basic unit test for the "combinations" function.
} | ||
|
||
// combinations returns all possible unique selections of size `pick` of a list | ||
// of strings for which order does not matter: wikipedia.org/wiki/Combination |
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.
Not sure if the link adds much. It doesn't link to this specific algorithm implemented here. If we use a link it should serve as a shortcut to understanding this algorithm. The wiki page is too vast.
switch { | ||
{{- range .Combinations }} | ||
{{- range . }} | ||
case {{ range $i, $v := . }}{{ if gt $i 0 }} && {{ end }}ok{{.}}{{ end }}: |
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.
Is it possible to remove $v
here just like in Go code? Not sure. It's unused.
…the response writer wrapper
- update comment to match convention - add more tests
…dd additional documentation explaining what it does and how it works
…ackage so it can be tested
61428f2
to
4ae3cdd
Compare
@gbbr I moved the combinations function to a separate, internal |
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!
…the response writer wrapper (DataDog#278)
This fixes: #256
So here's the idea. We need to wrap the
WriteHeader
call because that's really the only place to get the http response code. But we also want to make sure we don't accidentally remove interface implementations on the response writer (Flusher
,Hijacker
, etc).One solution would be to implement a super struct:
Because of field embedding we can call the methods of each interface on the outer struct (
w.Flush() -> w.Flusher.Flush()
), which in turn makes it so that we now actually implement each of the interfaces.Unfortunately this won't work because if we didn't already implement one of the http interfaces, we would falsely indicate to the calling code that we did. For example client code might have:
Which would now return true even though we didn't actually implement the method originally.
So to overcome this problem I wrote a script which generates all the possible combinations and creates an anonymous struct for each case implementing only the interfaces that the original response writer did.
Does that make sense?