1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Rebundle Builder 2.1.2 but prefer a newer RubyGem if available.

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@7828 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
Jeremy Kemper 2007-10-10 23:01:41 +00:00
parent cff25aae90
commit e7040b09e8
14 changed files with 1061 additions and 16 deletions

View file

@ -1,5 +1,7 @@
*SVN*
* Rebundle Builder 2.1.2 but prefer a newer RubyGem if available. [Jeremy Kemper]
* Add Range#overlaps?(range), Range#include?(range), and Range#step without a block. [brandon]
* Correct BufferedLogger#level? checks. #9806 [wildchild, Johan Sorensen]

View file

@ -48,8 +48,6 @@ spec = Gem::Specification.new do |s|
s.require_path = 'lib'
s.has_rdoc = true
s.add_dependency('builder', '~> 2.1.2')
s.author = "David Heinemeier Hansson"
s.email = "david@loudthinking.com"
s.homepage = "http://www.rubyonrails.org"

View file

@ -22,19 +22,9 @@
#++
$:.unshift(File.dirname(__FILE__))
$:.unshift(File.dirname(__FILE__) + "/active_support/vendor")
require 'rubygems'
begin
require 'builder'
rescue LoadError
$stderr.puts 'Builder has been unbundled from Active Support in Rails 2.0.'
$stderr.puts 'Please `gem install builder`'
raise
end
require 'active_support/vendor'
require 'active_support/basic_object'
require 'active_support/inflector'
require 'active_support/core_ext'

View file

@ -1,5 +1,5 @@
# Ruby 1.9 introduces BasicObject. Use Builder's BlankSlate until then.
unless defined? BasicObject
require 'builder/blankslate'
BasicObject = Builder::BlankSlate
require 'blankslate'
BasicObject = BlankSlate
end

View file

@ -1,8 +1,8 @@
require 'date'
require 'xml_simple'
require 'cgi'
require 'base64'
require 'builder'
require 'xmlsimple'
# Extensions needed for Hash#to_query
class Object

View file

@ -0,0 +1,14 @@
# Prefer gems to the bundled libs.
require 'rubygems'
begin
gem 'builder', '~> 2.1.2'
rescue Gem::LoadError
$:.unshift "#{File.dirname(__FILE__)}/vendor/builder-2.1.2"
end
begin
gem 'xml-simple', '~> 1.0.11'
rescue Gem::LoadError
$:.unshift "#{File.dirname(__FILE__)}/vendor/xml-simple-1.0.11"
end

View file

@ -0,0 +1,113 @@
#!/usr/bin/env ruby
#--
# Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
# All rights reserved.
# Permission is granted for use, copying, modification, distribution,
# and distribution of modified versions of this work as long as the
# above copyright notice is included.
#++
######################################################################
# BlankSlate provides an abstract base class with no predefined
# methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
# BlankSlate is useful as a base class when writing classes that
# depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
#
class BlankSlate
class << self
# Hide the method named +name+ in the BlankSlate class. Don't
# hide +instance_eval+ or any method beginning with "__".
def hide(name)
if instance_methods.include?(name.to_s) and
name !~ /^(__|instance_eval)/
@hidden_methods ||= {}
@hidden_methods[name.to_sym] = instance_method(name)
undef_method name
end
end
def find_hidden_method(name)
@hidden_methods ||= {}
@hidden_methods[name] || superclass.find_hidden_method(name)
end
# Redefine a previously hidden method so that it may be called on a blank
# slate object.
def reveal(name)
bound_method = nil
unbound_method = find_hidden_method(name)
fail "Don't know how to reveal method '#{name}'" unless unbound_method
define_method(name) do |*args|
bound_method ||= unbound_method.bind(self)
bound_method.call(*args)
end
end
end
instance_methods.each { |m| hide(m) }
end
######################################################################
# Since Ruby is very dynamic, methods added to the ancestors of
# BlankSlate <em>after BlankSlate is defined</em> will show up in the
# list of available BlankSlate methods. We handle this by defining a
# hook in the Object and Kernel classes that will hide any method
# defined after BlankSlate has been loaded.
#
module Kernel
class << self
alias_method :blank_slate_method_added, :method_added
# Detect method additions to Kernel and remove them in the
# BlankSlate class.
def method_added(name)
result = blank_slate_method_added(name)
return result if self != Kernel
BlankSlate.hide(name)
result
end
end
end
######################################################################
# Same as above, except in Object.
#
class Object
class << self
alias_method :blank_slate_method_added, :method_added
# Detect method additions to Object and remove them in the
# BlankSlate class.
def method_added(name)
result = blank_slate_method_added(name)
return result if self != Object
BlankSlate.hide(name)
result
end
def find_hidden_method(name)
nil
end
end
end
######################################################################
# Also, modules included into Object need to be scanned and have their
# instance methods removed from blank slate. In theory, modules
# included into Kernel would have to be removed as well, but a
# "feature" of Ruby prevents late includes into modules from being
# exposed in the first place.
#
class Module
alias blankslate_original_append_features append_features
def append_features(mod)
result = blankslate_original_append_features(mod)
return result if mod != Object
instance_methods.each do |name|
BlankSlate.hide(name)
end
result
end
end

