2
votes

I'm trying to build a very simple Go web application, and the golang "a folder per package" structure is making things difficult for me.

I'm using github.com/gorilla/mux as the router and github.com/unrolled/render for template rendering. This means that I need to create a new router and a new renderer when the app launches, and I need all my routes to access the renderer.

This is super easy to do in a single file:

func main() {

  ...

  r := render.New(render.Options{
    // a lot of app specific setup
  })


  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    r.HTML(w, http.StatusOK, "myTemplate", nil)
  })

  ...

}

However, this is where I don't understand Go. Because I want the routes in separate files in a subfolder (my project will grow), that forces them to be in a routes package. Of course that makes the renderer variable inaccesssible. I can't just create the renderer in the routes package, because the render.New() call relies on a me passing in a ton of app specific stuff like the template folder, and helpers for asset paths.

I went down the route of making my handler functions work on a struct with an already initialized renderer...

func (app *App) Hello2(w http.ResponseWriter, r *http.Request) {
  app.Renderer.HTML(w, http.StatusOK, "myTemplate", nil)
}

But I'm still confused as to how I'm going to access this app *App in the routes package when it's initialized in main. Everything in Go seems super easy if you have a flat list of files, but as soon as you want a bit of folder structure, the package setup becomes problematic.

There's probably something I'm missing here, so any help is appreciated.

1
Googling around, I found this solution: github.com/unrolled/render/issues/7#issuecomment-61735461. That seems to do the trick, but is that an effective solution? It seems weird that I need to attach the same object to every single call in a middleware handler.Ronze
I think that's a reasonable way to go. You could create a myapp/render package that just sets a var Render in its init() (or even just declares the var and lets main.main() set it). But the setup you linked provides a hook to change behavior later if, say, Render config is not the same for all requests someday (e.g., you host versions of your app on two domains and use different Layouts for each).twotwotwo

1 Answers

0
votes

Here's general info on dealing with dependencies in Go. A key trick is, you just have to declare the Render variable in a package that your views can import. You could create a myapp/render package with a var Render that's either inited in the package itself (func init()) or set from main.

But the context thing you found sounds totally sane, though it might be more than this app needs. The neat thing about it is that because the context is set in per-request code, later you could extend it to do sneaky things like use the Host: header to provide a different Layout for people loading the app via different domains. If the Layout is baked into a global, you can't. This can be a real advantage--I've tried to retrofit per-request changes onto big codebases whose config was sprayed around various global variables, and it's a pain.