Simple Example
----------------
Assume that the following XML document is a target document we generate.
Foo
22
Bar
We first prepare the following template 'text' and create a template object
'template'.
text = <
EOF
require 'xtemplate'
template = XTemplate::XMLTemplate.new(text)
We then expand the template with the following data.
data = {
"members" => [
{"@uid" => "&001", "name" => "Foo", "age" => 22},
{"@uid" => "&002", "name" => "Bar"},
]
}
doc = template.expand(data)
puts(doc.to_s)
Each '@uid' represents an attribute. All strings/objects in given data are
automatically sanitized. 'doc' is a result of expanding the template.
The following document is generated.
Foo
22
Bar
If you would like to pass sanitized strings/objects to xtemplate, use
XTemplate::SanitizedString (See samples/sample1.rb).
We can successively output strings using Template#expand2 as follows:
template2 = XTemplate::XMLTemplate.new(text2)
template2.expand2($stdout,data)
The template object dumps generated strings using a method '<<'.
Also, a given data is modified by an expansion. If you don't want
to modify it, give options like this:
template2.expand2($stdout,data, :keep_data => true)
Namespace of XTemplate
-----------------------
If you define a namespace with the URI "http://xtemplate.sourceforge.net/xtemplate",
the following tags are available.
- ,
-
-
-
-
-
-
-
-
When you would not like to eliminate " " in .. , use
the following template.
text = <
EOF
'' is replaced with the indicated data. '' is
equivalent to '', where XTemplate::BIND_URI is
'http://xtemplate.sourceforge.net/xtemplate'.
Explicit Loop
--------------
The following two templates explicitly use 'each' to generate multiple
'member' elements by converting a specified Enumerable object to an
Array object using 'to_a' method.
text = <
EOF
text = <
EOF
Attribute Substitution
----------------------
Attribute values are replaced with specified data by using special form '@{...}'
such as . Consider the following expansion data.
data = {
"members" => [
{"uid" => "&001", "name" => "Foo", "age" => 22},
{"uid" => "&002", "name" => "Bar"},
]
}
If we give the following template, we can obtain the same result as the foregoing.
text = <
EOF
XTemplate's XPath
------------------
We can specify a path to data in each attribute 'id' as follows.
text2 = <
EOF
template2 = XTemplate::XMLTemplate.new(text2)
doc2 = template2.expand(data)
print(doc2)
We get the following document as the result of the above.
Foo Bar
The path format is very similar to W3C's XPath, however our XPath is not
exactly same as W3C's XPath (See samples/xpath.rb) and roughly implemented.
There is a special mechanism in XTemplate's XPath which is called 'action'.
For example, we prepare the following template.
We first choose only members who are older than 20 by [age>=20], which
syntax is similar to W3C XPath. '{'...'}' is an action in which we can reconstruct
collected data. 'copy(uid,@uid)' rename the key 'uid' with '@uid', and sort members
with a value of each 'age'. We then give the following data,
{'members' => [
{'name' => 'name1', age => 22, 'uid' => 1},
{'name' => 'name2', age => 18, 'uid' => 2},
{'name' => 'name3', age => 30, 'uid' => 3},
{'name' => 'name4', age => 23, 'uid' => 4},
]}
and get the following result.
name1
22
name4
23
name3
30
In the same mechanism, we can store and restore the collected data
using an internal stack. Some of such actions are 'push(stackname)'
and 'pop(stackname)'. The life time of an internal stack is same as
one of the current thread. So we can pass collected data across tags.
Altanative Attribute
---------------------
You can also altanatively choice a sub-template using the 'alt' attribute.
Dot notations and Property tags
-------------------------------
See sample40.rb and sample41.rb. :-)
Using XMLDocument
------------------
We can give an object instantiated from XTemplate::XMLDocument as expansion
data, since the class has a method 'to_hash'. The conversion mechanism
is as follows, though this is an experimental specification.
* All elements are converted into a Hash data which consists of
single element.
EX: ... => {'tag' => ...}
* Children elements are converted into an Array data which consists of
Hash data of single element.
EX: 1 ...N
=> {'tag' => [{'tag1' => '1'}, ..., {'tagN' => 'N'}]}
* All attributes are regarded as children of an elements having the attributes.
EX: ...
=> {'tag' => [{'@attr1' => '1', ..., '@attrN' => '2'}]}
So 1 ...N is normally transformed
to {'tag' => [{'tag1' => '1'}, ..., {'tagN' => 'N'}]}. However, in an
expansion process, the array object is automatically transformed to
something like {'tag1' => '1', ..., 'tagN' => 'N'}, if there is no
duplicated tag.
Replacing/Expanding Expansion Data
-----------------------------------
XTemplate can replace an expansion data with the result of expansion by
using the tag 'expand' as follows.
( )
In this example, ... replaces the value of
{'member' => value} with the result of expanding itself. If we give the following
data,
data = {
'members' => [
{'member' => {'name' => 'name1', '@uid' => 1, 'email' => 'name1@com'}},
{'member' => {'name' => 'name2', '@uid' => 2, 'email' => 'name2@com'}},
{'member' => {'name' => 'name3', '@uid' => 3, 'email' => 'name3@com'}},
]
}
a value of each {'member' => ...} is replaced as follows.
{'member' => '(1)name1 '}
{'member' => '(2)name2 '}
{'member' => '(3)name3 '}
So the result of expansion is as follows.
(1)name1
(2)name2
(3)name3
Replacing Sub-template
-----------------------
We can directly access a sub-template which is enclosed by
and by using a key specified by the "name" attribute. For example,
We replace a sub-template with a copy of another sub-template with the following
script.
t1 = XTemplate::XMLTemplate.new(<
EOF
t2 = XTemplate::XMLTemplate.new(<
EOF
print(t1.expand(data, :keep_data => true),"\n")
t1['ul'] = t2['ol']
print(t1.expand(data),"\n")
The following XML document is an output.
name1 name2 name3
For Debugging
-------------
If the result of expanded template is not what you want, please use the
action 'p()' in XPath for inspecting an expansion data. If using 'p(stderr)',
the expansion data is printed to $stderr.
Speedup Tips
-------------
* XTemplate::XMLTemplate#dump(), XTemplate::XMLTemplate.load(str)
XMLTemplate#dump() serializes the object and returns a string object.
XMLTemplate.load(str) is a singleton method for loading a serialized
object and returns a XMLTemplate object. However, this method is not
so effective with respect to expanding a template.
* XTemplate::use_simple_xpath()
This function speeds up evaluation of XPath expression. However we can't
use actions and conditions after calling this function.
* XTemplate::use_simple_expand()
More speedup! However we can't use any XPath expression after calling this
function.
* Install 'xtemplate_ext'
'xtemplate_ext' is an extension module in which some functions are
implemented in C. If you have an environment to make the extension,
I recommend you to run 'install.rb config' with '--with-ext' option.
XTemplate's XPath Syntax
------------------------
xpath := apath
| rpath
| xpath '|' xpath
| xpath '?' xpath
apath := '/' rpath
rpath := node opts '/' rpath
| node opts
node := '@' attr # attribute
| data-id # data ID
| '*' # all nodes
| '**' # all nodes and children
| '@*' # all attributes
|
opts := opt opts
|
opt := '{' action '}' # data reconstruction
| '[' filter ']' # filter
action := action;action
| node
| p()
| p(stderr)
| sort()
| sort(key)
| reverse()
| node()
| text()
| attr(a,b,...)
| unattr(a,b,...)
| copy(a,b)
| rename(a,b)
| delete(a,b,...)
| tag(a)
| untag(a)
| index(a)
| index(a,i)
| size()
| push(a)
| pop(a)
| pop_all(a)
| clear_stack(a)
| int()
| float()
| array()
| dump()
| dump(tag)
| time(fmt)
| time(fmt,tag)
| mktime(fmt)
| sanitize()
| unsanitize()
| import(xml://file)
| import(yaml://file)
| import(data://file)
| import(var:xxx)
| import(dbi:xxx, query)
| import(soap://..., namespace, method, arg0,...)
| import(xmlrpc://..., method, arg0,...)
|
filter := cond # select nodes which satisfies the condition.
| beg ',' n # apply the method [](beg,n)
| beg '..' end # apply the method [](beg..end)
| pos # apply the method [](pos)
cond := 'not' cond
| cond 'and' cond
| cond 'or' cond
| node # true if there is a node 'node'.
| expr op expr
| 'int' '(' expr ')'
| 'float' '(' expr ')'
expr := node | int | string
op := '<' | '>' | '<=' | '>=' | '=' | '!=' | '=~' | '!~'