class Builder::XmlBase

Builder::XmlMarkup and Builder::XmlEvents for examples.
XmlBase is a base class for building XML builders. See

def <<(text)

targets.
method/operation builders can use other builders as their
use << to append to the target, so by supporting this
It is also useful for stacking builder objects. Builders only

builder without changing the inserted markup.
generates strings. Just insert the string directly into the
This is useful when using non-builder enabled software that

builder.p { |x| x << "
HI" } #=>


HI



May be used within the markup brackets as:
Append text to the output target without escaping any markup.
def <<(text)
  _text(text)
end

def _escape(text)

def _escape(text)
  result = XChar.encode(text)
  begin
    encoding = ::Encoding::find(@encoding)
    raise Exception if encoding.dummy?
    result.encode(encoding)
  rescue
    # if the encoding can't be supported, use numeric character references
    result.
      gsub(/[^\u0000-\u007F]/) {|c| "&##{c.ord};"}.
      force_encoding('ascii')
  end
end

def _escape(text)

def _escape(text)
  if (text.method(:to_xs).arity == 0)
    text.to_xs
  else
    text.to_xs((@encoding != 'utf-8' or $KCODE != 'UTF8'))
  end
end

def _escape_attribute(text)

def _escape_attribute(text)
  _escape(text).gsub("\n", "&#10;").gsub("\r", "&#13;").
    gsub(%r{"}, '&quot;') # " WART
end

def _indent

def _indent
  return if @indent == 0 || @level == 0
  text!(" " * (@level * @indent))
end

def _nested_structures(block)

def _nested_structures(block)
  @level += 1
  block.call(self)
ensure
  @level -= 1
end

def _newline

def _newline
  return if @indent == 0
  text! "\n"
end

def cache_method_call(sym)

significantly.
method_missing is very slow, this speeds up document generation
be handled by the new method instead of method_missing. As
documents are usually very repetative in nature, the next node will
missed as an instance method on the XMLBase object. Because XML
If XmlBase.cache_method_calls = true, we dynamicly create the method
def cache_method_call(sym)
  class << self; self; end.class_eval do
    define_method(sym) do |*args, &block|
      tag!(sym, *args, &block)
    end
  end
end

def initialize(indent=0, initial=0, encoding='utf-8')

the output stream.
characters aren't converted to character entities in
encoding:: When encoding and $KCODE are set to 'utf-8'
initial:: Level of initial indentation.
indentation and no line breaks).
indent:: Number of spaces used for indentation (0 implies no
<<.
out:: Object receiving the markup. +out+ must respond to

Create an XML markup builder.
def initialize(indent=0, initial=0, encoding='utf-8')
  @indent = indent
  @level  = initial
  @encoding = encoding.downcase
end

def method_missing(sym, *args, &block)

in the markup block that isn't cached.
is never invoked directly, but is called for each markup method
Create XML markup based on the name of the method. This method
def method_missing(sym, *args, &block)
  cache_method_call(sym) if ::Builder::XmlBase.cache_method_calls
  tag!(sym, *args, &block)
end

def nil?

cf. http://fishbowl.pastiche.org/2004/10/13/cargo_cult_programming).
cargo cult programming,
is pretty safe to define it here. (Note: this is an example of
of recursion happens. Since nil? won't ever be an XML tag, it
is not defined and method_missing is invoked, some strange kind
For some reason, nil? is sent to the XmlMarkup object. If nil?
def nil?
  false
end

def tag!(sym, *args, &block)

implemented via method_missing.
is the tag name, the arguments are the same as the tags
Create a tag named +sym+. Other than the first argument which
def tag!(sym, *args, &block)
  text = nil
  attrs = nil
  sym = "#{sym}:#{args.shift}" if args.first.kind_of?(::Symbol)
  sym = sym.to_sym unless sym.class == ::Symbol
  args.each do |arg|
    case arg
    when ::Hash
      attrs ||= {}
      attrs.merge!(arg)
    when nil
      # do nothing
    else
      text ||= ''
      text << arg.to_s
    end
  end
  if block
    unless text.nil?
      ::Kernel::raise ::ArgumentError,
        "XmlMarkup cannot mix a text argument with a block"
    end
    _indent
    _start_tag(sym, attrs)
    _newline
    begin
      _nested_structures(block)
    ensure
      _indent
      _end_tag(sym)
      _newline
    end
  elsif text.nil?
    _indent
    _start_tag(sym, attrs, true)
    _newline
  else
    _indent
    _start_tag(sym, attrs)
    text! text
    _end_tag(sym)
    _newline
  end
  @target
end

def text!(text)

builder.p { |b| b.br; b.text! "HI" } #=>


HI



used within the markup brackets as:
Append text to the output target. Escape any markup. May be
def text!(text)
  _text(_escape(text))
end