class Builder::XmlBase
Builder::XmlMarkup and Builder::XmlEvents for examples.
XmlBase is a base class for building XML builders. See
def <<(text)
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", " ").gsub("\r", " "). gsub(%r{"}, '"') # " 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)
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')
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)
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?
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)
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)
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