Here's an ecto changeset validation for urls:
@doc """
validates field is a valid url
## Examples
iex> Ecto.Changeset.cast(%ZB.Account{}, %{"website" => "https://www.zipbooks.com"}, [:website])
...> |> Utils.Changeset.validate_url(:website)
...> |> Map.get(:valid?)
true
iex> Ecto.Changeset.cast(%ZB.Account{}, %{"website" => "http://zipbooks.com/"}, [:website])
...> |> Utils.Changeset.validate_url(:website)
...> |> Map.get(:valid?)
true
iex> Ecto.Changeset.cast(%ZB.Account{}, %{"website" => "zipbooks.com"}, [:website])
...> |> Utils.Changeset.validate_url(:website)
...> |> Map.get(:valid?)
false
iex> Ecto.Changeset.cast(%ZB.Account{}, %{"website" => "https://zipbooks..com"}, [:website])
...> |> Utils.Changeset.validate_url(:website)
...> |> Map.get(:valid?)
false
"""
def validate_url(changeset, field, opts \\ []) do
validate_change changeset, field, fn _, value ->
case URI.parse(value) do
%URI{scheme: nil} -> "is missing a scheme (e.g. https)"
%URI{host: nil} -> "is missing a host"
%URI{host: host} ->
case :inet.gethostbyname(Kernel.to_charlist host) do
{:ok, _} -> nil
{:error, _} -> "invalid host"
end
end
|> case do
error when is_binary(error) -> [{field, Keyword.get(opts, :message, error)}]
_ -> []
end
end
end
π π π