sztosz
How to ensure atom exists?
I have in module a very long list of filters like this:
@mapping %{
published_at: {:date_time, "published_at"},
indexed_at: {:date_time, "indexed_at"},
...
not_parent_shared_uid: {:terms_exclusion, "parent.shared_uid"},
...
}
Because I wan’t to dynamically call some functions like this
def parse_filter({filter_name, value}, topic_id, acc) do
{function, field} = Map.get(@mapping, String.to_existing_atom(filter_name))
apply(__MODULE__, function, [value, field, topic_id, acc])
end
Yet for existing atoms in @mapping I sometimes, occasionally get
Elixir.ArgumentError in :erlang.binary_to_existing_atom/2
arg0: "not_parent_shared_uid"
arg1: :utf8
So the question is, how can I make sure that those atoms do exist?
Marked As Solved
NobbZ
Are you sure, that the string does not contain any unprintable bytes (zero width space or similar)?
But instead of relying on String.to_existing_atom/1, I’d use something like this:
@mapping %{...}
@filter_name_to_atom @mapping |> Enum.map(fn {atom, _} -> {to_string(atom), atom} end) |> Enum.into(%{})
def parse_filter({filter_name, value}, topic_id, acc) do
{function, field} = Map.get(@mapping, @filter_name_to_atom[filter_name])
apply(__MODULE__, function, [value, field, topic_id, acc])
end
Or if you need the crash on invalid filter names use Map.fetch!/2 instead of Access-Syntax.
edit
Using Map.fetch!/2 had actually the benefit that we would also fail for "ok", while String.to_existing_atom("ok") fould simply return :ok.
Also Liked
idi527
Maybe you can replace your map with a “function lookup”. And have mappings keys be strings? Would it change anything?
defmodule SomeModule do
@moduledoc ""
mappings = %{
"published_at" => {:date_time, "published_at"},
"indexed_at" => {:date_time, "indexed_at"},
"not_parent_shared_uid" => {:terms_exclusion, "parent.shared_uid"}
}
@spec lookup_mapping(String.t()) :: {atom, String.t()} | nil
defp lookup_mapping(filter_name)
Enum.map(mappings, fn {filter_name, value} ->
defp lookup_mapping(unquote(filter_name)) do
unquote(value) # would create :date_time and :terms_exclusion atoms during compilation
end
end)
defp lookup_mapping(_unmatched), do: nil
def parse_filter({filter_name, value}, topic_id, acc) do
case lookup_mapping(filter_name) do
{function, field} ->
apply(__MODULE__, function, [value, field, topic_id, acc])
nil ->
acc
end
end
end
sztosz
@idi527: I’d rather use atoms as map keys, although it’s just a personal preference. But I’ll look more into your code, thanks ![]()
I can’t be sure, it comes from the outside. It’s not really something I can control, I have to have “good faith” in the maintainer of the app that’s queering this app of mine. He’s got no malicious intent, though, as he’s a my work colleague.
With Map.fetch! this would fail every time exactly where it should, so I may go with this instead. Thanks ![]()
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









