Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combine Nokogiri XML Builders

Tags:

xml

ruby

nokogiri

I like the Nokogiri::XML::Builder construct, but it would be easier to assemble large XML documents if I could modularize the building process further by splitting the job among several builders.

Does anybody see a way to get multiple builders to cooperate? (for instance, a parent builder calling functions that set child builders to create smaller portions of the document)

Or is there a way to modify a builder after its block terminates? (---short of outputting XML, then parsing it into a Nokogiri::XML::Document, then adding nodes, then outputting XML again)

like image 684
JellicleCat Avatar asked Jul 24 '12 16:07

JellicleCat


1 Answers

Delegating Builder Functionality

for instance, a parent builder calling functions that set child builders to create smaller portions of the document

You can easily delegate responsibility to methods that take the builder's current state and use it. For example:

require 'nokogiri'

def add_kids_for(name,xml)
  xml.send(name){ 1.upto(3){ |i| xml.kid("#{name}'s kid ##{i}") } }
end

build = Nokogiri::XML::Builder.new do |xml|
  xml.root do
    add_kids_for("Danny",xml)
    add_kids_for("Billy",xml)
  end
end
puts build.to_xml
#=> <?xml version="1.0"?>
#=> <root>
#=>   <Danny>
#=>     <kid>Danny's kid #1</kid>
#=>     <kid>Danny's kid #2</kid>
#=>     <kid>Danny's kid #3</kid>
#=>   </Danny>
#=>   <Billy>
#=>     <kid>Billy's kid #1</kid>
#=>     <kid>Billy's kid #2</kid>
#=>     <kid>Billy's kid #3</kid>
#=>   </Billy>
#=> </root>

Just pass the xml (or whatever you call your builder object) to the method and let that method do what it needs to (either procedurally or manually).

Modifying a Builder after Creation

Or is there a way to modify a builder after its block terminates?

Yes! :) You want to the doc method of the Builder to get the Nokogiri::XML::Document driving it. Carrying on the above example:

doc = build.doc
doc.css('kid').each do |kid|
  kid['name'] = %w[Bobby Jenny Jill Sam Gish Gavin Lisa Imogen Lachlan].sample
end
puts doc
#=> <root>
#=>   <Danny>
#=>     <kid name="Lisa">Danny's kid #1</kid>
#=>     <kid name="Imogen">Danny's kid #2</kid>
#=>     <kid name="Lachlan">Danny's kid #3</kid>
#=>   </Danny>
#=>   <Billy>
#=>     <kid name="Gish">Billy's kid #1</kid>
#=>     <kid name="Gavin">Billy's kid #2</kid>
#=>     <kid name="Sam">Billy's kid #3</kid>
#=>   </Billy>
#=> </root>
like image 106
Phrogz Avatar answered Oct 04 '22 15:10

Phrogz