Hi, I’m working on a project that requires generating and serving a (potentially large) number of static HTML files. Since the project also uses Phoenix, I’d like to take advantage of Phoenix’s ability to quickly render HTML templates with layouts, sanitizing contents, etc.
I know I could classify these HTML files as templates and use Phoenix.View, pattern: "**/*", root: "path/to/generated/html" to find all of them, then use render to serve them, but that requires compiling each generated file into a template, and I’m guessing (big assumption, please tell me if I’m wrong) that it recompiles all of them whenever a new file is added. I think this would become unwieldy with large numbers of files and frequent changes.
Alternatively, I could forego the view/template system entirely and use EEX.eval_file/1 to create the static files and just serve them with send_file, but that would be throwing out most/all of the benefits that Phoenix templating provides.
My question: is there a middle ground that allows me to use Phoenix’s HTML rendering/sanitizing/more goodies without necessarily compiling a large number of frequently-changing static HTML files into templates every time a new file is added/changed? I’ve looked at the source code for Phoenix.View and it’s not clear if this is possible.
To be fair, as noted above I only briefly explored the “make every file a template” option, so I could be wrong about how Phoenix decides what to compile when something changes.
Can you elaborate a bit more. What “benefits” would you think phoenix provides for static files? E.g. included handling of html entity encoding is really only useful for dynamic data. Static content is not affected by that.
I’d just use Plug.Static to serve static html and be done.
If you want to embed a html fragment inside your template as is, like a CMS, you can just read the file and raw/1 it. If you want to do some light templating and find the EEX too heavy/imposing for your liking, you can use bbmustache, which is just mustache so even non-Elixir people should feel comfortable with it.
I would like to wrap the static HTML in layouts for some consistent styling when it’s served, like how it’s done with . I know I could just do that with EEx.eval_file with the layout file once I have the static contents read, but I think when I do that I’m not taking advantage of the functionality Phoenix provides. I know I could also read the file, but some of these files are pretty big and I’d like to make serving them as fast as possible. I just want to know if there’s a middle ground somewhere, where I can render files within layouts without having to EEx.eval_file them when they’re generated.
A while back I needed to do something similar in a project I was working on so I hacked together a simple way to serve static HTML & Markdown out of a public directory at the root of the app. Nothing too fancy, but it gets the job done.
It could probably be improved but I haven’t really touched it much since I cooked it up. I haven’t tried to use it with really large pages, so YMMV.
I carved out the relevant code an pushed up a demo repo here:
In a release the settings in config.exs are plucked out of the environment by runtime.exs so that way I can use Application.put_env/3 to add additional directories for the static assets etc.
Earmark is not a very fast parser; I’ve seen it choke on some large files or pathetic cases. If you are going to serve in volume, it would be much faster to pre-render the html fragments and raw/1 them in the template.
Don’t get me wrong, I love earmark. I see it as a build time tool, not a runtime tool except on very short inputs. Markdown is a fantastic format as a source format, but a terrible format as an intermediate or serialization format. Parsing it repeatly is not the best use of cpu cycles.
I agree. This particular solution was hacked together in a short time to solve an immediate problem and has been in use on a few small low volume pages, most all of which use HTML and not Markdown. The Markdown option was added at the time to make it a little easier for a client that was unfamiliar with HTML to be able to upload some content.
At the time I intended to re-visit the issue and come up with a better solution but… time hasn’t yet been on my side!