What is a DSL
DSL's are meant to target a particular type of problem. They are short expressive means of programming that fit well in a narrow context. For example with GORM, you can express hibernate mapping with a DSL rather than XML.
static mapping = { table 'person' columns { name column:'name' } }Much of the theory for DSLs and the benefits they deliver are well documented. Refer to these sources as a starting point:
A Simple DSL Example in Groovy
The following example offers a simplified view of implementing an internal DSL. Frameworks have much more advanced methods of creating a DSL. However this example does highlight closure delegation and meta object protocol concepts that are essential to understanding the inner workings of a DSL.
Requirements Overview
Imagine that a customer needs a memo generator. The memos needs to have a few simple fields, such as "to", "from", and "body". The memo can also have sections such as "Summary" or "Important." The summary fields are dynamic and can be anything on demand. In addition, the memo needs to be outputed in three formats: xml, html, and text.
We elect to implement this as a DSL in Groovy. The DSL result looks like this:
MemoDsl.make { to "Nirav Assar" from "Barack Obama" body "How are things? We are doing well. Take care" idea "The economy is key" request "Please vote for me" xml }The output from the code yields:
<memo> <to>Nirav Assar</to> <from>Barack Obama</from> <body>How are things? We are doing well. Take care</body> <idea>The economy is key</idea> <request>Please vote for me</request> </memo>The last line in the DSL can also be changed to 'html' or 'text'. This affects the output format.
Implementation
A static method that accepts a closure is a hassle free way to implement a DSL. In the memo example, the class MemoDsl has a make method. It creates an instance and delegates all calls in the closure to the instance. This is the mechanism where the "to", and "from" sections end up executing methods inside the MemoDsl class. Once the to() method is called, we store the text in the instance for formatting later on.
class MemoDsl { String toText String fromText String body def sections = [] /** * This method accepts a closure which is essentially the DSL. Delegate the * closure methods to * the DSL class so the calls can be processed */ def static make(closure) { MemoDsl memoDsl = new MemoDsl() // any method called in closure will be delegated to the memoDsl class closure.delegate = memoDsl closure() } /** * Store the parameter as a variable and use it later to output a memo */ def to(String toText) { this.toText = toText } def from(String fromText) { this.fromText = fromText } def body(String bodyText) { this.body = bodyText } }Dynamic Sections
When the closure includes a method that is not present in the MemoDsl class, groovy identifies it as a missing method. With Groovy's meta object protocol, the methodMissing interface on the class is invoked. This is how we handle sections for the memo. In the client code above we have entries for idea and request.
MemoDsl.make { to "Nirav Assar" from "Barack Obama" body "How are things? We are doing well. Take care" idea "The economy is key" request "Please vote for me" xml }The sections are processed with the following code in MemoDsl. It creates a section class and appends it to a list in the instance.
/** * When a method is not recognized, assume it is a title for a new section. Create a simple * object that contains the method name and the parameter which is the body. */ def methodMissing(String methodName, args) { def section = new Section(title: methodName, body: args[0]) sections << section }Processing Various Outputs
Finally the most interesting part of the DSL is how we process the various outputs. The final line in the closure specifies the output desired. When a closure contains a string such as "xml" with no parameters, groovy assumes this is a 'getter' method. Thus we need to implement 'getXml()' to catch the delegation execution:
/** * 'get' methods get called from the dsl by convention. Due to groovy closure delegation, * we had to place MarkUpBuilder and StringWrite code in a static method as the delegate of the closure * did not have access to the system.out */ def getXml() { doXml(this) } /** * Use markupBuilder to create a customer xml output */ private static doXml(MemoDsl memoDsl) { def writer = new StringWriter() def xml = new MarkupBuilder(writer) xml.memo() { to(memoDsl.toText) from(memoDsl.fromText) body(memoDsl.body) // cycle through the stored section objects to create an xml tag for (s in memoDsl.sections) { "$s.title"(s.body) } } println writer }The code for html and text is quite similar. The only variation is how the output is formatted.
Entire Code
The code in its entirety is displayed next. The best approach I found was to design the DSL client code and the specified formats first, then tackle the implementation. I used TDD and JUnit to drive my implementation. Note that I did not go the extra mile to do asserts on the system output in the tests, although this could be easily enhanced to do so. The code is fully executable inside any IDE. Run the various tests to view the DSL output.
package com.solutionsfit.dsl.memotemplate class MemolDslTest extends GroovyTestCase { void testDslUsage_outputXml() { MemoDsl.make { to "Nirav Assar" from "Barack Obama" body "How are things? We are doing well. Take care" idea "The economy is key" request "Please vote for me" xml } } void testDslUsage_outputHtml() { MemoDsl.make { to "Nirav Assar" from "Barack Obama" body "How are things? We are doing well. Take care" idea "The economy is key" request "Please vote for me" html } } void testDslUsage_outputText() { MemoDsl.make { to "Nirav Assar" from "Barack Obama" body "How are things? We are doing well. Take care" idea "The economy is key" request "Please vote for me" text } } } package com.solutionsfit.dsl.memotemplate import groovy.xml.MarkupBuilder /** * Processes a simple DSL to create various formats of a memo: xml, html, and text */ class MemoDsl { String toText String fromText String body def sections = [] /** * This method accepts a closure which is essentially the DSL. Delegate the closure methods to * the DSL class so the calls can be processed */ def static make(closure) { MemoDsl memoDsl = new MemoDsl() // any method called in closure will be delegated to the memoDsl class closure.delegate = memoDsl closure() } /** * Store the parameter as a variable and use it later to output a memo */ def to(String toText) { this.toText = toText } def from(String fromText) { this.fromText = fromText } def body(String bodyText) { this.body = bodyText } /** * When a method is not recognized, assume it is a title for a new section. Create a simple * object that contains the method name and the parameter which is the body. */ def methodMissing(String methodName, args) { def section = new Section(title: methodName, body: args[0]) sections << section } /** * 'get' methods get called from the dsl by convention. Due to groovy closure delegation, * we had to place MarkUpBuilder and StringWrite code in a static method as the delegate of the closure * did not have access to the system.out */ def getXml() { doXml(this) } def getHtml() { doHtml(this) } def getText() { doText(this) } /** * Use markupBuilder to create a customer xml output */ private static doXml(MemoDsl memoDsl) { def writer = new StringWriter() def xml = new MarkupBuilder(writer) xml.memo() { to(memoDsl.toText) from(memoDsl.fromText) body(memoDsl.body) // cycle through the stored section objects to create an xml tag for (s in memoDsl.sections) { "$s.title"(s.body) } } println writer } /** * Use markupBuilder to create an html xml output */ private static doHtml(MemoDsl memoDsl) { def writer = new StringWriter() def xml = new MarkupBuilder(writer) xml.html() { head { title("Memo") } body { h1("Memo") h3("To: ${memoDsl.toText}") h3("From: ${memoDsl.fromText}") p(memoDsl.body) // cycle through the stored section objects and create uppercase/bold section with body for (s in memoDsl.sections) { p { b(s.title.toUpperCase()) } p(s.body) } } } println writer } /** * Use markupBuilder to create an html xml output */ private static doText(MemoDsl memoDsl) { String template = "Memo\nTo: ${memoDsl.toText}\nFrom: ${memoDsl.fromText}\n${memoDsl.body}\n" def sectionStrings ="" for (s in memoDsl.sections) { sectionStrings += s.title.toUpperCase() + "\n" + s.body + "\n" } template += sectionStrings println template } } package com.solutionsfit.dsl.memotemplate class Section { String title String body }
Your code is missing a Section class:
ReplyDeleteclass Section {
String title
String body
}
Thank you. I have reposted.
DeleteLooks really groovi-er ;)
ReplyDeleteI think it's important to emphasize that this is a builder DSL. There are many forms of DSLs, each with different techniques.
ReplyDeleteBut good article! A getJson() method would be useful too ;) It's not clear why toText, fromText, and body are different from sections, except that maybe they are required. But they're not explicitly enforced, are they?
Does anyone know any Grails developers looking for work around Columbus, Ohio or willing to relocate to Columbus? I am desperate!
ReplyDeletepleased update your codes
ReplyDeletethank
What codes do you want me to update?
ReplyDeleteCan you describe with GUI Interface?
ReplyDeleteselamet, i am not sure what you mean by this question
ReplyDeleteAssignment of closure.delegate in MemoDsl seems to be not thread safe. What happens if this code is being called from multiple threads? Will different threads attempt to change the state ("delegate" property) of the same object simultaneously? Just a question.
ReplyDeleteI imagine it is not thread safe
ReplyDeleteInteresting Article
ReplyDeleteSpring Hibernate Online Training | Hibernate Training in Chennai
Hibernate Online Training | Java Online Training | Java EE Online Training
Very interesting, good job and thanks for sharing such a good blog. Interesting stuff to read..................
ReplyDeleteR12 SCM Online Training
Good explanation having in this blog.detailed about online training.
ReplyDeletevery informative .thanks for sharing this article.
Good explanation having in this blog.detailed about online training.
ReplyDeletevery informative .thanks for sharing this article.
oracle fusion procurement online training
wonderful information, I had come to know about your blog from my friend nandu , hyderaba.i have read atleast 7 posts of yours by now, and let me tell you, your website gives the best and the most interesting information. This is just the kind of information that i had been looking for, i'm already your rss reader now and i would regularly watch out for the new posts.
ReplyDeletefrom
Oracle Fusion HCM Online Training
I have been trying to understand the DSL concept in Java programming in vain but I am glad that I landed on this site and found a comprehensive article on the concept and I have been able to grasp both the basic and the complex concepts of Groovy DSL. Thanks so much for sharing this article and please find time and check out my article by clicking on Quality Dissertation Editing Services.
ReplyDeleteGood explanation having in this blog.detailed about online training.very informative .thanks for sharing this article.
ReplyDeleteOracle Fusion Financials Online Training
Thanks for all the information, it was very helpful I really like that you are providing information................Get some great info about Fusion Cloud Financials Course.
ReplyDeleteYour given most of the usefull information, i read your post is very nice thank you....................Search for Oracle Project Accounting Training Institute details in our local search engine Calfre.
ReplyDeleteGreat Work, after reading this post I felt comfortable with this post thank you very much.
ReplyDeleteOracle HRMS Training in Al Karama, Dubai
click Here
This site has lots of advantage.I found many interesting things from this site. It helps me in many ways.Thanks for posting this again.
ReplyDeleteOracle Fusion SCM Training Institutes in Hyderabad
I found your blog while searching for the updates, I am happy to be here. Very useful content and also easily understandable providing.. Believe me I did wrote an post about tutorials for beginners with reference of your blog.
ReplyDeleteDevops Training courses
python Training in chennai
Devops Training in Bangalore
I have picked cheery a lot of useful clothes outdated of this amazing blog. I’d love to return greater than and over again. Thanks!
ReplyDeletepython Training institute in Chennai
python Training institute in Bangalore
python Training in Pune
Thanks for sharing.
ReplyDeleteEnsures best online Job Support.
We help IT professionals by providing them Best Online Job Support in 250+ technologies. Our services are very reliable and most affordable. Call Today for free demo.
Thanks for sharing this informative content , Great work
ReplyDeleteLeanpitch provides online training inScrum Master during this lockdown period everyone can use it wisely.
Advanced Scrum Master Training
This is an amazing blog, thank you so much for sharing such valuable information with us.
ReplyDeleteWorkday Integration Training
Workday Integration Online Training
Workday Integration Course India
Workday Online Integration Course Hyderabad
Workday Integration Course India
Workday Online Integration Course
Workday Online Integration Course in India
Workday Online Integration Course India
impressive words alignment!
ReplyDeleteJoe Biden Silver Leather Jacket
thanks for sharing blog if you intersted to learn more visit it https://snowflakemasters.in/
ReplyDeleteThanks for sharing this post with us.. good post!!!
ReplyDeleteWhat is DevOps
AWS DevOps Tools
This post is so useful and informative. Keep updating with more information.....
ReplyDeleteCyber Security Institute In Bangalore
Best Cyber Security Training Institute In Bangalore
Nice blog post,
ReplyDeleteEmbedded Systems Course in Hyderabad
ReplyDeleteThanks for your information. very good article.
Best ServiceNow Online Training in Hyderabad
ServiceNow Training
This comment has been removed by the author.
ReplyDeleteNice thanks for sharing informative post like this keep posting.
ReplyDeleteAre u looking for Acne Treatment in Hyderabad
Click here: Aira Clinics
Nice blog thanks for sharing information if like more visit this site.
ReplyDeleteAre u looking for UCATCoaching
Great deals of important information and also a great article. I am currently following for your blog site and I am bookmarking it future reference. thanks for sharing!
ReplyDeletekralbet
ReplyDeletebetpark
tipobet
slot siteleri
kibris bahis siteleri
poker siteleri
bonus veren siteler
mobil ödeme bahis
betmatik
B5658
Nice blog article, thanks for sharing
ReplyDeletepython full stack training in hyderabad
bus rental Dubai full of information with great content thanks for sharing
ReplyDeleteThis comment has been removed by a blog administrator. our sclinbio.com
ReplyDeleteNICE BLOG ARTICLE AND GOOD BLOG
ReplyDeleteYour post is really informative and attractive keep doing like this Bhutan National ParkThank you for sharing your good content.
ReplyDeleteNice blog! Good explanation about Java "I'm impressed with the quality of your posts." Keep posting more
ReplyDeleteJava full stack training institute in KPHB
"Great article, felt good after reading, worth it.
ReplyDeletei would like to read more from you.
keep posting more.
also follow Mern Stack course in hyderabad"
nice work "Top Digital Marketing Agency In Hyderabad
ReplyDelete"
very nice article wonderful blog keep postingSelenium Training Institute In Hyderabad">digital marketing course in Kukatpally
ReplyDeletedfhdgnbdndnghfm
ReplyDeleteشركة تنظيف افران بمكة
nbvcncvnbvnmvbm
ReplyDeleteشركة تنظيف افران بمكة
شركة الركن المثالي تقدم خدمات متخصصة في صيانة ولحام خزانات المياه بكفاءة عالية وحلول مبتكرة لضمان استدامتها.
ReplyDeleteشركة لحام خزانات
5346859
شركة كشف تسربات المياه بالدمام 4aVF7qPQpP
ReplyDeleteشركة مكافحة حشرات بالجبيل 8fLUHOL6Nt
ReplyDeleteشركة تنظيف مجالس بخميس مشيط QsmNLs21pa
ReplyDeleteصيانة افران الغاز بمكة Df4w65IhQL
ReplyDeleteشركة تسليك مجاري بالاحساء fm451mnEOh
ReplyDeleteشركة تنظيف بالدمام KSKys7ROTZ
ReplyDelete