Thanks for the advice guys. I tried a couple of different things in order to 
get it to work including returning `Future[void]` from my `async` procs. Turns 
out the error I was getting is just the linter being a tad nit-picky and not 
really an error at all.

I did run into another issue which had to do with passing a `proc` as a default 
argument to a type constructor. For this issue I tried using the `procvar` 
pragma in the proc declaration, in the type signature for the type attributes 
which accept the proc, and in the default argument itself. @dom96 had some 
great advice for this on irc. Unfortunately, none of it really worked 
correctly. The solution that I came up with was to create a variable that 
aliases the `proc` that I wanted to pass as the default argument. It's all 
working as expected now. I ended up with the following:
    
    
    import asynchttpserver, asyncdispatch
    
    
    type
        Router* = ref object of RootObj
            routes: seq[Route]
        Route* = ref object of RootObj
            path*: string
            get*: proc (req: Request): Future[void]
            post*: proc (req: Request): Future[void]
            put*: proc (req: Request): Future[void]
            delete*: proc(req: Request): Future[void]
        Response* = ref object of RootObj
            request*: Request
            status*: HttpCode
            data*: string
            headers*: HttpHeaders
    
    
    proc newRouter*(): Router =
        result = Router(
            routes: @[]
        )
    
    
    proc notFoundHandler*(req: Request): Future[void] {.async.} =
        await req.respond(Http404, "404 Not Found")
    
    
    #[
        Until I can figure out how the hell procvars work, we need
        to alias the default request handler to a variable in order
        for the Route constructor to accept it as a default argument.
    ]#
    var notFoundHandlerVar = notFoundHandler
    
    
    proc newRoute*(
        path: string,
        get, post, put, delete: proc = notFoundHandlerVar
    ): Route =
        result = Route(
            path: path,
            get: get,
            post: post,
            put: put,
            delete: delete
        )
    
    
    
    let DEFAULT_HEADERS = newHttpHeaders([
        ("Content-Type", "text/html"),
        ("Accept", "text/html"),
        ("Accept-Charset", "utf-8"),
        ("Accept-Encoding", "gzip, deflate"),
        ("Connection", "keep-alive")
    ])
    
    
    
    proc newResponse*(
        request: Request,
        status: HttpCode = Http200,
        data: string = "",
        headers: HttpHeaders = DEFAULT_HEADERS
    ): Response =
        result = Response(
            request: request,
            status: status,
            data: data,
            headers: headers
        )
    
    
    proc log*(resp: Response): void =
        echo $resp.status
    
    
    proc send*(resp: Response) {.async.} =
        log(resp)
        await resp.request.respond(
            resp.status,
            resp.data,
            resp.headers
        )
    
    
    proc register*(router: Router, routes: varargs[Route]) =
        for route in items(routes):
            router.routes.add(route)
    
    
    proc route*(router: Router, req: Request) {.async.} =
        for route in router.routes:
            if route.path == req.url.path:
                case req.reqMethod:
                    of HttpGet:
                        await route.get(req)
                    else: continue
        await notFoundHandler(req)
    
    

After I add the mapping for request methods other than `GET`, the next step is 
route matching with url path variables. Then I'm thinking I'll implement a more 
robust `Response.render` proc to handle rendering source code filters. The 
router can be used like this:
    
    
    import asyncdispatch, asynchttpserver, strutils
    import router
    
    from templates import page, index, hello
    
    
    let server = newAsyncHttpServer()
    
    
    let myRouter = router.newRouter()
    
    
    proc indexHandler(req: Request) {.async.} =
        var data = page(
            title="Home",
            contents=index()
        )
        var resp = newResponse(req, data=data)
        await resp.send()
    
    
    proc helloWorldHandler(req: Request) {.async.} =
        var data = page(
            title="Hello",
            contents=hello("Justin")
        )
        var resp = newResponse(req, data=data)
        await resp.send()
    
    
    let index = newRoute(
        path="/",
        get=indexHandler
    )
    
    
    let helloWorld = newRoute(
        path="/hello/",
        get=helloWorldHandler
    )
    
    
    myRouter.register(index, helloWorld)
    
    
    proc log(req: Request): void =
        echo "[$1] $2" % [$req.reqMethod, req.url.path]
    
    
    proc cb(req: Request) {.async.} =
        log(req)
        await myRouter.route(req)
    
    
    waitFor server.serve(Port(8000), cb)
    
    

Does anyone have anything specific they would like to see in a url routing 
library? I'm thinking that as I continue to learn Nim, I can build a more 
robust api for creating web applications.

At some point I want to try and tackle creating a simple ORM as well. I'm very 
familiar with Postresql and SQLite administration, but I will say that my 
skills in writing pure SQL are a bit lacking. Therefore, I have a feeling it 
will take me quite a while to get the ORM project rolling. 

Reply via email to