View file

@ -0,0 +1,13 @@
#!/usr/bin/env ruby
#--
# Copyright 2004 by Jim Weirich (jim@weirichhouse.org).
# All rights reserved.
# Permission is granted for use, copying, modification, distribution,
# and distribution of modified versions of this work as long as the
# above copyright notice is included.
#++
require 'builder/xmlmarkup'
require 'builder/xmlevents'

View file

@ -0,0 +1,20 @@
#!/usr/bin/env ruby
#--
# Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
# All rights reserved.
# Permission is granted for use, copying, modification, distribution,
# and distribution of modified versions of this work as long as the
# above copyright notice is included.
#++
require 'blankslate'
######################################################################
# BlankSlate has been promoted to a top level name and is now
# available as a standalone gem. We make the name available in the
# Builder namespace for compatibility.
#
module Builder
BlankSlate = ::BlankSlate
end

View file

@ -0,0 +1,250 @@
#!/usr/bin/env ruby
#--
# Copyright 2004, 2005 by Jim Weirich (jim@weirichhouse.org).
# Copyright 2005 by Scott Barron (scott@elitists.net).
# All rights reserved.
#
# Permission is granted for use, copying, modification, distribution,
# and distribution of modified versions of this work as long as the
# above copyright notice is included.
#
# Much of this is taken from Jim's work in xmlbase.rb and xmlmarkup.rb.
# Documentation has also been copied and pasted and modified to reflect
# that we're building CSS here instead of XML. Jim is conducting the
# orchestra here and I'm just off in the corner playing a flute.
#++
# Provide a flexible and easy to use Builder for creating Cascading
# Style Sheets (CSS).
require 'builder/blankslate'
module Builder
# Create a Cascading Style Sheet (CSS) using Ruby.
#
# Example usage:
#
# css = Builder::CSS.new
#
# text_color = '#7F7F7F'
# preferred_fonts = 'Helvetica, Arial, sans_serif'
#
# css.comment! 'This is our stylesheet'
# css.body {
# background_color '#FAFAFA'
# font_size 'small'
# font_family preferred_fonts
# color text_color
# }
#
# css.id!('navbar') {
# width '500px'
# }
#
# css.class!('navitem') {
# color 'red'
# }
#
# css.a :hover {
# text_decoration 'underline'
# }
#
# css.div(:id => 'menu') {
# background 'green'
# }
#
# css.div(:class => 'foo') {
# background 'red'
# }
#
# This will yield the following stylesheet:
#
# /* This is our stylesheet */
# body {
# background_color: #FAFAFA;
# font_size: small;
# font_family: Helvetica, Arial, sans_serif;
# color: #7F7F7F;
# }
#
# #navbar {
# width: 500px;
# }
#
# .navitem {
# color: red;
# }
#
# a:hover {
# text_decoration: underline;
# }
#
# div#menu {
# background: green;
# }
#
# div.foo {
# background: red;
# }
#
class CSS < BlankSlate
# Create a CSS builder.
#
# out:: Object receiving the markup.1 +out+ must respond to
# <tt><<</tt>.
# indent:: Number of spaces used for indentation (0 implies no
# indentation and no line breaks).
#
def initialize(indent=2)
@indent = indent
@target = []
@parts = []
@library = {}
end
def +(part)
_join_with_op! '+'
self
end
def >>(part)
_join_with_op! ''
self
end
def >(part)
_join_with_op! '>'
self
end
def |(part)
_join_with_op! ','
self
end
# Return the target of the builder
def target!
@target * ''
end
# Create a comment string in the output.
def comment!(comment_text)
@target << "/* #{comment_text} */\n"
end
def id!(arg, &block)
_start_container('#'+arg.to_s, nil, block_given?)
_css_block(block) if block
_unify_block
self
end
def class!(arg, &block)
_start_container('.'+arg.to_s, nil, block_given?)
_css_block(block) if block
_unify_block
self
end
def store!(sym, &block)
@library[sym] = block.to_proc
end
def group!(*args, &block)
args.each do |arg|
if arg.is_a?(Symbol)
instance_eval(&@library[arg])
else
instance_eval(&arg)
end
_text ', ' unless arg == args.last
end
if block
_css_block(block)
_unify_block
end
end
def method_missing(sym, *args, &block)
sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
if block
_start_container(sym, args.first)
_css_block(block)
_unify_block
elsif @in_block
_indent
_css_line(sym, *args)
_newline
return self
else
_start_container(sym, args.first, false)
_unify_block
end
self
end
# "Cargo culted" from Jim who also "cargo culted" it. See xmlbase.rb.
def nil?
false
end
private
def _unify_block
@target << @parts * ''
@parts = []
end
def _join_with_op!(op)
rhs, lhs = @target.pop, @target.pop
@target << "#{lhs} #{op} #{rhs}"
end
def _text(text)
@parts << text
end
def _css_block(block)
_newline
_nested_structures(block)
_end_container
_end_block
end
def _end_block
_newline
_newline
end
def _newline
_text "\n"
end
def _indent
_text ' ' * @indent
end
def _nested_structures(block)
@in_block = true
self.instance_eval(&block)
@in_block = false
end
def _start_container(sym, atts = {}, with_bracket = true)
selector = sym.to_s
selector << ".#{atts[:class]}" if atts && atts[:class]
selector << '#' + "#{atts[:id]}" if atts && atts[:id]
@parts << "#{selector}#{with_bracket ? ' {' : ''}"
end
def _end_container
@parts << "}"
end
def _css_line(sym, *args)
_text("#{sym.to_s.gsub('_','-')}: #{args * ' '};")
end
end
end

