Skip to content
Browse files

Applied Sam Ruby's 1.9 patch.

  • Loading branch information...
1 parent 7d9bd24 commit b7c99a9d13c717996ad82ffaaf18fbed320db627 @jimweirich committed Nov 17, 2010
Showing with 283 additions and 113 deletions.
  1. +26 −25 Rakefile
  2. +6 −3 lib/builder/blankslate.rb
  3. +3 −3 lib/builder/css.rb
  4. +108 −30 lib/builder/xchar.rb
  5. +27 −9 lib/builder/xmlbase.rb
  6. +10 −9 lib/builder/xmlmarkup.rb
  7. +7 −0 test/test_blankslate.rb
  8. +72 −31 test/test_markupbuilder.rb
  9. +24 −3 test/test_xchar.rb
View
51 Rakefile
@@ -41,6 +41,7 @@ task :tu => [:test_units]
Rake::TestTask.new("test_units") do |t|
t.test_files = FileList['test/test*.rb']
+ t.libs << "."
t.verbose = false
end
@@ -59,7 +60,7 @@ rd = Rake::RDocTask.new("rdoc") { |rdoc|
# gem files.
PKG_FILES = FileList[
- 'lib/**/*.rb',
+ 'lib/**/*.rb',
'test/**/*.rb',
'scripts/**/*.rb'
]
@@ -68,14 +69,14 @@ PKG_FILES.exclude('lib/builder/css.rb')
BLANKSLATE_FILES = FileList[
'lib/blankslate.rb',
- 'test/testblankslate.rb'
+ 'test/test_blankslate.rb'
]
if ! defined?(Gem)
puts "Package Target requires RubyGEMs"
else
spec = Gem::Specification.new do |s|
-
+
#### Basic information.
s.name = 'builder'
@@ -92,58 +93,58 @@ simple to do. Currently the following builder objects are supported:
s.files = PKG_FILES.to_a
s.require_path = 'lib'
s.autorequire = 'builder'
-
+
s.test_files = PKG_FILES.select { |fn| fn =~ /^test\/test/ }
-
+
s.has_rdoc = true
s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a
s.rdoc_options <<
'--title' << 'Builder -- Easy XML Building' <<
'--main' << 'README.rdoc' <<
'--line-numbers'
-
+
s.author = "Jim Weirich"
s.email = "jim@weirichhouse.org"
s.homepage = "http://onestepback.org"
end
-
+
blankslate_spec = Gem::Specification.new do |s|
-
+
#### Basic information.
-
+
s.name = 'blankslate'
s.version = PKG_VERSION
s.summary = "Blank Slate base class."
s.description = %{\
BlankSlate provides a base class where almost all of the methods from Object and
Kernel have been removed. This is useful when providing proxy object and other
-classes that make heavy use of method_missing.
+classes that make heavy use of method_missing.
}
-
+
s.files = BLANKSLATE_FILES.to_a
s.require_path = 'lib'
s.autorequire = 'builder'
-
+
s.test_files = PKG_FILES.select { |fn| fn =~ /^test\/test/ }
-
+
s.has_rdoc = true
s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a
s.rdoc_options <<
'--title' << 'BlankSlate -- Base Class for building proxies.' <<
'--main' << 'README.rdoc' <<
'--line-numbers'
-
+
s.author = "Jim Weirich"
s.email = "jim@weirichhouse.org"
s.homepage = "http://onestepback.org"
end
-
+
namespace 'builder' do
Rake::GemPackageTask.new(spec) do |t|
t.need_tar = true
end
end
-
+
namespace 'blankslate' do
Rake::GemPackageTask.new(blankslate_spec) do |t|
t.need_tar = true
@@ -157,7 +158,7 @@ desc "Look for Debugging print lines"
task :dbg do
FileList['**/*.rb'].egrep /\bDBG|\bbreakpoint\b/
end
-
+
# RCov ---------------------------------------------------------------
begin
@@ -183,15 +184,15 @@ end
namespace "tags" do
desc "Create a TAGS file"
task :emacs => "TAGS"
-
+
TAGS = 'xctags -e'
-
+
file "TAGS" => SRC_RB do
puts "Makings TAGS"
sh "#{TAGS} #{SRC_RB}", :verbose => false
end
end
-
+
# --------------------------------------------------------------------
# Creating a release
@@ -207,23 +208,23 @@ task :release => [
:update_version,
:package,
:tag] do
-
- announce
+
+ announce
announce "**************************************************************"
announce "* Release #{PKG_VERSION} Complete."
announce "* Packages ready to upload."
announce "**************************************************************"
- announce
+ announce
end
# Validate that everything is ready to go for a release.
task :prerelease do
- announce
+ announce
announce "**************************************************************"
announce "* Making RubyGem Release #{PKG_VERSION}"
announce "* (current version #{CURRENT_VERSION})"
announce "**************************************************************"
- announce
+ announce
# Is a release number supplied?
unless ENV['REL']
View
9 lib/builder/blankslate.rb
@@ -8,13 +8,16 @@
# 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
+ if Object::const_defined?(:BasicObject)
+ BlankSlate = ::BasicObject
+ else
+ require 'blankslate'
+ BlankSlate = ::BlankSlate
+ end
end
View
6 lib/builder/css.rb
@@ -136,14 +136,14 @@ def comment!(comment_text)
end
def id!(arg, &block)
- _start_container('#'+arg.to_s, nil, block_given?)
+ _start_container('#'+arg.to_s, nil, ::Kernel.block_given?)
_css_block(block) if block
_unify_block
self
end
def class!(arg, &block)
- _start_container('.'+arg.to_s, nil, block_given?)
+ _start_container('.'+arg.to_s, nil, ::Kernel.block_given?)
_css_block(block) if block
_unify_block
self
@@ -169,7 +169,7 @@ def group!(*args, &block)
end
def method_missing(sym, *args, &block)
- sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
+ sym = "#{sym}:#{args.shift}" if args.first.kind_of?(::Symbol)
if block
_start_container(sym, args.first)
_css_block(block)
View
138 lib/builder/xchar.rb
@@ -10,14 +10,14 @@
module Builder
def self.check_for_name_collision(klass, method_name, defined_constant=nil)
- if klass.instance_methods.include?(method_name.to_s)
+ if klass.method_defined?(method_name.to_s)
fail RuntimeError,
"Name Collision: Method '#{method_name}' is already defined in #{klass}"
end
end
end
-if ! defined?(Builder::XChar)
+if ! defined?(Builder::XChar) and ! String.method_defined?(:encode)
Builder.check_for_name_collision(String, "to_xs")
Builder.check_for_name_collision(Fixnum, "xchr")
end
@@ -78,42 +78,120 @@ module XChar # :nodoc:
(0xE000..0xFFFD),
(0x10000..0x10FFFF)
]
+
+ # http://www.fileformat.info/info/unicode/char/fffd/index.htm
+ REPLACEMENT_CHAR =
+ if String.method_defined?(:encode)
+ "\uFFFD"
+ elsif $KCODE == 'UTF8'
+ "\xEF\xBF\xBD"
+ else
+ '*'
+ end
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. When <tt>escape</tt> is set to false
- # the CP1252 fix is still applied but utf-8 characters are not
- # converted to character entities.
- def xchr(escape=true)
- n = XChar::CP1252[self] || self
- case n when *XChar::VALID
- XChar::PREDEFINED[n] or (n<128 ? n.chr : (escape ? "&##{n};" : [n].pack('U*')))
- else
- '*'
+if String.method_defined?(:encode)
+ module Builder
+ module XChar # :nodoc:
+ CP1252_DIFFERENCES, UNICODE_EQUIVALENT = Builder::XChar::CP1252.each.
+ inject([[],[]]) {|(domain,range),(key,value)|
+ [domain << key,range << value]
+ }.map {|seq| seq.pack('U*').force_encoding('utf-8')}
+
+ XML_PREDEFINED = Regexp.new('[' +
+ Builder::XChar::PREDEFINED.keys.pack('U*').force_encoding('utf-8') +
+ ']')
+
+ INVALID_XML_CHAR = Regexp.new('[^'+
+ Builder::XChar::VALID.map { |item|
+ case item
+ when Fixnum
+ [item].pack('U').force_encoding('utf-8')
+ when Range
+ [item.first, '-'.ord, item.last].pack('UUU').force_encoding('utf-8')
+ end
+ }.join +
+ ']')
+
+ ENCODING_BINARY = Encoding.find('BINARY')
+ ENCODING_UTF8 = Encoding.find('UTF-8')
+ ENCODING_ISO1 = Encoding.find('ISO-8859-1')
+
+ # convert a string to valid UTF-8, compensating for a number of
+ # common errors.
+ def XChar.unicode(string)
+ if string.encoding == ENCODING_BINARY
+ if string.ascii_only?
+ string
+ else
+ string = string.clone.force_encoding(ENCODING_UTF8)
+ if string.valid_encoding?
+ string
+ else
+ string.encode(ENCODING_UTF8, ENCODING_ISO1)
+ end
+ end
+
+ elsif string.encoding == ENCODING_UTF8
+ if string.valid_encoding?
+ string
+ else
+ string.encode(ENCODING_UTF8, ENCODING_ISO1)
+ end
+
+ else
+ string.encode(ENCODING_UTF8)
+ end
+ end
+
+ # encode a string per XML rules
+ def XChar.encode(string)
+ unicode(string).
+ tr(CP1252_DIFFERENCES, UNICODE_EQUIVALENT).
+ gsub(INVALID_XML_CHAR, REPLACEMENT_CHAR).
+ gsub(XML_PREDEFINED) {|c| PREDEFINED[c.ord]}
+ end
end
end
-end
+else
-######################################################################
-# Enhance the String class with a XML escaped character version of
-# to_s.
-#
-class String
- # XML escaped version of to_s. When <tt>escape</tt> is set to false
- # the CP1252 fix is still applied but utf-8 characters are not
- # converted to character entities.
- def to_xs(escape=true)
- unpack('U*').map {|n| n.xchr(escape)}.join # ASCII, UTF-8
- rescue
- unpack('C*').map {|n| n.xchr}.join # ISO-8859-1, WIN-1252
+ ######################################################################
+ # Enhance the Fixnum class with a XML escaped character conversion.
+ #
+ class Fixnum
+ XChar = Builder::XChar if ! defined?(XChar)
+
+ # XML escaped version of chr. When <tt>escape</tt> is set to false
+ # the CP1252 fix is still applied but utf-8 characters are not
+ # converted to character entities.
+ def xchr(escape=true)
+ n = XChar::CP1252[self] || self
+ case n when *XChar::VALID
+ XChar::PREDEFINED[n] or
+ (n<128 ? n.chr : (escape ? "&##{n};" : [n].pack('U*')))
+ else
+ Builder::XChar::REPLACEMENT_CHAR
+ end
+ end
+ end
+
+
+ ######################################################################
+ # Enhance the String class with a XML escaped character version of
+ # to_s.
+ #
+ class String
+ # XML escaped version of to_s. When <tt>escape</tt> is set to false
+ # the CP1252 fix is still applied but utf-8 characters are not
+ # converted to character entities.
+ def to_xs(escape=true)
+ unpack('U*').map {|n| n.xchr(escape)}.join # ASCII, UTF-8
+ rescue
+ unpack('C*').map {|n| n.xchr}.join # ISO-8859-1, WIN-1252
+ end
end
end
View
36 lib/builder/xmlbase.rb
@@ -40,10 +40,10 @@ def tag!(sym, *args, &block)
def method_missing(sym, *args, &block)
text = nil
attrs = nil
- sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
+ sym = "#{sym}:#{args.shift}" if args.first.kind_of?(::Symbol)
args.each do |arg|
case arg
- when Hash
+ when ::Hash
attrs ||= {}
attrs.merge!(arg)
else
@@ -53,15 +53,19 @@ def method_missing(sym, *args, &block)
end
if block
unless text.nil?
- raise ArgumentError, "XmlMarkup cannot mix a text argument with a block"
+ ::Kernel::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
+ begin
+ _nested_structures(block)
+ ensure
+ _indent
+ _end_tag(sym)
+ _newline
+ end
elsif text.nil?
_indent
_start_tag(sym, attrs, true)
@@ -114,8 +118,22 @@ def nil?
private
require 'builder/xchar'
- def _escape(text)
- text.to_xs((@encoding != 'utf-8' or $KCODE != 'UTF8'))
+ 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)
+ text.to_xs((@encoding != 'utf-8' or $KCODE != 'UTF8'))
+ end
end
def _escape_quote(text)
View
19 lib/builder/xmlmarkup.rb
@@ -196,7 +196,7 @@ def target!
end
def comment!(comment_text)
- _ensure_no_block block_given?
+ _ensure_no_block ::Kernel::block_given?
_special("<!-- ", " -->", comment_text, nil)
end
@@ -211,13 +211,13 @@ def declare!(inst, *args, &block)
@target << "<!#{inst}"
args.each do |arg|
case arg
- when String
+ when ::String
@target << %{ "#{arg}"} # " WART
- when Symbol
+ when ::Symbol
@target << " #{arg}"
end
end
- if block_given?
+ if ::Kernel::block_given?
@target << " ["
_newline
_nested_structures(block)
@@ -240,7 +240,7 @@ def declare!(inst, *args, &block)
# $KCODE is "UTF8", then builder will emit UTF-8 encoded strings
# rather than the entity encoding normally used.
def instruct!(directive_tag=:xml, attrs={})
- _ensure_no_block block_given?
+ _ensure_no_block ::Kernel::block_given?
if directive_tag == :xml
a = { :version=>"1.0", :encoding=>"UTF-8" }
attrs = a.merge attrs
@@ -262,7 +262,7 @@ def instruct!(directive_tag=:xml, attrs={})
# #=> <![CDATA[text to be included in cdata]]>
#
def cdata!(text)
- _ensure_no_block block_given?
+ _ensure_no_block ::Kernel::block_given?
_special("<![CDATA[", "]]>", text, nil)
end
@@ -314,7 +314,7 @@ def _insert_attributes(attrs, order=[])
def _attr_value(value)
case value
- when Symbol
+ when ::Symbol
value.to_s
else
_escape_quote(value.to_s)
@@ -323,8 +323,9 @@ def _attr_value(value)
def _ensure_no_block(got_block)
if got_block
- fail IllegalBlockError,
- "Blocks are not allowed on XML instructions"
+ ::Kernel::raise IllegalBlockError.new(
+ "Blocks are not allowed on XML instructions"
+ )
end
end
View
7 test/test_blankslate.rb
@@ -81,6 +81,13 @@ def direct_global
# Test case for blank slate.
#
class TestBlankSlate < Test::Unit::TestCase
+ if Object::const_defined?(:BasicObject)
+ def self.suite
+ # skip tests if :BasicObject is present
+ Test::Unit::TestSuite.new(name)
+ end
+ end
+
def setup
@bs = BlankSlate.new
end
View
103 test/test_markupbuilder.rb
@@ -118,7 +118,7 @@ def test_append_text
end
def test_ambiguous_markup
- ex = assert_raises(ArgumentError) {
+ ex = assert_raise(ArgumentError) {
@xml.h1("data1") { b }
}
assert_match /\btext\b/, ex.message
@@ -228,6 +228,22 @@ def test_long
assert_match /<owl:Restriction>/m, xml.target!
end
+ def test_ensure
+ xml = Builder::XmlMarkup.new
+ xml.html do
+ xml.body do
+ begin
+ xml.p do
+ raise Exception.new('boom')
+ end
+ rescue Exception => e
+ xml.pre e
+ end
+ end
+ end
+ assert_match %r{<p>}, xml.target!
+ assert_match %r{</p>}, xml.target!
+ end
end
class TestDeclarations < Test::Unit::TestCase
@@ -334,10 +350,10 @@ def test_xml_instruct_with_standalong
end
def test_no_blocks
- assert_raises(Builder::IllegalBlockError) do
+ assert_raise(Builder::IllegalBlockError) do
@xml.instruct! { |x| x.hi }
end
- assert_raises(Builder::IllegalBlockError) do
+ assert_raise(Builder::IllegalBlockError) do
@xml.comment!(:element) { |x| x.hi }
end
end
@@ -378,58 +394,83 @@ def test_initial_level
end
class TestUtfMarkup < Test::Unit::TestCase
- def setup
- @old_kcode = $KCODE
- end
+ if ! String.method_defined?(:encode)
+ def setup
+ @old_kcode = $KCODE
+ end
- def teardown
- $KCODE = @old_kcode
- end
+ def teardown
+ $KCODE = @old_kcode
+ end
- def test_use_entities_if_no_encoding_is_given_and_kcode_is_none
- $KCODE = 'NONE'
- xml = Builder::XmlMarkup.new
- xml.p("\xE2\x80\x99")
- assert_match(%r(<p>&#8217;</p>), xml.target!) #
+ def test_use_entities_if_no_encoding_is_given_and_kcode_is_none
+ $KCODE = 'NONE'
+ xml = Builder::XmlMarkup.new
+ xml.p("\xE2\x80\x99")
+ assert_match(%r(<p>&#8217;</p>), xml.target!) #
+ end
+
+ def test_use_entities_if_encoding_is_utf_but_kcode_is_not
+ $KCODE = 'NONE'
+ xml = Builder::XmlMarkup.new
+ xml.instruct!(:xml, :encoding => 'UTF-8')
+ xml.p("\xE2\x80\x99")
+ assert_match(%r(<p>&#8217;</p>), xml.target!) #
+ end
+ else
+ # change in behavior. As there is no $KCODE anymore, the default
+ # moves from "does not understand utf-8" to "supports utf-8".
+
+ def test_use_entities_if_no_encoding_is_given_and_kcode_is_none
+ xml = Builder::XmlMarkup.new
+ xml.p("\xE2\x80\x99")
+ assert_match("<p>\u2019</p>", xml.target!) #
+ end
+
+ def test_use_entities_if_encoding_is_utf_but_kcode_is_not
+ xml = Builder::XmlMarkup.new
+ xml.instruct!(:xml, :encoding => 'UTF-8')
+ xml.p("\xE2\x80\x99")
+ assert_match("<p>\u2019</p>", xml.target!) #
+ end
end
- def test_use_entities_if_encoding_is_utf_but_kcode_is_not
- $KCODE = 'NONE'
- xml = Builder::XmlMarkup.new
- xml.instruct!(:xml, :encoding => 'UTF-8')
- xml.p("\xE2\x80\x99")
- assert_match(%r(<p>&#8217;</p>), xml.target!) #
+ def encode string, encoding
+ if !String.method_defined?(:encode)
+ $KCODE = encoding
+ string
+ elsif encoding == 'UTF8'
+ string.force_encoding('UTF-8')
+ else
+ string
+ end
end
def test_use_entities_if_kcode_is_utf_but_encoding_is_something_else
- $KCODE = 'UTF8'
xml = Builder::XmlMarkup.new
xml.instruct!(:xml, :encoding => 'UTF-16')
- xml.p("\xE2\x80\x99")
+ xml.p(encode("\xE2\x80\x99", 'UTF8'))
assert_match(%r(<p>&#8217;</p>), xml.target!) #
end
def test_use_utf8_if_encoding_defaults_and_kcode_is_utf8
- $KCODE = 'UTF8'
xml = Builder::XmlMarkup.new
- xml.p("\xE2\x80\x99")
- assert_equal "<p>\xE2\x80\x99</p>", xml.target!
+ xml.p(encode("\xE2\x80\x99",'UTF8'))
+ assert_equal encode("<p>\xE2\x80\x99</p>",'UTF8'), xml.target!
end
def test_use_utf8_if_both_encoding_and_kcode_are_utf8
- $KCODE = 'UTF8'
xml = Builder::XmlMarkup.new
xml.instruct!(:xml, :encoding => 'UTF-8')
- xml.p("\xE2\x80\x99")
- assert_match(%r(<p>\xE2\x80\x99</p>), xml.target!)
+ xml.p(encode("\xE2\x80\x99",'UTF8'))
+ assert_match encode("<p>\xE2\x80\x99</p>",'UTF8'), xml.target!
end
def test_use_utf8_if_both_encoding_and_kcode_are_utf8_with_lowercase
- $KCODE = 'UTF8'
xml = Builder::XmlMarkup.new
xml.instruct!(:xml, :encoding => 'utf-8')
- xml.p("\xE2\x80\x99")
- assert_match(%r(<p>\xE2\x80\x99</p>), xml.target!)
+ xml.p(encode("\xE2\x80\x99",'UTF8'))
+ assert_match encode("<p>\xE2\x80\x99</p>",'UTF8'), xml.target!
end
end
View
27 test/test_xchar.rb
@@ -15,7 +15,28 @@
require 'test/unit'
require 'builder/xchar'
+if String.method_defined?(:encode)
+ class String
+ ENCODING_BINARY = Encoding.find('BINARY')
+
+ # shim method for testing purposes
+ def to_xs(escape=true)
+ raise NameError.new('to_xs') unless caller[0].index(__FILE__)
+
+ result = Builder::XChar.encode(self)
+ if escape
+ result.gsub(/[^\u0000-\u007F]/) {|c| "&##{c.ord};"}
+ else
+ # really only useful for testing purposes
+ result.force_encoding(ENCODING_BINARY)
+ end
+ end
+ end
+end
+
class TestXmlEscaping < Test::Unit::TestCase
+ REPLACEMENT_CHAR = Builder::XChar::REPLACEMENT_CHAR.to_xs
+
def test_ascii
assert_equal 'abc', 'abc'.to_xs
end
@@ -27,9 +48,9 @@ def test_predefined
end
def test_invalid
- assert_equal '*', "\x00".to_xs # null
- assert_equal '*', "\x0C".to_xs # form feed
- assert_equal '*', "\xEF\xBF\xBF".to_xs # U+FFFF
+ assert_equal REPLACEMENT_CHAR, "\x00".to_xs # null
+ assert_equal REPLACEMENT_CHAR, "\x0C".to_xs # form feed
+ assert_equal REPLACEMENT_CHAR, "\xEF\xBF\xBF".to_xs # U+FFFF
end
def test_iso_8859_1

0 comments on commit b7c99a9

Please sign in to comment.
Something went wrong with that request. Please try again.