Michael St Clair
Michael St Clair

Reputation: 6605

Elixir check if string is integer in guard

Is it possible to check if a string parameter being passed in is an integer? For example I need to retrieve a model based either on its id ("12345") or external_id ("eUv9wWzZ48bMZsuII6ivCle2NHgIEPoMLWC9ioDV"). Is this possible to achieve? I tried is_integer but that returns false as it doesn't try to parse a string.

def call(%{params: %{"id" => id}} = conn, module) when is_atom(module) and is_integer(id) do
    ZB.Repo.get!(module, id)
  |> check(conn)
end
def call(%{params: %{"id" => id}} = conn, module) when is_atom(module) do
  ZB.Repo.get_by!(module, external_id: id)
end

Upvotes: 5

Views: 11002

Answers (2)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

This is an XY problem and I believe you are trying to solve inexistent issue in an incorrect way :)

According to what I can see from your input, external_id is supposed to be the alphanumeric string of length 40. The keyword here is alphanumeric. That said, 40 times "1" might be an external_id. And you know what? It is perfectly parsed by Integer.parse/2:

iex|1 ▶ ["1"] |> List.duplicate(40) |> Enum.join |> Integer.parse
#⇒ {1111111111111111111111111111111111111111, ""}

That said, checking for integer is invalid in this context, because it might result in false positive.

What you do actually need to do, is to try to get the record by external_id, possibly based on it’s length or something, and fallback to id if not succeeded. Also, the length of binary might be indeed checked in guard (implicitly, by introducing an intermediate wrapper binary ⇒ bitstring.)

def call(%{params: %{"id" => id}} = conn, module) when is_atom(module) do
  case ZB.Repo.get_by(module, external_id: id) do
    {:ok, record} -> record
    _ -> ZB.Repo.get!(module, id) |> check(conn)
  end
end

Upvotes: 7

Dogbert
Dogbert

Reputation: 222050

No, this can't be done in a guard. The functions you can call in a guard is limited and does not include any function to check whether a string contains only digits.

You can instead use Integer.parse/1 in the body:

def call(%{params: %{"id" => id}} = conn, module) when is_atom(module) do
  case Integer.parse(id) do
    {_, ""} -> 
      ZB.Repo.get!(module, id) |> check(conn)
    
    _ -> 
      ZB.Repo.get_by!(module, external_id: id)
  end
end

Upvotes: 6

Related Questions