View file

@ -0,0 +1,115 @@
#!/usr/bin/env ruby
# The XChar library is provided courtesy of Sam Ruby (See
# http://intertwingly.net/stories/2005/09/28/xchar.rb)
# --------------------------------------------------------------------
# If the Builder::XChar module is not currently defined, fail on any
# name clashes in standard library classes.
module Builder
def self.check_for_name_collision(klass, method_name, defined_constant=nil)
if klass.instance_methods.include?(method_name.to_s)
fail RuntimeError,
"Name Collision: Method '#{method_name}' is already defined in #{klass}"
end
end
end
if ! defined?(Builder::XChar)
Builder.check_for_name_collision(String, "to_xs")
Builder.check_for_name_collision(Fixnum, "xchr")
end
######################################################################
module Builder
####################################################################
# XML Character converter, from Sam Ruby:
# (see http://intertwingly.net/stories/2005/09/28/xchar.rb).
#
module XChar # :nodoc:
# See
# http://intertwingly.net/stories/2004/04/14/i18n.html#CleaningWindows
# for details.
CP1252 = { # :nodoc:
128 => 8364, # euro sign
130 => 8218, # single low-9 quotation mark
131 => 402, # latin small letter f with hook
132 => 8222, # double low-9 quotation mark
133 => 8230, # horizontal ellipsis
134 => 8224, # dagger
135 => 8225, # double dagger
136 => 710, # modifier letter circumflex accent
137 => 8240, # per mille sign
138 => 352, # latin capital letter s with caron
139 => 8249, # single left-pointing angle quotation mark
140 => 338, # latin capital ligature oe
142 => 381, # latin capital letter z with caron
145 => 8216, # left single quotation mark
146 => 8217, # right single quotation mark
147 => 8220, # left double quotation mark
148 => 8221, # right double quotation mark
149 => 8226, # bullet
150 => 8211, # en dash
151 => 8212, # em dash
152 => 732, # small tilde
153 => 8482, # trade mark sign
154 => 353, # latin small letter s with caron
155 => 8250, # single right-pointing angle quotation mark
156 => 339, # latin small ligature oe
158 => 382, # latin small letter z with caron
159 => 376, # latin capital letter y with diaeresis
}
# See http://www.w3.org/TR/REC-xml/#dt-chardata for details.
PREDEFINED = {
38 => '&amp;', # ampersand
60 => '&lt;', # left angle bracket
62 => '&gt;', # right angle bracket
}
# See http://www.w3.org/TR/REC-xml/#charsets for details.
VALID = [
0x9, 0xA, 0xD,
(0x20..0xD7FF),
(0xE000..0xFFFD),
(0x10000..0x10FFFF)
]
end
end
######################################################################
# Enhance the Fixnum class with a XML escaped character conversion.
#
class Fixnum
XChar = Builder::XChar if ! defined?(XChar)
# XML escaped version of chr
def xchr
n = XChar::CP1252[self] || self
case n when *XChar::VALID
XChar::PREDEFINED[n] or (n<128 ? n.chr : "&##{n};")
else
'*'
end
end
end
######################################################################
# Enhance the String class with a XML escaped character version of
# to_s.
#
class String
# XML escaped version of to_s
def to_xs
unpack('U*').map {|n| n.xchr}.join # ASCII, UTF-8
rescue
unpack('C*').map {|n| n.xchr}.join # ISO-8859-1, WIN-1252
end
end

