From aedfdff7921b750e204ecf0544873d23b36dfd59 Mon Sep 17 00:00:00 2001 From: Matt Haggard Date: Fri, 18 Sep 2020 10:34:36 -0400 Subject: [PATCH] Fix #211 - custom routers now get the same default error handling as normal routers --- jester.nim | 46 +++++++++++++++++++++++++++++++++--------- tests/customRouter.nim | 20 ++++++++++++++++++ tests/tester.nim | 14 +++++++++++++ 3 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 tests/customRouter.nim diff --git a/jester.nim b/jester.nim index 6e614b0..a0bc266 100644 --- a/jester.nim +++ b/jester.nim @@ -42,6 +42,14 @@ type request: Request, error: RouteError ): Future[ResponseData] {.gcsafe, closure.} + MatchPair* = tuple + matcher: MatchProc + errorHandler: ErrorProc + + MatchPairSync* = tuple + matcher: MatchProcSync + errorHandler: ErrorProc + Jester* = object when not useHttpBeast: httpServer*: AsyncHttpServer @@ -454,18 +462,20 @@ proc initJester*( result.errorHandlers = @[] proc initJester*( - matcher: MatchProc, + pair: MatchPair, settings: Settings = newSettings() ): Jester = result = initJester(settings) - result.register(matcher) + result.register(pair.matcher) + result.register(pair.errorHandler) proc initJester*( - matcher: MatchProcSync, # TODO: Annoying nim bug: `MatchProc | MatchProcSync` doesn't work. + pair: MatchPairSync, # TODO: Annoying nim bug: `MatchPair | MatchPairSync` doesn't work. settings: Settings = newSettings() ): Jester = result = initJester(settings) - result.register(matcher) + result.register(pair.matcher) + result.register(pair.errorHandler) proc serve*( self: var Jester @@ -1306,7 +1316,7 @@ proc routesEx(name: string, body: NimNode): NimNode = `afterRoutes` ) - let matchIdent = newIdentNode(name) + let matchIdent = newIdentNode(name & "Matcher") let reqIdent = newIdentNode("request") let needsAsync = needsAsync(body) case needsAsync @@ -1366,6 +1376,26 @@ proc routesEx(name: string, body: NimNode): NimNode = errorHandlerProc[6][0][1][^1][2][1][0] = stmts result.add(errorHandlerProc) + # Pair the matcher and error matcher + let pairIdent = newIdentNode(name) + let matchProcVarIdent = newIdentNode(name & "MatchProc") + let errorProcVarIdent = newIdentNode(name & "ErrorProc") + if needsAsync in {ImplicitTrue, ExplicitTrue}: + # TODO: I don't understand why I have to assign these procs to intermediate + # variables in order to get them into the tuple. It would be nice if it could + # just be: + # let `pairIdent`: MatchPair = (`matchIdent`, `errorHandlerIdent`) + result.add quote do: + let `matchProcVarIdent`: MatchProc = `matchIdent` + let `errorProcVarIdent`: ErrorProc = `errorHandlerIdent` + let `pairIdent`: MatchPair = (`matchProcVarIdent`, `errorProcVarIdent`) + else: + result.add quote do: + let `matchProcVarIdent`: MatchProcSync = `matchIdent` + let `errorProcVarIdent`: ErrorProc = `errorHandlerIdent` + let `pairIdent`: MatchPairSync = (`matchProcVarIdent`, `errorProcVarIdent`) + + # TODO: Replace `body`, `headers`, `code` in routes with `result[i]` to # get these shortcuts back without sacrificing usability. # TODO2: Make sure you replace what `guessAction` used to do for this. @@ -1376,13 +1406,11 @@ proc routesEx(name: string, body: NimNode): NimNode = macro routes*(body: untyped) = result = routesEx("match", body) let jesIdent = genSym(nskVar, "jes") - let matchIdent = newIdentNode("match") - let errorHandlerIdent = newIdentNode("matchErrorHandler") + let pairIdent = newIdentNode("match") let settingsIdent = newIdentNode("settings") result.add( quote do: - var `jesIdent` = initJester(`matchIdent`, `settingsIdent`) - `jesIdent`.register(`errorHandlerIdent`) + var `jesIdent` = initJester(`pairIdent`, `settingsIdent`) ) result.add( quote do: diff --git a/tests/customRouter.nim b/tests/customRouter.nim new file mode 100644 index 0000000..5f4f141 --- /dev/null +++ b/tests/customRouter.nim @@ -0,0 +1,20 @@ +import jester + +router myrouter: + get "/": + resp "Hello world" + + get "/raise": + raise newException(Exception, "Foobar") + + error Exception: + resp Http500, "Something bad happened: " & exception.msg + +when isMainModule: + let s = newSettings( + Port(5454), + bindAddr="127.0.0.1", + ) + var jest = initJester(myrouter, s) + # jest.register(myrouterErrorHandler) + jest.serve() diff --git a/tests/tester.nim b/tests/tester.nim index c2222a4..e36480a 100644 --- a/tests/tester.nim +++ b/tests/tester.nim @@ -253,12 +253,26 @@ proc issue150(useStdLib: bool) = check resp.code == Http500 check (waitFor resp.body).startsWith("Something bad happened") +proc customRouterTest(useStdLib: bool) = + waitFor startServer("customRouter.nim", useStdLib) + var client = newAsyncHttpClient(maxRedirects = 0) + + suite "customRouter useStdLib=" & $useStdLib: + test "error handler": + let resp = waitFor client.get(address & "/raise") + check resp.code == Http500 + let body = (waitFor resp.body) + checkpoint body + check body.startsWith("Something bad happened: Foobar") + when isMainModule: try: allTest(useStdLib=false) # Test HttpBeast. allTest(useStdLib=true) # Test asynchttpserver. issue150(useStdLib=false) issue150(useStdLib=true) + customRouterTest(useStdLib=false) + customRouterTest(useStdLib=true) # Verify that Nim in Action Tweeter still compiles. test "Nim in Action - Tweeter":