]> git.sesse.net Git - xml-template/commitdiff
Hand-merge from borked repository. Main changes are a Ruby version (with
authorsgunderson@bigfoot.com <>
Thu, 1 Mar 2007 13:23:05 +0000 (14:23 +0100)
committersgunderson@bigfoot.com <>
Thu, 1 Mar 2007 13:23:05 +0000 (14:23 +0100)
associated documentation tweaks) and namespace tests.

24 files changed:
doc/intro.txt
perl/namespace.pl [new file with mode: 0644]
perl/namespace2.pl [new file with mode: 0644]
php/namespace.php [new file with mode: 0644]
php/namespace2.php [new file with mode: 0644]
python/namespace.py [new file with mode: 0644]
python/namespace2.py [new file with mode: 0644]
python/xmltemplate.py
ruby/attribute-empty.rb [new file with mode: 0644]
ruby/attribute.rb [new file with mode: 0644]
ruby/attribute2.rb [new file with mode: 0644]
ruby/attribute3.rb [new file with mode: 0644]
ruby/clone.rb [new file with mode: 0644]
ruby/include.rb [new file with mode: 0644]
ruby/namespace.rb [new file with mode: 0644]
ruby/namespace2.rb [new file with mode: 0644]
ruby/passthru.rb [new file with mode: 0644]
ruby/simple.rb [new file with mode: 0644]
ruby/xmltemplate.rb [new file with mode: 0644]
tests/reference/namespace.xml [new file with mode: 0644]
tests/reference/namespace2.xml [new file with mode: 0644]
tests/test.sh
xml/namespace.xml [new file with mode: 0644]
xml/namespace2.xml [new file with mode: 0644]

index 448ded6c6138d2a81103ddc5b5fe88ba92579606..f4f7e978d23fec4c2e320d6f16c293b2adb54302 100644 (file)
@@ -16,9 +16,9 @@ favourite distribution and do "bzr get http://bzr.sesse.net/xml-template/"
 to check out the code and this documentation.
 
 There is a lot to be said about design philosophy, but let's first give a
-simple example to give you the feel of how it works. (The example is in
-Perl, but there are also functionally equivalent PHP and Python versions,
-and more languages should probably come soon.)
+simple example to give you the feel of how it works. (The example is in Perl,
+but there are also functionally equivalent PHP, Python and Ruby versions, and
+more languages should probably come soon.)
 
 Template (simple.xml):
 
