It's all part of the (not always clear or clearly documented) magic of render
. Per a comment in the code (github):
If no options hash is passed or if :update is
specified, then:
If an object responding to render_in
is passed,
render_in
is called on the object, passing in the current view
context.
Otherwise, a partial is rendered using the second parameter
as the locals hash.
It's the last part of the comment that is relevant. When you use the shorthand form of render
to render a partial implicitly, Rails just assumes that the hash that follows are meant to be passed as locals. So:
<%= render 'my_partial', message: 'I hate bananas' %>
will work fine and message
ends up as a local in your partial.
As soon as you specify an option to render (including, ironically, :partial
), you need to explicitly pass a locals
hash to pass locals:
# BAD
render template: 'foo/bar', message: 'I hate bananas'
render partial: 'my_partial', message: 'I hate bananas'
render 'full_view', message: 'I hate bananas' # no options, but not a partial
# GOOD
render template: 'foo/bar', locals: { message: 'I hate bananas' }
render 'full_view', locals: { message: 'I hate bananas' }
render partial: 'my_partial', locals: { message: 'I hate bananas' }
(I don't really hate bananas.)
This part is just opinion, but I don't think the implicit locals in one specific use case is worth it. I tend to just always wrap partial locals explicitly as :locals
to save remembering stuff. Note also that some folks would argue that Rails convention is to use instance vars for full views rather than locals, but it is totally valid syntactically.