-
Notifications
You must be signed in to change notification settings - Fork 10
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
Idea: Nimib plugin system (both dynlib & ordinary import) #209
Comments
I haven't tried playing around with shared libraries myself yet, but the way I understand it is that we would be able to load the import std/dynlib
type
NimibInitProc* = proc (doc: var NbDoc)
let lib = loadLib("plugin.so")
let initProc = cast[NimibInitProc](lib.symAddr("initNimibPlugin")) |
yep, that's pretty much the idea I had in mind, thanks for writing it down! in general the idea is that:
I like the idea of multiple plugins (also in principle it is an api we can introduce without breaking anything), it would help if we split down our current defaultTheme to multiple plugins since it does a lot more stuff than just a theme (in particular it does set up all the rendering). As a side note, I am realising now that none of that we load during init actually needs to be loaded during init, it actually needs to be loaded before rendering (and then saving) the final document. |
Good to know that we are on the same page 👍
Very good point 🚀
Haha, very true actually :o But I don't see any reason to move them anywhere else than |
Something else that I've noticed would be nice is to be able to run some functions when This would basically allow us to use arbitrary ordinary Nim types and not have to fight against the non-ergonomic |
I'm actually tempted to start implementing parts of this. Especially the procs that should run on |
Hi, I have just re read this. I am not sure I have completely understood the nbSave and the example in reveal js, could you expand a little bit on that and a brief plan on what is the part you plan to start implementing? |
So, the objective in nimiSlides is to allow users to specify fields in a configuration object sent to Reveal.js. E.g: Reveal.initialize({
mouseWheel: true,
slideNumber: 'c/t',
center: false
}); So we dynamically want to create these key-value pairs (e.g. The natural type to store this information in would be a
So if I could use a Do you understand my problem here? I can't use a nice type because mustache wants to use a nasty type. And I can't convert the nice type to the nasty type without forcing the user to call a proc before calling
It will be very simple actually:
No worries, it's summer, so I don't expect 24/7 lightning fast responses 😄 The one thing that I'm not entirely sure about is if we need to make distinctions between "render-plugins" and "functionality-plugins" and possibly more kinds of plugins. I wouldn't want to run this proc in both stages in the context of an SSG. I would want to run it when generating the JSON, but not when loading the JSON back again because then we would have an empty Table (because we have started a new program) that would override the value in the context. So we would need a way to specify that it shouldn't run specific plugins. Also we could want "functionality-plugins" that run either at the start ( Ok, this turned out to be more complicated now that I think about it. My proposed solution would work for the current nimib, but making this work well with the future SSG makes it all a bit harder. If and where each plugin is called will matter very much. |
The last two paragraphs are mostly brain-storming so don't be afraid to ask if there is parts that are not making sense (there definetly is!). The summary of it is that different kinds of plugins will have to be run either at the start or end (and others doesn't matter) and then we have some plugins that the SSG should only run in the Nim->JSON stage and not in the JSON -> HTML stage. |
Hey, thanks for the extended summary, I understand it better now. How much of the ugliness you experience is due to use of mustache Value type and would using JsonNode for data be helpful here? Especially considering that with jsony we could add interface to feel like we are dealing with actual types. It is something I definitely want to do at some point, and it should probably happen sooner rather than later. I was thinking about having plug-ins added in nbInit and called in nbSave and while I like the idea (having stuff called in nbSave means we do not have extraneous information in the blocks that we need to skip during serialization), I think it might break something at the moment: say you override some parameters in default theme, if then the plug-ins of default theme is executed at the end, it will override the override. Probably this can be fixed by initializing a new context with plug-ins first and then overriding with the context available from nbInit. I have a feeling though that changing to have a data: JsonNode might make this a bit better (the idea was that context is initialized from JsonNode and this could be done after running plug-ins). |
I'd say that it wouldn't solve the core problem: even if I used a JsonNode that was a Table, it would still be converted to a Table in the context, which can't be rendered the way I want it to. It would make working with the
That's very true 🤔 And yes, running the plugins first would make it work in this case.
Yes, it would help a lot to be able to save and reproduce a context from an object in the case of an SSG. |
So basically we have these three (four) kinds of actions we want plugins to be able to do:
So I'm thinking, could we represent a plugin as an object instead? Something like: type
NimibPlugin = ref object
f: proc (doc: var NbDoc)
kind: NimibPluginKind
of Render: discard
of Functionality:
runAt: NimibRun
NimibRun = enum
Init, Block, Save Then we would be able to separate render-plugins from the functionality-plugins and only run the ones we need. |
Okay, have thought a bit more about it now and a render-plugin only really needs the type
NimibPlugin = ref object
kind: NimibPluginKind
of Render:
fRender: proc (partials: var Table[string, string])
of Functionality:
fFunc: proc (doc: var NbDoc)
runAt: NimibRun With this, it would be more obvious what makes a render-plugin different from a functionality-plugin. |
I'll keep brainstorming here. I realized all of these (render, init, block, save) are just different hooks the plugin can run at! They are all just different points in the program that they can be executed. The render-hooks are simply run right before rendering the document. So we only need 1 enum instead of 2: type
NimibPlugin = ref object
kind: NimibPluginHook
of Render:
fRender: proc (partials: var Table[string, string])
of Init, Block, Save:
fFunc: proc (doc: var NbDoc)
NimibPluginHook = enum
Render, Init, Block, Save With this, it's much easier for a user to reason about 1 kind instead of the 2 kinds I used previously. It's also easier to extend it with more kinds of hooks in the future. |
(relevant forum post: https://forum.nim-lang.org/t/10423) |
This is an idea inspired by @pietroppeter's work on nblog using JSON as an intermediate form for a static site generator and Ark (former Ivy). To summarize, the idea is to generate the
NbDoc
as usual, but instead of rendering it at the end, it is converted and save to a JSON file. Then a separate program reads these JSON files and renders them.This got me thinking, how does the second program have access to all the partials, templates and renderProcs needed to render all of these files? One answer to this question is that we start to structure nimib themes and libraries as plugins. More specifically, we could say that each plugin must export a
initNimibPlugin
proc that accepts avar NbDoc
. In it, the plugin would populate the contexts and renderProcs. This is basically how themes work in nimib today:Now instead, imagine that we could supply multiple of these procs, one for each plugin/theme:
The sweet part about this is that these
initNimibPlugin
procs could come from anywhere! They could come from a module we have imported or (and this is the important bit) it could come from a shared library. In other words; this would allow the partials, templates and renderProcs to be accessed by the rendering executable by loading them using for examplestd/dynlib
.This might not be enough for implementing a static site generator in nimib, but it is a step towards it. And it is not that big of a change relative to what we currently have, it's mainly just that we start to recommend a certain structure of nimib libraries.
You have probably thought about a lot of these aspects already, but I haven't found a good issue to discuss it, so I'm creating this one. Is there anything I'm getting wrong or that you have different opinions on?
The text was updated successfully, but these errors were encountered: