Updated to Builder 2.0 [DHH]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@4260 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
David Heinemeier Hansson 2006-04-25 02:47:44 +00:00
parent a9e02fd99f
commit 71a570ffd7
5 changed files with 167 additions and 21 deletions

View File

@ -1,5 +1,7 @@
*SVN* *SVN*
* Updated to Builder 2.0 [DHH]
* Add Array#split for dividing arrays into one or more subarrays by value or block. [Sam Stephenson] * Add Array#split for dividing arrays into one or more subarrays by value or block. [Sam Stephenson]
*1.3.1* (April 6th, 2006) *1.3.1* (April 6th, 2006)

View File

@ -14,12 +14,15 @@ module Builder #:nodoc:
# methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>). # methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
# BlankSlate is useful as a base class when writing classes that # BlankSlate is useful as a base class when writing classes that
# depend upon <tt>method_missing</tt> (e.g. dynamic proxies). # depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
class BlankSlate #:nodoc: class BlankSlate
class << self class << self
# Hide the method named +name+ in the BlankSlate class. Don't
# hide +instance_eval+ or any method beginning with "__".
def hide(name) def hide(name)
undef_method name if undef_method name if
instance_methods.include?(name.to_s) and instance_methods.include?(name.to_s) and
name !~ /^(__|instance_eval)/ name !~ /^(__|instance_eval)/
end end
end end
@ -29,10 +32,14 @@ end
# Since Ruby is very dynamic, methods added to the ancestors of # Since Ruby is very dynamic, methods added to the ancestors of
# BlankSlate <em>after BlankSlate is defined</em> will show up in the # 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 defined # list of available BlankSlate methods. We handle this by defining a
# hook in the Object and Kernel classes that will hide any defined
module Kernel #:nodoc: module Kernel #:nodoc:
class << self class << self
alias_method :blank_slate_method_added, :method_added alias_method :blank_slate_method_added, :method_added
# Detect method additions to Kernel and remove them in the
# BlankSlate class.
def method_added(name) def method_added(name)
blank_slate_method_added(name) blank_slate_method_added(name)
return if self != Kernel return if self != Kernel
@ -44,9 +51,12 @@ end
class Object #:nodoc: class Object #:nodoc:
class << self class << self
alias_method :blank_slate_method_added, :method_added alias_method :blank_slate_method_added, :method_added
# Detect method additions to Object and remove them in the
# BlankSlate class.
def method_added(name) def method_added(name)
blank_slate_method_added(name) blank_slate_method_added(name)
return if self != Object return if self != Object
Builder::BlankSlate.hide(name) Builder::BlankSlate.hide(name)
end end
end end

View File

@ -0,0 +1,112 @@
#!/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)
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 #:nodoc:
XChar = Builder::XChar if ! defined?(XChar)
# XML escaped version of chr
def xchr
n = XChar::CP1252[self] || self
n = 42 unless XChar::VALID.find {|range| range.include? n}
XChar::PREDEFINED[n] or (n<128 ? n.chr : "&##{n};")
end
end
######################################################################
# Enhance the String class with a XML escaped character version of
# to_s.
#
class String #:nodoc:
# 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

