user944849
user944849

Reputation: 14971

Calling method from within a Groovy DSL

I have a simple Groovy method that uses Groovy's MarkupBuilder to print HTML, very simplified version below:

void writeHtmlFile(<args>) {
  def writer = new FileWriter(fileName.toFile())
  def html = new MarkupBuilder(writer)

  html.mkp.yieldUnescaped '<!DOCTYPE html>'
  html.mkp.xmlDeclaration(version: "1.0", encoding: "utf-8")
  html.html {
    head { ... }
    body(id: 'main') {
      h1 "Report Title"
    }      
  }
  writer.flush()
  writer.close()
}

This works well. Say I wanted to call a method after the h1 that does some calculations and adds more to the MarkupBuilder. How do I get the elements defined in the called method added to the MarkupBuilder? Here's something I tried that doesn't cause an exception, but also doesn't work (the resulting HTML has no <h2> element):

Closure testNested() {
  println '---'
  return { h2 "here's a subheading" } 
}

// .... other stuff from above example not repeated ...
html.html {
    head {...}
    body(id: 'main') {
      h1 "Report Title"
      testNested()
    }      

I know I can easily do this inline. I'm trying to deepen my understanding of how Groovy uses closures and delegates in DSLs and clearly I'm missing something.

Upvotes: 0

Views: 669

Answers (1)

Michael Easter
Michael Easter

Reputation: 24498

Consider the following code, which executes fine for me, using Groovy 2.4.5.

The builder pattern is a bit tricky because it can be viewed as hierarchical data and/or code, depending on your perspective. With practice, one can switch perspectives as necessary.

import groovy.xml.*

void testNested(def html) {
    html.h2("here's a subheading from testNested")
}

void writeHtmlFile(def fileName) {
  def writer = new FileWriter(fileName)
  def html = new MarkupBuilder(writer)

  html.mkp.yieldUnescaped '<!DOCTYPE html>'
  html.mkp.xmlDeclaration(version: "1.0", encoding: "utf-8")
  html.html {
    body(id: 'main') {
      h1 "Report Title"
      testNested(html)
    }
  }
  writer.flush()
  writer.close()
}

writeHtmlFile("out.html")

Upvotes: 2

Related Questions