Sinatra で content_for を使う.
最近、Sinatraを使い出しました. 凄く手軽です.
シンプルだけあってRailsにある機能が欲しいときが良くあります.
Railsのcontent_for
知らない人はググってください. でもまー、概要ぐらいは説明します.
RailsもSinatraもテンプレートを外枠と内枠で分けることができます. でも内枠から外枠に値を渡したいときがあります。そんなときに役立つのがcontent_forです.
(例)タイトルを変えてみる
外枠のテンプレート「layout.rb」
<html> <head><title><%= yield :title %></title></head> <body> <%= yield %> </body> </html>
内枠のテンプレート「index.rb」
<% content_for :title, 'よほほほ' %> ブルックのステッカー買ったけど張る場所がない.
実行結果
<html> <head><title>よほほほ</title></head> <body> ブルックのステッカー買ったけど張る場所がない. </body> </html>
こんな感じの機能です.
Sinatra での実現方法
では本題、Sinatraではcontent_forがありません.
手軽な方法としてはsinatraのプラグインsinatra-content-forがあります.
でも今回はプラグインを使わずにやってみました.
以下のコードをapp.rbとかsinatraの実装ファイルから参照するなり、貼付けてください. それだけでcontent_forが使えます.
module Sinatra module Helpers def content_for(name, content=nil, &block) @content_for_param ||= {} content ||= block if content.nil? if @content_for_param.include?(name) @content_for_param[name].join else nil end else content = content.call if content.is_a? Proc content = content.dup if content.is_a? String @content_for_param[name] ||= [] @content_for_param[name] << content nil end end end module Templates def render_with_content_for(engine, data, options={}, locals={}, &block) if block_given? interrupt_block = Proc.new { |name| if name.nil? block.call else content_for name end } render_without_content_for engine, data, options, locals, &interrupt_block else render_without_content_for engine, data, options, locals end end alias_method :render_without_content_for, :render alias_method :render, :render_with_content_for end end
っで使い方ですがRailsのcontent_forと全く同じってわけではないです. blockを使った表記がerbのプリコンパイルが期待通りに成ってなかったのでちょっと工夫する必要があります.
本来なら以下のように書けると思ったのですが
<% content_for(:title) do %> FooBar <% end %>
プリコンパイルを見てみると以下のような感じで内枠の文字列として追加されちゃってますorz
content_for(:title) do ; @_out_buf.concat "FooBar"; end;
そこで工夫してあげると.以下の書き方で回避できました.
<% content_for(:title){ "FooBar" } %>
ちなみに以下のようにも書けます.
<% content_for(:title, "FooBar") %>
さらに同じ名前で数回コールすると結合されるようにしました。
<% content_for(:title, "FooBar") %> <% content_for(:title, "Boo") %> => FooBarBoo