From their blog they have an example of a ‘proper context usage’ in the form of a search server thing, let’s take it piecemeal.
They start with (this forum’s syntax highlighting never ceases to impress me ^.^;):
func handleSearch(w http.ResponseWriter, req *http.Request) {
// ctx is the Context for this handler. Calling cancel closes the
// ctx.Done channel, which is the cancellation signal for requests
// started by this handler.
var (
ctx context.Context
cancel context.CancelFunc
)
timeout, err := time.ParseDuration(req.FormValue("timeout"))
if err == nil {
// The request has a timeout, so create a context that is
// canceled automatically when the timeout expires.
ctx, cancel = context.WithTimeout(context.Background(), timeout)
} else {
ctx, cancel = context.WithCancel(context.Background())
}
defer cancel() // Cancel ctx as soon as handleSearch returns.
// Check the search query.
query := req.FormValue("q")
if query == "" {
http.Error(w, "no query", http.StatusBadRequest)
return
}
// Store the user IP in ctx for use by code in other packages.
userIP, err := userip.FromRequest(req)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
ctx = userip.NewContext(ctx, userIP)
// Run the Google search and print the results.
start := time.Now()
results, err := google.Search(ctx, query)
elapsed := time.Since(start)
if err := resultsTemplate.Execute(w, struct {
Results google.Results
Timeout, Elapsed time.Duration
}{
Results: results,
Timeout: timeout,
Elapsed: elapsed,
}); err != nil {
log.Print(err)
return
}
}
Something like that in Elixir/plug/phoenix would be more like:
def handle_search(conn, %{"q" => query} = params) do
import DateTime
timeout = parse_duration(params[:timeout] || :infinite) # Yes they default to an infinite timeout it seems, wtf...
user_ip = conn.remote_ip # The Google Search thing needs the user's IP for some reason...
start = utc_now() |> to_unix()
results = Google.search(query, user_ip, timeout: timeout)
elapsed = (utc_now() |> to_unix()) - start
render(conn, :handle_search, results: results, timeout: timeout, elapsed: elapsed)
end
And that has all the same cancelability and error handling as the go code (it would be even less code if it was not such a direct code-to-code translation and more traditionally Elixir). Like wtf… >.>