Railsのcontent_tagを読もう
railsバージョンは4.2.5
content_tagの使用例
http://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.htmlのExamplesには
content_tag(:p, "Hello world!") # => <p>Hello world!</p> content_tag(:div, content_tag(:p, "Hello world!"), class: "strong") # => <div class="strong"><p>Hello world!</p></div> content_tag(:div, "Hello world!", class: ["strong", "highlight"]) # => <div class="strong highlight">Hello world!</div> content_tag("select", options, multiple: true) # => <select multiple="multiple">...options...</select> <%= content_tag :div, class: "strong" do -%> Hello world! <% end -%> # => <div class="strong">Hello world!</div>
とある. 第4引数のescapeにfalseを指定するとどうなるのかこの例には載ってないが, 説明にはSet escape to false to disable attribute value escaping.
とある.
試した例が以下.
content_tag :p, '<p>hoge</p>', {}, true # => <p><p>hoge</p></p> content_tag :p, '<p>hoge</p>', {}, false # => <p><p>hoge</p></p>
要素内のHTML特殊文字をエスケープするかどうかということらしい. 説明通りだね.
ソースコード
rails/tag_helper.rb at v4.2.5 · rails/rails · GitHubから抜粋
def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block) if block_given? options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash) content_tag_string(name, capture(&block), options, escape) else content_tag_string(name, content_or_options_with_block, options, escape) end end
読もう
1行目
ブロックが渡った場合とそうでない場合で分岐する
2,3行目
ブロックが渡っている場合は, 第2引数が省略されたかのように呼び出されるため, 本来は第3引数optionsに渡されるはずのHashが第2引数に渡ってきてしまう.
# Exampleの'第2引数が省略されたかのように呼び出されている'コード <%= content_tag :div, class: "strong" do -%> Hello world! <% end -%>
ので, 改めて第3引数optionsに第2引数のHashを渡している. このとき, 第2引数content_or_options_with_blockがHashであるかどうかのチェックが挟まっている.
さらに, 3行目ではcontent_or_options_with_blockは使用されていないので, ブロックを渡してcontent_tagを呼び出した場合は, 第2引数が省略されたかのように呼び出そうが普通に第2引数渡そうが関係ないのだ. 第2引数が無視されるような挙動になる.
はじめcontent_or_options_with_blockという名前を見た時は「長すぎだろ」と思ったが, どうもこのへんの挙動から命名された引数名だったようだ.
あとこういう実装見るとJavaみたくメソッドオーバーロードさせてくれよと思う.
3行目
content_tag_string(name, capture(&block), options, escape)
の返り値がcontent_tagの返り値になる.
content_tag_string is 何
コードは以下. rails/tag_helper.rb at v4.2.5 · rails/rails · GitHub より.
def content_tag_string(name, content, options, escape = true) tag_options = tag_options(options, escape) if options content = ERB::Util.unwrapped_html_escape(content) if escape "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name.to_sym]}#{content}</#{name}>".html_safe end
なんかもうめんどくさいので省略するが,
- tag_optionsメソッドはHashをHTML属性の文字列に連結して返す(rails/tag_helper.rb at v4.2.5 · rails/rails · GitHubの149行目参照)
- ERB::Util.unwrapped_html_escapeメソッドはHTMLエスケープした文字列を返す
- PRE_CONTENT_STRINGS[name.to_sym]はnameが'textarea'の場合のみ"\n"を返す
そしてcontent_tag_stringの返り値が
"<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name.to_sym]}#{content}</#{name}>".html_safe
になる.
capture(&block) is 何
コードは以下. rails/capture_helper.rb at v4.2.5 · rails/rails · GitHub より.
def capture(*args) value = nil buffer = with_output_buffer { value = yield(*args) } if string = buffer.presence || value and string.is_a?(String) ERB::Util.html_escape string end end
ブロックの内容を文字列にして返すメソッドということだけはわかった. 詳しくはまた今度読もう. (もう疲れたゾ)
5行目
ブロックが渡っていない場合はシンプル. 第2引数と第3引数の格納しなおしなんかもないので
content_tag_string(name, content_or_options_with_block, options, escape)
がcontent_tagの返り値になる.
読めた
今度はcaptureとかも読もう.