Partials in XML builder are proving to be non-trivial.
After some initial Google searching, I found the following to work, although it's not 100%
xml.foo do
xml.id(foo.id)
xml.created_at(foo.created_at)
xml.last_updated(foo.updated_at)
foo.bars.each do |bar|
xml << render(:partial => 'bar/_bar', :locals => { :bar => bar })
end
end
this will do the trick, except the XML output is not properly indented. the output looks something similar to:
<foo>
<id>1</id>
<created_at>sometime</created_at>
<last_updated>sometime</last_updated>
<bar>
...
</bar>
<bar>
...
</bar>
</foo>
The <bar>
element should align underneath the <last_updated>
element, it is a child of <foo>
like this:
<foo>
<id>1</id>
<created_at>sometime</created_at>
<last_updated>sometime</last_updated>
<bar>
...
</bar>
<bar>
...
</bar>
</foo>
Works great if I copy the content from bar/_bar.xml.builder into the template, but then things just aren't DRY.
I worked around this by passing in the builder reference as a local in the partial. No monkey patching needed. Using the original example:
xml.foo do
xml.id(foo.id)
xml.created_at(foo.created_at)
xml.last_updated(foo.updated_at)
foo.bars.each do |bar|
render(:partial => 'bar/_bar', :locals => {:builder => xml, :bar => bar })
end
end
Then in your partial make sure to use the 'builder' object.
builder.bar do
builder.id bar.id
end
Also, the above appears to only work up to Rails 4. Rails 5 and up see @srghma's comment below