URL書式のチェックをもうちょっと考えてみる。

今のURLのチェックは下記の1行で行っているが、http:、https:、ftp:で始まっていれば、その後は、ほぼ、どんな文字が来てもOKで通ってしまう。今になってそれがちょっと気になってきたので、見直してみた。

validates_format_of :url, :with => URI.regexp(['http', 'https', 'ftp'])
      • 生成される正規表現については意味を追いきれないので、何がOKで、何がNGなのかは確認していないが...。


そもそも、rubyURIライブラリの存在を知った時には、こんな風にメソッド定義しておくと、簡単にHTTPの書式チェックが出来ると知ったことからだ。

def http?(str)
  begin
    uri = URI.parse(str)
  rescue URI::InvalidURIError
    return false
  end
  return uri.scheme == 'http'
end


URI.parseのエラーをチェックするこのやり方だと、正規表現を利用するよりも、なぜかチェックが厳しい。自分としてもこのくらい厳しいチェックの方が好きだ。

http:    => false
http://  => false
http://a => true
http://@ => false
http://> => false


そして、上記とほぼ同じ機能はこのような書き方で実現できることも、やっと理解できた。

def valid_http_uri?(str)
  URI.split(str).first == 'http' rescue false
end

はじめはこの定義の中の1行が意味することがチンプンカンプンだったが、ようやく分かってきた。きっと、beginの構文に当てはめれば、このように書けるのだと思う。

def valid_http_uri?(str)
  begin
    return URI.split(str).first == 'http'
  rescue
    return false
  end
end
  • Rubyではreturnを省略しても、そのメソッドで実行した最後の結果が呼び出し元に返される。
  • rescue修飾子という書き方もあり、「式1 rescue 式2」のように書けば、式1を実行して、エラーがあれば式2を実行する。ちょうどbegin~rescue~endを1行で書いたような感覚だ。
  • 以上のことを利用して、1行で表現することが出来るのか...。分かってしまえば、すごくシンプルで簡潔な表現だ!


嬉しくなって、早速、softwarebook2プロジェクトに取り入れてみた。

モデル software.rb
class Software < ActiveRecord::Base
  def validate
    errors.add(:url, " format is wrong.") unless http? read_attribute(:url)
  end

  def http?(str)
    URI.parse(str).scheme == 'http' rescue false
  end
end


おお、今まで通っていたURLがエラーになった!「<>"」の文字がエラーと見なされているようだ。
さらに、httpsftpも通したい、不正文字だけ入力できなければいいと割り切って以下のようにしてみた。

  def url?(str)
    URI.parse(str).scheme rescue false
  end

*1:「begin 処理1 rescue 処理2 end」構文:処理1で、rescue行で指定したエラーが発生すれば、処理2を実行する。この場合は、URI.perseできなかったらfalseを返す。正しくパースできた時は、それがhttp書式であればtrueを返す、それ以外はfalseを返す。

*2:変数strの文字列を元に、URIオブジェクトを作成する。URIオブジェクトは、例えばschemeなどのメッセージを送信すれば、この場合はhttpという答えを返してくれる。他にもいろいろなメッセージがあり、メッセージによって必要な答えを返してくれる。URIオブジェクトにすることで、単なる文字列から、簡単に必要な情報を取り出すことが出来るようになる。このことをパースすると言うらしい。もしhttp文字列の状態のまま、情報を取り出そうとしたら、書式が複雑で結構大変だ...。

*3:URIを要素に分割した配列を返す。配列の最初はscheme要素のため、firstで指定してhttpと比較している。