Sanjibukai

Sanjibukai

Hello everybody,

When I look at what an error looks like in a Changeset, it’s something like this:

[username: {"should be at most %{count} character(s)", [count: 20, validation: :length, kind: :max, type: :string]}]
# which renders to
# "should be at most 20 character(s) max"

It seems that there is some kind of templating with interpolation done inside %{keyword} as soon as the keyword is present as a key in the given list of keywords..

I don’t think it’s something native to elixir like printf in C (or I never heard of on my learning path) but more a convenience or a syntactic sugar provided by Phoenix..

So I wanted to know where this happens?

This behavior seems to be related to the Gettext module but when I follow down the path it seems (at least to me btw) overly complicated with functions with 6 to 8 parameters with odd names (dngettext, dpngettext etc.)

And I expected to find calls to String.replace or Regex.replace in order to replace %{} but I don’t find anything..

Does anyone have any clues?

Thank you..

Showing Posts 1 to 7

nicanor

nicanor

Looks like the suggestion is to use Changeset.traverse_errors to get the errors in the format you want.

According to Elixir School:

You may be surprised that the error message contains the cryptic %{count} — this is to aid translation to other languages; if you want to display the errors to the user directly, you can make them human readable using traverse_errors/2 — take a look at the example provided in the docs.

If you are using Phoenix, you can see how this error format is managed for you using Gettext in the file web/views/error_helpers.ex.

Update:
There is a nice example on how to use the traverse_errors function to replace the values yourself:

{"should be at least %{count} characters", [count: 3, validation: :length, min: 3]}

iex> traverse_errors(changeset, fn {msg, opts} ->
      ...>   Enum.reduce(opts, msg, fn {key, value}, acc ->
      ...>     String.replace(acc, "%{#{key}}", to_string(value))
      ...>   end)
      ...> end)
      %{title: ["should be at least 3 characters"]}
Sanjibukai

Sanjibukai OP

Hello..

I should have been more clear.. Indeed, using error_tag from views/error_helper.ex actually display the message in the expected formatted output..

I just wanted to learn where that non-standard elixir %{} interpolation is happening..

This is exactly what I was expecting to find somewhere in the code base (as explained above). The interpolation implementation using String.replace or Regex.replace..
But this one is an example from the comments/docs..

So I still wonder where that interpolation is happening.. But at least I know how it’s done.. Thank you..

jordelver

jordelver

This seems to be happening in Gettext here and here but I have to admit that I don’t really understand what is happening. Perhaps someone else can shed some light :slight_smile:

nicanor

nicanor

Oh, yes, now I understand your question!

And yes, it’s a very complicated code.

Looks like what you are looking for is happening in Gettext.Interpolation

Sanjibukai

Sanjibukai OP

Wow… I wasn’t expecting a Rube Goldberg machine-like implementation..
I wonder why it’s implemented like this while they gave (more intelligible) example using Regex.replace as pointed out above..
I guess it’s about performance (maybe related to how Elixir immutability is optimized with linked list?), though I would like to have the confirmation from experts about it.

Thanks! One minute too late :wink:

hauleth

hauleth

This is common pattern in Erlang (and by extension - Elixir) that you use “quasi” io lists for that. So you have type like template() :: maybe_improper_list(template() | iolist()) and then you can easily traverse it and replace all atoms with proper values (while building another io list). So this “Goldberg machine” does exactly that - first compile all the strings into simpler forms and then replacing all needed values within the io list.

Sanjibukai

Sanjibukai OP

Thanks for the explanations.. It does make total sense.

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews