When you run FTL templates, you have a (possibly empty) set of
variables that you have created with assign
and
macro
directives, as can be seen from the previous chapter. A set of variables
like this is called a namespace. In
simple cases you use only one namespace, the so-called main namespace. You don't realize this, since
normally you use only this namespace.
But if you want to build reusable collection of macros, functions and other variables -- usually referred as library by lingo -- the usage of multiple namespaces becomes inevitable. Just consider if you have a big collection of macros, that you use in several projects, or even you want to share it with other people. It becomes impossible to be sure that the library does not have a macro (or other variable) with the same name as the name of a variable in the data-model, or with the same name as a the name of a variable in another library used in the template. In general, variables can clobber each other because of the name clashes. So you should use a separate namespace for the variables of each library.
Creating a library
Let's create a simple library. Assume you commonly need the
variables copyright
and mail
(before you ask, macros are variables):
<#macro copyright date> <p>Copyright (C) ${date} Julia Smith. All rights reserved.</p> </#macro> <#assign mail = "jsmith@acme.com">
Store the above in the file lib/my_test.ftl
(in the directory where you store the templates). Assume you want to
use this in aWebPage.ftl
. If you use
<#include "/lib/my_test.ftl">
in the
aWebPage.ftl
, then it will create the two
variables in the main namespace, and it is not good now, since you
want them to be in a namespace that is used exclusively by the ``My
Test Library''. Instead of include
you have to
use import
directive. This directive is, at the first glance, similar to
include
, but it will create an empty namespace
for lib/my_test.ftl
and will execute that there.
lib/my_test.ftl
will find itself in an clean new
world, where only the variables of data-model are present (since
they are visible from everywhere), and will create the two variables
in this new world. That's fine for now, but you want to access the
two variables from aWebPage.ftl
, and that uses
the main namespace, so it can't see the variables of the other
namespace. The solution is that the import
directive not only creates the new namespace, but a new hash
variable in the namespace used by the caller of
import
(the main namespace in this case), that
will act as a gate into the newly created namespace. So this is how
aWebPage.ftl
will look like:
<#import "/lib/my_test.ftl" as my> <#-- the hash called "my" will be the "gate" --> <@my.copyright date="1999-2002"/> ${my.mail}
Note how it accesses the variables in the namespace created
for /lib/my_test.ftl
using the newly created
namespace accessing hash, my
. This will
print:
<p>Copyright (C) 1999-2002 Julia Smith. All rights reserved.</p> jsmith@acme.com
If you would have a variable called mail
or
copyright
in the main namespace, that would not
cause any confusion, since the two templates use separated
namespaces. For example, modify the copyright
macro in lib/my_test.ftl
to this:
<#macro copyright date> <p>Copyright (C) ${date} Julia Smith. All rights reserved. <br>Email: ${mail}</p> </#macro>
and then replace aWebPage.ftl
with
this:
<#import "/lib/my_test.ftl" as my> <#assign mail="fred@acme.com"> <@my.copyright date="1999-2002"/> ${my.mail} ${mail}
and the output will be this:
<p>Copyright (C) 1999-2002 Julia Smith. All rights reserved. <br>Email: jsmith@acme.com</p> jsmith@acme.com fred@acme.com
This is like that because when you have called the
copyright
macro, FreeMarker has temporarily
switch to the namespace that was created by the
import
directive for
/lib/my_test.ftl
. Thus, the
copyright
macro always sees the
mail
variable that exists there, and not the
other mail
that exists in the main
namespace.
Writing the variables of imported namespaces
Occasionally you may want to create or replace a variable in
an imported namespace. You can do this with the
assign
directive, if you use its
namespace
parameter. For example, this:
<#import "/lib/my_test.ftl" as my> ${my.mail} <#assign mail="jsmith@other.com" in my> ${my.mail}
will output this:
jsmith@acme.com jsmith@other.com
Namespaces and data-model
The variables of the data-model are visible from everywhere.
For example, if you have a variable called user
in the data-model, lib/my_test.ftl
will access
that, exactly as aWebPage.ftl
does:
<#macro copyright date> <p>Copyright (C) ${date} ${user}. All rights reserved.</p> </#macro> <#assign mail = "${user}@acme.com">
If user
is ``Fred'', then the usual
example:
<#import "/lib/my_test.ftl" as my> <@my.copyright date="1999-2002"/> ${my.mail}
will print this:
<p>Copyright (C) 1999-2002 Fred. All rights reserved.</p> Fred@acme.com
Don't forget that the variables in the namespace (the
variables you create with assign
or
macro
directives) have precedence over the
variables of the data-model when you are in that namespace. Thus,
the contents of data-model does not interfere with the variables
created by the library.
In some unusual applications you want to create variables in
the template those are visible from all namespaces, exactly like
the variables of the data-model. But you can't change the
data-model with templates. Still, it is possible to achieve
similar result with the global
directive; read
the reference for more
details.
The life-cycle of namespaces
A namespace is identified by the path that was used with the
import
directive. If you try to
import
with the same path for multiple times, it
will create the namespace and run the template specified by the path
for the very first invocation of import
only. The
later import
s with the same path will just create
a ``gate'' hash to the same namespace. For example, let this be the
aWebPage.ftl
:
<#import "/lib/my_test.ftl" as my> <#import "/lib/my_test.ftl" as foo> <#import "/lib/my_test.ftl" as bar> ${my.mail}, ${foo.mail}, ${bar.mail} <#assign mail="jsmith@other.com" in my> ${my.mail}, ${foo.mail}, ${bar.mail}
The output will be:
jsmith@acme.com, jsmith@acme.com, jsmith@acme.com jsmith@other.com, jsmith@other.com, jsmith@other.com
since you see the same namespace through
my
, foo
and
bar
.
Note that namespaces are not hierarchical, they exist
independently of each other. That is, if you
import
namespace N2 while you are in name space
N1, N2 will not be inside N1. N1 just gets a hash by which it can
access N2. This is the same N2 namespace that you would access if,
say, you import
N2 when you are in the main
namespace.
Each template
processing job has its own private set of namespaces. Each
template-processing job is a separated cosmos that exists only for
the short period of time while the given page is rendered, and then
it vanishes with all its populated namespaces. Thus, whenever we say
that ``import
is called for the first time'' and
such, we are always talking in the context of a single template
processing job.
Writing libraries for other people
If you have written a good quality library that can be useful for other people, you may want to make it available on the Internet (like on http://freemarker.org/libraries.html). To prevent clashes with the names of libraries used by other authors, and to make it easy to write libraries that import other published libraries, there is a de-facto standard that specifies the format of library paths. The standard is that the library must be available (importable) for templates and other libraries with a path like this:
/lib/yourcompany.com/your_library.ftl
For example if you work for Example Inc. that owns the www.example.com homepage, and you develop a widget library, then the path of the FTL file to import should be:
/lib/example.com/widget.ftl
Note that the www is omitted. The part after the 3rd slash can contain subdirectories such as:
/lib/example.com/commons/string.ftl
An important rule is that the path should not contain
upper-case letters. To separate words, use _
, as
in wml_form
(not
wmlForm
).
Note that if you do not develop the library for a company or
organization, you should use the URL of the project homepage, such
as /lib/example.sourceforge.net/example.ftl
, or
/lib/geocities.com/jsmith/example.ftl
.