render :partialのcollectionとしてHashのArrayを渡せるか?
タイトルを分かりやすく書くと、例えば
<%= render :partial => "foo/foo", :collection => @foox %>
のとき、@foox
としてHashのArrayを渡せるか?つまり
@foox = [ {:a => "moo", :b => "baw"}, {:a => "coo", :b => "bah"} ]
を渡したら、{:a => "moo", :b => "baw"}
と{:a => "coo", :b => "bah"}
を使ってfoo/_foo.rhtml
がrenderされるか?という問題です。
答えは”されません”。どうなるかと言うと、Hashオブジェクトの代わりにnil
が渡され、nil.[]
がNoMethodErrorになります。
なぜか。render :partial => "foo/foo", :collection => @foox
はActionView::Partials#render_partial_collectionでrenderされますが、このメソッドはcollectionのメンバ毎にActionView::Partials#render_partialを呼び出します。このときメンバがHashだと(問題のケース)、render_partialはそのHashをlocalsだと思ってしまい、代わりにpartial名のついたインスタンス変数(上の例だと@foo
)を探しに行ってしまうからです。@foo
は用意していないのでnil
になります。
どうしてもHashのArrayを使いたい場合は、次のようにpartial名のキーを持ったHashで包んでやります。
@foox = [ {:foo => {:a => "moo", :b => "baw"}}, {:foo => {:a => "coo", :b => "bah"}} ]
なぜかというと、render_partialは最終的に@foo
をlocalsにpushしてrenderを実行するので、その通りにpushされたようなHashを作っておいてやればよい、ということです。
しかし普通はそんな変なハックはせずに、Hashではないオブジェクトを渡すようにするべきです。当然ですが。