Self-contained phone harness β country data, country/dial-code helpers, E.164 normalization, display formatting, and an Ecto changeset validator.
No third-party dependency by default (the country list ships in
Skua.Phone.Countries). For full libphonenumber-grade validation install
the optional ex_phone_number via mix skua.install --with-phone, which
swaps valid?/1 to delegate to it.
Changeset usage
import Skua.Phone, only: [validate_phone: 3]
def changeset(lead, attrs) do
lead
|> cast(attrs, [:phone])
|> validate_phone(:phone, required: true)
endThe phone field component (Skua.Components.Phone) writes a canonical E.164
string (e.g. "+15555555550") into a hidden input, so the value you validate
and store is already normalized.
Summary
Functions
Dial code (string) for an ISO-3166 alpha-2 code; defaults to "1".
All {name, iso2, dial} country tuples.
ISO-3166 flag emoji for an alpha-2 code (e.g. "US" -> πΊπΈ).
Just the digits of any phone-ish string.
Canonical E.164 for a country + national number, or "" if the national part
is empty.
Filters the country list by name, ISO code, or dial-code prefix.
Country-specific national formatting (US/CA get (555) 555-0100).
Infers the ISO code from a full number by longest dial-code prefix; defaults to "US".
Strips the dial code and returns the formatted national number for display.
Normalizes any phone string to canonical E.164 ("+" <> digits).
Strict, metadata-backed validity via ex_phone_number (opt-in β install it
with mix skua.install --with-phone). Falls back to valid?/1 when the
library isn't present, so calling this never crashes a build without it.
Lenient E.164 shape check β a leading + followed by 7β15 digits.
Ecto changeset validator β bring your own validation.
Functions
Dial code (string) for an ISO-3166 alpha-2 code; defaults to "1".
All {name, iso2, dial} country tuples.
ISO-3166 flag emoji for an alpha-2 code (e.g. "US" -> πΊπΈ).
Just the digits of any phone-ish string.
Canonical E.164 for a country + national number, or "" if the national part
is empty.
iex> Skua.Phone.e164("US", "(555) 555-0100")
"+15555550100"
Filters the country list by name, ISO code, or dial-code prefix.
Country-specific national formatting (US/CA get (555) 555-0100).
Infers the ISO code from a full number by longest dial-code prefix; defaults to "US".
Strips the dial code and returns the formatted national number for display.
Normalizes any phone string to canonical E.164 ("+" <> digits).
iex> Skua.Phone.normalize("+1 (555) 555-0100")
"+15555550100"
Strict, metadata-backed validity via ex_phone_number (opt-in β install it
with mix skua.install --with-phone). Falls back to valid?/1 when the
library isn't present, so calling this never crashes a build without it.
Lenient E.164 shape check β a leading + followed by 7β15 digits.
This is deliberately permissive: Skua does not enforce country-specific
phone rules by default (that's a slippery slope and a common source of
false rejections for valid international numbers). It only confirms the value
looks like an E.164 number. For strict, metadata-backed validation, see
strict_valid?/1 (requires ex_phone_number) or pass your own :with
function to validate_phone/3.
Ecto changeset validator β bring your own validation.
By default it only checks the lenient valid?/1 shape. Phone validation is a
slippery slope, so Skua keeps the default permissive and makes stricter rules
a deliberate opt-in:
# default: just the E.164 shape
validate_phone(changeset, :phone)
# required
validate_phone(changeset, :phone, required: true)
# strict (needs ex_phone_number)
validate_phone(changeset, :phone, with: &Skua.Phone.strict_valid?/1)
# your own rule (e.g. US-only)
validate_phone(changeset, :phone, with: fn p -> String.starts_with?(p, "+1") end)Options: :required (boolean), :message (string), :with (a
(String.t() -> boolean) validator; defaults to &valid?/1).