Ruby: про email вообще и в частности ради валидации без регекспов

от автора

image

Немного наблюдений.

Привет Хабр!

По предложенному вопросу пергамента исписано непозволительно много. Тем не менее я бы хотел остановится на трех важных но игнорируемых аттрибутах свойственных 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 

Ложноинвалидных или ложноположительных адресов за более чем два года не выявлено.
Больше сказать нечего.
Всем добра.

ссылка на оригинал статьи http://habrahabr.ru/post/175399/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *