You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@groovy.apache.org by Per Nyfelt <pe...@alipsa.se> on 2022/07/23 17:59:59 UTC

Defining functions in text processed by the StreamingTemplateEngine

Hi,

I am trying to use the StreamingTemplateEngine to create dynamic 
markdown (similarly to the rmd (r markdown, 
https://rmarkdown.rstudio.com/authoring_quick_tour.html) but I've 
encountered a snag:

Simple usage such as the following works fine using the 
StreamingTemplateEngine:

def text = """\ <% def now = java.time.LocalDate.now() %> # Hello Today 
(<%= now.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, 
java.util.Locale.getDefault()) %>) is <%= java.time.LocalDate.now() %>. 
The weather in next 3 days will be: <% def weather = [ "Sunny", "Rainy", 
"Cloudy", "Windy" ] for (i = 1; i < 4; i++) { def day = now.plusDays(i) 
def dayName = 
day.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, 
java.util.Locale.getDefault()) Collections.shuffle weather out.println 
"- " + dayName + ": " + weather.first() } %> """.stripIndent() def engine = new StreamingTemplateEngine() def template = 
engine.createTemplate(text) println template.make()

But if if want to define a function to simplify the code e.g.

def text = """\ <% def now = java.time.LocalDate.now() def 
dayName(java.time.LocalDate theDate) { return 
theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, 
java.util.Locale.getDefault()) } %> # Hello Today (<%= dayName(now) %>) 
is <%= now %>. The weather in next 3 days will be: <% def weather = [ 
"Sunny", "Rainy", "Cloudy", "Windy" ] for (i = 1; i < 4; i++) { def day 
= now.plusDays(i) Collections.shuffle weather out.println "- " + 
dayName(day) + ": " + weather.first() } %> """.stripIndent() def engine = new StreamingTemplateEngine() def template = 
engine.createTemplate(text) println template.make()

...the StreamingTemplateEngine does not accept the function definition:


Template parse error 'Unexpected input: '(' ' at line 4, column 12
          3:
      --> 4: def dayName(java.time.LocalDate theDate) {
          5:     return 
theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, 
java.util.Locale.getDefault())

groovy.text.TemplateParseException: Template parse error 'Unexpected 
input: '(' ' at line 4, column 12
          3:
      --> 4: def dayName(java.time.LocalDate theDate) {
          5:     return 
theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, 
java.util.Locale.getDefault())


If there a way to define function in the text processed that would work 
with the StreamingTemplateEngine?


The code works fine "the other way around" i.e from Groovy code:

def now = java.time.LocalDate.now()

def dayName(java.time.LocalDate theDate) {
     return 
theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, 
java.util.Locale.getDefault())
}

println "# Hello"

println "Today ${dayName(now)}) is ${now}."

println "The weather in next 3 days will be:"

def weather = [ "Sunny", "Rainy", "Cloudy", "Windy" ]
for (i = 1; i < 4; i++) {
   def day = now.plusDays(i)
   Collections.shuffle weather
   println "- " + dayName(day) + ": " + weather.first()
}

But I would like a "Markdown first" kind of experience rather than a 
"Groovy first" since in the "normal" case, the amount of text vastly 
outnumbers the amount of code.


Best regards,

Per


Re: Defining functions in text processed by the StreamingTemplateEngine

Posted by Per Nyfelt <pe...@alipsa.se>.
Even better! Thank you!


On 7/23/22 23:55, Paul King wrote:
> The SimpleTemplateEngine supports methods (and imports and even local 
> class definitions):
>
> def text ="""\ <% import java.time.LocalDate import java.time.format.TextStyle 
> def now = LocalDate.now() def dayName(theDate) { 
> theDate.dayOfWeek.getDisplayName(TextStyle.FULL, Locale.default) } 
> class Const { static days = 3 } %> # Hello Today (<%= dayName(now) %>) 
> is <%= now %>. The weather in next <%= Const.days %> days will be: <% 
> def weather = [ "Sunny", "Rainy", "Cloudy", "Windy" ] Const.days.times 
> { out.println "- " + dayName(now + it) + ": " + weather.shuffled()[0] 
> } %> """.stripIndent()
>
> def engine =new groovy.text.SimpleTemplateEngine()
> def template =engine.createTemplate(text)
> printlntemplate.make()
> Cheers, Paul.
>
>
> On Sun, Jul 24, 2022 at 6:35 AM Per Nyfelt <pe...@alipsa.se> wrote:
>
>     I figured it out. although functions (methods) cannot be defined,
>     a closure works fine:
>
>     def text = """\ <% def now = java.time.LocalDate.now() def dayName
>     = { theDate -> return
>     theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL,
>     java.util.Locale.getDefault()) } %> # Hello Today (<%=
>     dayName(now) %>) is <%= now %>. The weather in next 3 days will
>     be: <% def weather = [ "Sunny", "Rainy", "Cloudy", "Windy" ] for
>     (i = 1; i < 4; i++) { def day = now.plusDays(i)
>     Collections.shuffle weather out.println "- " + dayName(day) + ": "
>     + weather.first() } %> """.stripIndent() def engine = new StreamingTemplateEngine() def
>     template = engine.createTemplate(text) println template.make()
>
>     On 7/23/22 19:59, Per Nyfelt wrote:
>>
>>     Hi,
>>
>>     I am trying to use the StreamingTemplateEngine to create dynamic
>>     markdown (similarly to the rmd (r markdown,
>>     https://rmarkdown.rstudio.com/authoring_quick_tour.html) but I've
>>     encountered a snag:
>>
>>     Simple usage such as the following works fine using the
>>     StreamingTemplateEngine:
>>
>>     def text = """\ <% def now = java.time.LocalDate.now() %> # Hello
>>     Today (<%=
>>     now.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL,
>>     java.util.Locale.getDefault()) %>) is <%=
>>     java.time.LocalDate.now() %>. The weather in next 3 days will be:
>>     <% def weather = [ "Sunny", "Rainy", "Cloudy", "Windy" ] for (i =
>>     1; i < 4; i++) { def day = now.plusDays(i) def dayName =
>>     day.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL,
>>     java.util.Locale.getDefault()) Collections.shuffle weather
>>     out.println "- " + dayName + ": " + weather.first() } %> """.stripIndent() def engine = new StreamingTemplateEngine() def
>>     template = engine.createTemplate(text) println template.make()
>>
>>     But if if want to define a function to simplify the code e.g.
>>
>>     def text = """\ <% def now = java.time.LocalDate.now() def
>>     dayName(java.time.LocalDate theDate) { return
>>     theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL,
>>     java.util.Locale.getDefault()) } %> # Hello Today (<%=
>>     dayName(now) %>) is <%= now %>. The weather in next 3 days will
>>     be: <% def weather = [ "Sunny", "Rainy", "Cloudy", "Windy" ] for
>>     (i = 1; i < 4; i++) { def day = now.plusDays(i)
>>     Collections.shuffle weather out.println "- " + dayName(day) + ":
>>     " + weather.first() } %> """.stripIndent() def engine = new StreamingTemplateEngine() def
>>     template = engine.createTemplate(text) println template.make()
>>
>>     ...the StreamingTemplateEngine does not accept the function
>>     definition:
>>
>>
>>     Template parse error 'Unexpected input: '(' ' at line 4, column 12
>>              3:
>>          --> 4: def dayName(java.time.LocalDate theDate) {
>>              5:     return
>>     theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL,
>>     java.util.Locale.getDefault())
>>
>>     groovy.text.TemplateParseException: Template parse error
>>     'Unexpected input: '(' ' at line 4, column 12
>>              3:
>>          --> 4: def dayName(java.time.LocalDate theDate) {
>>              5:     return
>>     theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL,
>>     java.util.Locale.getDefault())
>>
>>
>>     If there a way to define function in the text processed that
>>     would work with the StreamingTemplateEngine?
>>
>>
>>     The code works fine "the other way around" i.e from Groovy code:
>>
>>     def now = java.time.LocalDate.now()
>>
>>     def dayName(java.time.LocalDate theDate) {
>>         return
>>     theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL,
>>     java.util.Locale.getDefault())
>>     }
>>
>>     println "# Hello"
>>
>>     println "Today ${dayName(now)}) is ${now}."
>>
>>     println "The weather in next 3 days will be:"
>>
>>     def weather = [ "Sunny", "Rainy", "Cloudy", "Windy" ]
>>     for (i = 1; i < 4; i++) {
>>       def day = now.plusDays(i)
>>       Collections.shuffle weather
>>       println "- " + dayName(day) + ": " + weather.first()
>>     }
>>
>>     But I would like a "Markdown first" kind of experience rather
>>     than a "Groovy first" since in the "normal" case, the amount of
>>     text vastly outnumbers the amount of code.
>>
>>
>>     Best regards,
>>
>>     Per
>>
>>

Re: Defining functions in text processed by the StreamingTemplateEngine

Posted by Paul King <pa...@asert.com.au>.
The SimpleTemplateEngine supports methods (and imports and even local class
definitions):

def text = """\
<%
import java.time.LocalDate
import java.time.format.TextStyle

def now = LocalDate.now()

def dayName(theDate) {
    theDate.dayOfWeek.getDisplayName(TextStyle.FULL, Locale.default)
}
class Const {
  static days = 3
}
%>
# Hello

Today (<%= dayName(now) %>) is <%= now %>.

The weather in next <%= Const.days %> days will be:
<%
  def weather = [ "Sunny", "Rainy", "Cloudy", "Windy" ]
  Const.days.times {
    out.println "- " + dayName(now + it) + ": " + weather.shuffled()[0]
  }
%>
""".stripIndent()

def engine = new groovy.text.SimpleTemplateEngine()
def template = engine.createTemplate(text)
println template.make()


Cheers, Paul.


On Sun, Jul 24, 2022 at 6:35 AM Per Nyfelt <pe...@alipsa.se> wrote:

> I figured it out. although functions (methods) cannot be defined, a
> closure works fine:
>
> def text = """\<% def now = java.time.LocalDate.now() def dayName = { theDate ->        return theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, java.util.Locale.getDefault())}%># HelloToday (<%= dayName(now) %>) is <%= now %>.The weather in next 3 days will be:<%  def weather = [ "Sunny", "Rainy", "Cloudy", "Windy" ]  for (i = 1; i < 4; i++) {    def day = now.plusDays(i)    Collections.shuffle weather    out.println "- " + dayName(day) + ": " + weather.first()  }%>""".stripIndent()def engine = new StreamingTemplateEngine()def template = engine.createTemplate(text)println template.make()
>
>
> On 7/23/22 19:59, Per Nyfelt wrote:
>
> Hi,
>
> I am trying to use the StreamingTemplateEngine to create dynamic markdown
> (similarly to the rmd (r markdown,
> https://rmarkdown.rstudio.com/authoring_quick_tour.html) but I've
> encountered a snag:
>
> Simple usage such as the following works fine using the
> StreamingTemplateEngine:
>
> def text = """\  <%   def now = java.time.LocalDate.now()   %>  # Hello  Today (<%= now.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, java.util.Locale.getDefault()) %>) is <%= java.time.LocalDate.now() %>.  The weather in next 3 days will be:  <%    def weather = [ "Sunny", "Rainy", "Cloudy", "Windy" ]    for (i = 1; i < 4; i++) {      def day = now.plusDays(i)      def dayName = day.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, java.util.Locale.getDefault())      Collections.shuffle weather      out.println "- " + dayName + ": " + weather.first()    }  %>""".stripIndent()def engine = new StreamingTemplateEngine()def template = engine.createTemplate(text)println template.make()
>
> But if if want to define a function to simplify the code e.g.
>
> def text = """\  <%   def now = java.time.LocalDate.now()   def dayName(java.time.LocalDate theDate) {    return theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, java.util.Locale.getDefault())  }  %>  # Hello  Today (<%= dayName(now) %>) is <%= now %>.  The weather in next 3 days will be:  <%    def weather = [ "Sunny", "Rainy", "Cloudy", "Windy" ]    for (i = 1; i < 4; i++) {      def day = now.plusDays(i)      Collections.shuffle weather      out.println "- " + dayName(day) + ": " + weather.first()    }  %>""".stripIndent()def engine = new StreamingTemplateEngine()def template = engine.createTemplate(text)println template.make()
>
> ...the StreamingTemplateEngine does not accept the function definition:
>
>
> Template parse error 'Unexpected input: '(' ' at line 4, column 12
>          3:
>      --> 4: def dayName(java.time.LocalDate theDate) {
>          5:     return
> theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL,
> java.util.Locale.getDefault())
>
> groovy.text.TemplateParseException: Template parse error 'Unexpected
> input: '(' ' at line 4, column 12
>          3:
>      --> 4: def dayName(java.time.LocalDate theDate) {
>          5:     return
> theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL,
> java.util.Locale.getDefault())
>
>
> If there a way to define function in the text processed that would work
> with the StreamingTemplateEngine?
>
>
> The code works fine "the other way around" i.e from Groovy code:
>
> def now = java.time.LocalDate.now()
>
> def dayName(java.time.LocalDate theDate) {
>     return
> theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL,
> java.util.Locale.getDefault())
> }
>
> println "# Hello"
>
> println "Today ${dayName(now)}) is ${now}."
>
> println "The weather in next 3 days will be:"
>
> def weather = [ "Sunny", "Rainy", "Cloudy", "Windy" ]
> for (i = 1; i < 4; i++) {
>   def day = now.plusDays(i)
>   Collections.shuffle weather
>   println "- " + dayName(day) + ": " + weather.first()
> }
>
> But I would like a "Markdown first" kind of experience rather than a
> "Groovy first" since in the "normal" case, the amount of text vastly
> outnumbers the amount of code.
>
>
> Best regards,
>
> Per
>
>
>

Re: Defining functions in text processed by the StreamingTemplateEngine

Posted by Per Nyfelt <pe...@alipsa.se>.
I figured it out. although functions (methods) cannot be defined, a 
closure works fine:

def text = """\ <% def now = java.time.LocalDate.now() def dayName = { 
theDate -> return 
theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, 
java.util.Locale.getDefault()) } %> # Hello Today (<%= dayName(now) %>) 
is <%= now %>. The weather in next 3 days will be: <% def weather = [ 
"Sunny", "Rainy", "Cloudy", "Windy" ] for (i = 1; i < 4; i++) { def day 
= now.plusDays(i) Collections.shuffle weather out.println "- " + 
dayName(day) + ": " + weather.first() } %> """.stripIndent() def engine = new StreamingTemplateEngine() def template = 
engine.createTemplate(text) println template.make()

On 7/23/22 19:59, Per Nyfelt wrote:
>
> Hi,
>
> I am trying to use the StreamingTemplateEngine to create dynamic 
> markdown (similarly to the rmd (r markdown, 
> https://rmarkdown.rstudio.com/authoring_quick_tour.html) but I've 
> encountered a snag:
>
> Simple usage such as the following works fine using the 
> StreamingTemplateEngine:
>
> def text = """\ <% def now = java.time.LocalDate.now() %> # Hello 
> Today (<%= 
> now.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, 
> java.util.Locale.getDefault()) %>) is <%= java.time.LocalDate.now() 
> %>. The weather in next 3 days will be: <% def weather = [ "Sunny", 
> "Rainy", "Cloudy", "Windy" ] for (i = 1; i < 4; i++) { def day = 
> now.plusDays(i) def dayName = 
> day.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, 
> java.util.Locale.getDefault()) Collections.shuffle weather out.println 
> "- " + dayName + ": " + weather.first() } %> """.stripIndent() def engine = new StreamingTemplateEngine() def template 
> = engine.createTemplate(text) println template.make()
>
> But if if want to define a function to simplify the code e.g.
>
> def text = """\ <% def now = java.time.LocalDate.now() def 
> dayName(java.time.LocalDate theDate) { return 
> theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, 
> java.util.Locale.getDefault()) } %> # Hello Today (<%= dayName(now) 
> %>) is <%= now %>. The weather in next 3 days will be: <% def weather 
> = [ "Sunny", "Rainy", "Cloudy", "Windy" ] for (i = 1; i < 4; i++) { 
> def day = now.plusDays(i) Collections.shuffle weather out.println "- " 
> + dayName(day) + ": " + weather.first() } %> """.stripIndent() def engine = new StreamingTemplateEngine() def template 
> = engine.createTemplate(text) println template.make()
>
> ...the StreamingTemplateEngine does not accept the function definition:
>
>
> Template parse error 'Unexpected input: '(' ' at line 4, column 12
>          3:
>      --> 4: def dayName(java.time.LocalDate theDate) {
>          5:     return 
> theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, 
> java.util.Locale.getDefault())
>
> groovy.text.TemplateParseException: Template parse error 'Unexpected 
> input: '(' ' at line 4, column 12
>          3:
>      --> 4: def dayName(java.time.LocalDate theDate) {
>          5:     return 
> theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, 
> java.util.Locale.getDefault())
>
>
> If there a way to define function in the text processed that would 
> work with the StreamingTemplateEngine?
>
>
> The code works fine "the other way around" i.e from Groovy code:
>
> def now = java.time.LocalDate.now()
>
> def dayName(java.time.LocalDate theDate) {
>     return 
> theDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, 
> java.util.Locale.getDefault())
> }
>
> println "# Hello"
>
> println "Today ${dayName(now)}) is ${now}."
>
> println "The weather in next 3 days will be:"
>
> def weather = [ "Sunny", "Rainy", "Cloudy", "Windy" ]
> for (i = 1; i < 4; i++) {
>   def day = now.plusDays(i)
>   Collections.shuffle weather
>   println "- " + dayName(day) + ": " + weather.first()
> }
>
> But I would like a "Markdown first" kind of experience rather than a 
> "Groovy first" since in the "normal" case, the amount of text vastly 
> outnumbers the amount of code.
>
>
> Best regards,
>
> Per
>
>