Dynamic EEx templates for web app

I’m currently making a blog engine for my own use. It has Distillery releases, and is very configurable with the new Distillery config provider. The user can also reverse proxy it with Nginx and serve their own JS and CSS files, if they want to do so. So it should be usable for someone else than me as well, except… templates.

The HTML (and XML for RSS) templates use the EExHTML library and they are compiled in to the app as view functions. This means that a user must compile their own version if they want to change a template. If it was made with Phoenix or Plug, the same thing would happen, as releases are compiled beforehand.

Now (disregarding all the other differences) with PHP, it was very easy for a user to take an app and customise a single PHP file. I realise Elixir does not work this way, but I’m wondering if there is any way to accomplish something similar. Say you had to implement a feature where a user could put their own EEx files into a directory and reboot the release, and the release would pick up the new files and use them instead of the built in templates. How would you accomplish that? You would have to compile the templates at runtime and reload the corresponding modules somehow.

What I’m after is a blog engine where the user can customise any templates they want without compiling their own release. Just get the premade release from GitLab/where-ever, put your own files in a path, and boot it. Does this give anyone any ideas?

2 Likes

You can do eval_string/eval_file with EEx too: https://hexdocs.pm/eex/EEx.html - just make sure to pass the EExHTML engine as an option.

1 Like

That would be loading the files on every request though, an unnecessary waste of resources. I was thinking more of compiling them but dynamically.

Discussing this on IRC, seems this is a solution:

  1. Store a reference to the module containing a specific template’s function, for example Mebe.Web.Templates.Foo.
  2. When told to reload template, compile template with EEx (and configured EEx engine) into a new module with unique name, like Mebe.Web.Templates.Foo.V2. If compilation fails, don’t proceed, but log error instead.
  3. Update stored module reference to point to new modules, other processes would be passed or would obtain this module reference somehow.
  4. Unload earlier module from system.

Does this make sense?

If it is for things like blogs, you can cache the result of the rendered template instead.

You don’t need to version it because Elixir modules are versioned in the VM due to hot code reloading. Just keep in mind that this will leak atoms but it is fine if only “admins” can do that.

1 Like

This is true, but I was hoping to avoid that, because the use case might be different from a blog too. And even blogs have things like comments. It also would not work for partials that are called in loops with different inputs for each iteration.

I can recompile and reload the same module without compiling to a different version first? When do the other processes pick it up, when they make their next call into it? If a compilation error happens, I assume the old version of the module will remain?

Yeah I don’t think people will reload the templates that often, but of course it needs to be measured and taken into consideration, thanks for the reminder. :slight_smile:

1 Like

Immediately on the next fully qualified call to it (ModuleName.function(...)), and yes. Specifically the system keeps around 2 copies of each module that is hot-loaded until no more active references (actively running code) exist to the older one, if there ‘would be’ an even older one than any code actively running it would be killed (and the supervisor would restart it with the new code), but for that to happen in general usage you are either looping manually and near infinitely or you are hot-code reloading new versions extremely rapidly. :slight_smile:

1 Like