AlchemistCamp

AlchemistCamp

How to replace render_existing/3 with function_exported?/3

I have several apps with something like the following in the app.html.eex or app.html.heex:

<%= render_existing(view_module(@conn), "_meta." <> view_template(@conn), assigns)
  || render AppWeb.LayoutView, "_meta.html", assigns %>

This setup renders the metadata for a given view “foo.html” by looking for “_meta.foo.html” and rendering it if it exists. I use the meta templates for holding SEO-related meta, OG meta, and various other tags that go into the tag of a document.

If meta doesn’t exist, then the logic above renders a default template called layout/_meta.html. as the default source.

After upgrading Phoenix, I’m now seeing a compilation warning telling me to use function_exported?/3 instead of render_existing/3.

warning: Phoenix.View.render_existing/3 is deprecated. Use function_exported?/3 instead
  lib/app_web/templates/layout/app.html.eex:2: AppWeb.LayoutView."app.html"/1

What would be a good way of following this advice (without breaking the behavior of 100+ _meta templates)? There’s a considerable gap in the interface of the suggested replacement function.

Most Liked

AlchemistCamp

AlchemistCamp

I gave it my best shot this morning during a livestream before work and unfortunately, just couldn’t get a working solution.

Without render_existing/3, we can no longer determine whether a template exists. Depending on deployment strategies, the filesystem is sometimes no longer available.

Unfortunately, function_exported?/3 doesn’t fill the gap since render/3 will always be exported.

This means the only options would be

  • writing and maintaining hundreds and hundreds of meta functions throughout the application
  • crawling the filesystem at compile time to create a list of all existing template names
  • using try and rescue on every page load
  • some kind of macro wizardry I haven’t yet been able to think of
LostKobrakai

LostKobrakai

The idea is moving to function components over templates. Function components are a lot more flexible than templates and given they’re based on (differently named) functions there’s less phoenix magic needed – magic the phoenix teams seems to like to get away from. But not everybody will jump on board of restructing their projects immediately, therefore I feel the deprecation was a bit premature.

You’d replace your code with something like this:

<%= if function_exported?(view_module(@conn), :meta 1), do: view_module(@conn).meta(Map.merge(assigns, %{template: view_template(@conn)}), else: meta(assigns) %>
# LayoutView
def meta(assigns) do
  ~H"""
  …
  """
end

# OtherView
def meta(%{template: "index.html"} = assigns) do
  ~H"""
  …
  """
end

…

From those meta functions you can also call your existing templates using:

render("_meta.#{assigns.template}", assigns)
AlchemistCamp

AlchemistCamp

I looked at the docs before creating this thread. It’s because the examples provided weren’t applicable to my use case that I asked here in the first place.

I’ve found that _meta.foo.html files for simpler cases and defining a render("_meta.foo.html", assigns) functions in the view for more complex cases a very useful pattern.

AlchemistCamp

AlchemistCamp

Oh, not at all! Your comment was fine.

I was just clarifying that I had already looked through the suggestions and hadn’t seen a way to apply them to this use case.

I also wonder the exact same thing you asked. Was there a usage the team was trying to prevent by deprecating the function? Or was it maybe for some more general optimization in Phoenix?

I’m curious.

msimonborg

msimonborg

Perhaps one approach would be to define a global view helper render_meta/2

def render_meta(conn, assigns) do
  try do
    render(view_module(conn), "_meta." <> view_template(conn), assigns)
  rescue
    Phoenix.Template.UndefinedError ->
      render(AppWeb.LayoutView, "_meta.html", assigns)
  end
end

and call it in your templates with render_meta(@conn, assigns)

Also I’m not 100% sure, but I think you can remove the module name view_module(@conn) from your render call when you’re referencing the view that you’re calling from.

EDIT: There are some examples on using function_exported?/3 in the docs if you want to follow the compiler warning’s suggestion, but seems like it might require you to rewrite your meta templates into functions

Where Next?

Popular in Questions Top

dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
LegitStack
I’m hoping you guys can give me some general advice and perhaps code examples if you’re feeling up to it. I’m very interested in Elixir,...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New

Other popular topics Top

pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New

We're in Beta

About us Mission Statement