モデルでこの正規表現を使用して、ユーザーが送信した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" }
どうすればそれを行うことができますか?検証と正規表現の経験はほとんどありません。
存在しない場合は、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
彼らがタイプミスをした場合、彼らはプロトコルを修正できるので、あなたが持っている検証をそのままにします。
これを正規表現で行わないでください 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など)を簡単にクリーンアップできます。
受け入れられた答えはかなり大丈夫です。ただし、フィールド(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
_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_password(永続化、非大量取得など)のような仮想フィールドパターンを使用することを選択できます。割り当て可能)およびsanitize_url(仮想、大量割り当て可能)とurl(永続化、大量割り当て不可)を使用します。
Muの答えに基づいて、モデルで使用しているコードは次のとおりです。これは、モデルフィルターを必要とせずに:linkが保存されたときに実行されます。デフォルトのsaveメソッドを呼び出すにはSuperが必要です。
def link=(_link)
u=URI.parse(_link)
if (!u.scheme)
link = "http://" + _link
else
link = _link
end
super(link)
end
前述の正規表現のいくつかを使用して、モデルのデフォルト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
これは実際には検証の一部ではないので、検証では実行しません。
検証でオプションでそれをチェックするようにします。彼らがそれを台無しにすると、それは検証エラーになるでしょう、それは良いことです。
コールバック(after_create
、after_validation
、何でも)プロトコルがまだない場合は、プロトコルを先頭に追加します。
(私は他の回答に投票しました;私はどちらも私のものより優れていると思います。しかし、ここに別の選択肢があります:)