Index: builder-2.1.2/lib/builder/blankslate.rb
===================================================================
--- builder-2.1.2.orig/lib/builder/blankslate.rb	2006-12-24 20:29:01.000000000 +0100
+++ builder-2.1.2/lib/builder/blankslate.rb	2010-03-21 16:38:02.000000000 +0100
@@ -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
Index: builder-2.1.2/lib/builder/css.rb
===================================================================
--- builder-2.1.2.orig/lib/builder/css.rb	2010-03-21 16:37:51.000000000 +0100
+++ builder-2.1.2/lib/builder/css.rb	2010-03-21 16:38:02.000000000 +0100
@@ -136,14 +136,14 @@
     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 @@
     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)
Index: builder-2.1.2/lib/builder/xchar.rb
===================================================================
--- builder-2.1.2.orig/lib/builder/xchar.rb	2010-03-21 16:37:51.000000000 +0100
+++ builder-2.1.2/lib/builder/xchar.rb	2010-03-21 16:38:02.000000000 +0100
@@ -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 @@
       (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
Index: builder-2.1.2/lib/builder/xmlbase.rb
===================================================================
--- builder-2.1.2.orig/lib/builder/xmlbase.rb	2010-03-21 16:37:51.000000000 +0100
+++ builder-2.1.2/lib/builder/xmlbase.rb	2010-03-21 16:38:02.000000000 +0100
@@ -40,10 +40,10 @@
     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 @@
       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 @@
     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)
Index: builder-2.1.2/lib/builder/xmlmarkup.rb
===================================================================
--- builder-2.1.2.orig/lib/builder/xmlmarkup.rb	2010-03-21 16:37:51.000000000 +0100
+++ builder-2.1.2/lib/builder/xmlmarkup.rb	2010-03-21 16:38:02.000000000 +0100
@@ -196,7 +196,7 @@
     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 @@
       @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 @@
     # $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 @@
     #        #=> <![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 _attr_value(value)
       case value
-      when Symbol
+      when ::Symbol
         value.to_s
       else
         _escape_quote(value.to_s)
@@ -323,8 +323,9 @@
 
     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
 
Index: builder-2.1.2/Rakefile
===================================================================
--- builder-2.1.2.orig/Rakefile	2010-03-21 16:37:51.000000000 +0100
+++ builder-2.1.2/Rakefile	2010-03-21 16:38:02.000000000 +0100
@@ -68,7 +68,7 @@
 
 BLANKSLATE_FILES = FileList[
   'lib/blankslate.rb',
-  'test/testblankslate.rb'
+  'test/test_blankslate.rb'
 ]
 
 if ! defined?(Gem)
Index: builder-2.1.2/test/test_blankslate.rb
===================================================================
--- builder-2.1.2.orig/test/test_blankslate.rb	2010-03-21 16:37:51.000000000 +0100
+++ builder-2.1.2/test/test_blankslate.rb	2010-03-21 16:38:02.000000000 +0100
@@ -80,126 +80,132 @@
 ######################################################################
 # Test case for blank slate.
 #
-class TestBlankSlate < Test::Unit::TestCase
-  def setup
-    @bs = BlankSlate.new
-  end
 
-  def test_undefined_methods_remain_undefined
-    assert_raise(NoMethodError) { @bs.no_such_method }
-    assert_raise(NoMethodError) { @bs.nil? }
-  end
+# skip tests if :BasicObject is present
+unless Object::const_defined?(:BasicObject)
 
+  class TestBlankSlate < Test::Unit::TestCase
 
-  # NOTE: NameError is acceptable because the lack of a '.' means that
-  # Ruby can't tell if it is a method or a local variable.
-  def test_undefined_methods_remain_undefined_during_instance_eval
-    assert_raise(NoMethodError, NameError)  do
-      @bs.instance_eval do nil? end
+    def setup
+      @bs = BlankSlate.new
     end
-    assert_raise(NoMethodError, NameError)  do
-      @bs.instance_eval do no_such_method end
+
+    def test_undefined_methods_remain_undefined
+      assert_raise(NoMethodError) { @bs.no_such_method }
+      assert_raise(NoMethodError) { @bs.nil? }
     end
