Rails: 对将局部变量传递给局部变量的语法感到困惑

理解 Rails 在呈现部分数据方面的“魔力”(并将局部变量传递给它们)。

为什么会这样:

<%= render "rabbits/form" %>

这个工作:

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

但是这个 没有可以工作:

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

但这个可以:

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

还有,我怎么才能查到这些细微差别这样我就不用去骚扰监控室的人了?

17523 次浏览

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

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

should work

Here is the source of render method from http://api.rubyonrails.org/classes/ActionView/Rendering.html#method-i-render:

def render(options = {}, locals = {}, &block)
case options
# Here is your last case
when Hash
if block_given?
_render_partial(options.merge(:partial => options.delete(:layout)), &block)
elsif options.key?(:partial)
_render_partial(options)
else
template = _determine_template(options)
lookup_context.freeze_formats(template.formats, true)
_render_template(template, options[:layout], options)
end
when :update
update_page(&block)
else
# here the first three cases
_render_partial(:partial => options, :locals => locals)
end
end

Hope this help!

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.

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.