Load struct given its name from string

Hi!

I have a couple of structs and I want to build one of them depending on some STRING as input:

defmodule Mystruct1 do ... end
defmodule Mystruct2 do ... end
...
defmodule Mystructn do ... end

def get_struct(name) do
    name
    |> <transformations_here>
    |> struct()
end

the_struct = get_struct("Mystruct5")

Naively I’m trying something like

name |> String.capitalize |> String.to_atom |> struct

but I’m getting this error

** (UndefinedFunctionError) function :Mystruct5.__struct__/0 is undefined (module :Mystruct5 is not available)
    :Mystruct5.__struct__()
    (elixir) lib/kernel.ex:2161: Kernel.struct/3

Besides whether this can be done or not, is this a good idea in order to allow the app to scale by merely adding new structs instead of use pattern matching for every possible input?

Thanks

Module name in elixir are atoms with the :"Elixir." prefix, so :"Elixir.Mystruct5" == Mystruct5.

And i think it’s better to use String.to_existing_atom/1, just in case

Works perfectly.

Thanks! @LostKobrakai @dmitrykleymenov

Maybe you should use Module.safe_concat([your_string]) for this? It will fail with ArgumentError if the module doesn’t exist.

7 Likes

Consider writing this out explicitly, unless there are a LOT of Mystructs:

def name_to_module(name) do
  case name do
    "Mystruct1" -> Mystruct1
    "Mysturct3" -> Mystruct3 # NOTE: client sends this one spelled wrong and won't change it
  end
end

Advantages:

  • very clear exactly which modules this could return
  • easy to add aliases / typo-corrections

Downsides:

  • more typing when adding a new struct
3 Likes

Nice improvement, thanks @Nicd.

@al2o3cr, yes, I also prefer to be explicit. In this case

  • the input is not coming from the user but from a generated code so I expect no mispelling happening here.
  • the idea is to keep the same code working and don’t touch it as that library grows with more structs names, it’s a bit of magic, but I was wondering how useful this approach would be.