Over .get_optional_template(name[, options]), (https://freemarker.apache.org/builds/fm2.3.28/ref_specvar.html#ref_specvar_get_optional_template) I have also added:
- .caller_template_name (https://issues.apache.org/jira/browse/FREEMARKER-83) https://freemarker.apache.org/builds/fm2.3.28/ref_specvar.html#ref_specvar_caller_template_name - ?absolute_template_name (you will soon see the connection...) https://freemarker.apache.org/builds/fm2.3.28/ref_builtins_expert.html#ref_builtin_absolute_template_name In https://freemarker.apache.org/builds/fm2.3.28/ref_builtins_expert.html#ref_builtin_absolute_template_name there's an example where all three are used together: <#-- <@smileyInclude name /> behaves like <#include name>, but prints a "(:" before the template, or prints "):" instead if the template is missing. Note that just like with #include, if name is relative, it's resolved based on the directory of the caller template, not of the template that defines this macro. As .get_optional_template resolves relative names based on the current template, we had to convert the name to an absolute name based on the caller template before passing it to it. --> <#macro smileyInclude name> <#local t = .get_optional_template( name?absolute_template_name(.caller_template_name))> <#if t.exists> (: <@t.include /> <#else> ): </#if> </#macro> Any opinions? Wednesday, February 28, 2018, 8:57:06 PM, Daniel Dekany wrote: > .get_optional_template(name[, options]) now implemented in the 2.3-gae > and 2.3 branches. Testing/feedback is welcome! > > See commit: > https://github.com/apache/incubator-freemarker/commit/51c2476621809d8f4183f23e894be0106cabe810 > > You can find some examples in the tests: > https://github.com/apache/incubator-freemarker/blob/2.3-gae/src/test/java/freemarker/core/GetOptionalTemplateTest.java > > > Sunday, February 11, 2018, 10:02:30 PM, Daniel Dekany wrote: > >> See the RFE here: >> https://issues.apache.org/jira/browse/FREEMARKER-84 >> >> As you see, the first consensus was introducing `.last_include_found`, >> but it has two problems: >> >> * Sometimes it happens that if, and only if the template exists then >> you want to do (usually print) something *before* it. Problem is, by >> the time you know that from `.last_include_found`, it's too late, as >> the template was already processed. >> >> * Like many global state variables in general, this can lead to some >> confusing edge cases and hard-to-follow code. Like, if `#include` >> throws an exception, which is then handled by the user with >> `#attempt`/`#recover`, then `.last_include_found` may or may not be >> updated, as perhaps we haven't yet reached the point where it can be >> told if the template exists. (Consider an expression evaluation >> error in the `#include` parameter, or an I/O error due to which we >> can't access the template directory). Also there are some public >> `include` methods in the `Environment` API, but they can't set this >> variable, as they return a `Template`, and the variable must be set >> after the `Template` was processed, unless the template was missing. >> (If you can't figure out why it must be done that way, that proves >> again how tricky this is... think about includes inside includes.) >> >> So, I propose the solution below. Maybe somewhat difficult to grasp >> first, but it meant to be used rarely, and mostly by "experts"... >> Let's hope SO and examples in the Manual will help people though. (-; >> >> Introduce a new special variable (see >> https://freemarker.apache.org/docs/ref_specvar.html) called >> "get_optional_template", which is a TemplateMethodModelEx with these >> parameters: >> >> 1. template name (maybe a relative path, resolved as #include/#import >> does it) 2. A hash that can have the following keys: "parse", >> "encoding" (similarly to >> https://freemarker.apache.org/docs/ref_directive_include.html#ref.directive.include). >> >> Example method call (the `.` prefix is the special variable reference >> syntax): >> >> <#assign t = .get_optional_template("foo.ftl", { 'encoding': 'utf-8' })> >> >> The method returns a hash (`t` above) that contains the following keys: >> >> - "include": directive (or missing); `<@t.include />` has similar >> effect as `<#include "foo.ftl">` >> >> - "import": method (or missing); returns a namespace. `<#assign f = >> t.import()>` has similar effect as `<#import 'foo.ftl' as f>` >> >> - "exists": boolean; returns if the template was found. >> >> The method call loads the target template eagerly, i.e., it won't wait >> until `t.include`, `t.exist` etc. is actually used. >> >> Note that the hash is returned even if the template wasn't found (but >> then it won't contain "include" and "import", and "exists" will be >> `false`). If some other error occurs, like an I/O error other than a >> "template not found", or the template has invalid syntax, it will >> throw exception (just like #include). >> >> Use cases: >> >> - `#include` with fallback templates or fallback macro (note how we >> can use the `exp!defaultExp` operator): >> >> <@.get_optional_template('foo.ftl') >> !.get_optional_template('bar.ftl').include >> !defaultMacro /> >> >> - Doing something before `#include` if the template exists: >> >> <#assign t = .get_optional_template('foo.ftl')> >> <#if t.exists> >> Do before existing template >> <@t.include /> >> </#if> >> >> Opinions? >> > -- Thanks, Daniel Dekany