Some more opinions guys? Especially as we got one opinion against the feature.
Tuesday, February 13, 2018, 9:59:41 AM, Daniel Dekany wrote: > Tuesday, February 13, 2018, 9:28:18 AM, Jacopo Cappellato wrote: > >> For less common use cases like this my preference is to defer the >> implementation to the template developer rather than adding complexity to >> the language. >> If I understand the use case that originated this request, something >> similar could be achieved with a simple trick like the following: >> 1) the calling code would be: >> <#include "possibly-missing-template.ftl" ignore_missing=true> >> <#if !processed??> >> The template was not found or processed! >> </#if> >> 2) somewhere in possibly-missing-template.ftl (i.e. at the bottom of it) we >> add an assign directive like: >> <#assign processed=true> >> >> There are some cons to this approach (the most relevant one is that the >> referenced template has to contain the #assign directive) but FM users >> could live with this approach and in the meantime we could try to get their >> feedback to better understand how much this requirement is desired to >> justify a change to the codebase. > > The need for optional includes is something that was brought up for > several times during the years. It's mostly needed for some custom > fallback logic as far as I can tell. (While there's #include > ignore_missing=true for a while, it doesn't let you to take some extra > action depending on if the template is missing.) > > As of it's important enough to add a new feature of this weight (which > low, as it's just yet another special variable, no new directive or > syntax): It's a template language, and in that context > including/importing other templates is probably an important enough > topic to warrant some extras. > >> Jacopo >> >> On Sun, Feb 11, 2018 at 10:02 PM, Daniel Dekany <[email protected]> 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 >>> >>> > -- Thanks, Daniel Dekany
