Giter Club home page Giter Club logo

rte's People

Contributors

jwilner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

soryrawyer

rte's Issues

Better encapsulate wrapper types

The generated function types are really an implementation detail; I think it's probably for users if we just make rte.Route.Handler interface{} and do the type conversion in the background with a generated switch block -- i.e.:

type Route struct {
       ...
       Handler interface{}
}

func convert(i interface{}) (h handler, e error) {
       switch v := i.(type) {
       case http.Handler:
                h = func0(v.ServeHttp)
       case func(http.ResponseWriter, *http.Request):
                h = func0(v)
       case func(http.ResponseWriter, *http.Request, string):
                h = func1(v)
       ...
       default:
                e = fmt.Errorf("unsupported handler type: %T", h)
       }
       return
}

Benchmarking

One of the design goals is performance, but we have no real benchmarking suite to measure anything against. go-http-routing-benchmark is a test suite for a bunch of routing stuff -- we could fork and adapt?

generate tests for generated code

I like that sqlboiler generates tests alongside its generated models -- seems like a good idea for the generated code, here, too.

Is it possible to support gzip content?

I want to use RTE with mithril.js as frontend. The original mithril.js uncompressed is about 24K bytes. The compressed size is about 8K.
Could you provide an example to handle:

  1. if browser client accept gzip content, then send compressed version of mithril.js.
  2. if not accept gzip content, then send un-compressed version of mithril.js.

BadRequest

Right now, when we can't parse a non-string path var as expected, it panics -- https://github.com/jwilner/rte/blob/master/cmd/rte-gen/tmpl_func.go#L64 -- but we need to be returning a 400.

We could just do the default w.WriteHeader(http.StatusBadRequest), but users should be able to parameterize that behavior -- so we should probably inject that sort of behavior via Bind, something like:

type Binder interface {
	Bind(segIdxes []int, badReqStrat BadRequestStrategy) (http.HandlerFunc, error)
}

Support 405 handling?

Currently there isn't an easy way to. I think inverting the tree and letting "405" be a special method value seems plausible.

Further 405 handling

Further support for 405s is probably in order.

  • It'd be nice to declare a 405 for many paths at once
  • It'd be nice to reuse a 405 handler, but currently only one signature can be used -- maybe add handling to ignore extra params?

Vanity route constructor

After #16, it'd be possible to have a function func Parse(is ...interface{}) []Route so that users can construct routes like:

h := rte.Must(rte.Parse(
      "GET", "/foo/:foo_id", func(w http.ResponseWriter, r *http.Request, fooID string) {},
     "POST", "/foo/:foo_id/bar/:bar_id", func(w http.ResponseWriter, r *http.Request, fooID, barID string) {
     },  myMiddleware
))

go generate

Do we want to use go generate? Client code could use go generate with cmd/rte-gen, so that they can do something like:

tbl := rte.New(
...
     // go:generate rte-gen hndlrs.MyHndlr string hex-int64
     hndlrs.MyHndlr(
          "GET", "/foo/:str-param/:hex-param",
           func(w http.ResponseWriter, r *http.Request, strParam string, hexParam int64) {
                  ...
           }, 
     ),
)

If we do, seems like a good goal to start using it ourself for the binding handlers we ship in rte_func.go:

// go:generate rte-gen funcs.S1 string
// go:generate rte-gen funcs.S1H1 string hex-int64
// go:generate rte-gen funcs.S1I1 string int64

Another way of putting this would be that the Gen logic needs a real cli

Generated code should be in own dir

For general code cleanliness, and given #2 and #3, we probably want to isolate generated code in its own package. Might make sense to give each its own generated file, if we want a simple interface for go:generate that only generates one file (and its tests). Maybe a nice package name, too?

rte/
  |_ funcs/
     |_ s1_h1.go
     |_test_s1_h1.go

Trie?

Currently just using a hash map for each segment. It works, but would we be better suited by a trie / radix tree? Probably best to determine w/ the benchmarking suite in #5

Segmenting paths

Currently, when matching path variables, we parse the path twice on each request for segments / variables (https://github.com/jwilner/rte/blob/master/rte.go#L153 and again within findNSegments within each handler https://github.com/jwilner/rte/blob/master/rte.go#L200). Since we're not in the business of heap allocations, we can't just save a dynamic slice of strings to pass into the handlers -- but we also of course don't know how many variables we're gonna get from a given path before we parse. That said, when the table is "compiled", we know the maximum number of path variables we'll ever need to match -- e.g. each Binder could have a factory method for an array (returning it as a slice) and the compiled table could just always use the factory method of the binder with the most path variables.

MW application logic

We should be able to use the standard func(http.Handler) http.Handler signature -- but we'll need some wrapper logic to adapt it to be able to get func(rte.Handler) rte.Handler

Route construction helpers

There are a number of routing options I think we should look at as transforms on lists of routes.

E.g. If people want to permit the absence of a slash, it's usually available as a switch in the router API. IMO, that can bloat the router API. Instead I think we should model these sorts of things as transformations on the route data -- e.g. a function that takes in a list of routes and returns a list of routes with slash made optional. Something like:

func SlashOptional(routes ...Route) []Route {
     var copies []Route
     for _, r := range route {
           c := r
           if l := len(r.Path); l > 0 && r.Path[l - 1] == "/" {
                    c.Path = r.Path[:l - 1]                  
           } else {
                    c.Path = r.Path + "/"
           }
           copies = append(copies, c, r)
     }
     return copies
}

The catch here is that we'd want to be more defensive -- if someone already passed in both paths, we wouldn't want to create duplicates -- so we'd some deduping logic.

A similar API:

func Prefixed(prefix string, routes ...Route) []Route {
     ....
}

I also sort of think we should drop the variadic spreading everywhere.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.