-  end
 
-  def test_private_methods_are_undefined
-    assert_raise(NoMethodError) do
-      @bs.puts "HI"
+
+    # NOTE: NameError is acceptable because the lack of a '.' means that
+    # Ruby can't tell if it is a method or a local variable.
+    def test_undefined_methods_remain_undefined_during_instance_eval
+      assert_raise(NoMethodError, NameError)  do
+        @bs.instance_eval do nil? end
+      end
+      assert_raise(NoMethodError, NameError)  do
+        @bs.instance_eval do no_such_method end
+      end
     end
-  end
-  
-  def test_targetted_private_methods_are_undefined_during_instance_eval
-    assert_raise(NoMethodError, NameError) do
-      @bs.instance_eval do self.puts "HI" end
+
+    def test_private_methods_are_undefined
+      assert_raise(NoMethodError) do
+        @bs.puts "HI"
+      end
     end
-  end
-  
-  def test_untargetted_private_methods_are_defined_during_instance_eval
-    oldstdout = $stdout
-    $stdout = StringIO.new
-    @bs.instance_eval do 
-      puts "HI"
+    
+    def test_targetted_private_methods_are_undefined_during_instance_eval
+      assert_raise(NoMethodError, NameError) do
+        @bs.instance_eval do self.puts "HI" end
+      end
+    end
+    
+    def test_untargetted_private_methods_are_defined_during_instance_eval
+      oldstdout = $stdout
+      $stdout = StringIO.new
+      @bs.instance_eval do 
+        puts "HI"
+      end
+    ensure
+      $stdout = oldstdout
+    end
+    
+    def test_methods_added_late_to_kernel_remain_undefined
+      assert_equal 1234, nil.late_addition
+      assert_raise(NoMethodError) { @bs.late_addition }
     end
-  ensure
-    $stdout = oldstdout
-  end
-  
-  def test_methods_added_late_to_kernel_remain_undefined
-    assert_equal 1234, nil.late_addition
-    assert_raise(NoMethodError) { @bs.late_addition }
-  end
 
-  def test_methods_added_late_to_object_remain_undefined
-    assert_equal 4321, nil.another_late_addition
-    assert_raise(NoMethodError) { @bs.another_late_addition }
-  end
-  
-  def test_methods_added_late_to_global_remain_undefined
-    assert_equal 42, global_inclusion
-    assert_raise(NoMethodError) { @bs.global_inclusion }
-  end
+    def test_methods_added_late_to_object_remain_undefined
+      assert_equal 4321, nil.another_late_addition
+      assert_raise(NoMethodError) { @bs.another_late_addition }
+    end
+    
+    def test_methods_added_late_to_global_remain_undefined
+      assert_equal 42, global_inclusion
+      assert_raise(NoMethodError) { @bs.global_inclusion }
+    end
 
-  def test_preload_method_added
-    assert Kernel.k_added_names.include?(:late_addition)
-    assert Object.o_added_names.include?(:another_late_addition)
-  end
+    def test_preload_method_added
+      assert Kernel.k_added_names.include?(:late_addition)
+      assert Object.o_added_names.include?(:another_late_addition)
+    end
 
-  def test_method_defined_late_multiple_times_remain_undefined
-    assert_equal 22, nil.double_late_addition
-    assert_raise(NoMethodError) { @bs.double_late_addition }
-  end
+    def test_method_defined_late_multiple_times_remain_undefined
+      assert_equal 22, nil.double_late_addition
+      assert_raise(NoMethodError) { @bs.double_late_addition }
+    end
 
-  def test_late_included_module_in_object_is_ok
-    assert_equal 33, 1.late_object
-    assert_raise(NoMethodError) { @bs.late_object }
-  end
+    def test_late_included_module_in_object_is_ok
+      assert_equal 33, 1.late_object
+      assert_raise(NoMethodError) { @bs.late_object }
+    end
 
-  def test_late_included_module_in_kernel_is_ok
-    assert_raise(NoMethodError) { @bs.late_kernel }
-  end
+    def test_late_included_module_in_kernel_is_ok
+      assert_raise(NoMethodError) { @bs.late_kernel }
+    end
 
