URL書式のチェックをもうちょっと考えてみる。
今のURLのチェックは下記の1行で行っているが、http:、https:、ftp:で始まっていれば、その後は、ほぼ、どんな文字が来てもOKで通ってしまう。今になってそれがちょっと気になってきたので、見直してみた。
validates_format_of :url, :with => URI.regexp(['http', 'https', 'ftp'])
-
-
- 生成される正規表現については意味を追いきれないので、何がOKで、何がNGなのかは確認していないが...。
-
そもそも、rubyのURIライブラリの存在を知った時には、こんな風にメソッド定義しておくと、簡単にHTTPの書式チェックが出来ると知ったことからだ。
-
-
- URIのパターンマッチとURIクラスより、タイムリーな話題に感謝です。
-
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
そして、上記とほぼ同じ機能はこのような書き方で実現できることも、やっと理解できた。
-
-
- URI が valid な http な URI か?より、タイムリーな話題に感謝です。
-
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がエラーになった!「<>"」の文字がエラーと見なされているようだ。
さらに、httpsやftpも通したい、不正文字だけ入力できなければいいと割り切って以下のようにしてみた。
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と比較している。