web-dev-qa-db-ja.com

URLがない場合は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" }

どうすればそれを行うことができますか?検証と正規表現の経験はほとんどありません。

33
user393964

存在しない場合は、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

彼らがタイプミスをした場合、彼らはプロトコルを修正できるので、あなたが持っている検証をそのままにします。

70

これを正規表現で行わないでください 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ライブラリを使用すると、誰かがURLに挿入しようとするかもしれない迷惑な意味(userinfoなど)を簡単にクリーンアップできます。

36
mu is too short

受け入れられた答えはかなり大丈夫です。ただし、フィールド(url)がオプションの場合、nilクラスに対してundefined method +などのエラーが発生する可能性があります。以下はそれを解決するはずです:

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
5
Muntasim

序文、正当化、およびその方法

_before_validation_フックでモデルを変更するときは嫌です。その後、何らかの理由でsave(validate:false)を使用してモデルを永続化する必要がある場合、割り当てられたフィールドで常に実行されると想定されていた一部のフィルターは実行されません。確かに、無効なデータを持つことは通常避けたいことですが、使用しない場合はそのようなオプションは必要ありません。もう1つの問題は、モデルから要求するたびに、これらの変更が有効であることです。モデルが有効かどうかを尋ねるだけでモデルが変更される可能性があるという事実は、予期せぬことであり、おそらく望ましくないことです。フックを選択する必要がある場合は、_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懸念パターンを使用したい場合は、上記のモジュールを懸念モジュールに簡単に変換できます(_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
_

追伸上記のアプローチはRails 3およびMongoid 2でテストされました。
PPSこのメソッドの再定義とエイリアシングがあまりにも不思議であることがわかった場合、メソッドをオーバーライドせずに、パスワード(仮想、大量割り当て可能)やencrypted_pa​​ssword(永続化、非大量取得など)のような仮想フィールドパターンを使用することを選択できます。割り当て可能)およびsanitize_url(仮想、大量割り当て可能)とurl(永続化、大量割り当て不可)を使用します。

4
Timo

Muの答えに基づいて、モデルで使用しているコードは次のとおりです。これは、モデルフィルターを必要とせずに:linkが保存されたときに実行されます。デフォルトのsaveメソッドを呼び出すにはSuperが必要です。

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

    if (!u.scheme)
        link = "http://" + _link
    else
        link = _link
    end
    super(link)
end
4
Michael

前述の正規表現のいくつかを使用して、モデルのデフォルト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
1
Caleb

これは実際には検証の一部ではないので、検証では実行しません。

検証でオプションでそれをチェックするようにします。彼らがそれを台無しにすると、それは検証エラーになるでしょう、それは良いことです。

コールバック(after_createafter_validation、何でも)プロトコルがまだない場合は、プロトコルを先頭に追加します。

(私は他の回答に投票しました;私はどちらも私のものより優れていると思います。しかし、ここに別の選択肢があります:)

0
Dave Newton