-  def test_revealing_previously_hidden_methods_are_callable
-    with_to_s = Class.new(BlankSlate) do
-      reveal :to_s
+    def test_revealing_previously_hidden_methods_are_callable
+      with_to_s = Class.new(BlankSlate) do
+        reveal :to_s
+      end
+      assert_match /^#<.*>$/, with_to_s.new.to_s
     end
-    assert_match /^#<.*>$/, with_to_s.new.to_s
-  end
 
-  def test_revealing_previously_hidden_methods_are_callable_with_block
-    Object.class_eval <<-EOS
+    def test_revealing_previously_hidden_methods_are_callable_with_block
+      Object.class_eval <<-EOS
       def given_block(&block)
         block
       end   
-    EOS
-  
-    with_given_block = Class.new(BlankSlate) do
-      reveal :given_block
+      EOS
+      
+      with_given_block = Class.new(BlankSlate) do
+        reveal :given_block
+      end
+      assert_not_nil with_given_block.new.given_block {}
     end
-    assert_not_nil with_given_block.new.given_block {}
-  end
 
-  def test_revealing_a_hidden_method_twice_is_ok
-    with_to_s = Class.new(BlankSlate) do
-      reveal :to_s
-      reveal :to_s
+    def test_revealing_a_hidden_method_twice_is_ok
+      with_to_s = Class.new(BlankSlate) do
+        reveal :to_s
+        reveal :to_s
+      end
+      assert_match /^#<.*>$/, with_to_s.new.to_s
     end
-    assert_match /^#<.*>$/, with_to_s.new.to_s
-  end
 
-  def test_revealing_unknown_hidden_method_is_an_error
-    assert_raises(RuntimeError) do
-      Class.new(BlankSlate) do
-        reveal :xyz
+    def test_revealing_unknown_hidden_method_is_an_error
+      assert_raises(RuntimeError) do
+        Class.new(BlankSlate) do
+          reveal :xyz
+        end
       end
     end
-  end
 
-  def test_global_includes_still_work
-    assert_nothing_raised do
-      assert_equal 42, global_inclusion
-      assert_equal 42, Object.new.global_inclusion
-      assert_equal 42, "magic number".global_inclusion
-      assert_equal 43, direct_global
+    def test_global_includes_still_work
+      assert_nothing_raised do
+        assert_equal 42, global_inclusion
+        assert_equal 42, Object.new.global_inclusion
+        assert_equal 42, "magic number".global_inclusion
+        assert_equal 43, direct_global
+      end
     end
   end
 
Index: builder-2.1.2/test/test_markupbuilder.rb
===================================================================
--- builder-2.1.2.orig/test/test_markupbuilder.rb	2010-03-21 16:37:51.000000000 +0100
+++ builder-2.1.2/test/test_markupbuilder.rb	2010-03-21 16:38:03.000000000 +0100
@@ -108,7 +108,7 @@
   end
 
   def test_reference_methods
-    @xml.title { |x| x.a { x.b(name) } }
+    @xml.title { |x| x.a { x.b("bob") } }
     assert_equal "<title><a><b>bob</b></a></title>", @xml.target!
   end
 
@@ -118,7 +118,7 @@
   end
   
   def test_ambiguous_markup
-    ex = assert_raises(ArgumentError) {
+    ex = assert_raise(ArgumentError) {
       @xml.h1("data1") { b }
     }
     assert_match /\btext\b/, ex.message
@@ -151,9 +151,6 @@
     assert_equal "<div><span><a href=\"ref\">text</a></span></div>", @xml.target!
   end
 
-  def name
-    "bob"
-  end
 end
 
 class TestAttributeEscaping < Test::Unit::TestCase
@@ -228,6 +225,22 @@
     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 +347,10 @@
   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 +391,83 @@
   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
 
Index: builder-2.1.2/test/test_xchar.rb
===================================================================
--- builder-2.1.2.orig/test/test_xchar.rb	2010-03-21 16:37:51.000000000 +0100
+++ builder-2.1.2/test/test_xchar.rb	2010-03-21 16:38:03.000000000 +0100
@@ -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 @@
   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
