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 7 to 1

Sanjibukai

Sanjibukai OP

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

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

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:

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

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:

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..

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"]}
— 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
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
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
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

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
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
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews