Current section
Files
Jump to
Current section
Files
lib/ecto/changeset_ext.ex
defmodule ElixirExtensions.Ecto.ChangesetExt do
@moduledoc """
Extend Changeset with some useful functions.
Add this line to your schema file:
defmodule MyApp.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
alias ElixirExtensions.Ecto.ChangesetExt
...
end
Some extra validations for you to use:
trim(changeset, :field)
trim(changeset, [:field1, :field2])
validate_email(changeset, :field)
validate_url(changeset, field)
"""
import Ecto.Changeset
@doc """
Trims the whitespace off both ends of the string.
" John Doe " -> "John Doe"
Examples:
defp changeset(user) do
user
|> cast([:first_name, :last_name, :email])
|> trim([:first_name, :last_name])
|> trim(:email)
end
"""
def ensure_trimmed(changeset, field) when is_atom(field) do
update_change(changeset, field, &trim/1)
end
def ensure_trimmed(changeset, fields) when is_list(fields) do
Enum.reduce(fields, changeset, fn field, cs ->
update_change(cs, field, &trim/1)
end)
end
@doc """
Validate emails. Will check mx records in production (see the email_checker library).
Examples:
defp changeset(user) do
user
|> cast([:email])
|> validate_email(:email)
end
"""
def validate_email(changeset, field) do
changeset
|> ensure_trimmed(field)
|> validate_change(field, fn ^field, email ->
if email_valid?(email), do: [], else: [email: "is invalid"]
end)
|> validate_length(field, max: 160)
end
@doc """
Validate URLs. Adds https if a scheme doesn't exist.
eg. "google.com" -> "https://google.com"
Examples:
defp changeset(user) do
user
|> cast([:website])
|> validate_and_fix_url(:website)
end
"""
def validate_url(changeset, field) do
url_change = get_change(changeset, field)
cond do
is_nil(url_change) ->
changeset
valid_url?(url_change) ->
validate_url_works(changeset, field)
without_scheme?(url_change) && valid_url?("https://" <> url_change) ->
changeset
|> force_change(field, "https://" <> url_change)
|> validate_url_works(field)
true ->
add_error(changeset, field, "should be a HTTP or HTTPS link")
end
end
def validate_url_works(changeset, field) do
url = get_change(changeset, field)
uri = URI.parse(url)
case :inet.gethostbyname(to_charlist(uri.host)) do
{:error, :nxdomain} ->
add_error(
changeset,
field,
"The link to #{uri.host} doesn't work. Did you spell it right?"
)
_ ->
changeset
end
end
def validate_website_link(changeset) do
link_website = get_field(changeset, :link_website, "") || ""
if link_website != "" do
# parse website and look up the hostname
link_website_parse = URI.parse("https://" <> link_website)
if link_website_parse.host != nil do
else
changeset
end
else
changeset
end
end
defp valid_url?(url) when is_binary(url) do
uri = URI.parse(url)
uri.scheme in ~w(http https) && uri.host
end
defp without_scheme?(url) when is_binary(url) do
is_nil(URI.parse(url).scheme)
end
def email_valid?(email) do
EmailChecker.valid?(email)
end
@doc "Trim whitespace on either end of a string. Account for nil"
def trim(str) when is_nil(str), do: str
def trim(str) when is_binary(str), do: String.trim(str)
end