Class: Nokogiri::XML::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/nokogiri/xml/builder.rb

Overview

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.

Direct Known Subclasses

HTML::Builder

Defined Under Namespace

Classes: NodeBuilder

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}, root = nil, &block) ⇒ Builder

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|
  ...
end


236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/nokogiri/xml/builder.rb', line 236

def initialize options = {}, root = nil, &block

  if root
    @doc    = root.document
    @parent = root
  else
    namespace     = self.class.name.split('::')
    namespace[-1] = 'Document'
    @doc          = eval(namespace.join('::')).new
    @parent       = @doc
  end

  @context  = nil
  @arity    = nil
  @ns       = nil

  options.each do |k,v|
    @doc.send(:"#{k}=", v)
  end

  return unless block_given?

  @arity = block.arity
  if @arity <= 0
    @context = eval('self', block.binding)
    instance_eval(&block)
  else
    yield self
  end

  @parent = @doc
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

:nodoc:



310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/nokogiri/xml/builder.rb', line 310

def method_missing method, *args, &block # :nodoc:
  if @context && @context.respond_to?(method)
    @context.send(method, *args, &block)
  else
    node = @doc.create_element(method.to_s.sub(/[_!]$/, '')) { |n|
      # Set up the namespace
      if @ns
        n.namespace = @ns
        @ns = nil
      end

      args.each do |arg|
        case arg
        when Hash
          arg.each { |k,v|
            key = k.to_s
            if key =~ /^xmlns(:\w+)?$/
              ns_name = key.split(":", 2)[1]
              n.add_namespace_definition ns_name, v
              next
            end
            n[k.to_s] = v.to_s
          }
        else
          n.content = arg
        end
      end
    }
    insert(node, &block)
  end
end

Instance Attribute Details

#arityObject

:nodoc:



207
208
209
# File 'lib/nokogiri/xml/builder.rb', line 207

def arity
  @arity
end

#contextObject

A context object for use when the block has no arguments



205
206
207
# File 'lib/nokogiri/xml/builder.rb', line 205

def context
  @context
end

#docObject

The current Document object being built



199
200
201
# File 'lib/nokogiri/xml/builder.rb', line 199

def doc
  @doc
end

#parentObject

The parent of the current node being built



202
203
204
# File 'lib/nokogiri/xml/builder.rb', line 202

def parent
  @parent
end

Class Method Details

.with(root, &block) ⇒ Object

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


223
224
225
# File 'lib/nokogiri/xml/builder.rb', line 223

def self.with root, &block
  builder = self.new({}, root, &block)
end

Instance Method Details

#<<(string) ⇒ Object

Append the given raw XML string to the document



306
307
308
# File 'lib/nokogiri/xml/builder.rb', line 306

def << string
  @doc.fragment(string).children.each { |x| insert(x) }
end

#[](ns) ⇒ Object

Build a tag that is associated with namespace ns. Raises an ArgumentError if ns has not been defined higher in the tree.

Raises:

  • (ArgumentError)


285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/nokogiri/xml/builder.rb', line 285

def [] ns
  @ns = @parent.namespace_definitions.find { |x| x.prefix == ns.to_s }
  return self if @ns

  @parent.ancestors.each do |a|
    next if a == doc
    @ns = a.namespace_definitions.find { |x| x.prefix == ns.to_s }
    return self if @ns
  end

  raise ArgumentError, "Namespace #{ns} has not been defined"
end

#cdata(string) ⇒ Object

Create a CDATA Node with content of string



277
278
279
280
# File 'lib/nokogiri/xml/builder.rb', line 277

def cdata string
  node = Nokogiri::XML::CDATA.new(@doc, string.to_s)
  insert(node)
end

#text(string) ⇒ Object

Create a Text Node with content of string



271
272
273
# File 'lib/nokogiri/xml/builder.rb', line 271

def text string
  insert @doc.create_text_node(string)
end

#to_xml(*args) ⇒ Object

Convert this Builder object to XML



300
301
302
# File 'lib/nokogiri/xml/builder.rb', line 300

def to_xml(*args)
  @doc.to_xml(*args)
end