lapis
lapis copied to clipboard
Fully qualified calls to HTML generation functions
Hi there, thanks for Lapis. I'm testing it out and it seems really cool.
I found this example for generating HTML from within Lua:
local MyApp = lapis.Application()
MyApp:match("/", function(self)
return self:html(function()
h1({class = "header"}, "Hello")
div({class = "body"}, function()
text "Welcome to my site!"
end)
end)
end)
I've always preferred this pattern over templates in other languages, so I'm happy that it's to be found here too.
I noticed though that the HTML functions like h1
and div
aren't actually in scope here; they're not called as children of any imported Module table. I did a quick search of the Lapis code base and didn't find where they were actually defined (to say call them manually as lapis.h1
etc.).
Is there a way to call them in a "fully qualified way" like that? I saw also this comment in the Guide:
The environment of the function passed to self.html is set to one that support the HTML builder functions described above.
and was wondering about its implications. Thank you!
the html
method creates a dynamic function scope that lazily creates these functions as you call them. (For this to work there must be no local variable that is shadowing the name of the function you want to call).
This is the exact line where the HTML tag function is generated: https://github.com/leafo/lapis/blob/master/lapis/html.moon#L180
Because these functions are bound to the background buffer that is accumulating the output, there is no way to access these function objects outside of the special rendering scope.
Does that help? If you could tell me more about what you're trying to do I might have a better answer for you.
Thanks! To the matter at hand then: I'm trying to use Lapis with Fennel and it seems that in its Fennel->Lua translation phase, it strictly considers which function symbols exist and which don't (rejecting the latter).
I have in a app.lua
:
local app = require("fennel").install().dofile("app.fnl")
return app
followed by this in Fennel:
(local lapis (require :lapis))
(local app (lapis.Application))
(fn index [self]
(self:html (fn []
(h1 {:class "header"} "Hello")
(div {:class "body"} "Welcome to my site!"))))
(app:match "/" index)
app
My server starts fine with lapis server
, but attempting to access localhost:8080/
gives this in my terminal:
2023/04/18 13:06:22 [error] 63794#0: *1 lua entry thread aborted: runtime error: app.fnl:10:16 Compile error: unknown identifier: h1
(h1 {:class "header"} "Hello")
* Try looking to see if there's a typo.
* Try using the _G table instead, eg. _G.h1 if you really want a global.
* Try moving this code to somewhere that h1 is in scope.
* Try binding h1 as a local in the scope of this code.
stack traceback:
coroutine 0:
[C]: in function 'require'
/home/colin/.luarocks/share/lua/5.1/lapis/init.lua:15: in function 'serve'
content_by_lua(nginx.conf.compiled:26):2: in main chunk, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", host: "localhost:8080"
2023/04/18 13:06:22 [error] 63794#0: *2 open() "/home/colin/code/fennel/lapit-test/static/favicon.ico" failed (2: No such file or directory), client: 127.0.0.1, server: , request: "GET /favicon.ico HTTP/1.1", host: "localhost:8080", referrer: "http://localhost:8080/"
It (re: the Fennel compiler) is unable to find the h1
, since it doesn't exist yet as you explained.
Any thoughts of how I could approach this better?
@fosskers I had the same problem, and found the solution in this comment: https://github.com/leafo/lapis/issues/259#issuecomment-543876041
We need to let Fennel know that these globals [will] exist. To make your example work, I modified app.lua like this:
local globals = {"h1", "div"}
for key, _ in pairs(_G) do
table.insert(globals, key)
end
local app = require("fennel").install().dofile("app.fnl", {allowedGlobals = globals})
return app
EDIT: Unfortunately the above broke fennel.view
for me. In the end I reached for a heavier hammer:
local app = require("fennel").install().dofile("app.fnl", {allowedGlobals = false})
return app