View file

@ -0,0 +1,139 @@
#!/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.
#
def initialize(indent=0, initial=0)
@indent = indent
@level = initial
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?
raise ArgumentError, "XmlMarkup cannot mix a text argument with a block"
end
_indent
_start_tag(sym, attrs)
_newline
_nested_structures(block)
_indent
_end_tag(sym)
_newline
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'
def _escape(text)
text.to_xs
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

View file

@ -0,0 +1,63 @@
#!/usr/bin/env ruby
#--
# Copyright 2004 by Jim Weirich (jim@weirichhouse.org).
# All rights reserved.
# Permission is granted for use, copying, modification, distribution,
# and distribution of modified versions of this work as long as the
# above copyright notice is included.
#++
require 'builder/xmlmarkup'
module Builder
# Create a series of SAX-like XML events (e.g. start_tag, end_tag)
# from the markup code. XmlEvent objects are used in a way similar
# to XmlMarkup objects, except that a series of events are generated
# and passed to a handler rather than generating character-based
# markup.
#
# Usage:
# xe = Builder::XmlEvents.new(hander)
# xe.title("HI") # Sends start_tag/end_tag/text messages to the handler.
#
# Indentation may also be selected by providing value for the
# indentation size and initial indentation level.
#
# xe = Builder::XmlEvents.new(handler, indent_size, initial_indent_level)
#
# == XML Event Handler
#
# The handler object must expect the following events.
#
# [<tt>start_tag(tag, attrs)</tt>]
# Announces that a new tag has been found. +tag+ is the name of
# the tag and +attrs+ is a hash of attributes for the tag.
#
# [<tt>end_tag(tag)</tt>]
# Announces that an end tag for +tag+ has been found.
#
# [<tt>text(text)</tt>]
# Announces that a string of characters (+text+) has been found.
# A series of characters may be broken up into more than one
# +text+ call, so the client cannot assume that a single
# callback contains all the text data.
#
class XmlEvents < XmlMarkup
def text!(text)
@target.text(text)
end
def _start_tag(sym, attrs, end_too=false)
@target.start_tag(sym, attrs)
_end_tag(sym) if end_too
end
def _end_tag(sym)
@target.end_tag(sym)
end
end
end

View file

