This is an automated email from the ASF dual-hosted git repository.
ddekany pushed a commit to branch 2.3-gae
in repository https://gitbox.apache.org/repos/asf/freemarker.git
The following commit(s) were added to refs/heads/2.3-gae by this push:
new ca68d7d Manual: Finished and improved ?spread_args and .args
documentation
ca68d7d is described below
commit ca68d7d94d7db5ae14b9ba92e21cd0645ebf9ab2
Author: ddekany <[email protected]>
AuthorDate: Sat Oct 26 20:30:47 2019 +0200
Manual: Finished and improved ?spread_args and .args documentation
---
src/manual/en_US/book.xml | 222 +++++++++++++++++----
.../java/freemarker/manual/SpreadArgsExamples.java | 35 ++++
...adArgsExamples-usingWithArgsSpecialVariable.ftl | 11 +
...gsExamples-usingWithArgsSpecialVariable.ftl.out | 4 +
4 files changed, 228 insertions(+), 44 deletions(-)
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 6c03186..f712b44 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -20,7 +20,10 @@
<book conformance="docgen" version="5.0" xml:lang="en"
xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
->
+ xmlns:ns5="http://www.w3.org/2000/svg"
+ xmlns:ns4="http://www.w3.org/1998/Math/MathML"
+ xmlns:ns3="http://www.w3.org/1999/xhtml"
+ xmlns:ns="http://docbook.org/ns/docbook">
<info>
<title>Apache FreeMarker Manual</title>
@@ -19136,13 +19139,14 @@ Filer for positives:
<para>This built-in is available since 2.3.30</para>
</note>
- <para>The goal of this built in is to add parameters dynamically to
- the call of a directive (like a macro), function or method.
- Dynamically means that parameters are added based on a hash value
- (like <literal>{'a': 1, 'b': 2, 'c': 3}</literal>), or a sequence
- value (like <literal>[1, 2, 3]</literal> or a Java
+ <para>The goal of this built-in is to add parameters
+ <emphasis>dynamically</emphasis> to the call of a directive (like a
+ macro), function or method. Dynamically means that parameters are
+ added based on a hash value (like <literal>{'a': 1, 'b': 2, 'c':
+ 3}</literal> or a Java <literal>Map</literal>), or a sequence value
+ (like <literal>[1, 2, 3]</literal> or a Java
<literal>List</literal>), whose the actual content is might only
- known when the call happens.</para>
+ known at the moment when the call happens.</para>
<para>For example, we have this macro <literal>m</literal>:</para>
@@ -19152,14 +19156,14 @@ Filer for positives:
<programlisting role="template"><@m a=1 b=2 c=3
/></programlisting>
- <para>This calls does the same, assuming <literal>dynArgs</literal>
+ <para>Below call does the same, assuming <literal>dynArgs</literal>
is the hash <literal>{'a': 1, 'b': 2, 'c': 3}</literal>:</para>
<programlisting role="template"><@m?spread_args(dynArgs)
/></programlisting>
<programlisting role="output">a=1, b=1, c=1</programlisting>
- <para>This call also does the same, but combines dynamic arguments
+ <para>Below call also does the same, but combines dynamic arguments
from <literal>dynArgsAB</literal>, assumed to be <literal>{'a': 1,
'b': 2}</literal>, and argument <literal>c</literal> specified
directly:</para>
@@ -19168,33 +19172,147 @@ Filer for positives:
<programlisting role="output">a=1, b=1, c=1</programlisting>
- <para>The way if works is this.
+ <para>To understand why this works, you need to realize that macros,
+ custom directives, functions, and methods in FreeMarker are just
+ values, just like numbers, strings, etc. <literal><#macro m
+ <replaceable>...</replaceable>></literal> just creates a value
+ that's a macro (as opposed to a number, or string, etc.), and then
+ assigns it to variable <literal>m</literal>. Thus,
+ <literal>m</literal> in itself is a valid expression, which
+ evaluates to the macro (but it doesn't <emphasis>call</emphasis> the
+ macro). <literal><@m <replaceable>...</replaceable>
+ /></literal> evaluates the expression <literal>m</literal> (and
+ you can use arbitrarily complex expressions there too, like
+ <literal>m?spread_args(<replaceable>...</replaceable>)</literal>),
+ and then <emphasis>calls</emphasis> the resulting macro.
<literal>m?spread_args(<replaceable>dynArgs</replaceable>)</literal>
- returns a macro (or what <literal>m</literal> is, like function
- etc.) whose arguments defaults to the values specified in
- <literal><replaceable>dynArgs</replaceable></literal>. For
- example:</para>
+ returns a macro that's very similar to the original macro (that's
+ stored in <literal>m</literal>), but its arguments default to the
+ values specified in
+ <literal><replaceable>dynArgs</replaceable></literal>. So the result
+ of <literal>m?spread_args({'b': 22, 'c': 33})</literal> is similar
+ to a modified macro that was created as <literal><#macro
+ <replaceable>unspefiedName</replaceable> a b=22 c=33></literal>.
+ With an example:</para>
<programlisting role="template"><#assign mWithDefs =
m?spread_args({'b': 22, 'c': 33})>
<@myWithDefs a=1 c='overridden'/></programlisting>
<programlisting role="output">a=1, b=22,
c=overridden</programlisting>
- <para>So far we have only shown the case where the argument to
- <literal>spread_args</literal> was a hash, which is supported for
- all kind of directives, including macros. When this built-in is
- applied on a function or method, the argument to
- <literal>spread_args</literal> must be a sequence, as named
- parameters aren't supported for those:</para>
+ <para>Above we have created a new macro based on the value of
+ <literal>m</literal>, stored it in variable
+ <literal>mWithDefs</literal>, and then later we called it with
+ <literal><@myWithDefs <replaceable>...</replaceable>
+ /></literal>.</para>
+
+ <para><literal>spread_args</literal> can also be applied on
+ functions (crated with <literal><#function
+ <replaceable>...</replaceable>></literal>) and Java methods
+ (usually get from the data-model, like
+ <literal>myObject.myMethod</literal>). But because functions and
+ methods can only be called with positional arguments (like
+ <literal>f(1, 2, 3)</literal>, and <emphasis>not</emphasis> as
+ <literal>f(a=1, b=2, c=3)</literal>), the argument to
+ <literal>spread_args</literal> must be a sequence instead of a hash.
+ Other than that, the same tricks work as with macros:</para>
<programlisting role="template"><#function f(a, b,
c)><#return "a=${a}, b=${b}, c=${c}"></#function>
<#assign dynArgs=[1, 2, 3]>
-${f(1, 2, 3)()}
+${f(1, 2, 3)}
+Same as:
+${f?spread_args(dynArgs)()}
+or as:
+${f?spread_args([1, 2])(3)}
+or as:
+${f?spread_args([1])(2, 3)}
+
+<#assign fWithOneAsFirstArg = f?spread_args([1])>
+${fWithOneAsFirstArg(2, 3)} <#-- same as f(1, 2, 3) --></programlisting>
+
+ <para>Note the double application of
+ <literal>(<replaceable>...</replaceable>)</literal> above, like in
+
<literal>f?spread_args(<replaceable>dynArgs</replaceable>)()</literal>.
+ That's because
+ <literal>f?spread_args(<replaceable>dynArgs</replaceable>)</literal>
+ just returns a new function (which is just a value), but doesn't
+ call it. So if you want to call that new function immediately (as
+ opposed to assigning it to a variable for example), you need the
+ second <literal>()</literal>.</para>
+
+ <para>Because macro calls support both named and positional
+ arguments, the <literal>spread_args</literal> argument can be a
+ sequence for macros as well (though using a hash is usually a better
+ practice):</para>
+
+ <programlisting role="template"><#macro m a b c>a=${a},
b=${b}, c=${c}</#macro>
+
+<#-- Called with named parameters: -->
+<@m a=1 b=2 c=3 />
+Same as:
+<#-- Called with positional parameters: -->
+<@m 1 2 3 />
+Same as:
+<@m?spread_args([1, 2, 3]) />
Same as:
-${f?spread_args(dynArgs)()}</programlisting>
+<#-- Sequence spread_args with positional c parameter: -->
+<@m?spread_args([1, 2]) 3 />
+Same as:
+<#-- Sequence spread_args with named c parameter: -->
+<@m?spread_args([1, 2]) c=3 /></programlisting>
+
+ <para>To summarize, depending on the type of the value
+ <literal>spread_args</literal> is applied on, the type of argument
+ to <literal>spread_args</literal> can be:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>Function or method: sequence. Note that WRONG
+ <literal>f?spread_args(1, 2)</literal> is WRONG, the correct
+ form is <literal>f?spread_args([1, 2])</literal>.</para>
+ </listitem>
+
+ <listitem>
+ <para>Macro: hash or sequence</para>
+ </listitem>
- <para>[TODO] More details and edge cases...</para>
+ <listitem>
+ <para>Directive (user defined): hash</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>The return type of <literal>spread_args</literal> is the same
+ as the type of value it was applied on, like if it's applied on a
+ method (like
+ <literal>myObj.myMethod?spread_args(dynArgs)</literal>), then it
+ returns a method.</para>
+
+ <para>Note that it's not possible to apply
+ <literal>spread_args</literal> on built-in directives, like
+ <literal><#if <replaceable>...</replaceable>></literal>,
+ <literal><#list <replaceable>...</replaceable>></literal>,
+ etc., because they aren't available as values.</para>
+
+ <para>This built-in is often used together with the <link
+ linkend="specvar.args"><literal>.args</literal> special
+ variable</link>. For example:</para>
+
+ <programlisting role="template"><#macro m1 a b c>
+ m1 does things with ${a}, ${b}, ${c}
+</#macro>
+
+<#macro m2 a b c>
+ m2 does things with ${a}, ${b}, ${c}
+ Delegate to m1:
+ <@m1?spread_args(.args) />
+</#macro>
+
+<@m2 a=1 b=2 c=3 /></programlisting>
+
+ <programlisting role="output"> m2 does things with 1, 2, 3
+ Delegate to m1:
+ m1 does things with 1, 2, 3</programlisting>
</section>
<section xml:id="ref_builtin_eval">
@@ -24565,7 +24683,7 @@ There was no specific handler for node y
<para>The supported special variables are:</para>
<itemizedlist spacing="compact">
- <listitem>
+ <listitem xml:id="specvar.args">
<para><indexterm>
<primary>args</primary>
</indexterm><literal>args</literal> (since 2.3.30): Used inside
@@ -24624,13 +24742,19 @@ There was no specific handler for node y
and not with positional arguments (like <literal><@m 1 2
/></literal>). That's because for macros
<literal>.args</literal> is always a hash, but if the arguments
- are positional, the catch-all arguments won't have names.</para>
+ are positional, the catch-all arguments won't have names, so
+ it's impossible to put them into the hash.</para>
</listitem>
<listitem>
- <para><literal>.args</literal> is initialized when the macro or
- function is called, and so it doesn't reflect changes made later
- on the local variables that correspond to the arguments.</para>
+ <para><literal>.args</literal> is initialized immediately when
+ the macro or function is called, and so it doesn't reflect
+ changes made later on the local variables that correspond to the
+ arguments. (However, if the argument value is a mutable object,
+ and the objects itself is mutated, <literal>.args</literal> will
+ contain the mutated object. Setting an argument variable with
+ the <link linkend="ref.directive.local"><literal>local</literal>
+ directive</link> doesn't mutate the object.)</para>
</listitem>
<listitem>
@@ -24640,8 +24764,10 @@ There was no specific handler for node y
parsing error. So you can't use it in a template fragment that
you insert dynamically into a macro or function (like via the
<link linkend="ref.directive.include"><literal>include</literal>
- directive</link> or <link
+ directive</link>, <link
linkend="ref_builtin_eval"><literal>eval</literal>
+ built-in</link>, or <link
+ linkend="ref_builtin_interpret"><literal>interpret</literal>
built-in</link>). That's because the <literal>macro</literal> or
<literal>function</literal> has to know if it contains
<literal>.args</literal> earlier than it's called.</para>
@@ -24649,11 +24775,14 @@ There was no specific handler for node y
<listitem>
<para>For macros, the order of elements in the
- <literal>.args</literal> hash corresponds to the order as the
- parameters were declared in the <literal>macro</literal>
- directive, not to the order the caller has used. Except,
- catch-all arguments will use the order that the caller
- used.</para>
+ <literal>.args</literal> hash is the same as the order in which
+ the parameters were declared in the <literal>macro</literal>
+ directive, and not the order the caller has used. Except,
+ catch-all arguments will use the order that the caller used.
+ (For functions and methods, as it only support positional
+ arguments, the parameter order of on the caller side is the same
+ as the parameter order in the <literal>function</literal>
+ directive or Java method, so there's no doubt there.)</para>
</listitem>
</itemizedlist>
</listitem>
@@ -28840,7 +28969,9 @@ TemplateModel x = env.getVariable("x"); // get
variable x</programlisting>
<itemizedlist>
<listitem>
- <para>Added
+ <para><link
+
xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-107">FREEMARKER-107</link>:
+ Added
<literal>?<replaceable>spread_args</replaceable>(dynamicArguments)</literal>
to add parameters dynamically to directive (like macro),
function and method calls. Actually, this built-in returns
@@ -28850,19 +28981,22 @@ TemplateModel x = env.getVariable("x"); // get
variable x</programlisting>
</listitem>
<listitem>
- <para>Added new <link linkend="ref_specvar">special
- variable</link>, <literal>.args</literal>. This evaluates to a
- hash (in macros), or sequence (in functions) that contains all
- the arguments. This is useful for operations that act on all the
- arguments uniformly, like for example to pass the arguments to
-
<literal>?spread_args(<replaceable>...</replaceable>)</literal>.</para>
+ <para><link
+
xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-107">FREEMARKER-107</link>:
+ Added new special variable, <link
+ linkend="specvar.args"><literal>.args</literal></link>. This
+ evaluates to a hash in macros, and to a sequence in functions,
+ which contains all the arguments. This is useful for operations
+ that act on all the arguments uniformly, like for example to
+ pass the arguments to <link
+
linkend="ref_builtin_spread_args"><literal>?spread_args(<replaceable>...</replaceable>)</literal></link>.</para>
</listitem>
<listitem>
<para><link linkend="ref.directive.macro">Macro catch-all
- parameters</link> (aka. macro varargs), when capture arguments
- passed by name (as opposed to by position), now keep the
- original order of arguments. Earlier the order wasn't
+ parameters</link> (aka. varargs parameters), when capture
+ arguments passed by name (as opposed to by position), now keep
+ the original order of arguments. Earlier the order wasn't
predictable.</para>
</listitem>
diff --git a/src/test/java/freemarker/manual/SpreadArgsExamples.java
b/src/test/java/freemarker/manual/SpreadArgsExamples.java
new file mode 100644
index 0000000..95ad3cd
--- /dev/null
+++ b/src/test/java/freemarker/manual/SpreadArgsExamples.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package freemarker.manual;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import freemarker.template.TemplateException;
+
+public class SpreadArgsExamples extends ExamplesTest {
+
+ @Test
+ public void usingWithArgsSpecialVariable() throws IOException,
TemplateException {
+
assertOutputForNamed("SpreadArgsExamples-usingWithArgsSpecialVariable.ftl");
+ }
+
+}
diff --git
a/src/test/resources/freemarker/manual/SpreadArgsExamples-usingWithArgsSpecialVariable.ftl
b/src/test/resources/freemarker/manual/SpreadArgsExamples-usingWithArgsSpecialVariable.ftl
new file mode 100644
index 0000000..ceb3941
--- /dev/null
+++
b/src/test/resources/freemarker/manual/SpreadArgsExamples-usingWithArgsSpecialVariable.ftl
@@ -0,0 +1,11 @@
+<#macro m1 a b c>
+ m1 does things with ${a}, ${b}, ${c}
+</#macro>
+
+<#macro m2 a b c>
+ m2 does things with ${a}, ${b}, ${c}
+ Delegate to m1:
+ <@m1?spread_args(.args) />
+</#macro>
+
+<@m2 a=1 b=2 c=3 />
diff --git
a/src/test/resources/freemarker/manual/SpreadArgsExamples-usingWithArgsSpecialVariable.ftl.out
b/src/test/resources/freemarker/manual/SpreadArgsExamples-usingWithArgsSpecialVariable.ftl.out
new file mode 100644
index 0000000..54488c3
--- /dev/null
+++
b/src/test/resources/freemarker/manual/SpreadArgsExamples-usingWithArgsSpecialVariable.ftl.out
@@ -0,0 +1,4 @@
+
+ m2 does things with 1, 2, 3
+ Delegate to m1:
+ m1 does things with 1, 2, 3