Widened README to 100 columns.
Not sure why I had it at 80 before, though maybe I'm about to find out when I push and the formatting is all hosed.
This commit is contained in:
@@ -1,56 +1,49 @@
|
||||
This is a Java implementation of the [Mustache template
|
||||
language](http://mustache.github.com/). There exists [another Java
|
||||
implementation of Mustache](http://github.com/spullara/mustache.java), but the
|
||||
motivations for this version are sufficiently different as to justify (in the
|
||||
author's mind, anyhow) the duplication.
|
||||
This is a Java implementation of the [Mustache template language](http://mustache.github.com/).
|
||||
There exists [another Java implementation of Mustache](http://github.com/spullara/mustache.java),
|
||||
but the motivations for this version are sufficiently different as to justify (in the author's
|
||||
mind, anyhow) the duplication.
|
||||
|
||||
Motivations
|
||||
===========
|
||||
|
||||
* Zero dependencies. You can include this single tiny library in your project
|
||||
and start making use of templates.
|
||||
* Usability on a variety of target platforms. The other Java Mustache
|
||||
implementation requires that a Java compiler be available to compile
|
||||
templates into Java classes. This implementation makes no such requirements
|
||||
and as a result is usable on Android, or other exciting places where a Java
|
||||
compiler is not available. It is even possible to avoid the use of
|
||||
reflection and provide all of your data as a series of nested Maps, if
|
||||
desired.
|
||||
* Zero dependencies. You can include this single tiny library in your project and start making
|
||||
use of templates.
|
||||
* Usability on a variety of target platforms. The other Java Mustache implementation requires
|
||||
that a Java compiler be available to compile templates into Java classes. This implementation
|
||||
makes no such requirements and as a result is usable on Android, or other exciting places where
|
||||
a Java compiler is not available. It is even possible to avoid the use of reflection and
|
||||
provide all of your data as a series of nested Maps, if desired.
|
||||
|
||||
* [Proguard](http://proguard.sourceforge.net/) and
|
||||
[JarJar](http://code.google.com/p/jarjar/) friendly. Though the library
|
||||
will reflectively access your data (if you desire it), the library makes no
|
||||
other internal use of reflection or by name instantiation of classes. So
|
||||
you can embed it using Proguard or JarJar without any annoying surprises.
|
||||
* [Proguard](http://proguard.sourceforge.net/) and [JarJar](http://code.google.com/p/jarjar/)
|
||||
friendly. Though the library will reflectively access your data (if you desire it), the library
|
||||
makes no other internal use of reflection or by name instantiation of classes. So you can embed
|
||||
it using Proguard or JarJar without any annoying surprises.
|
||||
|
||||
* Minimal API footprint. There are really only two methods you need to know
|
||||
about: `compile` and `execute`. You can even chain them together in cases
|
||||
where performance is of no consequence.
|
||||
* Minimal API footprint. There are really only two methods you need to know about: `compile` and
|
||||
`execute`. You can even chain them together in cases where performance is of no consequence.
|
||||
|
||||
Its existence justified by the above motivations, this implementation then
|
||||
strives to provide additional benefits:
|
||||
Its existence justified by the above motivations, this implementation then strives to provide
|
||||
additional benefits:
|
||||
|
||||
* It is available via Maven Central, see below for details.
|
||||
* It is reasonably performant. Templates are parsed separately from
|
||||
execution. A template will specialize its variables on (class of context,
|
||||
name) pairs so that if a variable is first resolved to be (for example) a
|
||||
field of the context object, that will be attempted directly on subsequent
|
||||
template invocations, and the slower full resolution will only be tried if
|
||||
accessing the variable as a field fails.
|
||||
* It is reasonably performant. Templates are parsed separately from execution. A template will
|
||||
specialize its variables on (class of context, name) pairs so that if a variable is first
|
||||
resolved to be (for example) a field of the context object, that will be attempted directly on
|
||||
subsequent template invocations, and the slower full resolution will only be tried if accessing
|
||||
the variable as a field fails.
|
||||
|
||||
Get It
|
||||
======
|
||||
|
||||
JMustache is available via Maven Central and can thus be easily added to your
|
||||
Maven, Ivy, etc. projects by adding a dependency on
|
||||
`com.samskivert:jmustache:1.9`. Or download the [pre-built jar
|
||||
file](http://repo1.maven.org/maven2/com/samskivert/jmustache/1.9/jmustache-1.9.jar).
|
||||
JMustache is available via Maven Central and can thus be easily added to your Maven, Ivy, etc.
|
||||
projects by adding a dependency on `com.samskivert:jmustache:1.9`. Or download the
|
||||
[pre-built jar file](http://repo1.maven.org/maven2/com/samskivert/jmustache/1.9/jmustache-1.9.jar).
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Using JMustache is very simple. Supply your template as a `String` or a
|
||||
`Reader` and get back a `Template` that you can execute on any context:
|
||||
Using JMustache is very simple. Supply your template as a `String` or a `Reader` and get back a
|
||||
`Template` that you can execute on any context:
|
||||
|
||||
String text = "One, two, {{three}}. Three sir!";
|
||||
Template tmpl = Mustache.compiler().compile(text);
|
||||
@@ -65,8 +58,8 @@ Use `Reader` and `Writer` if you're doing something more serious:
|
||||
Mustache.compiler().compile(template).execute(data, out);
|
||||
}
|
||||
|
||||
The execution context can be any Java object. Variables will be resolved via
|
||||
the following mechanisms:
|
||||
The execution context can be any Java object. Variables will be resolved via the following
|
||||
mechanisms:
|
||||
|
||||
* If the context is a `Map`, `Map.get` will be used.
|
||||
* If a non-void method with the same name as the variable exists, it will be called.
|
||||
@@ -96,23 +89,25 @@ Example:
|
||||
// Elvis: 75
|
||||
// Madonna: 52
|
||||
|
||||
As you can see from the example, the fields (and methods) need not be public.
|
||||
The `persons` field in the anonymous class created to act as a context is
|
||||
accessible. Note that the use of non-public fields will not work in a sandboxed
|
||||
security environment.
|
||||
As you can see from the example, the fields (and methods) need not be public. The `persons` field
|
||||
in the anonymous class created to act as a context is accessible. Note that the use of non-public
|
||||
fields will not work in a sandboxed security environment.
|
||||
|
||||
Sections behave as you would expect:
|
||||
|
||||
* `Boolean` values enable or disable the section.
|
||||
* Array, `Iterator`, or `Iterable` values repeatedly execute the section with each element used as the context for each iteration. Empty collections result in zero instances of the section being included in the template.
|
||||
* An unresolvable or null value is treated as false (by default, see _Default Values_ for more details).
|
||||
* Array, `Iterator`, or `Iterable` values repeatedly execute the section with each element used as
|
||||
the context for each iteration. Empty collections result in zero instances of the section being
|
||||
included in the template.
|
||||
* An unresolvable or null value is treated as false (by default, see _Default Values_ for more
|
||||
details).
|
||||
* Any other object results in a single execution of the section with that object as a context.
|
||||
|
||||
See the code in
|
||||
[MustacheTest.java](http://github.com/samskivert/jmustache/blob/master/src/test/java/com/samskivert/mustache/MustacheTest.java)
|
||||
for concrete examples. See also the [Mustache
|
||||
documentation](http://mustache.github.com/mustache.5.html) for details on the
|
||||
template syntax.
|
||||
for concrete examples. See also the
|
||||
[Mustache documentation](http://mustache.github.com/mustache.5.html) for details on the template
|
||||
syntax.
|
||||
|
||||
Partials
|
||||
--------
|
||||
@@ -129,16 +124,14 @@ If you wish to make use of partials (e.g. `{{>subtmpl}}`) you must provide a
|
||||
String tmpl = "...{{>subtmpl}}...";
|
||||
c.compile(tmpl).execute();
|
||||
|
||||
The above snippet will load `new File(templateDir, "subtmpl")` when compiling
|
||||
the template.
|
||||
The above snippet will load `new File(templateDir, "subtmpl")` when compiling the template.
|
||||
|
||||
Lambdas
|
||||
-------
|
||||
|
||||
JMustache implements lambdas by passing you a `Template.Fragment` instance
|
||||
which you can use to execute the fragment of the template that was passed to
|
||||
the lambda. You can decorate the results of the fragment execution, as shown in
|
||||
the standard Mustache documentation on lambdas:
|
||||
JMustache implements lambdas by passing you a `Template.Fragment` instance which you can use to
|
||||
execute the fragment of the template that was passed to the lambda. You can decorate the results of
|
||||
the fragment execution, as shown in the standard Mustache documentation on lambdas:
|
||||
|
||||
String tmpl = "{{#bold}}{{name}} is awesome.{{/bold}}";
|
||||
Mustache.compiler().compile(tmpl).execute(new Object() {
|
||||
@@ -154,8 +147,8 @@ the standard Mustache documentation on lambdas:
|
||||
// result:
|
||||
<b>Willy is awesome.</b>
|
||||
|
||||
You can also obtain the results of the fragment execution to do things like
|
||||
internationalization or caching:
|
||||
You can also obtain the results of the fragment execution to do things like internationalization or
|
||||
caching:
|
||||
|
||||
Object ctx = new Object() {
|
||||
Mustache.Lambda i18n = new Mustache.Lambda() {
|
||||
@@ -170,16 +163,16 @@ internationalization or caching:
|
||||
<h2>{{#i18n}}title{{/i18n}</h2>
|
||||
{{#i18n}}welcome_msg{{/i18n}}
|
||||
|
||||
Currently there is no support for "unexecuting" the template and obtaining the
|
||||
original Mustache template text contained in the section. File a feature
|
||||
request with a sane use case if you have one.
|
||||
Currently there is no support for "unexecuting" the template and obtaining the original Mustache
|
||||
template text contained in the section. File a feature request with a sane use case if you have
|
||||
one.
|
||||
|
||||
Default Values
|
||||
--------------
|
||||
|
||||
By default, an exception will be thrown any time a variable cannot be resolved,
|
||||
or resolves to null. You can change this behavior in two ways. If you want to
|
||||
provide a value for use in all such circumstances, use `defaultValue()`:
|
||||
By default, an exception will be thrown any time a variable cannot be resolved, or resolves to
|
||||
null. You can change this behavior in two ways. If you want to provide a value for use in all such
|
||||
circumstances, use `defaultValue()`:
|
||||
|
||||
String tmpl = "{{exists}} {{nullValued}} {{doesNotExist}}?";
|
||||
Mustache.compiler().defaultValue("what").compile(tmpl).execute(new Object() {
|
||||
@@ -190,9 +183,8 @@ provide a value for use in all such circumstances, use `defaultValue()`:
|
||||
// result:
|
||||
Say what what?
|
||||
|
||||
If you only wish to provide a default value for variables that resolve to null,
|
||||
and wish to preserve exceptions in cases where variables cannot be resolved,
|
||||
use `nullValue()`:
|
||||
If you only wish to provide a default value for variables that resolve to null, and wish to
|
||||
preserve exceptions in cases where variables cannot be resolved, use `nullValue()`:
|
||||
|
||||
String tmpl = "{{exists}} {{nullValued}} {{doesNotExist}}?";
|
||||
Mustache.compiler().nullValue("what").compile(tmpl).execute(new Object() {
|
||||
@@ -202,9 +194,9 @@ use `nullValue()`:
|
||||
});
|
||||
// throws MustacheException when executing the template because doesNotExist cannot be resolved
|
||||
|
||||
When using a `Map` as a context, `nullValue()` will only be used when the map
|
||||
contains a mapping to `null`. If the map lacks a mapping for a given variable,
|
||||
then it is considered unresolvable and throws an exception.
|
||||
When using a `Map` as a context, `nullValue()` will only be used when the map contains a mapping to
|
||||
`null`. If the map lacks a mapping for a given variable, then it is considered unresolvable and
|
||||
throws an exception.
|
||||
|
||||
Map<String,String> map = new HashMap<String,String>();
|
||||
map.put("exists", "Say");
|
||||
@@ -214,20 +206,18 @@ then it is considered unresolvable and throws an exception.
|
||||
Mustache.compiler().nullValue("what").compile(tmpl).execute(map);
|
||||
// throws MustacheException when executing the template because doesNotExist cannot be resolved
|
||||
|
||||
Note that section behavior deviates from the above specification (for
|
||||
historical reasons and because it's kind of useful). By default, a section that
|
||||
is not resolvable or resolves to null will be omitted (and conversely, an
|
||||
inverse section that is not resolvable or resolves to null will be included).
|
||||
If you use `defaultValue()`, this behavior is preserved. If you use
|
||||
`nullValue()`, sections that refer to an unresolvable variable will now throw
|
||||
an exception (sections that refer to a resolvable, but null-valued variable,
|
||||
will behave as before).
|
||||
Note that section behavior deviates from the above specification (for historical reasons and
|
||||
because it's kind of useful). By default, a section that is not resolvable or resolves to null will
|
||||
be omitted (and conversely, an inverse section that is not resolvable or resolves to null will be
|
||||
included). If you use `defaultValue()`, this behavior is preserved. If you use `nullValue()`,
|
||||
sections that refer to an unresolvable variable will now throw an exception (sections that refer to
|
||||
a resolvable, but null-valued variable, will behave as before).
|
||||
|
||||
Extensions
|
||||
==========
|
||||
|
||||
JMustache extends the basic Mustache template language with some additional
|
||||
functionality. These additional features are enumerated below:
|
||||
JMustache extends the basic Mustache template language with some additional functionality. These
|
||||
additional features are enumerated below:
|
||||
|
||||
Not escaping HTML by default
|
||||
----------------------------
|
||||
@@ -280,9 +270,8 @@ Special variables
|
||||
-----------------
|
||||
|
||||
### this
|
||||
You can use the special variable `this` to refer to the context object itself
|
||||
instead of one of its members. This is particularly useful when iterating over
|
||||
lists.
|
||||
You can use the special variable `this` to refer to the context object itself instead of one of its
|
||||
members. This is particularly useful when iterating over lists.
|
||||
|
||||
Mustache.compiler().compile("{{this}}").execute("hello"); // returns: hello
|
||||
Mustache.compiler().compile("{{#names}}{{this}}{/names}}").execute(new Object() {
|
||||
@@ -298,19 +287,17 @@ Note that you can also use the special variable `.` to mean the same thing.
|
||||
});
|
||||
// result: TomDickHarry
|
||||
|
||||
`.` is apparently supported by other Mustache implementations, though it does
|
||||
not appear in the official documentation.
|
||||
`.` is apparently supported by other Mustache implementations, though it does not appear in the
|
||||
official documentation.
|
||||
|
||||
### -first and -last
|
||||
You can use the special variables `-first` and `-last` to perform special
|
||||
processing for list elements. `-first` resolves to `true` when inside a section
|
||||
that is processing the first of a list of elements. It resolves to `false` at
|
||||
all other times. `-last` resolves to `true` when inside a section that is
|
||||
processing the last of a list of elements. It resolves to `false` at all other
|
||||
You can use the special variables `-first` and `-last` to perform special processing for list
|
||||
elements. `-first` resolves to `true` when inside a section that is processing the first of a list
|
||||
of elements. It resolves to `false` at all other times. `-last` resolves to `true` when inside a
|
||||
section that is processing the last of a list of elements. It resolves to `false` at all other
|
||||
times.
|
||||
|
||||
One will often make use of these special variables in an inverted section, as
|
||||
follows:
|
||||
One will often make use of these special variables in an inverted section, as follows:
|
||||
|
||||
String tmpl = "{{#things}}{{^-first}}, {{/-first}}{{this}}{{/things}}";
|
||||
Mustache.compiler().compile(tmpl).execute(new Object() {
|
||||
@@ -318,16 +305,14 @@ follows:
|
||||
});
|
||||
// result: one, two, three
|
||||
|
||||
Note that the values of `-first` and `-last` refer only to the inner-most
|
||||
enclosing section. If you are processing a section within a section, there is
|
||||
no way to find out whether you are in the first or last iteration of an outer
|
||||
section.
|
||||
Note that the values of `-first` and `-last` refer only to the inner-most enclosing section. If you
|
||||
are processing a section within a section, there is no way to find out whether you are in the first
|
||||
or last iteration of an outer section.
|
||||
|
||||
### -index
|
||||
The `-index` special variable contains 1 for the first iteration through a
|
||||
section, 2 for the second, 3 for the third and so forth. It contains 0 at all
|
||||
other times. Note that it also contains 0 for a section that is populated by a
|
||||
singleton value rather than a list.
|
||||
The `-index` special variable contains 1 for the first iteration through a section, 2 for the
|
||||
second, 3 for the third and so forth. It contains 0 at all other times. Note that it also contains
|
||||
0 for a section that is populated by a singleton value rather than a list.
|
||||
|
||||
String tmpl = "My favorite things:\n{{#things}}{{-index}}. {{this}}\n{{/things}}";
|
||||
Mustache.compiler().compile(tmpl).execute(new Object() {
|
||||
@@ -342,9 +327,8 @@ singleton value rather than a list.
|
||||
Compound variables
|
||||
------------------
|
||||
|
||||
In addition to resolving simple variables using the context, you can use
|
||||
compound variables to extract data from sub-objects of the current context. For
|
||||
example:
|
||||
In addition to resolving simple variables using the context, you can use compound variables to
|
||||
extract data from sub-objects of the current context. For example:
|
||||
|
||||
Mustache.compiler().compile("Hello {{field.who}}!").execute(new Object() {
|
||||
public Object field = new Object() {
|
||||
@@ -358,22 +342,21 @@ By taking advantage of reflection and bean-property-style lookups, you can do ko
|
||||
Mustache.compiler().compile("Hello {{class.name}}!").execute(new Object());
|
||||
// result: Hello java.lang.Object!
|
||||
|
||||
Note that compound variables are essentially short-hand for using singleton
|
||||
sections. The above examples could also be represented as:
|
||||
Note that compound variables are essentially short-hand for using singleton sections. The above
|
||||
examples could also be represented as:
|
||||
|
||||
Hello {{#field}}{{who}}{{/field}}!
|
||||
Hello {{#class}}{{name}}{{/class}}!
|
||||
|
||||
Note also that one semantic difference exists between nested singleton sections
|
||||
and compound variables: after resolving the object for the first component of
|
||||
the compound variable, parent contexts will not be searched when resolving
|
||||
subcomponents.
|
||||
Note also that one semantic difference exists between nested singleton sections and compound
|
||||
variables: after resolving the object for the first component of the compound variable, parent
|
||||
contexts will not be searched when resolving subcomponents.
|
||||
|
||||
Newline trimming
|
||||
----------------
|
||||
|
||||
If the opening or closing section tag are the only thing on a line, any newline
|
||||
following the tag is trimmed. This allows for civilized templates, like:
|
||||
If the opening or closing section tag are the only thing on a line, any newline following the tag
|
||||
is trimmed. This allows for civilized templates, like:
|
||||
|
||||
Favorite foods:
|
||||
{{#people}}
|
||||
@@ -389,21 +372,20 @@ which produces output like:
|
||||
rather than:
|
||||
|
||||
Favorite foods:
|
||||
|
||||
|
||||
- Elvis Presley likes peanut butter.
|
||||
|
||||
|
||||
- Mahatma Gandhi likes aloo dum.
|
||||
|
||||
|
||||
|
||||
which would be produced without the newline trimming. Note: the current
|
||||
implementation does not handle Windows-style CRLF data. If you're a Windows
|
||||
user, how about sending me a patch?
|
||||
which would be produced without the newline trimming. Note: the current implementation does not
|
||||
handle Windows-style CRLF data. If you're a Windows user, how about sending me a patch?
|
||||
|
||||
Nested Contexts
|
||||
---------------
|
||||
|
||||
If a variable is not found in a nested context, it is resolved in the next
|
||||
outer context. This allows usage like the following:
|
||||
If a variable is not found in a nested context, it is resolved in the next outer context. This
|
||||
allows usage like the following:
|
||||
|
||||
String template = "{{outer}}:\n{{#inner}}{{outer}}.{{this}}\n{{/inner}}";
|
||||
Mustache.compiler().compile(template).execute(new Object() {
|
||||
@@ -416,17 +398,15 @@ outer context. This allows usage like the following:
|
||||
// foo.baz
|
||||
// foo.bif
|
||||
|
||||
Note that if a variable _is_ defined in an inner context, it shadows the same
|
||||
name in the outer context. There is presently no way to access the variable
|
||||
from the outer context.
|
||||
Note that if a variable _is_ defined in an inner context, it shadows the same name in the outer
|
||||
context. There is presently no way to access the variable from the outer context.
|
||||
|
||||
Invertible Lambdas
|
||||
------------------
|
||||
|
||||
For some applications, it may be useful for lambdas to be executed for an
|
||||
inverse section rather than having the section omitted altogether. This allows
|
||||
for proper conditional substitution when statically translating templates into
|
||||
other languages or contexts:
|
||||
For some applications, it may be useful for lambdas to be executed for an inverse section rather
|
||||
than having the section omitted altogether. This allows for proper conditional substitution when
|
||||
statically translating templates into other languages or contexts:
|
||||
|
||||
String template = "{{#condition}}result if true{{/condition}}\n" +
|
||||
"{{^condition}}result if false{{/condition}}";
|
||||
@@ -460,9 +440,8 @@ InvertibleLambda whenever you need a single function with two modes of operation
|
||||
Standards Mode
|
||||
--------------
|
||||
|
||||
The more intrusive of these extensions, specifically the searching of parent
|
||||
contexts and the use of compound varables, can be disabled when creating a
|
||||
compiler, like so:
|
||||
The more intrusive of these extensions, specifically the searching of parent contexts and the use
|
||||
of compound varables, can be disabled when creating a compiler, like so:
|
||||
|
||||
Map<String,String> ctx = new HashMap<String,String>();
|
||||
ctx.put("foo.bar", "baz");
|
||||
@@ -472,7 +451,7 @@ compiler, like so:
|
||||
Limitations
|
||||
===========
|
||||
|
||||
In the name of simplicity, some features of Mustache were omitted or
|
||||
simplified:
|
||||
In the name of simplicity, some features of Mustache were omitted or simplified:
|
||||
|
||||
* `{{= =}}` only supports one or two character delimiters. This is just because I'm lazy and it simplifies the parser.
|
||||
* `{{= =}}` only supports one or two character delimiters. This is just because I'm lazy and it
|
||||
simplifies the parser.
|
||||
|
||||
Reference in New Issue
Block a user