1

Ok.. I have, or am writing a web framework package, called mao.

I'd like to have my Route defined in the controller.

in mao

type (
    Controller struct {
        Route Route
    }
    Route struct {
        Name, Host, Path, Method string
    }
)

in mao importing package

controller/default.go

type DefaultController struct {
    mao.Controller
}
func (this *DefaultController) Index() Response {
    this.Route = mao.Route{"default_index","localhost","/", "GET"}
}

Now since I'd like to define my route inside the controller, the router, when instanced should read all controllers. That's the problem.

How do I pass the package name to my router so it's able to get all structs and functions in that package? Is it even possible?

  • I think this is a duplication of: http://stackoverflow.com/questions/20803758/how-to-get-all-defined-struct-in-golang/20805018#20805018 (the answer is "no, it's not possible") – Linear Mar 04 '14 at 23:40
  • I'm not so sure it's not possible. I've stumbled upon http://godoc.org/code.google.com/p/go.tools/go/types another approach might be to give each controller a Reflect() method, yet another would be to not have routes inside the controller and use a Controller reflect.Type as the Route attribute. I don't like "it's not possible" everything is possible, it's just a matter of if it's worth the tradeoff. I think I'll ask in golang-nuts. Thanks for the reference to the other question tho. –  Mar 04 '14 at 23:58
  • What I mean is getting all functions and structs isn't possible with just the package. Obviously you could, say, implement a package-level slice containing the reflect.Type of all functions, or go grab a parser and parse the source files, but it's "not possible" in the sense that grabbing the info from the package isn't possible because the information isn't retained in compilation. – Linear Mar 05 '14 at 00:02
  • @Jsor you were right tho. I started fresh and took a different approach where the routes are defined beforehand. –  Mar 14 '14 at 11:31

3 Answers3

2

What you ask isn't possible in Go, since it doesn't have a way to enumerate all types in a package/program.

One alternative would be to follow the lead of the database/sql package, and have a system where other packages can register with it when imported.

For example, to use the PostgreSQL driver with that package, you might do:

import (
    _ "github.com/lib/pq"
    "database/sql"
)

...
db, err := sql.Open("postgres", "dbname=test")

The postgres driver is registered during initialisation of the github.com/lib/pq package. Here is the relevant code from that package (eliding some parts that aren't relevant):

package pq

import (
    "database/sql"
)

type drv struct{}

func (d *drv) Open(name string) (driver.Conn, error) {
    return Open(name)
}

func init() {
    sql.Register("postgres", &drv{})
}

Perhaps you could create a registration API like this to find the various implementations available in the program?

James Henstridge
  • 42,244
  • 6
  • 132
  • 114
0

Honestly, I think you are doing it the wrong way. "auto registering" is obfuscating what happens and will lead to code that is hard to test and to reason about.

I would suggest to make controller an interface that should be satisfied by the concrete controllers and have a method Add(c Controller) on the router to assign the controller in the calling main project (that imports the router and the controllers). This should make your code understandable and explicit and is more in the spirit of go.

The database/sql driver registration is more of a hack and should not be considered best practice.

metakeule
  • 3,724
  • 2
  • 23
  • 29
  • I have something different now `type Route struct { Method string Path string Controller interface{} regex *regexp.Regexp mu sync.RWMutex }` and then reflect what type of or "ValueOf" interface{} it is and then call the method with the results from the regex match as params, so simple (and efficient), but will have to extend it. It's similar to what webgo.io does as in you can have a function and pass it params and have any return type, and depending on the return type use different ways of responding to the request. –  Mar 14 '14 at 12:46
0

I think you should have one struct for router (which may well be global, like http.DefaultClient) and then in constructor functions for your controllers you'd be able to inject this router as a dependency so that a relevant route is injected for the router. DI+interfaces make your code nice and testable, not only in Go.

Just an idea.

Marcin Wyszynski
  • 2,188
  • 1
  • 17
  • 16