Namespaces

Page Contents

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 = "[email protected]">  

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>
[email protected]  

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="[email protected]">
<@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: [email protected]</p>
[email protected]
[email protected]  

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="[email protected]" in my>
${my.mail}  

will output this:

[email protected]
[email protected]  

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>
[email protected]  

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.

Note

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 imports 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="[email protected]" in my>
${my.mail}, ${foo.mail}, ${bar.mail}  

The output will be:

[email protected], [email protected], [email protected]
[email protected], [email protected], [email protected]  

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.

FreeMarker Manual -- For FreeMarker 2.3.22
HTML generated: 2015-02-28 21:34:03 GMT
Edited with XMLMind XML Editor
Here!