rails/rails — pull request #5892 by bradediger (2012-04-18 16:10:49+00:00)
Title: Don't fall back to HTML if requested :js format exists anywhere in path set

ActionView has a fallback feature to render an `:html` format template as a "template of last resort" even when `:js` is the only format specified, to support rendering HTML-formatted responses to XHR requests.

I believe this fallback is broken when using multiple view paths. LookupContext treats a requested `:js` format as `[:js, :html]` when rendering anything except a layout. So the first view path will be matched against formats of `[:js, :html]` before the second view path is considered. Thus an "index.html.erb" in the first path will prevent an "index.js.erb" in any subsequent view paths from being rendered, even though the only format requested was JS.

My fix pushes down the fallback to lookup time; it attempts to look up the JS template in the PathSet, rescues any MissingTemplate, and retries with :html. I was conflicted about using exceptions for flow control here, but I wanted to do as little violence to the PathSet API as possible, and it currently raises MissingTemplate on missing templates.

Some care had to be taken to ensure that layouts still only render their first format (so, for example, a JS request never renders an HTML template). That motivated the introduction of the `@layout_format` ivar.

Major props to the Rails team, especially @josevalim, for how accessible this code is. I went from "hmm, that's weird" to a failing test case in about 30 minutes. Thanks!