moduleNokogirimoduleXML#### Nokogiri builder can be used for building XML and HTML documents.## == Synopsis:## builder = Nokogiri::XML::Builder.new do |xml|# xml.root {# xml.products {# xml.widget {# xml.id_ "10"# xml.name "Awesome widget"# }# }# }# end# puts builder.to_xml## Will output:## <?xml version="1.0"?># <root># <products># <widget># <id>10</id># <name>Awesome widget</name># </widget># </products># </root>### === Builder scope## The builder allows two forms. When the builder is supplied with a block# that has a parameter, the outside scope is maintained. This means you# can access variables that are outside your builder. If you don't need# outside scope, you can use the builder without the "xml" prefix like# this:## builder = Nokogiri::XML::Builder.new do# root {# products {# widget {# id_ "10"# name "Awesome widget"# }# }# }# end## == Special Tags## The builder works by taking advantage of method_missing. Unfortunately# some methods are defined in ruby that are difficult or dangerous to# remove. You may want to create tags with the name "type", "class", and# "id" for example. In that case, you can use an underscore to# disambiguate your tag name from the method call.## Here is an example of using the underscore to disambiguate tag names from# ruby methods:## @objects = [Object.new, Object.new, Object.new]## builder = Nokogiri::XML::Builder.new do |xml|# xml.root {# xml.objects {# @objects.each do |o|# xml.object {# xml.type_ o.type# xml.class_ o.class.name# xml.id_ o.id# }# end# }# }# end# puts builder.to_xml## The underscore may be used with any tag name, and the last underscore# will just be removed. This code will output the following XML:## <?xml version="1.0"?># <root># <objects># <object># <type>Object</type># <class>Object</class># <id>48390</id># </object># <object># <type>Object</type># <class>Object</class># <id>48380</id># </object># <object># <type>Object</type># <class>Object</class># <id>48370</id># </object># </objects># </root>## == Tag Attributes## Tag attributes may be supplied as method arguments. Here is our# previous example, but using attributes rather than tags:## @objects = [Object.new, Object.new, Object.new]## builder = Nokogiri::XML::Builder.new do |xml|# xml.root {# xml.objects {# @objects.each do |o|# xml.object(:type => o.type, :class => o.class, :id => o.id)# end# }# }# end# puts builder.to_xml## === Tag Attribute Short Cuts## A couple attribute short cuts are available when building tags. The# short cuts are available by special method calls when building a tag.## This example builds an "object" tag with the class attribute "classy"# and the id of "thing":## builder = Nokogiri::XML::Builder.new do |xml|# xml.root {# xml.objects {# xml.object.classy.thing!# }# }# end# puts builder.to_xml## Which will output:## <?xml version="1.0"?># <root># <objects># <object class="classy" id="thing"/># </objects># </root>## All other options are still supported with this syntax, including# blocks and extra tag attributes.## == Namespaces## Namespaces are added similarly to attributes. Nokogiri::XML::Builder# assumes that when an attribute starts with "xmlns", it is meant to be# a namespace:## builder = Nokogiri::XML::Builder.new { |xml|# xml.root('xmlns' => 'default', 'xmlns:foo' => 'bar') do# xml.tenderlove# end# }# puts builder.to_xml## Will output XML like this:## <?xml version="1.0"?># <root xmlns:foo="bar" xmlns="default"># <tenderlove/># </root>## === Referencing declared namespaces## Tags that reference non-default namespaces (i.e. a tag "foo:bar") can be# built by using the Nokogiri::XML::Builder#[] method.## For example:## builder = Nokogiri::XML::Builder.new do |xml|# xml.root('xmlns:foo' => 'bar') {# xml.objects {# xml['foo'].object.classy.thing!# }# }# end# puts builder.to_xml## Will output this XML:## <?xml version="1.0"?># <root xmlns:foo="bar"># <objects># <foo:object class="classy" id="thing"/># </objects># </root>## Note the "foo:object" tag.classBuilder# The current Document object being builtattr_accessor:doc# The parent of the current node being builtattr_accessor:parent# A context object for use when the block has no argumentsattr_accessor:contextattr_accessor:arity# :nodoc:#### Create a builder with an existing root object. This is for use when# you have an existing document that you would like to augment with# builder methods. The builder context created will start with the# given +root+ node.## For example:## doc = Nokogiri::XML(open('somedoc.xml'))# Nokogiri::XML::Builder.with(doc.at('some_tag')) do |xml|# # ... Use normal builder methods here ...# xml.awesome # add the "awesome" tag below "some_tag"# end#defself.withroot,&blockbuilder=self.new({},root,&block)end#### Create a new Builder object. +options+ are sent to the top level# Document that is being built.## Building a document with a particular encoding for example:## Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|# ...# enddefinitializeoptions={},root=nil,&blockifroot@doc=root.document@parent=rootelsenamespace=self.class.name.split('::')namespace[-1]='Document'@doc=eval(namespace.join('::')).new@parent=@docend@context=nil@arity=nil@ns=niloptions.eachdo|k,v|@doc.send(:"#{k}=",v)endreturnunlessblock_given?@arity=block.arityif@arity<=0@context=eval('self',block.binding)instance_eval(&block)elseyieldselfend@parent=@docend#### Create a Text Node with content of +string+deftextstringinsert@doc.create_text_nodestringend#### Create a CDATA Node with content of +string+defcdatastringnode=Nokogiri::XML::CDATA.new(@doc,string.to_s)insert(node)end#### Build a tag that is associated with namespace +ns+. Raises an# ArgumentError if +ns+ has not been defined higher in the tree.def[]ns@ns=@parent.namespace_definitions.find{|x|x.prefix==ns.to_s}returnselfif@ns@parent.ancestors.eachdo|a|nextifa==doc@ns=a.namespace_definitions.find{|x|x.prefix==ns.to_s}returnselfif@nsendraiseArgumentError,"Namespace #{ns} has not been defined"end#### Convert this Builder object to XMLdefto_xml(*args)@doc.to_xml(*args)end#### Append the given raw XML +string+ to the documentdef<<string@doc.fragment(string).children.each{|x|insert(x)}enddefmethod_missingmethod,*args,&block# :nodoc:if@context&&@context.respond_to?(method)@context.send(method,*args,&block)elsenode=@doc.create_element(method.to_s.sub(/[_!]$/,'')){|n|# Set up the namespaceif@nsn.namespace=@ns@ns=nilendargs.eachdo|arg|caseargwhenHasharg.each{|k,v|key=k.to_sifkey=~/^xmlns(:\w+)?$/ns_name=key.split(":",2)[1]n.add_namespace_definitionns_name,vnextendn[k.to_s]=v.to_s}elsen.content=argendend}insert(node,&block)endendprivate#### Insert +node+ as a child of the current Nodedefinsert(node,&block)node.parent=@parentifblock_given?@parent=node@arity||=block.arityif@arity<=0instance_eval(&block)elseblock.call(self)end@parent=node.parentendNodeBuilder.new(node,self)endclassNodeBuilder# :nodoc:definitializenode,doc_builder@node=node@doc_builder=doc_builderenddef[]=k,v@node[k]=venddef[]k@node[k]enddefmethod_missing(method,*args,&block)opts=args.last.is_a?(Hash)?args.pop:{}casemethod.to_swhen/^(.*)!$/@node['id']=$1@node.content=args.firstifargs.firstwhen/^(.*)=/@node[$1]=args.firstelse@node['class']=((@node['class']||'').split(/\s/)+[method.to_s]).join(' ')@node.content=args.firstifargs.firstend# Assign any extra optionsopts.eachdo|k,v|@node[k.to_s]=((@node[k.to_s]||'').split(/\s/)+[v]).join(' ')endifblock_given?old_parent=@doc_builder.parent@doc_builder.parent=@nodevalue=@doc_builder.instance_eval(&block)@doc_builder.parent=old_parentreturnvalueendselfendendendendend