@ -2,7 +2,7 @@
require 'builder/blankslate' require 'builder/blankslate'
module Builder #:nodoc: module Builder
# Generic error for builder # Generic error for builder
class IllegalBlockError < RuntimeError #:nodoc: class IllegalBlockError < RuntimeError #:nodoc:
@ -14,7 +14,7 @@ module Builder #:nodoc:
# Create an XML markup builder. # Create an XML markup builder.
# #
# out:: Object receiving the markup.1 +out+ must respond to # out:: Object receiving the markup. +out+ must respond to
# <tt><<</tt>. # <tt><<</tt>.
# indent:: Number of spaces used for indentation (0 implies no # indent:: Number of spaces used for indentation (0 implies no
# indentation and no line breaks). # indentation and no line breaks).
@ -76,15 +76,15 @@ module Builder #:nodoc:
end end
# Append text to the output target. Escape any markup. May be # Append text to the output target. Escape any markup. May be
# used within the markup brackets as: # used within the markup brakets as:
# #
# builder.p { br; text! "HI" } #=> <p><br/>HI</p> # builder.p { |b| b.br; b.text! "HI" } #=> <p><br/>HI</p>
def text!(text) def text!(text)
_text(_escape(text)) _text(_escape(text))
end end
# Append text to the output target without escaping any markup. # Append text to the output target without escaping any markup.
# May be used within the markup brackets as: # May be used within the markup brakets as:
# #
# builder.p { |x| x << "<br/>HI" } #=> <p><br/>HI</p> # builder.p { |x| x << "<br/>HI" } #=> <p><br/>HI</p>
# #
@ -112,11 +112,13 @@ module Builder #:nodoc:
private private
require 'builder/xchar'
def _escape(text) def _escape(text)
text. text.to_xs
gsub(%r{&}, '&amp;'). end
gsub(%r{<}, '&lt;').
gsub(%r{>}, '&gt;') def _escape_quote(text)
_escape(text).gsub(%r{"}, '&quot;') # " WART
end end
def _capture_outer_self(block) def _capture_outer_self(block)

View File

@ -1,6 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
#-- #--
# Copyright 2004 by Jim Weirich (jim@weirichhouse.org). # Copyright 2004, 2005 by Jim Weirich (jim@weirichhouse.org).
# All rights reserved. # All rights reserved.
# Permission is granted for use, copying, modification, distribution, # Permission is granted for use, copying, modification, distribution,
@ -165,13 +165,23 @@ module Builder
# :target=><em>target_object</em>:: # :target=><em>target_object</em>::
# Object receiving the markup. +out+ must respond to the # Object receiving the markup. +out+ must respond to the
# <tt><<</tt> operator. The default is a plain string target. # <tt><<</tt> operator. The default is a plain string target.
#
# :indent=><em>indentation</em>:: # :indent=><em>indentation</em>::
# Number of spaces used for indentation. The default is no # Number of spaces used for indentation. The default is no
# indentation and no line breaks. # indentation and no line breaks.
#
# :margin=><em>initial_indentation_level</em>:: # :margin=><em>initial_indentation_level</em>::
# Amount of initial indentation (specified in levels, not # Amount of initial indentation (specified in levels, not
# spaces). # 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={}) def initialize(options={})
indent = options[:indent] || 0 indent = options[:indent] || 0
margin = options[:margin] || 0 margin = options[:margin] || 0
@ -239,12 +249,13 @@ module Builder
[:version, :encoding, :standalone]) [:version, :encoding, :standalone])
end end
# Surrounds the given text with a CDATA tag # Insert a CDATA section into the XML markup.
# #
# For example: # For example:
# #
# xml.cdata! "blah blah blah" # xml.cdata!("text to be included in cdata")
# # => <![CDATA[blah blah blah]]> # #=> <![CDATA[text to be included in cdata]]>
#
def cdata!(text) def cdata!(text)
_ensure_no_block block_given? _ensure_no_block block_given?
_special("<![CDATA[", "]]>", text, nil) _special("<![CDATA[", "]]>", text, nil)
@ -289,10 +300,19 @@ module Builder
return if attrs.nil? return if attrs.nil?
order.each do |k| order.each do |k|
v = attrs[k] v = attrs[k]
@target << %{ #{k}="#{v}"} if v @target << %{ #{k}="#{_attr_value(v)}"} if v
end end
attrs.each do |k, v| attrs.each do |k, v|
@target << %{ #{k}="#{v}"} unless order.member?(k) @target << %{ #{k}="#{_attr_value(v)}"} unless order.member?(k)
end
end
def _attr_value(value)
case value
when Symbol
value.to_s
else
_escape_quote(value.to_s)
end end
end end