@ -0,0 +1,328 @@
#!/usr/bin/env ruby
#--
# Copyright 2004, 2005 by Jim Weirich (jim@weirichhouse.org).
# All rights reserved.
# Permission is granted for use, copying, modification, distribution,
# and distribution of modified versions of this work as long as the
# above copyright notice is included.
#++
# Provide a flexible and easy to use Builder for creating XML markup.
# See XmlBuilder for usage details.
require 'builder/xmlbase'
module Builder
# Create XML markup easily. All (well, almost all) methods sent to
# an XmlMarkup object will be translated to the equivalent XML
# markup. Any method with a block will be treated as an XML markup
# tag with nested markup in the block.
#
# Examples will demonstrate this easier than words. In the
# following, +xm+ is an +XmlMarkup+ object.
#
# xm.em("emphasized") # => <em>emphasized</em>
# xm.em { xmm.b("emp & bold") } # => <em><b>emph &amp; bold</b></em>
# xm.a("A Link", "href"=>"http://onestepback.org")
# # => <a href="http://onestepback.org">A Link</a>
# xm.div { br } # => <div><br/></div>
# xm.target("name"=>"compile", "option"=>"fast")
# # => <target option="fast" name="compile"\>
# # NOTE: order of attributes is not specified.
#
# xm.instruct! # <?xml version="1.0" encoding="UTF-8"?>
# xm.html { # <html>
# xm.head { # <head>
# xm.title("History") # <title>History</title>
# } # </head>
# xm.body { # <body>
# xm.comment! "HI" # <!-- HI -->
# xm.h1("Header") # <h1>Header</h1>
# xm.p("paragraph") # <p>paragraph</p>
# } # </body>
# } # </html>
#
# == Notes:
#
# * The order that attributes are inserted in markup tags is
# undefined.
#
# * Sometimes you wish to insert text without enclosing tags. Use
# the <tt>text!</tt> method to accomplish this.
#
# Example:
#
# xm.div { # <div>
# xm.text! "line"; xm.br # line<br/>
# xm.text! "another line"; xmbr # another line<br/>
# } # </div>
#
# * The special XML characters <, >, and & are converted to &lt;,
# &gt; and &amp; automatically. Use the <tt><<</tt> operation to
# insert text without modification.
#
# * Sometimes tags use special characters not allowed in ruby
# identifiers. Use the <tt>tag!</tt> method to handle these
# cases.
#
# Example:
#
# xml.tag!("SOAP:Envelope") { ... }
#
# will produce ...
#
# <SOAP:Envelope> ... </SOAP:Envelope>"
#
# <tt>tag!</tt> will also take text and attribute arguments (after
# the tag name) like normal markup methods. (But see the next
# bullet item for a better way to handle XML namespaces).
#
# * Direct support for XML namespaces is now available. If the
# first argument to a tag call is a symbol, it will be joined to
# the tag to produce a namespace:tag combination. It is easier to
# show this than describe it.
#
# xml.SOAP :Envelope do ... end
#
# Just put a space before the colon in a namespace to produce the
# right form for builder (e.g. "<tt>SOAP:Envelope</tt>" =>
# "<tt>xml.SOAP :Envelope</tt>")
#
# * XmlMarkup builds the markup in any object (called a _target_)
# that accepts the <tt><<</tt> method. If no target is given,
# then XmlMarkup defaults to a string target.
#
# Examples:
#
# xm = Builder::XmlMarkup.new
# result = xm.title("yada")
# # result is a string containing the markup.
#
# buffer = ""
# xm = Builder::XmlMarkup.new(buffer)
# # The markup is appended to buffer (using <<)
#
# xm = Builder::XmlMarkup.new(STDOUT)
# # The markup is written to STDOUT (using <<)
#
# xm = Builder::XmlMarkup.new
# x2 = Builder::XmlMarkup.new(:target=>xm)
# # Markup written to +x2+ will be send to +xm+.
#
# * Indentation is enabled by providing the number of spaces to
# indent for each level as a second argument to XmlBuilder.new.
# Initial indentation may be specified using a third parameter.
#
# Example:
#
# xm = Builder.new(:indent=>2)
# # xm will produce nicely formatted and indented XML.
#
# xm = Builder.new(:indent=>2, :margin=>4)
# # xm will produce nicely formatted and indented XML with 2
# # spaces per indent and an over all indentation level of 4.
#
# builder = Builder::XmlMarkup.new(:target=>$stdout, :indent=>2)
# builder.name { |b| b.first("Jim"); b.last("Weirich) }
# # prints:
# # <name>
# # <first>Jim</first>
# # <last>Weirich</last>
# # </name>
#
# * The instance_eval implementation which forces self to refer to
# the message receiver as self is now obsolete. We now use normal
# block calls to execute the markup block. This means that all
# markup methods must now be explicitly send to the xml builder.
# For instance, instead of
#
# xml.div { strong("text") }
#
# you need to write:
#
# xml.div { xml.strong("text") }
#
# Although more verbose, the subtle change in semantics within the
# block was found to be prone to error. To make this change a
# little less cumbersome, the markup block now gets the markup
# object sent as an argument, allowing you to use a shorter alias
# within the block.
#
# For example:
#
# xml_builder = Builder::XmlMarkup.new
# xml_builder.div { |xml|
# xml.stong("text")
# }
#
class XmlMarkup < XmlBase
# Create an XML markup builder. Parameters are specified by an
# option hash.
#
# :target=><em>target_object</em>::
# Object receiving the markup. +out+ must respond to the
# <tt><<</tt> operator. The default is a plain string target.
#
# :indent=><em>indentation</em>::
# Number of spaces used for indentation. The default is no
# indentation and no line breaks.
#
# :margin=><em>initial_indentation_level</em>::
# Amount of initial indentation (specified in levels, not
# spaces).
#
# :escape_attrs=><b>OBSOLETE</em>::
# The :escape_attrs option is no longer supported by builder
# (and will be quietly ignored). String attribute values are
# now automatically escaped. If you need unescaped attribute
# values (perhaps you are using entities in the attribute
# values), then give the value as a Symbol. This allows much
# finer control over escaping attribute values.
#
def initialize(options={})
indent = options[:indent] || 0
margin = options[:margin] || 0
super(indent, margin)
@target = options[:target] || ""
end
# Return the target of the builder.
def target!
@target
end
def comment!(comment_text)
_ensure_no_block block_given?
_special("<!-- ", " -->", comment_text, nil)
end
# Insert an XML declaration into the XML markup.
#
# For example:
#
# xml.declare! :ELEMENT, :blah, "yada"
# # => <!ELEMENT blah "yada">
def declare!(inst, *args, &block)
_indent
@target << "<!#{inst}"
args.each do |arg|
case arg
when String
@target << %{ "#{arg}"} # " WART
when Symbol
@target << " #{arg}"
end
end
if block_given?
@target << " ["
_newline
_nested_structures(block)
@target << "]"
end
@target << ">"
_newline
end
# Insert a processing instruction into the XML markup. E.g.
#
# For example:
#
# xml.instruct!
# #=> <?xml version="1.0" encoding="UTF-8"?>
# xml.instruct! :aaa, :bbb=>"ccc"
# #=> <?aaa bbb="ccc"?>
#
def instruct!(directive_tag=:xml, attrs={})
_ensure_no_block block_given?
if directive_tag == :xml
a = { :version=>"1.0", :encoding=>"UTF-8" }
attrs = a.merge attrs
end
_special(
"<?#{directive_tag}",
"?>",
nil,
attrs,
[:version, :encoding, :standalone])
end
# Insert a CDATA section into the XML markup.
#
# For example:
#
# xml.cdata!("text to be included in cdata")
# #=> <![CDATA[text to be included in cdata]]>
#
def cdata!(text)
_ensure_no_block block_given?
_special("<![CDATA[", "]]>", text, nil)
end
private
# NOTE: All private methods of a builder object are prefixed when
# a "_" character to avoid possible conflict with XML tag names.
# Insert text directly in to the builder's target.
def _text(text)
@target << text
end
# Insert special instruction.
def _special(open, close, data=nil, attrs=nil, order=[])
_indent
@target << open
@target << data if data
_insert_attributes(attrs, order) if attrs
@target << close
_newline
end
# Start an XML tag. If <tt>end_too</tt> is true, then the start
# tag is also the end tag (e.g. <br/>
def _start_tag(sym, attrs, end_too=false)
@target << "<#{sym}"
_insert_attributes(attrs)
@target << "/" if end_too
@target << ">"
end
# Insert an ending tag.
def _end_tag(sym)
@target << "</#{sym}>"
end
# Insert the attributes (given in the hash).
def _insert_attributes(attrs, order=[])
return if attrs.nil?
order.each do |k|
v = attrs[k]
@target << %{ #{k}="#{_attr_value(v)}"} if v # " WART
end
attrs.each do |k, v|
@target << %{ #{k}="#{_attr_value(v)}"} unless order.member?(k) # " WART
end
end
def _attr_value(value)
case value
when Symbol
value.to_s
else
_escape_quote(value.to_s)
end
end
def _ensure_no_block(got_block)
if got_block
fail IllegalBlockError,
"Blocks are not allowed on XML instructions"
end
end
end
end