LoginSignup
11
9

More than 5 years have passed since last update.

HTMLタグを出力する一時変数の初期化

Last updated at Posted at 2014-09-02

tl;dr

  • content_tagを連結していく処理で、一時変数の初期化をするときは空文字をhtml_safeしよう
content_string = ''.html_safe
content_string << link_to('Hogeページヘ', hoge_path) if hoge?
content_string << link_to('Fugaページヘ', huga_path) if huga?

kwsk

Railsのヘルパーに、条件によってリンクを動的に作成するようなメソッドを実装しようと思いたち、以下のように作ってみました。

application_helper.rb
# 利用者を判別してメニューを作成する
# @return [String] メニューのHTML
def user_menu
  content_string = ''

  content_string << link_to('管理ページ', admin_path) if current_user.try(:admin?)

  content_string << if topics_exists?
    link_to 'Top', top_with_topics_path
  else
    link_to 'Top', top_path
  end

  content_string << content_tag(:p, "ようこそ#{current_user.try(:user_name) || 'Guest'}さん!!")
  content_string
end
_menu.html.haml
.user_menu
  =user_menu

ブラウザで表示すると

<a href="/admin">管理ページ</a><a href="/">Top</a><p>ようこそGuestさん!!</p>

と表示されます :weary:
HTMLソースを覗いてみると<などの記号が&lt;のようにエスケープされているようです。
これをちゃんと表示するには

_menu.html.haml
.user_menu
  = raw user_menu

としてあげなければいけないのですが、面倒くさい せっかくヘルパーにしたのに、呼び出し元でも更に加工しなければならないのが不便 :sweat:
さらにいえば、例えばuser_nameをユーザーが自由に入力できるときに、ページに意図しないHTMLソースを埋め込まれる可能性があります(今回の場合は、埋め込まれたところでどうってことありませんが)。

実はlink_tagform_forなどで生成される<xx>..</xx>という文字列は、Stringクラスを拡張したActiveSupport::SafeBufferというクラスのオブジェクトです。
ActiveSupport::SafeBufferオブジェクトがViewに描画されるときは、安全な文字列として判断しHTMLタグをエスケープせずに出力してくるようです。

ある文字列をActiveSupport::SafeBufferオブジェクトに変換するにはString#html_safeを使いましょう。

application_helper.rb
# 利用者を判別してメニューを作成する
# @return [String] メニューのHTML
def user_menu
  content_string = ''.html_safe

  content_string << link_to('管理ページ', admin_path) if current_user.try(:admin?)

  #中略

  content_string
end
_menu.html.haml
.user_menu
  =user_menu

ブラウザで確認してみれば、ちゃんとリンクで構成されたメニューが表示されました! :laughing:

btw

少し話しはずれますが、StringオブジェクトとActiveSupport::SafeBufferオブジェクトを連結した時の動作は以下のようになります。
動作確認はRails4.1.4で行いました。

str = ''
safe_str = ''.html_safe

str.class       #=> String
safe_str.class  #=> ActiveSupport::SafeBuffer

(str + str).class            #=> String
(str + safe_str).class       #=> String
(safe_str + str).class       #=> ActiveSupport::SafeBuffer
(safe_str + safe_str).class  #=> ActiveSupport::SafeBuffer

下から2つめのパターンは時に罠となりえそうです。

content_string = ''.html_safe
content_string << link_to('hoge', hoge_path)
content_string << user.profile # ユーザーの自己紹介を連結

上のコードでは、ユーザーの自己紹介に悪意あるコードが含まれていても、Viewにcontent_stringが書き込まれる際にエスケープされません。注意しましょう。

代換え・類似の手段として、以下のようなものもあるようです。

ref.

rails/actionview/.../tag_helper.rb - Github
rails/actionview /.../buffers.rb - Github

11
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
9