Strange rendering trouble

I have an odd rendering problem. Wonder if anyone has seen anything like this before.

I have to render some XML to send as a request body to a cranky old external API. I am thus rendering the XML with builder outside of the normal controller/view setup. Because there are a few different request types, I am using a layout as well as templates.

The problem is that if I specify the layout, only the layout is rendered, without yielding the template; if I don’t specify the layout, the template itself renders. If I jump into the layout with a debugger, to view the output of yield, it does output the template.

In simplified code:

ApplicationController.new.render_to_string(
  template: "path/to/template",
  locals: { data: },
  layout: "path/to/layout"
)

Template:

xml.data do
  data.each do |blah|
    xml.something blah.blag
  end
end

Layout:

xml.instruct!(:xml, version: "1.0", encoding: "UTF-8")
  xml.batch do
    xml.body do
      yield
    end
  end
end

This gives me just the layout XML:

<?xml version="1.0" encoding="UTF-8"?>
<batch>
  <body>
  </body>
</batch>

… but f I don’t specify a layout I get just the template:

ApplicationController.new.render_to_string(
  template: "path/to/template",
  locals: { data: }
)
<data>
  <something>whatever</something>
</data>

Potential issue here is the indentation and structure of the XML being output by Builder. In Ruby Builder, the yield content may sometimes get indented or misinterpreted due to the way the XML is being output, which can cause the layout to not properly capture and include the rendered template.

Update your layout file like this and try out again

# app/views/layouts/xml_layout.xml.builder

xml.instruct!(:xml, version: "1.0", encoding: "UTF-8")
xml.batch do
  xml.body do
    xml << yield  # This forces Builder to correctly insert the XML string from the template
  end
end
1 Like

That was it! Well done. Thanks so much.