Добавить http (s) к URL, если его там нет? - PullRequest
33 голосов
/ 27 октября 2011

Я использую это регулярное выражение в своей модели для проверки 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" }

Есть идеи, как мне это сделать?У меня очень мало опыта с проверкой и регулярным выражением ..

Ответы [ 7 ]

69 голосов
/ 27 октября 2011

Используйте фильтр до, чтобы добавить его, если его там нет:

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

Оставьте подтверждение, таким образом, если они сделают опечатку, они могут исправить протокол.

36 голосов
/ 27 октября 2011

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

5 голосов
/ 01 августа 2013

Принято, все в порядке.Но если поле (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 голосов
/ 05 марта 2014

Предисловие, обоснование и как это должно быть сделано

Я ненавижу, когда люди меняют модель на крючке before_validation. Затем, когда когда-нибудь случится так, что по какой-то причине модели должны быть сохранены с сохранением (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 (сохраняется, не массовый назначаемый).

4 голосов
/ 03 июля 2012

Исходя из ответа му, вот код, который я использую в моей модели. Это работает, когда: ссылка сохраняется без необходимости фильтров модели. Super требуется для вызова метода сохранения по умолчанию.

def link=(_link)
    u=URI.parse(_link)

    if (!u.scheme)
        link = "http://" + _link
    else
        link = _link
    end
    super(link)
end
1 голос
/ 20 июля 2014

Используя некоторые из вышеупомянутых регулярных выражений, вот удобный метод для переопределения 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
0 голосов
/ 27 октября 2011

Я бы не стал делать это при проверке, так как это на самом деле не является частью проверки.

Имейте проверку по желанию проверить это; если они облажаются, это будет ошибкой проверки, и это хорошо.

Рассмотрите возможность использования обратного вызова (after_create, after_validation, что угодно) для добавления протокола, если его там еще нет.

(Я проголосовал за другие ответы; я думаю, что они оба лучше, чем у меня. Но вот еще один вариант:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...