Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 4c4b69e215
Fetching contributors…

Cannot retrieve contributors at this time

165 lines (148 sloc) 4.649 kb
#!/usr/bin/env ruby
require 'builder/blankslate'
module Builder
# Generic error for builder
class IllegalBlockError < RuntimeError; end
# XmlBase is a base class for building XML builders. See
# Builder::XmlMarkup and Builder::XmlEvents for examples.
class XmlBase < BlankSlate
# Create an XML markup builder.
#
# out:: Object receiving the markup. +out+ must respond to
# <tt><<</tt>.
# indent:: Number of spaces used for indentation (0 implies no
# indentation and no line breaks).
# initial:: Level of initial indentation.
# encoding:: When <tt>encoding</tt> and $KCODE are set to 'utf-8'
# characters aren't converted to character entities in
# the output stream.
def initialize(indent=0, initial=0, encoding='utf-8')
@indent = indent
@level = initial
@encoding = encoding.downcase
end
# Create a tag named +sym+. Other than the first argument which
# is the tag name, the arguments are the same as the tags
# implemented via <tt>method_missing</tt>.
def tag!(sym, *args, &block)
method_missing(sym.to_sym, *args, &block)
end
# Create XML markup based on the name of the method. This method
# is never invoked directly, but is called for each markup method
# in the markup block.
def method_missing(sym, *args, &block)
text = nil
attrs = nil
sym = "#{sym}:#{args.shift}" if args.first.kind_of?(::Symbol)
args.each do |arg|
case arg
when ::Hash
attrs ||= {}
attrs.merge!(arg)
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
# Append text to the output target. Escape any markup. May be
# used within the markup brackets as:
#
# builder.p { |b| b.br; b.text! "HI" } #=> <p><br/>HI</p>
def text!(text)
_text(_escape(text))
end
# Append text to the output target without escaping any markup.
# May be used within the markup brackets as:
#
# builder.p { |x| x << "<br/>HI" } #=> <p><br/>HI</p>
#
# This is useful when using non-builder enabled software that
# generates strings. Just insert the string directly into the
# builder without changing the inserted markup.
#
# It is also useful for stacking builder objects. Builders only
# use <tt><<</tt> to append to the target, so by supporting this
# method/operation builders can use other builders as their
# targets.
def <<(text)
_text(text)
end
# For some reason, nil? is sent to the XmlMarkup object. If nil?
# is not defined and method_missing is invoked, some strange kind
# of recursion happens. Since nil? won't ever be an XML tag, it
# is pretty safe to define it here. (Note: this is an example of
# cargo cult programming,
# cf. http://fishbowl.pastiche.org/2004/10/13/cargo_cult_programming).
def nil?
false
end
private
require 'builder/xchar'
if ::String.method_defined?(:encode)
def _escape(text)
result = XChar.encode(text)
begin
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
else
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
end
def _escape_quote(text)
_escape(text).gsub(%r{"}, '&quot;') # " WART
end
def _newline
return if @indent == 0
text! "\n"
end
def _indent
return if @indent == 0 || @level == 0
text!(" " * (@level * @indent))
end
def _nested_structures(block)
@level += 1
block.call(self)
ensure
@level -= 1
end
end
end
Jump to Line
Something went wrong with that request. Please try again.