Rails: confused about syntax for passing locals to partials

Understanding Rails "magic" with regards to rendering partials (and passing locals into them).

Why does this work:

<%= render "rabbits/form" %>

And this work:

<%= render "rabbits/form", :parent => @warren, :flash => flash %>

but this does not work:

<%= render "rabbits/form", :locals => { :parent => @warren, :flash => flash } %>

But this does:

<%= render :partial =>"rabbits/form", :locals => { :parent => @warren, :flash => flash } %>

Also, how can I look up these nuances so I don't need to bother people on S.O.?


Solution 1:

The short answer is the render method looks at the first argument you pass in. If you pass in a hash (which includes :partial => 'foo', :locals => {blah blah blah}) then it will pass in all of your arguments as a hash and parse them accordingly.

If you pass in a string as your first argument, it assumes the first argument is your partial name, and will pass the remainder as your locals. However, in that subsequent call, it actually assigns :locals => your_locals_argument, which in this case is the entire :locals => {locals hash}, instead of just {locals hash}; i.e. you end up with :locals => {:locals => {locals hash}}, rather than :locals => {locals hash}.

So my advice is just to always explicitly pass values the same way all the time, and you won't have problems. In order to learn about this, I went directly to the code itself (actionpack/lib/base.rb, render() method in Rails 2; Rails 3 is different). It's a good exercise.

Furthermore, don't worry about "bothering" people on SO. That's why this site exists. I even learned something from this.

Solution 2:

if you need to specify :locals, you need to specify :partial or :template

<%= render :partial => "rabbits/form", :locals => {...} %>

should work

Solution 3:

To be honost, I only know about these use cases, because I have been keeping up with Rails for the last couple of years and read the announcements that a new way of doing it has been added. I often make a mistake in it myself, but usually it's easily corrected.

It's one of those parts of Rails API that hasn't been thoroughly thought through, if you ask me. It just accumulated more and more syntactic sugar over the years, without deprecating any of the old behavior. The render method has diabetes.

To make it even worse, render behaves differently in controller and view. I also looks at the first argument's content to see if it's a file, template, action or partial. If it starts with a slash then it's a file, or something like that.

I am in favor of using the shorter notation whenever possible. Because the short notations do communicate the intent quite well. When reading it, it usually does what you think it does. Writing partials is not straight forward.