Validate emails in Rails models
An ActiveModel validator that fails an email address that cannot receive email, in any Rails model or form object. It is built on the official Ruby client library, checks each address once, and never stops a real person from signing up when the API cannot answer in time.
class User < ApplicationRecord
validates :email, presence: true, deliverable_email: true
endInstall
The gem is not on RubyGems yet. Download it here and install it from a folder of your app:
Download email-validation-rails-0.1.0.zip and unzip it into your app's
vendor/folder, so that you havevendor/email-validation-rails/.Add it to your Gemfile and run
bundle install(Bundler installs the Ruby client library from RubyGems):gem "email-validation-rails", path: "vendor/email-validation-rails"Create an API key on the API keys page with the permission "Validate addresses", and give it a daily credit cap to limit what a busy form can spend. Set it in your environment as
EV_API_KEY, or read it from your credentials in the initializer below; never put it in code. Each new address a visitor sends is a paid check, so also rate-limit the form's action per visitor (the rate limiting built into Rails 7.2 controllers, or Rack::Attack).
It needs ActiveModel 7.1 or newer and Ruby 3.2 or newer. It works in any class that includes ActiveModel::Validations, with or without Rails.
Use it
class User < ApplicationRecord
validates :email, presence: true, deliverable_email: true
# fail disposable addresses too, and refuse the form when the check cannot finish
validates :work_email, deliverable_email: { fail_on: %w[invalid disposable], fail_open: false }, allow_blank: true
# your own message
validates :billing_email, deliverable_email: { message: "is not an address we can use" }, allow_blank: true
endA malformed address is refused without a request.
Settings
All optional, in config/initializers/email_validation.rb:
EmailValidation::Rails.configure do |c|
# default: ENV["EV_API_KEY"]
c.api_key = Rails.application.credentials.ev_api_key
# default: ENV["EV_BASE_URL"], else this
c.base_url = "https://api.email-validation.org"
c.fail_on = %w[invalid]
c.block_disposable = false
c.fail_open = true
c.timeout = 5
c.client_timeout = 8
# c.cache defaults to Rails.cache
c.cache_ttl = 3600
end- Which statuses fail. Only
invalidby default. Set the block-disposable setting totrueto faildisposableaddresses as well.catch_allandunknownpass unless you add them: see what each status means. - When the check cannot finish (it timed out, the API could not be reached, it answered with a 5xx or 429 error, the account has no credits or no key is set), the address passes and a warning goes to
Rails.loggerwith the error code and request id, never the address. With the fail-open setting atfalse, the field gets the error "could not be checked right now" instead. - EU data residency. Set the base URL to
https://api-eu.email-validation.org; the same key works on both.
Speed and caching
The validator gives the API 5 seconds for the check (the API takes 5 to 15) and waits 8 seconds for the answer, without retrying: a slow mail server then answers unknown, which passes and is free.
Each result is kept in Rails.cache for an hour, under a SHA-256 hash of the trimmed, lower-cased address, so a form sent again after fixing another field is not checked or charged again. Only the status is kept, never the address, and unknown results are not kept.
Messages
The error types and their English messages are under errors.messages in the gem's locale file: translate or override them in your own locale files, or pass message: to the validator.
en:
errors:
messages:
email_invalid: "cannot receive email. Please check it for typos"
email_disposable: "must be a permanent address, not a disposable one"
email_catch_all: "could not be confirmed. Please use another address"
email_unknown: "could not be confirmed. Please try again or use another address"
email_unavailable: "could not be checked right now. Please try again in a moment"The messages never show the API's own error text.
Costs
Each completed check costs one credit. Results with the status unknown are free, and so are malformed addresses and the test addresses: try the validator with accepted@sandbox.email-validation.org (passes) and no_mailbox@sandbox.email-validation.org (fails) at no cost. See credits and billing.
Related
- Validating sign-up forms: what to block and what to accept
- The Ruby client library for other calls, such as bulk lists