@@ -249,8 +249,8 @@ The main thoughts behind XML::Template have been, in no particular order:
    the entire DOM with wrappers for each language. (Thankfully, by relying on
    the DOM support in each language, the code so far is under 200 lines per
    implementation, so maintaining this hopefully shouldn't be much work.)
-   As proof-of-concept, I've got Perl, PHP and Python implementations that work
-   and feel largely the same -- Ruby and other implementations are welcome.
+   As proof-of-concept, I've got Perl, PHP, Python and Ruby implementations
+   that work and feel largely the same -- other implementations are welcome.
    (This is backed up by a test suite, which ensures that all the different
    implementations return structurally equivalent XML for a certain set of
    test cases. Porting to a new language is not difficult, and once you've
diff --git a/perl/namespace.pl b/perl/namespace.pl
new file mode 100644 (file)
index 0000000..d3ae6e3
--- /dev/null
@@ -0,0 +1,11 @@
+#! /usr/bin/perl
+use XML::Template;
+
+my $doc = XML::Template::process_file('../xml/namespace.xml', {
+       'title' => 'Namespace tests',
+       '#hello' => 'Hello world!',
+       '#test' => 'Replaced.',
+       'tagname' => 'foo',
+       '#moretest' => 'bar'
+});
+print $doc->toString;
diff --git a/perl/namespace2.pl b/perl/namespace2.pl
new file mode 100644 (file)
index 0000000..17a8db2
--- /dev/null
@@ -0,0 +1,8 @@
+#! /usr/bin/perl
+use XML::Template;
+
+my $doc = XML::Template::process_file('../xml/namespace2.xml', {
+       'title' => 'Namespace tests',
+       '#hello' => 'Replaced.',
+});
+print $doc->toString;
diff --git a/php/namespace.php b/php/namespace.php
new file mode 100644 (file)
index 0000000..35bb707
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+require('xml-template.php');
+
+$doc = XML_Template_process_file('../xml/namespace.xml', array( 
+       'title' => 'Namespace tests',
+       '#hello' => 'Hello world!',
+       '#test' => 'Replaced.',
+       'tagname' => 'foo',
+       '#moretest' => 'bar'
+));
+print $doc->dump_mem();
+?>
diff --git a/php/namespace2.php b/php/namespace2.php
new file mode 100644 (file)
index 0000000..6ff09fd
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+require('xml-template.php');
+
+$doc = XML_Template_process_file('../xml/namespace2.xml', array( 
+       'title' => 'Namespace tests',
+       '#hello' => 'Replaced.',
+));
+print $doc->dump_mem();
+?>
diff --git a/python/namespace.py b/python/namespace.py
new file mode 100644 (file)
index 0000000..2216578
--- /dev/null
@@ -0,0 +1,11 @@
+#! /usr/bin/python
+import xmltemplate
+
+doc = xmltemplate.process_file("../xml/namespace.xml", {
+       'title': 'Namespace tests',
+       '#hello': 'Hello world!',
+       '#test': 'Replaced.',
+       'tagname': 'foo',
+       '#moretest': 'bar'
+})
+print doc.toxml()
diff --git a/python/namespace2.py b/python/namespace2.py
new file mode 100644 (file)
index 0000000..5df48c5
--- /dev/null
@@ -0,0 +1,8 @@
+#! /usr/bin/python
+import xmltemplate
+
+doc = xmltemplate.process_file("../xml/namespace2.xml", {
+       'title': 'Namespace tests',
+       '#hello': 'Replaced.'
+})
+print doc.toxml()
index 94135251089c1df5e7ae3bf966be17b0e36d1a45..16225926876eef7dc95791a7c40401af6681b707 100644 (file)
@@ -36,7 +36,7 @@ def process(node, obj, clean = True):
                                if not attrs is None:
                                        for i in range(attrs.length):
                                                attr = attrs.item(0)
-                                               if attr.namespaceURI == "http://template.sesse.net/" or attr.name == "id":
+                                               if attr.namespaceURI == "http://template.sesse.net/" and attr.localName == "id":
                                                        id = attr.value
                                                        if clean:
                                                                attrs_to_remove.append(attr.name)
diff --git a/ruby/attribute-empty.rb b/ruby/attribute-empty.rb
new file mode 100644 (file)
index 0000000..0476b9d
--- /dev/null
@@ -0,0 +1,8 @@
+#! /usr/bin/ruby
+require "xmltemplate"
+
+doc = XMLTemplate.process_file("../xml/clone.xml", {
+       'color' => 'blue',
+       '#things' => []
+});
+print doc.to_s
diff --git a/ruby/attribute.rb b/ruby/attribute.rb
new file mode 100644 (file)
index 0000000..d636555
--- /dev/null
@@ -0,0 +1,13 @@
+#! /usr/bin/ruby
+require "xmltemplate"
+
+doc = XMLTemplate.process_file("../xml/clone.xml", {
+       'color' => 'red',
+       '#things' => [
+               { 'li' => 'Raindrops on roses',    'li/class' => 'odd' },
+               { 'li' => 'Whiskers on kittens',   'li/class' => 'even' },
+               { 'li' => 'Bright copper kettles', 'li/class' => 'odd' },
+               { 'li' => 'Warm, woolen mittens',  'li/class' => 'even' }
+       ]
+});
+print doc.to_s
diff --git a/ruby/attribute2.rb b/ruby/attribute2.rb
new file mode 100644 (file)
index 0000000..b2df184
--- /dev/null
@@ -0,0 +1,13 @@
+#! /usr/bin/ruby
+require "xmltemplate"
+
+doc = XMLTemplate.process_file("../xml/clone.xml", {
+       'color' => 'blue',
+       '#things' => XMLTemplate.alternate("li/class", [
+               { 'li' => 'Raindrops on roses' },
+               { 'li' => 'Whiskers on kittens' },
+               { 'li' => 'Bright copper kettles' },
+               { 'li' => 'Warm, woolen mittens' }
+       ], "odd", "even")
+});
+print doc.to_s
diff --git a/ruby/attribute3.rb b/ruby/attribute3.rb
new file mode 100644 (file)
index 0000000..70c94d8
--- /dev/null
@@ -0,0 +1,14 @@
+#! /usr/bin/ruby
+require "xmltemplate"
+
+doc = XMLTemplate.process_file("../xml/clone.xml", {
+       'color' => 'blue',
+       '#things' => XMLTemplate.alternate("li/class", [
+               { 'li' => 'Raindrops on roses' },
+               { 'li' => 'Whiskers on kittens' },
+               nil,
+               { 'li' => 'Bright copper kettles' },
+               { 'li' => 'Warm, woolen mittens' }
+       ], "odd", "even")
+});
+print doc.to_s
diff --git a/ruby/clone.rb b/ruby/clone.rb
new file mode 100644 (file)
index 0000000..9a86e8f
--- /dev/null
@@ -0,0 +1,13 @@
+#! /usr/bin/ruby
+require "xmltemplate"
+
+doc = XMLTemplate.process_file("../xml/clone.xml", {
+       'color' => 'blue',
+       '#things' => [
+               { 'li' => 'Raindrops on roses' },
+               { 'li' => 'Whiskers on kittens' },
+               { 'li' => 'Bright copper kettles' },
+               { 'li' => 'Warm, woolen mittens'} 
+       ]
+});
+print doc.to_s
diff --git a/ruby/include.rb b/ruby/include.rb
new file mode 100644 (file)
index 0000000..ce391e4
--- /dev/null
@@ -0,0 +1,12 @@
+#! /usr/bin/ruby
+require "xmltemplate"
+
+doc = XMLTemplate.process_file("../xml/included.xml", {
+       "color" => "red"
+}, false)
+master = XMLTemplate.process_file("../xml/master.xml", {
+       "title" => "Main HTML title",
+       "h1" => "Nice heading here",
+       "contents" => doc
+});
+print master.to_s
diff --git a/ruby/namespace.rb b/ruby/namespace.rb
new file mode 100644 (file)
index 0000000..6dddb1d
--- /dev/null
@@ -0,0 +1,11 @@
+#! /usr/bin/ruby
+require "xmltemplate"
+
+doc = XMLTemplate.process_file("../xml/namespace.xml", {
+       'title' => 'Namespace tests',
+       '#hello' => 'Hello world!',
+       '#test' => 'Replaced.',
+       'tagname' => 'foo',
+       '#moretest' => 'bar'
+})
+print doc.to_s
diff --git a/ruby/namespace2.rb b/ruby/namespace2.rb
new file mode 100644 (file)
index 0000000..be8eb6b
--- /dev/null
@@ -0,0 +1,8 @@
+#! /usr/bin/ruby
+require "xmltemplate"
+
+doc = XMLTemplate.process_file("../xml/namespace2.xml", {
+       'title' => 'Namespace tests',
+       '#hello' => 'Replaced.'
+})
+print doc.to_s
diff --git a/ruby/passthru.rb b/ruby/passthru.rb
new file mode 100644 (file)
index 0000000..e769555
--- /dev/null
@@ -0,0 +1,5 @@
+#! /usr/bin/ruby
+require "xmltemplate"
+
+doc = XMLTemplate.process_file("../xml/passthru.xml", {})
+print doc.to_s
diff --git a/ruby/simple.rb b/ruby/simple.rb
new file mode 100644 (file)
index 0000000..3b68b1c
--- /dev/null
@@ -0,0 +1,8 @@
+#! /usr/bin/ruby
+require "xmltemplate"
+
+doc = XMLTemplate.process_file("../xml/simple.xml", {
+       'title' => 'A very basic example',
+       '#hello' => 'Hello world!'
+})
+print doc.to_s
diff --git a/ruby/xmltemplate.rb b/ruby/xmltemplate.rb
new file mode 100644 (file)
index 0000000..3f4e11a
--- /dev/null
@@ -0,0 +1,128 @@
+require "rexml/document"
+
+module XMLTemplate
+       def XMLTemplate.process_file(filename, obj, clean = true) 
+               file = File.new(filename)
+               doc = REXML::Document.new file
+               XMLTemplate.process!(doc, obj, clean)
+               return doc
+       end
+
+       def XMLTemplate.process!(node, obj, clean = true)
+               if obj.is_a?(String)                    # overwrite
+                       node.children.delete_if { true }
+                       node.text = obj
+               elsif obj.is_a?(REXML::Element)         # overwrite
+                       if obj.is_a?(REXML::Document)
+                               obj = obj.root
+                       end
+               
+                       node.children.delete_if { true }
+                       
+                       newobj = obj.deep_clone
+                       node.add(newobj)
+
+                       process!(newobj, {}, clean)
+               elsif obj.is_a?(Hash) and node.is_a?(REXML::Element)                    # substitute
+                       node.children.each do |child|
+                               processed = false
+                               attributes_to_delete = []
+
+                               if child.is_a?(REXML::Element)
+                                       if child.namespace == "http://template.sesse.net/"
+                                               id = child.local_name
+                                       else
+                                               # workaround for .to_a being braindamaged
+                                               attr_list = []
+                                               child.attributes.each_attribute { |a| attr_list.push(a) }
+                                               id_attr = attr_list.find { |a| 
+                                                       a.namespace == "http://template.sesse.net/" and a.local_name == "id"
+                                               }
+                                               if id_attr.nil?
+                                                       id = nil
+                                               else
+                                                       id = id_attr.value
+                                               end     
+                                       end
+
+                                       if clean
+                                               child.attributes.each_attribute do |a|
+                                                       if a.namespace == "http://template.sesse.net/" then
+                                                               attributes_to_delete.push(a)
+                                                       elsif a.prefix == "xmlns" and a.value == "http://template.sesse.net/" then
+                                                               attributes_to_delete.push(a)
+                                                       end
+                                               end
+                                       end
+
+                                       # check all substitutions to see if we fuond anything appropriate
+                                       obj.keys.each do |key|
+                                               if (Regexp.new("^" + Regexp.escape(child.local_name) + "/") =~ key) or
+                                                   (not id.nil? and Regexp.new("^#" + Regexp.escape(id) + "/") =~ key)
+                                                       child.attributes[key.split("/")[1]] = obj[key]
+                                               end
+                               
+                                               if not processed
+                                                       if key == child.local_name or (not id.nil? and key == "#" + id)
+                                                               process!(child, obj[key], clean)
+                                                               processed = true
+                                                       end
+                                               end
+                                       end
+                               end
+
+                               if not processed
+                                       process!(child, obj, clean)
+                               end
+
+                               #
+                               # We need to do this _after_ processing all the sub-parts,
+                               # since if we remove xmlns: attributes before processing,
+                               # REXML loses track of the namespaces of the child elements
+                               # (since the prefix declaration is no longer there).
+                               #
+                               attributes_to_delete.each { |a| child.attributes.delete(a) }
+                       end
+               elsif obj.is_a?(Array)                          # repeat
+                       doc = node.document
+                       frag = REXML::Element.new
+
+                       node.children.each { |e| frag.add(e) }
+                       node.children.delete_if { true }
+                       
+                       obj.find_all { |x| not x.nil? } .each do |instance|
+                               newnode = frag.deep_clone
+                               node.add(newnode)
+                               process!(newnode, instance, clean)
+                       end
+               end
+
+               if clean then
+                       clean!(node)
+               end
+       end
+
+       def XMLTemplate.alternate(tag, array, *elems)
+               i = 0
+               array_copy = array.clone
+               
+               array_copy.each do |ref|
+                       if not ref.nil?
+                               ref[tag] = elems[i % elems.length]
+                               i = i + 1
+                       end
+               end
+
+               return array_copy
+       end
+       
+       def XMLTemplate.clean!(node)
+               if node.is_a?(REXML::Element) and (node.namespace == "http://template.sesse.net/" or node.name == "UNDEFINED")
+                       # as this is a dummy node, we want to remove it and move everything further up
+                       # after we've done any required replacements
+                       node.children.each { |n| node.parent.insert_before(node, n) }
+                       node.parent.delete(node)
+               end
+       end
+end
+
diff --git a/tests/reference/namespace.xml b/tests/reference/namespace.xml
new file mode 100644 (file)
index 0000000..c93b559
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+  <head>
+    <title>Namespace tests</title>
+  </head>
+  <body>
+    <p>Hello world!</p>
+    <p id="hello">This will not.</p>
+    <p id="test">Replaced.</p>
+    foo
+  </body>
+</html>
diff --git a/tests/reference/namespace2.xml b/tests/reference/namespace2.xml
new file mode 100644 (file)
index 0000000..1bf4200
--- /dev/null
@@ -0,0 +1,10 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+  <head>
+    <title>Namespace tests</title>
+  </head>
+  <body>
+    <p id="test">Replaced.</p>
+    <p id="hello">This will not.</p>
+  </body>
+</html>
index 24a105b616c491ee0cae8ccaf64d64e405d93a11..b66ed3812645e2d07d9fba3b6b64aec990ea568b 100755 (executable)
@@ -1,10 +1,10 @@
 #! /bin/sh
 
 if [ -z "$TESTS" ]; then
-       TESTS="passthru simple clone include attribute attribute2 attribute3 attribute-empty"
+       TESTS="passthru simple clone include attribute attribute2 attribute3 attribute-empty namespace namespace2"
 fi
 if [ -z "$LANGUAGES" ]; then
-       LANGUAGES="perl perl-sax php python"
+       LANGUAGES="perl perl-sax php python ruby"
 fi
 
 for L in $LANGUAGES; do
@@ -24,6 +24,9 @@ for L in $LANGUAGES; do
                if [ "$L" = "python" ]; then
                        python ../python/$T.py > $TEMPFILE
                fi
+               if [ "$L" = "ruby" ]; then
+                       ruby -I../ruby ../ruby/$T.rb > $TEMPFILE
+               fi
 
                perl ./xml-diff.pl $TEMPFILE reference/$T.xml
                if [ $? = 0 ]; then     
diff --git a/xml/namespace.xml b/xml/namespace.xml
new file mode 100644 (file)
index 0000000..e81948b
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<!DOCTYPE
+  html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:t="http://template.sesse.net/" xml:lang="en">
+  <head>
+    <title />
+  </head>
+  <body>
+    <p t:id="hello">This will be replaced.</p>
+    <p id="hello">This will not.</p>
+    <p id="test" t:id="test">This will also be replaced, but the id will stay.</p>
+    <t:tagname id="moretest">This will say "foo", not "bar".</t:tagname>
+  </body>
+</html>
diff --git a/xml/namespace2.xml b/xml/namespace2.xml
new file mode 100644 (file)
index 0000000..cf7790c
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<!DOCTYPE
+  html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:t="http://template.sesse.net/" xml:lang="en">
+  <head>
+    <title />
+  </head>
+  <body>
+    <p t:id="hello" id="test">This will be replaced.</p>
+    <p t:id="test" id="hello">This will not.</p>
+  </body>
+</html>