テキスト内のURLがaタグに変換されるようにする

目的

タスク管理アプリのタスクの説明部分にリンクを追加できるようにしたい。

つまり、text_areaなどで入力したテキストを表示する際にURLを含んでいたら自動的にリンクを作るようにしたい、ということです。

helperメソッドを作成

URIライブラリを使います。

module ApplicationHelper
require 'uri'  
  def convert_url_into_a_tag(text)
    URI.extract(text, ['http', 'https']).uniq.each do |url|
      sub_text = ""
      sub_text << "<a href=" << url << " target=\"_blank\">" << url << "</a>"

      text.gsub!(url, sub_text)
    end
   return text
  end
end

詳しい解説

URI.extract(text, ['http', 'https']).uniq.each do |url|

第一引数に与えられたテキスト(text)のなかから第二引数に与えられたスキーム名(httpとhttps)にマッチするものを探し出し、.uniqで重複を削除してurlとしてブロックが受け取ります。

 sub_text = ""
 sub_text << "<a href=" << url << " target=\"_blank\">" << url << "</a>"

 text.gsub!(url, sub_text)

ブロック内の処理は、用意した空のローカル変数sub_textにそれぞれの文字列を追加していきtext.gsub!(url, sub_text)でtext内のurlに該当する部分をsub_textに置き換えます。

注意
レシーバ.gsub(引数) → 置き換えを行った新しい文字列を返す
レシーバ.gsub!(引数) → レシーバ自身を置き換えて返す

最終的に、return textで「URLをaタグに置き換えたテキスト(text)」を返します。

使い方

以下のように記述して使います。

 <%= convert_url_into_a_tag(h(@task.description)).html_safe %>

h と html_safe

(h(@task.description)).html_safe

hというのはhtml_escapeメソッドの略名で、HTMLタグなどをエスケープするメソッドです。

.html_safeというのは、「レシーバに含まれる文字列は問題ないのでエスケープしないでね」というメソッドです。

2つは反対の意味を持つメソッドのようですが、なぜ一緒に使われているのでしょうか……

セキュリティ対策としてとても重要

まず、tasksコントローラから渡された@taskのdescription(タスクの説明文)から、html_escapeメソッドによってHTMLタグがエスケープされます。
これはユーザーが悪意のあるスクリプトを埋め込む事を防止する意味合いがあります。(クロスサイトスクリプティングXSS

そうしてXSSの対策をしたあとで、convert_url_into_a_tagメソッドによってテキスト内のURLをaタグに置き換えます。
そのときにつけたHTMLタグは安全ですので、html_safeメソッドで有効化する、という流れです。

ユーザーによる入力をエスケープした上で、改めてこちらでaタグを付与する ということですね。

参考リンク

[Ruby][Rails]テキスト内のURLをaタグに書き換える

[Rails]ERBのエスケープを自在に扱おうぜ