
Привет Хабр!
Немного наблюдений.
По предложенному вопросу пергамента исписано непозволительно много. Тем не менее, я бы хотел остановится на трех важных, но игнорируемых аттрибутах, свойственных email-у, с точки web-разработки.
Во первых email уникален, в отличии от никнейма, который, в половине случаев, занят кем-то до нас. Однако все еще встречаются сайты с логином по никнейму, который, для всех таких сайтов, ну никак не упомнить. Предлагаю использовать для логина только email.
Во вторых, часть разработчиков игнорирует type='email', когда JS валидаторы натравлены на это поле, и планшетные устройства переключают раскладку, что удобно.
В третьих, ради чего это статья, каждый год пишутся статьи вида «Почему плохо валидировать регекспом», что больше похоже на фетиш. Надеюсь гугл проиндексирует верно.
Собственно вся преамбула ради одного интересного реше��ия, которое должно помочь Ruby-стам, не исключено, что в других языках подобный метод тоже реализуем.
Собственно, когда проблема первый раз сформулировалась в моей голове как проблема, то я воспользовался гуглом, и в два-три клика наткнулся на решение, которым пользуюсь до сих пор. Важно: решение нагло стырено и выдается за свое, и если кто-то найдет ссылку на первоисточник, с радостью вставлю в статью.
Первый показатель валидности
gem 'mail' древняя как мир рубишная библиотека
require 'mail'
mail = Mail::Addres.new('antiqe@gmail.com')
mail.local #antiqe
mail.domain #gmail.com
Это первое что нужно сделать
Если в адресе почты содержатся недопустимые символы, то библиотека вызовет exception, который нужно поймать. Однако, нам ничего не ясно про домен, и домен тут может быть инвалидным.
Второй показатель валидности
mail = Mail::Address.new('antiqe@gmail.com')
=> #<Mail::Address:72490440 Address: |antiqe@gmail.com| >
tree = mail.__send__(:tree)
=> SyntaxNode+Address1+AddrSpec0 offset=0, "antiqe@gmail.com" (dig_comments,comments,local_part,domain):
SyntaxNode+LocalDotAtom0 offset=0, "antiqe" (local_dot_atom_text):
SyntaxNode+CFWS1 offset=0, "":
SyntaxNode offset=0, ""
SyntaxNode offset=0, ""
SyntaxNode offset=0, "antiqe":
SyntaxNode+LocalDotAtomText0 offset=0, "antiqe" (domain_text):
SyntaxNode offset=0, ""
SyntaxNode offset=0, "antiqe":
SyntaxNode offset=0, "a"
SyntaxNode offset=1, "n"
SyntaxNode offset=2, "t"
SyntaxNode offset=3, "i"
SyntaxNode offset=4, "q"
SyntaxNode offset=5, "e"
SyntaxNode+CFWS1 offset=6, "":
SyntaxNode offset=6, ""
SyntaxNode offset=6, ""
SyntaxNode offset=6, "@"
SyntaxNode+DotAtom0 offset=7, "gmail.com" (dot_atom_text):
SyntaxNode+CFWS1 offset=7, "":
SyntaxNode offset=7, ""
SyntaxNode offset=7, ""
SyntaxNode offset=7, "gmail.com":
SyntaxNode+DotAtomText0 offset=7, "gmail." (domain_text):
SyntaxNode offset=7, "gmail":
SyntaxNode offset=7, "g"
SyntaxNode offset=8, "m"
SyntaxNode offset=9, "a"
SyntaxNode offset=10, "i"
SyntaxNode offset=11, "l"
SyntaxNode offset=12, "."
SyntaxNode+DotAtomText0 offset=13, "com" (domain_text):
SyntaxNode offset=13, "com":
SyntaxNode offset=13, "c"
SyntaxNode offset=14, "o"
SyntaxNode offset=15, "m"
SyntaxNode offset=16, ""
SyntaxNode+CFWS1 offset=16, "":
SyntaxNode offset=16, ""
SyntaxNode offset=16, ""
У нас тут синтаксическое дерево, природа и свойства которого, для меня, чуть менее чем полностью непостижимы. Знаю только то, что синтаксическое дерево, в отличии от регекспа не рекурсивно по своей природе.
Можно только предположить, что создатели библиотеки, что-то знали, но сказали не всем.
Это дерево дает нам возможность ответить на один важный вопрос: из скольких элементов состоит домен. И если таких элементов более одного — домен валиден.
Частная реализация
В рельсах достаточно вот это:
require 'mail'
class EmailValidator < ActiveModel::EachValidator
def validate_each(record,attribute,value)
begin
address = Mail::Address.new(value)
result = address.domain && address.address == value # правда, что то что распарсил Mail в сумме вернет нам обратно наш email
tree = address.__send__(:tree)
result &&= (tree.domain.dot_atom_text.elements.size > 1) # правда ли и то, что елементов в домене больше одного
rescue Exception => e # ловим исключение, если в адресе русские или китайские символы
result = false
end
record.errors[attribute] << (options[:message] || "is invalid") unless result
end
end
положить в app/validators/email_validator.rb, чтобы в любой модели использовать:
validates :email, :presence => true, :email => true
Ложноинвалидных или ложноположительных адресов за более чем два года не выявлено.
Больше сказать нечего.
Всем добра.
