Добавить http (s) в URL, если это не так?
Я использую это регулярное выражение в своей модели для проверки URL, представленного пользователем. Я не хочу, чтобы пользователь вводил http-часть, но хотел бы добавить ее сам, если она не существует.
validates :url, :format => { :with => /^((http|https):\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+).[a-z]{2,5}(:[0-9]{1,5})?(\/.)?$/ix, :message => " is not valid" }
Любая идея, как я могу это сделать? У меня очень мало опыта с проверкой и регулярным выражением.
Ответы
Ответ 1
Используйте фильтр before, чтобы добавить его, если он отсутствует:
before_validation :smart_add_url_protocol
protected
def smart_add_url_protocol
unless self.url[/\Ahttp:\/\//] || self.url[/\Ahttps:\/\//]
self.url = "http://#{self.url}"
end
end
Оставьте проверку, которая у вас есть, таким образом, если они делают опечатку, они могут исправить протокол.
Ответ 2
Не делайте этого с помощью регулярного выражения, используйте URI.parse
, чтобы разделить его, а затем посмотреть, есть ли схема по URL-адресу
u = URI.parse('/pancakes')
if(!u.scheme)
# prepend http:// and try again
elsif(%w{http https}.include?(u.scheme))
# you're okay
else
# you've been give some other kind of
# URL and might want to complain about it
end
Использование библиотеки URI для этого также упрощает очистку любой блуждающей бессмыслицы (например, userinfo), которую кто-то может попытаться поместить в URL-адрес.
Ответ 3
Принятый ответ вполне в порядке.
Но если поле (url) является необязательным, оно может вызвать ошибку, например undefined method
+ для nil
.
Следующее должно решить следующее:
def smart_add_url_protocol
if self.url && !url_protocol_present?
self.url = "http://#{self.url}"
end
end
def url_protocol_present?
self.url[/\Ahttp:\/\//] || self.url[/\Ahttps:\/\//]
end
Ответ 4
Основываясь на ответе mu, вот код, который я использую в своей модели. Это выполняется, когда: ссылка сохраняется без использования фильтров модели. Для вызова метода сохранения по умолчанию требуется Super.
def link=(_link)
u=URI.parse(_link)
if (!u.scheme)
link = "http://" + _link
else
link = _link
end
super(link)
end
Ответ 5
Предисловие, обоснование и то, как это должно быть сделано
Я ненавижу, когда люди меняют модель в before_validation
. Затем, когда когда-нибудь случается, что по какой-то причине модели нужно сохранять с помощью save (validate: false), тогда какой-то фильтр, который, как предполагается, всегда запускается в назначенных полях, не запускается. Конечно, наличие недопустимых данных обычно является чем-то, чего вы хотите избежать, но не было бы необходимости в таком варианте, если он не использовался. Другая проблема заключается в том, что каждый раз, когда вы спрашиваете у модели, действительно ли эти изменения также имеют место. Тот факт, что просто спрашивает, действительна ли модель, может привести к тому, что модифицируемая модель просто неожиданна, возможно, даже нежелательна. Там, если мне придется выбирать крючок, я бы пошел за крюком before_save
. Тем не менее, это не будет сделано для меня, поскольку мы предоставляем предварительные просмотры для наших моделей, и это нарушит URI в представлении предварительного просмотра, так как крючок никогда не будет вызван. Там для меня я решил лучше отделить концепцию до модуля или проблемы и предоставить хороший способ применить "патч обезьяны", гарантирующий, что изменение значения полей всегда выполняется через фильтр, который добавляет протокол по умолчанию, если он отсутствует.
Модуль
#app/models/helpers/uri_field.rb
module Helpers::URIField
def ensure_valid_protocol_in_uri(field, default_protocol = "http", protocols_matcher="https?")
alias_method "original_#{field}=", "#{field}="
define_method "#{field}=" do |new_uri|
if "#{field}_changed?"
if new_uri.present? and not new_uri =~ /^#{protocols_matcher}:\/\//
new_uri = "#{default_protocol}://#{new_uri}"
end
self.send("original_#{field}=", new_uri)
end
end
end
end
В вашей модели
extend Helpers::URIField
ensure_valid_protocol_in_uri :url
#Should you wish to default to https or support other protocols e.g. ftp, it is
#easy to extend this solution to cover those cases as well
#e.g. with something like this
#ensure_valid_protocol_in_uri :url, "https", "https?|ftp"
В качестве беспокойства
Если по какой-то причине вы предпочитаете использовать шаблон Rails Concern, легко преобразовать вышеуказанный модуль в модуль беспокойства (он используется точно так же, за исключением того, что вы используете include Concerns::URIField
:
#app/models/concerns/uri_field.rb
module Concerns::URIField
extend ActiveSupport::Concern
included do
def self.ensure_valid_protocol_in_uri(field, default_protocol = "http", protocols_matcher="https?")
alias_method "original_#{field}=", "#{field}="
define_method "#{field}=" do |new_uri|
if "#{field}_changed?"
if new_uri.present? and not new_uri =~ /^#{protocols_matcher}:\/\//
new_uri = "#{default_protocol}://#{new_uri}"
end
self.send("original_#{field}=", new_uri)
end
end
end
end
end
P.S. Вышеуказанные подходы были протестированы с помощью Rails 3 и Mongoid 2.
PPS Если вы обнаружите, что переопределение этого метода и псевдонижение слишком волшебны, вы можете отказаться от переопределения метода, а скорее использовать шаблон виртуального поля, подобно паролю (виртуальный, назначаемый по массе) и encrypted_password (сохраняется, нецелесообразно назначать) и использовать sanitize_url (виртуальный, назначаемый по массе) и url (сохраняется, не назначается массой).
Ответ 6
Я бы не попытался сделать это в валидации, так как это не является частью проверки.
Проверить правильность проверки; если они ввернут его, это будет ошибка проверки, что хорошо.
Рассмотрите возможность использования обратного вызова (after_create
, after_validation
, что угодно) для добавления протокола, если его уже нет.
(Я проголосовал за другие ответы, я думаю, что они оба лучше моих. Но вот еще один вариант:)
Ответ 7
Используя некоторые из вышеупомянутых регулярных выражений, вот удобный метод для переопределения url по умолчанию для модели (если, например, ваша модель ActiveRecord имеет столбец "url" )
def url
_url = read_attribute(:url).try(:downcase)
if(_url.present?)
unless _url[/\Ahttp:\/\//] || _url[/\Ahttps:\/\//]
_url = "http://#{_url}"
end
end
_url
end