ruby-on-rails ruby xml xml-builder

Ruby on Rails: Usando Pariales de XML Builder



ruby-on-rails xml-builder (3)

Lamentablemente no hay una solución directa para esto. Cuando se mira el código con el que ActionPack inicializará el objeto Builder, el tamaño de la sangría está codificado en 2 y el tamaño del margen no está establecido. Es una pena que no haya ningún mecanismo para anular esto en este momento.

La solución ideal aquí sería una solución a ActionPack para permitir que estas opciones se pasen al constructor, pero esto requeriría una inversión de tiempo. Tengo 2 soluciones posibles para usted. Ambos sucios puedes elegir el que se siente menos sucio.

Modifique la representación del parcial para representar en una cadena y luego haga un poco de expresión regular en ella. Esto se vería así

_bar.xml.builder

xml.bar do xml.id(bar.id) xml.name(bar.name) xml.created_at(bar.created_at) xml.last_updated(bar.updated_at) end

foos / index.xml.builder

xml.foos do @foos.each do |foo| xml.foo do xml.id(foo.id) xml.name(foo.name) xml.created_at(foo.created_at) xml.last_updated(foo.updated_at) xml.bars do foo.bars.each do |bar| xml << render(:partial => ''bars/bar'', :locals => { :bar => bar } ).gsub(/^/, '' '') end end end end end

Tenga en cuenta el gsub al final de la línea de procesamiento. Esto produce los siguientes resultados.

<?xml version="1.0" encoding="UTF-8"?> <foos> <foo> <id>1</id> <name>Foo 1</name> <created_at>2010-06-11 21:54:16 UTC</created_at> <last_updated>2010-06-11 21:54:16 UTC</last_updated> <bars> <bar> <id>1</id> <name>Foo 1 Bar 1</name> <created_at>2010-06-11 21:57:29 UTC</created_at> <last_updated>2010-06-11 21:57:29 UTC</last_updated> </bar> </bars> </foo> </foos>

Eso es un poco pirateado y definitivamente bastante sucio, pero tiene la ventaja de estar contenido dentro de su código. La siguiente solución es Monkey-patch ActionPack para hacer que la instancia de Builder funcione como queremos.

config / initializers / builder_mods.rb

module ActionView module TemplateHandlers class BuilderOptions cattr_accessor :margin, :indent end end end module ActionView module TemplateHandlers class Builder < TemplateHandler def compile(template) "_set_controller_content_type(Mime::XML);" + "xml = ::Builder::XmlMarkup.new(" + ":indent => #{ActionView::TemplateHandlers::BuilderOptions.indent}, " + ":margin => #{ActionView::TemplateHandlers::BuilderOptions.margin});" + "self.output_buffer = xml.target!;" + template.source + ";xml.target!;" end end end end ActionView::TemplateHandlers::BuilderOptions.margin = 0 ActionView::TemplateHandlers::BuilderOptions.indent = 2

Esto crea una nueva clase en la inicialización de Rails llamada BuilderOptions cuyo único propósito es alojar 2 valores para sangría y margen (aunque realmente solo necesitamos el valor de margen). Intenté agregar estas variables como variables de clase directamente a la clase de plantilla de Builder, pero ese objeto estaba congelado y no pude cambiar los valores.

Una vez que se crea esa clase, parcheamos el método de compilación dentro de TemplateHandler para usar estos valores.

La plantilla se ve como sigue:

xml.foos do @foos.each do |foo| xml.foo do xml.id(foo.id) xml.name(foo.name) xml.created_at(foo.created_at) xml.last_updated(foo.updated_at) xml.bars do ActionView::TemplateHandlers::BuilderOptions.margin = 3 foo.bars.each do |bar| xml << render(:partial => ''bars/bar'', :locals => { :bar => bar } ) end ActionView::TemplateHandlers::BuilderOptions.margin = 0 end end end end

La idea básica es establecer el valor del margen en el nivel de sangría en el que estamos al procesar el parcial. El XML generado es idéntico al que se muestra arriba.

No copie / pegue este código sin compararlo con su versión de Rails para asegurarse de que sea del mismo código base. (Creo que lo anterior es 2.3.5)

Los parciales en el constructor de XML están demostrando ser no triviales.

Después de algunas búsquedas iniciales en Google, encontré lo siguiente para funcionar, aunque no es 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

esto hará el truco, excepto que la salida XML no está correctamente sangrada. La salida se ve algo similar a:

<foo> <id>1</id> <created_at>sometime</created_at> <last_updated>sometime</last_updated> <bar> ... </bar> <bar> ... </bar> </foo>

El elemento <bar> debe alinearse debajo del elemento <last_updated> , es un elemento secundario de <foo> así:

<foo> <id>1</id> <created_at>sometime</created_at> <last_updated>sometime</last_updated> <bar> ... </bar> <bar> ... </bar> </foo>

Funciona bien si copio el contenido de bar / _bar.xml.builder en la plantilla, pero las cosas simplemente no están SECAS.


Tal vez deberías hacer:

xml.foo do xml.id(foo.id) xml.created_at(foo.created_at) xml.last_updated(foo.updated_at) xml.bars do foo.bars.each do |bar| xml.bar bar.to_xml # or "xml.bar render(:xml => bar)" # or "xml.bar render(bar)" (loads bar/_bar partial) end end end

Echa un vistazo a este enlace sobre el generador de XML .

En la última alternativa podría reemplazar el bucle interno con:

xml.bars render(foo.bars) # will loop over bars automatically using bar/_bar

Probablemente también puedes probar:

xml << foo.to_xml(:include => :bars)

Si desea incluir todos los campos en el resultado.

No estoy seguro de la sangría de todos estos, por lo que es posible que deba retroceder para crear el contenido del bucle interno de la misma manera que lo hace en el bloque externo, por ejemplo, sin usar parcial.


Trabajé alrededor de esto pasando la referencia del constructor como local en el parcial. No se necesitan parches de mono. Usando el ejemplo original:

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

Luego, en su parcial, asegúrese de usar el objeto ''constructor''.

builder.bar do builder.id bar.id end

Además, lo anterior parece funcionar solo hasta Rails 4. Rails 5 y hasta ver el comentario de @ srghma a continuación