sztosz

sztosz

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?

Showing Posts 1 to 5

blackode

blackode

I think this will solve your problem.
Just checking whether the filter_name is atom or not.

    {function, field} = 
       Map.get(@mapping, is_atom(filter_name) && filter_name || String.to_atom(filter_name))
sztosz

sztosz OP

Actually I am 100% sure the filter_name is binary (string actually), but it comes from a json, so I don’t want to use String.to_atom() to circumvent potential creation of invalid atoms. I have no problems with errors being raised when there is no atom in system, but the error is raised for valid atoms.

Also UPDATE: I copied modified code from my debug session earlier. Instead of to_atom I am actually using to_existing_atom. With to_atom it works as intended, the problem is with to_existing_atom :confused:

idi527

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
NobbZ

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.

sztosz

sztosz OP

@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 :slight_smile:

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 :slight_smile:

— 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
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
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
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
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
ryanwinchester
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted” Version...
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
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
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