File format encoder that compiles to multiple templates

Hi

I am using phoenix_markdown to being able to have template.html.md files, it is working great, I’m happy with that.

I am also using swoosh along with phoenix_swoosh to send emails using templates, like:

  use Phoenix.Swoosh, view: Sample.EmailView, layout: {Sample.LayoutView, :email}

  def welcome(user) do
    new()
    |> from("tony@stark.com")
    |> to(user.email)
    |> subject("Hello, Avengers!")
    |> render_body("welcome.html", %{username: user.email})
  end

It also works, even if the template is welcome.html.md or welcome.text.md, which is pretty cool.

But now, I would like that render_body generates the html and the text format using the same template, and when it is html it is markdown precompiled, when it is text it is not (which is what markdown is about, isn’t it?)

I thought I’d create a new format_encoder that I would call email, so I can create welcome.email.markdown, which would compile both templates: welcome.html and welcome.text.

I looked into what’s going on at the .md pre-processing, but unfortunately, the entry is the function compile(template_path, template_name), which returns the pre-compiled template. I hoped it would return {template_name, compiled_template} which would be added to the pre-compiled store of the app, but it doesn’t. :frowning:

How can I achieve what I want, which is to DRY the template content, and that it works for both formats, still being processed, ideally at compile time (at runtime, I shall find a reasonable solution)?

Am I being clear about my question? Do you agree this would make sense? Shouldn’t this even be default?

The two tracks I see are either duplicating the file just before it gets compiled, or tricking the compile(template_path, template_name) caller so that it produces two compiled templates with each extension.

Anyone can help with this?

Thanks!

1 Like

Anyone?

:+1:

1 Like

Thanks for your support @overminddl1 :grin:

Lol, I’ve been busy to look too closely in to it and the post is a bit dense at the moment, but I’m curious in how this thread would proceed. ^.^

I’m curious if you could elaborate on this part:

Surely it is dense … :sweat:

Regarding the solutions I see:

  1. Duplicate files before compilation:

Since I get what I want if I have welcome.text.md and welcome.html.md, I imagine something like a pre-compilation hook that would duplicate welcome.email.md into those, and eventually delete those on a post-compilation hook.

This might be tricky to manage cleanly though, and I’m not even sure those hooks exist.

  1. Compile to two templates

Hence I guess the cleaner solution would be to have:

                     /-------- welcome.text.md.compiled
welcome.email.md ----
                     \-------- welcome.html.md.compiled

but the current API seems be written to expect that 1 template produces exactly 1 pre-compiled template.

Hence the trick should come at the phoenix level, which is pretty deep, and forking phoenix to achieve this is definitely not worth it.

But maybe there is a trick somewhere at the pre-compilation stage that would be trickable.

If you know where the template pre-compilation happens in the phoenix code, this would already help a lot!

How about defining one welcome.email.md and then in your view doing:

def render("welcome.html", assigns), do: render("welcome.email", assigns)
def render("welcome.text", assigns), do: render("welcome.email", assigns)

Thanks! Could you be more specific on what you suggest?

Where in my view?

Also did you not forget that I’m using phoenix_swoosh, hence I’m not calling render\2 myself, but render_body\3.

But I could definitely fork phoenix_swoosh if necessary in order to achieve what I want.

Ooo, are you trying to have a template, render more templates, that are then compiled? You could do that by compiling the template to some string templates, then compiling each of those to functions. Seems really round-about kind of way of doing things that assigns as @hauleth shows would be a lot more simple?

In the module that the template is compiled in to, whatever the swoosh thing calls.

I guess the best way forward for this would be to make your own version of Phoenix.Template, which does break the mapping of 1 template file -> 1 function. I’d doubt such a change would be accepted in phoenix as it makes the system quite a bit less straight forward and more complex for new people and I’m not sure how common of a usecase it would actually be.