-
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
Refactor NbBlock #235
base: main
Are you sure you want to change the base?
Refactor NbBlock #235
Conversation
milestone reached, now in sandbox/minib3.nim both a minimal html and json backend work. (your json work was great @HugoGranstrom). Still lots of details to work on, but I am feeling more confident now! A big realization was that in the end we might not need after all nim mustache anymore (we might still provide it for legacy reason and we should be able to provide a somewhat backward compatible NbLegacyDoc object that implements the default theme as before with partials), in a sense nimib itself is some code based (and block based) templating system with superpowers... |
(moved this note in sandbox/notes.md) |
Mmh, no to customize an existing block the idea is to replace the function that renders it in the nimib/sandbox/minib3/minib.nim Line 26 in 5e6727e
The part above is still an idea and will need more fleshing out. I would definitely start with bare html and would allow customization only in the sense of: give me the rendered output and I can put things before or after. Any kind of refinement to that idea would need a use case that we think makes it worth it. And I would not probably push this during this refactor, maybe for now we do not even do what is sketched above (just to keep the scope manageable).
Yes, that is an option (serializing also the theme), but I guess it is kind of wasteful and also not "clean". I would like if possible to have a somewhat readable json. Especially if it is a low cost thing and a simple rule such as: everything that is in
Yeah, that is a good observation and it might not be needed after all. In principle you should not remember to set it since it should be in the NbBlock generating sugar, but if we can avoid needing it I could probably remove it. It will get trickier once I introduce real custom container blocks. My idea there at the moment is that Thanks for the feedback on the work-in-progress, it is useful :) nothing here is yet set in stone but I think a cleaner implementation is finally coming out. |
note, I just pushed a possible implementation of a NbContainer object (and NbContainer = ref object of NbBlock
blocks: seq[NbBlock]
parent: NbContainer while the html backend works fine with it the json backend fails to serialize because of the circular reference. This is an issue with My current plan is to actually skip the |
ok the new Next steps:
|
Nice work! 🤩 I really like the |
notes from another discussion. in this PR we should also:
|
So, I've started working on implementing Another thing that I'm wondering about is whether we should do the compilation the same way we are doing it now. Now we do it in |
I've started thinking about the sugar for defining a block now and we have for example: type
NbImage = ref object of NbBlock
url: string
template nbImage*(turl: string) =
let blk = NbImage(url: turl, kind: "NbImage")
nb.add blk
func nbImageToHtml*(blk: NbBlock, nb: Nb): string =
let blk = blk.NbImage
"<img src= '" & blk.url & "'>"
nbToHtml.funcs["NbImage"] = nbImageToHtml
addNbBlockToJson(NbImage) Which should be shortened to: newNbBlock(nbImage):
url: string
toHtml:
"<img src= '" & blk.url & "'>" I'm not sure how the template could be automated with this sugar. It feels like it should be created manually unless we can come up with a nice way of abstracting it. So currently I think this is feasible: newNbBlock(nbImage):
url: string
toHtml:
"<img src= '" & blk.url & "'>"
template nbImage*(turl: string) =
let blk = NbImage(url: turl, kind: "NbImage")
nb.add blk Something else to consider is inheritance. By default the block inherits from newNbBlock(nbImage of NbContainer):
... to signify that it should inherit another block? |
Something we could do though to prevent the error of writing the wrong newNbBlock(nbImage):
url: string
toHtml:
"<img src= '" & blk.url & "'>"
template nbImage*(turl: string) =
let blk = newNbImage(url=turl)
nb.add blk |
Why the template cannot be created from the sugar macro? Is there some macro limitation I am not aware of (I know I am not aware of many things regarding macros... :)) |
Btw I forgot to add them but I think it might worth adding also a new proc image*(nb: var NbContext, url: string) =
nb.blk.add NbImage(url: url)
template nbImage*(url: string) =
nb.image(url) The new procedural api would also help to reduce the number of templates that nimib needs and incidentally should reduce the number of globals (of course for |
Also agree on the additional sugar for inheriting from other blocks |
Because we don't know the inputs and what to do with them 😄 Say we have two fields on the object and we only want the user to provide one of them. So the problem is that we don't know what the input to the template should be from just the information given right now. We could of course add the template body as a new section in the sugar syntax with the inputs as well. But then we have basically written the template already. So I think it is more flexible if we write the template manually. For example when you have a block that you want to create a few different templates with different defaults for. |
Yes it's a good idea to write the functional api as well 😄 I feel like I have regained some coding motivation now so I'll just need to find some time the coming weeks 😁 |
Ah right. I think we could provide maybe a default with all fields or indeed think of additional sugar but I guess it might be wiser for the moment to limit ourselves to leave it out of the sugar for now and see later if there is some good pattern that emerges |
And yeah probably we should just provide the functional api as default (with all the fields) and leave all the templates as a customization (indeed nbCode has a lot of variants, the skip one, the block one, ... and they all refer to the same type) |
In that case I would suggest the templates to use the functional api in their implementation (and this could be general advice). |
The problem with creating a default template is that it can cause conflict with a manually created one. |
So we automatically generate the functional api? How would we do that for blocks that require untyped arguments? Or do you mean we just create a constructor automatically? |
Agreed, when possible, the templates should be sugar on top of the functional api 👍 |
Looking at this again, I'll try to summarize the last conversation we had:
This means that for now, when defining a new block. We would have to create a functional api proc/template for each block type. And then create a "old-style" template using that (e.g. This could be included in the sugar, but then we are basically defining the proc/template in there instead. Which could be a good thing as stuff would be nicely contained. |
Sooo, now the sugar is finally finished. So instead of this: type
NbImage = ref object of NbBlock
url: string
template nbImage*(turl: string) =
let blk = NbImage(url: turl, kind: "NbImage")
nb.add blk
func nbImageToHtml*(blk: NbBlock, nb: Nb): string =
let blk = blk.NbImage
"<img src= '" & blk.url & "'>"
nbToHtml.funcs["NbImage"] = nbImageToHtml
addNbBlockToJson(NbImage) We now do this: newNbBlock(nbImage):
url: string
toHtml:
"<img src= '" & blk.url & "'>"
func image*(nb: var Nb, url: string) =
let blk = newNbImage(url=url)
nb.add blk
template nbImage*(url: string) =
nb.image(url) Note that |
I'm trying to think about what's left and it seems like I haven't converted nbJs to the sugar style yet. Will give it a try during the weekend. Other than that, is there anything else holding us back from actually starting to implement this in Nimib? |
Hi! not really sure if there is anything missing, have not had really time to think about it. I guess we could try and start implementing in Nimib and see how it goes! Cheers from Brussels (Fosdem)! |
Oh right it Fosdem! Have a great time and good luck! 😄 Yes I'll finish nbJs up and give it a try and we'll see if I get caught up somewhere. Imagine if we got this working before 27th Febuary, only a year from opening the PR 😎 |
There's one thing I remember that we have discussed, but I can't remember what our conclusion was. Namely, target backends. The sugar has We have the |
Well, as long as we can still support custom backends, we do not need the sugar to support them. I guess the markdown backend could be an example on how to add a custom backend. I think it make sense to have the html backend as the only one to be supported by the sugar (although json is also kind of a backend of a special kind) |
Okay, that sounds reasonable 👍 We could always expand the sugar in the future if the demand for it starts to exist (for example by adding a |
And oh, backwards compatibility with the old mustache-backend must be handled somehow. It should be possible to implement as a custom backend during a transition period and then in the next major release we drop support for it? The things we'd like to remove are these fields from NbDoc and move them to the mustache-render object:
|
Or should we just throw backwards-compatibility out the door and say that users can use Nimib 3.x if they want to use the old mustache backend? That would simplify things for us and we could make a more thorough refactoring of code now. It would mostly be libraries like nimibook and nimislides that would need to adapt their code to work with Nimib 4.0. |
I would say to start implementing without thinking about backwards compatibility with old backend. Then at the end we can evaluate how much effort is to introduce some backward compatibility (with warning on usage and dropping on next version) |
That sounds like a good plan 👍 |
this is a major change, implements #168 (and also #117). Very WIP in a sandbox folder (see sandbox/notes.md)