-
Notifications
You must be signed in to change notification settings - Fork 120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding support for "plugins" to Jester #226
Comments
Actually, only need two sections. I've got this working:
import jester
proc mystuff_before*(request: Request): string =
result = "Joe"
proc mystuff_after*(firstvar: string, request: Request) =
echo "done with route: " & firstvar
proc dependentCrazyPlugin_before*(request: Request, somevar: var string): int =
if somevar == "Joe":
result = 123
else:
result = 456
somevar &= " A."
proc dependentCrazyPlugin_after*(firstvar: int, request: Request, somevar: string) =
echo "done with route (dcpg): " & $firstvar
proc unrelatedPlugin_before*(request: Request): string =
discard
proc unrelatedPlugin_after*(firstvar: string, request: Request) =
echo "I'm doing my super logging function!"
import htmlgen
import jester
import theplugins
routes:
plugin foo <- mystuff()
plugin bar <- dependentCrazyPlugin(foo)
plugin xyz <- unrelatedPlugin()
get "/":
foo &= " Smith"
resp h1("Hello " & foo) this modifies the generated proc match(request: Request): Future[ResponseData] {.async, gcsafe.} =
block allRoutes:
setDefaultResp()
var request = request
var foo = mystuff_before(request)
var bar = dependentCrazyPlugin_before(request, foo)
var xyz = unrelatedPlugin_before(request)
block routesList:
block routesList:
case request.reqMethod
of HttpGet:
block outerRoute:
if request.pathInfo == "/":
block route:
foo &= " Smith"
resp h1("Hello " & foo)
if checkAction(result):
result.matched = true
break routesList
of HttpPost:
of HttpPut:
of HttpDelete:
of HttpHead:
of HttpOptions:
of HttpTrace:
of HttpConnect:
of HttpPatch:
block routesList:
unrelatedPlugin_after(xyz, request)
dependentCrazyPlugin_after(bar, request, foo)
mystuff_after(foo, request) The reason for the "var <- procname(...)" format is I'd like to both:
The "single variable" can, of course, be a object with lots of fields; so it's not that bad of a restriction I suspect. Proof of concept would be writing two or three real-world plug-ins. |
Possible plugins:
(This is actually what I want to do for one of my web sites. At the moment, I'm doing all these things in a fairly messy way.) |
I've created PR #227 for tracking progress. |
is this similar to the express request middleware? This is nice to have |
It will certainly have many of the same characteristics and role as the the express middleware. The biggest difference is that the mechanism used by That reminds me, I'll be posting a new commit fairly soon in the [WIP] PR #227 for EDIT: I'll also be likely modifying my |
Thanks for working on this @JohnAD. I currently don't have enough time to review your PR which I assume will require quite a bit of time, just want to let you know that I'm keeping this at the back of my mind. Also do rename your PR to get rid of the "WIP" when you're ready for review. |
As part of the testing and development, I've created a repo for a plugin: https://github.com/JohnAD/jestercookiemsgs It is a plugin for easily passing notice messages into and between web pages. (Ignore the README's nimble link. I won't be publishing this until if/after the plugin PR is live.) |
Also now made a database plugin (for MongoDB via https://github.com/JohnAD/jestermongopool I'm also using about 3 private plugins on one of my websites. Looking good so far. Will be testing against threaded use next. Not too far from cleaning up and finishing the PR. |
ready for review |
Made a short convenience plugin: https://github.com/JohnAD/jesterjson The idea is to make a thread-safe Like the other plugins, I'll refrain from posting onto nimble.directory until or if the plugin support goes live. |
Made a plugin to automatically pull Geo IP location information for each request, but with a 30-day sqlite3 cache to prevent hitting the upstream API too hard. Details at: https://github.com/JohnAD/jestergeoip Again, I'm refraining from posting on nimble until the plugin support is live in jester. |
I communicated yesterday with @dom96 about a way to move forward. For the time being, to allow more folks to "try plugins out" before merging the many changes into the mainline Then, after more real-world testing and development has occurred, we will re-merge the library back into canonical |
@dom96 and everyone else: Should I do a talk about jester plugins at the up-and-coming Nim conference? |
Sure, why not. |
Great work on this @JohnAD :) I tried out your fork and wrote a couple of simple plugins. Some feedback (note: I haven't read all the comments in the PR, only your documentation in the fork):
|
Thanks for all the great feedback! This is very useful.
I will investigate.
I will look into this. I suspect this would be a very non-trivial change. But, if done well, could add quite a bit to the power of the plugins.
I'm also unsure of the purpose of the I'll ask for feedback from @dom96 . It is possible they should be deprecated in light of plugins.
The plugins are not supposed to be global (to all the routers.) In fact, I use different plugins and plugin settings in different routers on my websites. But I might have a bug or I haven't thought it through enough. I'll investigate this also.
I agree. I'm going to add support for that. |
I'm referring to this construct in one of your examples: subrouter hutchRouter:
specific:
b.notFast()
get "/@name":
b = @"name" & " " & b
resp h1("Hello Inside " & b)
routes:
extend hutchRouter, "/hutch"
plugin b <- haveBunny()
get "/":
resp h1("Hello " & b)
get "/abc/@name":
b = @"name" & " " & b
resp h1("Hello " & b) Even though I'm now having some issues with how the plugins interact with Jesters error handling. Consider this example: import jester, json
proc loggingPlugin*(request: Request, response: ResponseData): bool =
discard
proc loggingPlugin_after*(request: Request, response: ResponseData, _: bool) =
echo "Responded with status code " & $response.code &
" and body '" & response.content & "'"
routes:
plugin x <- loggingPlugin()
post "/json":
type Payload = object
name: string
let payload = request.body.parseJson.to(Payload)
resp "Hello " & payload.name, "text/plain"
error JsonParsingError:
resp Http400, "Invalid JSON", "text/plain"
error Http404:
resp Http404, "Nothing to see here", "text/plain"
This could be partially solved by adding another plugin hook that runs at the end of the error handler, but it doesn't really fix the first issue. I suppose the intended behavior for |
I've moved the feedback from here to the new branch as issues at: https://github.com/JohnAD/jesterwithplugins This new library should make it's way to |
I've already written a version of this that works but is ugly. I'd like to write a cleaner version and create a PR.
Essentially, this will be a means for adding to Jester with additional nimble libraries. For example:
A "plugin" is a name that, by naming convention, will have four associated sections:
beforeThread
beforeRoutes
afterRoutes
afterThread
These are inserted into the generated code at the correct places for any router containing the plugin reference. Multiple plugins from disparate sources is supported, but order matters. The first plugin runs it's
beforeThread
first and it'safterThread
last; and so on.Interested?
The text was updated successfully, but these errors were encountered: