SlideShare a Scribd company logo
Mastering Grails 3
Plugins
Álvaro Sánchez-Mariscal
Álvaro Sánchez-Mariscal
Software Engineer
Grails Development Team
sanchezmariscala@ociweb.com
Mastering Grails 3 Plugins - GR8Conf US 2016
OCI is the new home of Grails
More at ociweb.com/grails
The Basics
Creating a Grails 3 plugin
$ grails create-plugin myWebPlugin
| Plugin created at /private/tmp/myWebPlugin
$ grails create-plugin myPlugin -profile plugin
| Plugin created at /private/tmp/myPlugin
Understanding profiles
• A profile defines:
• Project’s build.gradle.
• Commands: create-domain-class,
run-app, etc.
• Features: hibernate, json-views, etc.
• Skeleton: files and folders.
plugin vs. web-plugin
Trim your plugin!
Keep clean
• Start with the plugin profile whenever
possible.
• Remove empty and/or unwanted files/
folders.
• Otherwise, the burtbeckwith bot will
send you a cleanup pull request!
The burtbeckwith bot
The burtbeckwith bot
• Watches messy plugin repos and sends a PR
to clean them up.
• 14 pull requests in the last 3 months!
• Likely hundreds in the last years!
The minimal plugin
• Folder containing:
• build.gradle
• src/main/groovy with plugin descriptor.
• Empty grails-app folder.
• Everything else can be removed.
The plugin descriptor
• A class inside src/main/groovy. Extends
grails.plugins.Plugin.
• Can override methods to define behaviour
in the plugin lifecycle.
• Syntax has changed a bit from Grails 2.
Plugins features
Plugin configuration
• A plugin can define:
• Configuration values for the host Grails app.
• One of plugin.yml or plugin.groovy.
• Configuration for running the plugin as an
application, to test it.
• application.yml / application.groovy.
Excluding content
• In the plugin descriptor:
• In build.gradle:
// resources that are excluded from plugin packaging

def pluginExcludes = [

'**/com/example/myplugin/tests/**'

]
jar {

exclude 'com/example/myplugin/tests/**/**'

}
Command Line extensions
• Use create-script for code generation
commands.
• Runnable with the Grails CLI.
• Use create-command for interacting with a
loaded Grails application.
• Runnable with the Grails CLI or as a Gradle task.
Scripts
• Base class:
org.grails.cli.profile.commands.script.GroovyScriptCommand
import org.grails.cli.interactive.completers.DomainClassCompleter



description( "Generates a controller that performs REST operations" ) {

usage "grails generate-resource-controller [DOMAIN CLASS]"

argument name:'Domain Class', description:"The name of the domain class", required:true

completer DomainClassCompleter

flag name:'force', description:"Whether to overwrite existing files"

}



if(args) {

generateController(*args)

generateViews(*args)

generateUnitTest(*args)

generateFunctionalTest(*args)

} else {

error "No domain class specified"

}
Commands
import grails.dev.commands.ApplicationCommand

import grails.dev.commands.ExecutionContext



class MyCommand implements ApplicationCommand {



@Override

boolean handle(ExecutionContext ctx) {

def dataSource = applicationContext.getBean(DataSource)

//Run some SQL...



return true

}



}
Enhancing artefacts
import grails.artefact.Enhances

import groovy.transform.CompileStatic



@Enhances(['Controller', 'Service'])

@CompileStatic

trait DateSupport {



Date now() {

return new Date()

}



}
Modularisation
Modularisation
• If your plugin becomes to grow, you might
end up creating a monolith.
• You can modularise your plugins as you
would do with your apps.
Modularisation
Monolithic plugin
Multi-module plugin
Modularisation
• Benefits:
• Optional dependencies.
• Smaller JAR files.
• Build logic reuse.
Modularisation setup
• settings.gradle:
include ‘myPlugin-core', ‘myPlugin-domain' //etc
Modularisation setup
• Root build.gradle:
allprojects {

apply plugin:"idea"

}



subprojects { Project project ->

ext {

grailsVersion = project.grailsVersion

gradleWrapperVersion = project.gradleWrapperVersion

}



repositories {

//Common repos

}



version "1.0.0.M1"

group "org.grails.plugins"



apply plugin: "org.grails.grails-plugin"



dependencies {

//Common deps

}

}
Modularisation setup
• Sub-module build.gradle:
dependencyManagement {

imports {

mavenBom "org.grails:grails-bom:$grailsVersion"

}

applyMavenExclusions false

}



dependencies {

compile project(":myPlugin-core")



compile "com.example:library:1.0.0"

}
Publishing
Artifact publication
• Snapshots:
• Using the artifactory Gradle plugin.
• Published in OJO (oss.jfrog.org).
• Releases:
• Using the grails-plugin-publish Gradle plugin.
• Published in Bintray.
Bintray setup
• For Snapshots:
Build setup
artifactory {

contextUrl = 'http://oss.jfrog.org'

publish {

repository {

repoKey = 'oss-snapshot-local'

username = bintrayUser

password = bintrayKey

}

defaults {

publications('maven')

}

}

}



artifactoryPublish {

dependsOn sourcesJar, javadocJar

}
grailsPublish {

user = bintrayUser

key = bintrayKey

portalUser = pluginPortalUser

portalPassword = pluginPortalPassword



repo = 'plugins'

githubSlug = 'alvarosanchez/my-plugin'

license = 'APACHE 2.0'

title = "My Plugin"

desc = "A very cool Grails plugin"

developers = [

alvarosanchez: "Alvaro Sanchez-Mariscal"

]

}
• For Releases:
Build setup
Build setup
• Define rootProject.name in
settings.gradle.
• Define credentials in
~/.gradle/gradle.properties.
Running it
• Snapshot publishing:
• Release publishing:
$ ./gradlew artifactoryPublish
$ ./gradlew publishPlugin notifyPluginPortal
Plugin portals
• Once your packages are published in your
Bintray repo, go to https://
bintray.com/grails/plugins and click
on “Include my package”.
• Grails 3: http://grails.org/plugins.html
• Grails 2: http://grails.org/plugins
Testing
Testing with a profile
• You can create a profile and use it as a TCK
for your plugin:
• Create test apps from that profile.
• Apps come with a set of tests.
• Use features to test different configurations.
Profile descriptor
description: Creates a test app for Spring Security REST plugin

build:

excludes:

- org.grails.grails-core

dependencies:

compile:

- "org.grails.plugins:spring-security-rest:${pluginVersion}"

- "org.grails:grails-datastore-rest-client:5.0.0.RC3"

testCompile:

- "com.codeborne:phantomjsdriver:1.2.1"

- "org.seleniumhq.selenium:selenium-api:2.47.1"

- "org.seleniumhq.selenium:selenium-firefox-driver:2.47.1"
profile.yml.tmpl
Feature descriptor
description: First configuration of GORM

dependencies:

build:

- "org.grails.plugins:hibernate4:5.0.0.RC2"

compile:

- "org.grails.plugins:hibernate4"

- "org.hibernate:hibernate-ehcache"

- "org.grails.plugins:spring-security-rest-gorm:${pluginVersion}"

runtime:

- "com.h2database:h2"
features/gorm1/feature.yml.tmpl
Build setup
task generateProfileConfig << {

copy {

from 'profile.yml.tmpl'

into '.'

rename { String fileName -> fileName.replaceAll '.tmpl', '' }

expand pluginVersion: project.version

}



file('features').eachDir { feature ->

copy {

from "features/${feature.name}/feature.yml.tmpl"

into "features/${feature.name}/"

rename { String fileName -> fileName.replaceAll '.tmpl', '' }

expand pluginVersion: project.version

}

}

}



compileProfile.dependsOn generateProfileConfig
Skeleton
• Put in the skeleton all your test files and
resources.
• You can use features to have different sets of
tests, resources and configuration.
• Define global configuration values in profile’s
root skeleton folder.
Test them all!
for feature in `ls ../spring-security-rest-testapp-profile/features/`

do

grails create-app -profile 

org.grails.plugins:spring-security-rest-testapp-profile:$pluginVersion 

-features $feature $feature && cd $feature && ./gradlew check && cd ..

done
Use case: the Spring
Security REST plugin
Thank you!
Álvaro Sánchez-Mariscal

More Related Content

PDF
Creating applications with Grails, Angular JS and Spring Security - GR8Conf U...
PDF
Desarrollo de aplicaciones con Grails 3, Angular JS y Spring Security
PDF
Mastering Grails 3 Plugins - G3 Summit 2016
PDF
Creating applications with Grails, Angular JS and Spring Security - G3 Summit...
PDF
Creating applications with Grails, Angular JS and Spring Security - GR8Conf E...
PDF
Mastering Grails 3 Plugins - GR8Conf EU 2016
PDF
Creating applications with Grails, Angular JS and Spring Security
PDF
Rest with grails 3
Creating applications with Grails, Angular JS and Spring Security - GR8Conf U...
Desarrollo de aplicaciones con Grails 3, Angular JS y Spring Security
Mastering Grails 3 Plugins - G3 Summit 2016
Creating applications with Grails, Angular JS and Spring Security - G3 Summit...
Creating applications with Grails, Angular JS and Spring Security - GR8Conf E...
Mastering Grails 3 Plugins - GR8Conf EU 2016
Creating applications with Grails, Angular JS and Spring Security
Rest with grails 3

What's hot (20)

PDF
Swagger code motion talk
PPTX
你不可不知的 ASP.NET Core 3 全新功能探索 (.NET Conf 2019)
PPTX
Iterative Development with Swagger on the JDK
PDF
Swagger 2.0: Latest and Greatest
PDF
Mój przepis na skalowalną architekturę mikroserwisową? Apollo Federation i Gr...
PDF
JHipster
PPTX
Writer APIs in Java faster with Swagger Inflector
PPTX
Swagger 2.0 and Model-driven APIs
PDF
Swagger UI
PDF
Efficient HTTP applications on the JVM with Ratpack - Voxxed Days Berlin 2016
PDF
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesome
PDF
How to generate a REST CXF3 application from Swagger ApacheConEU 2016
PDF
Getting Started With Angular
PDF
Design Driven API Development
PDF
戦う情シス!全社 API で社内アプリ開発を加速させよう
PDF
Beautifying the Beautiful: Theming WSO2 API Manager
PDF
Web view
PPTX
Magic of web components
PPTX
ASP.NET Core
Swagger code motion talk
你不可不知的 ASP.NET Core 3 全新功能探索 (.NET Conf 2019)
Iterative Development with Swagger on the JDK
Swagger 2.0: Latest and Greatest
Mój przepis na skalowalną architekturę mikroserwisową? Apollo Federation i Gr...
JHipster
Writer APIs in Java faster with Swagger Inflector
Swagger 2.0 and Model-driven APIs
Swagger UI
Efficient HTTP applications on the JVM with Ratpack - Voxxed Days Berlin 2016
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesome
How to generate a REST CXF3 application from Swagger ApacheConEU 2016
Getting Started With Angular
Design Driven API Development
戦う情シス!全社 API で社内アプリ開発を加速させよう
Beautifying the Beautiful: Theming WSO2 API Manager
Web view
Magic of web components
ASP.NET Core
Ad

Similar to Mastering Grails 3 Plugins - GR8Conf US 2016 (20)

PDF
Mastering Grails 3 Plugins - Greach 2016
PPTX
Grails Custom Plugin
PPTX
Custom plugin
PPTX
Grails Advanced
PPTX
Grails plugin development
PDF
Cut your Grails application to pieces - build feature plugins
PDF
Grails 3.0 Preview
PDF
Building Grails Plugins - Tips And Tricks
PPT
Use Cases of #Grails in #WebApplications
PDF
GR8Conf 2011: Grails, how to plug in
PDF
Grails Plugin Best Practices
PPT
Introduction To Grails
PPTX
Introduction to Grails 2013
PDF
Adopting Grails - GR8Conf Europe
PDF
GR8Conf 2011: Adopting Grails
ODP
Plugin development
POT
intoduction to Grails Framework
PDF
Grails 101
ODP
Grails 0.3-SNAPSHOT Presentation WJAX 2006 English
PDF
Introduction To Groovy And Grails - SpringPeople
Mastering Grails 3 Plugins - Greach 2016
Grails Custom Plugin
Custom plugin
Grails Advanced
Grails plugin development
Cut your Grails application to pieces - build feature plugins
Grails 3.0 Preview
Building Grails Plugins - Tips And Tricks
Use Cases of #Grails in #WebApplications
GR8Conf 2011: Grails, how to plug in
Grails Plugin Best Practices
Introduction To Grails
Introduction to Grails 2013
Adopting Grails - GR8Conf Europe
GR8Conf 2011: Adopting Grails
Plugin development
intoduction to Grails Framework
Grails 101
Grails 0.3-SNAPSHOT Presentation WJAX 2006 English
Introduction To Groovy And Grails - SpringPeople
Ad

More from Alvaro Sanchez-Mariscal (20)

PDF
Serverless functions with Micronaut
PDF
Asynchronous and event-driven Grails applications
PDF
6 things you need to know about GORM 6
PDF
Reactive microservices with Micronaut - GR8Conf EU 2018
PDF
Reactive microservices with Micronaut - Greach 2018
PDF
Practical Spring Cloud
PDF
Efficient HTTP applications on the JVM with Ratpack - JDD 2015
PDF
Stateless authentication with OAuth 2 and JWT - JavaZone 2015
PDF
Stateless authentication for microservices - GR8Conf 2015
PDF
Ratpack 101 - GR8Conf 2015
PDF
Ratpack 101 - GeeCON 2015
PDF
Stateless authentication for microservices - Spring I/O 2015
PDF
Stateless authentication for microservices - Greach 2015
PDF
Stateless authentication for microservices applications - JavaLand 2015
PDF
Stateless authentication for microservices
PDF
Stateless token-based authentication for pure front-end applications
PDF
Workshop: Creating RESTful API’s with Grails and Spring Security (GR8Conf 2014)
PDF
Embrace the frontend revolution
PDF
DevQA: make your testers happier with Groovy, Spock and Geb (Greach 2014)
PDF
Creating RESTful API’s with Grails and Spring Security
Serverless functions with Micronaut
Asynchronous and event-driven Grails applications
6 things you need to know about GORM 6
Reactive microservices with Micronaut - GR8Conf EU 2018
Reactive microservices with Micronaut - Greach 2018
Practical Spring Cloud
Efficient HTTP applications on the JVM with Ratpack - JDD 2015
Stateless authentication with OAuth 2 and JWT - JavaZone 2015
Stateless authentication for microservices - GR8Conf 2015
Ratpack 101 - GR8Conf 2015
Ratpack 101 - GeeCON 2015
Stateless authentication for microservices - Spring I/O 2015
Stateless authentication for microservices - Greach 2015
Stateless authentication for microservices applications - JavaLand 2015
Stateless authentication for microservices
Stateless token-based authentication for pure front-end applications
Workshop: Creating RESTful API’s with Grails and Spring Security (GR8Conf 2014)
Embrace the frontend revolution
DevQA: make your testers happier with Groovy, Spock and Geb (Greach 2014)
Creating RESTful API’s with Grails and Spring Security

Recently uploaded (20)

PPTX
L1 - Introduction to python Backend.pptx
PPTX
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
PDF
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PDF
How Creative Agencies Leverage Project Management Software.pdf
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PDF
medical staffing services at VALiNTRY
PPTX
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
PDF
AI in Product Development-omnex systems
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PDF
PTS Company Brochure 2025 (1).pdf.......
PDF
Softaken Excel to vCard Converter Software.pdf
PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
PPTX
Essential Infomation Tech presentation.pptx
PPTX
ai tools demonstartion for schools and inter college
PDF
Understanding Forklifts - TECH EHS Solution
PDF
System and Network Administration Chapter 2
L1 - Introduction to python Backend.pptx
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
Adobe Illustrator 28.6 Crack My Vision of Vector Design
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
Which alternative to Crystal Reports is best for small or large businesses.pdf
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
How Creative Agencies Leverage Project Management Software.pdf
Internet Downloader Manager (IDM) Crack 6.42 Build 41
medical staffing services at VALiNTRY
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
AI in Product Development-omnex systems
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PTS Company Brochure 2025 (1).pdf.......
Softaken Excel to vCard Converter Software.pdf
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
Essential Infomation Tech presentation.pptx
ai tools demonstartion for schools and inter college
Understanding Forklifts - TECH EHS Solution
System and Network Administration Chapter 2

Mastering Grails 3 Plugins - GR8Conf US 2016

  • 2. Álvaro Sánchez-Mariscal Software Engineer Grails Development Team sanchezmariscala@ociweb.com
  • 4. OCI is the new home of Grails More at ociweb.com/grails
  • 6. Creating a Grails 3 plugin $ grails create-plugin myWebPlugin | Plugin created at /private/tmp/myWebPlugin $ grails create-plugin myPlugin -profile plugin | Plugin created at /private/tmp/myPlugin
  • 7. Understanding profiles • A profile defines: • Project’s build.gradle. • Commands: create-domain-class, run-app, etc. • Features: hibernate, json-views, etc. • Skeleton: files and folders.
  • 10. Keep clean • Start with the plugin profile whenever possible. • Remove empty and/or unwanted files/ folders. • Otherwise, the burtbeckwith bot will send you a cleanup pull request!
  • 12. The burtbeckwith bot • Watches messy plugin repos and sends a PR to clean them up. • 14 pull requests in the last 3 months! • Likely hundreds in the last years!
  • 13. The minimal plugin • Folder containing: • build.gradle • src/main/groovy with plugin descriptor. • Empty grails-app folder. • Everything else can be removed.
  • 14. The plugin descriptor • A class inside src/main/groovy. Extends grails.plugins.Plugin. • Can override methods to define behaviour in the plugin lifecycle. • Syntax has changed a bit from Grails 2.
  • 16. Plugin configuration • A plugin can define: • Configuration values for the host Grails app. • One of plugin.yml or plugin.groovy. • Configuration for running the plugin as an application, to test it. • application.yml / application.groovy.
  • 17. Excluding content • In the plugin descriptor: • In build.gradle: // resources that are excluded from plugin packaging
 def pluginExcludes = [
 '**/com/example/myplugin/tests/**'
 ] jar {
 exclude 'com/example/myplugin/tests/**/**'
 }
  • 18. Command Line extensions • Use create-script for code generation commands. • Runnable with the Grails CLI. • Use create-command for interacting with a loaded Grails application. • Runnable with the Grails CLI or as a Gradle task.
  • 19. Scripts • Base class: org.grails.cli.profile.commands.script.GroovyScriptCommand import org.grails.cli.interactive.completers.DomainClassCompleter
 
 description( "Generates a controller that performs REST operations" ) {
 usage "grails generate-resource-controller [DOMAIN CLASS]"
 argument name:'Domain Class', description:"The name of the domain class", required:true
 completer DomainClassCompleter
 flag name:'force', description:"Whether to overwrite existing files"
 }
 
 if(args) {
 generateController(*args)
 generateViews(*args)
 generateUnitTest(*args)
 generateFunctionalTest(*args)
 } else {
 error "No domain class specified"
 }
  • 20. Commands import grails.dev.commands.ApplicationCommand
 import grails.dev.commands.ExecutionContext
 
 class MyCommand implements ApplicationCommand {
 
 @Override
 boolean handle(ExecutionContext ctx) {
 def dataSource = applicationContext.getBean(DataSource)
 //Run some SQL...
 
 return true
 }
 
 }
  • 21. Enhancing artefacts import grails.artefact.Enhances
 import groovy.transform.CompileStatic
 
 @Enhances(['Controller', 'Service'])
 @CompileStatic
 trait DateSupport {
 
 Date now() {
 return new Date()
 }
 
 }
  • 23. Modularisation • If your plugin becomes to grow, you might end up creating a monolith. • You can modularise your plugins as you would do with your apps.
  • 25. Modularisation • Benefits: • Optional dependencies. • Smaller JAR files. • Build logic reuse.
  • 26. Modularisation setup • settings.gradle: include ‘myPlugin-core', ‘myPlugin-domain' //etc
  • 27. Modularisation setup • Root build.gradle: allprojects {
 apply plugin:"idea"
 }
 
 subprojects { Project project ->
 ext {
 grailsVersion = project.grailsVersion
 gradleWrapperVersion = project.gradleWrapperVersion
 }
 
 repositories {
 //Common repos
 }
 
 version "1.0.0.M1"
 group "org.grails.plugins"
 
 apply plugin: "org.grails.grails-plugin"
 
 dependencies {
 //Common deps
 }
 }
  • 28. Modularisation setup • Sub-module build.gradle: dependencyManagement {
 imports {
 mavenBom "org.grails:grails-bom:$grailsVersion"
 }
 applyMavenExclusions false
 }
 
 dependencies {
 compile project(":myPlugin-core")
 
 compile "com.example:library:1.0.0"
 }
  • 30. Artifact publication • Snapshots: • Using the artifactory Gradle plugin. • Published in OJO (oss.jfrog.org). • Releases: • Using the grails-plugin-publish Gradle plugin. • Published in Bintray.
  • 32. • For Snapshots: Build setup artifactory {
 contextUrl = 'http://oss.jfrog.org'
 publish {
 repository {
 repoKey = 'oss-snapshot-local'
 username = bintrayUser
 password = bintrayKey
 }
 defaults {
 publications('maven')
 }
 }
 }
 
 artifactoryPublish {
 dependsOn sourcesJar, javadocJar
 }
  • 33. grailsPublish {
 user = bintrayUser
 key = bintrayKey
 portalUser = pluginPortalUser
 portalPassword = pluginPortalPassword
 
 repo = 'plugins'
 githubSlug = 'alvarosanchez/my-plugin'
 license = 'APACHE 2.0'
 title = "My Plugin"
 desc = "A very cool Grails plugin"
 developers = [
 alvarosanchez: "Alvaro Sanchez-Mariscal"
 ]
 } • For Releases: Build setup
  • 34. Build setup • Define rootProject.name in settings.gradle. • Define credentials in ~/.gradle/gradle.properties.
  • 35. Running it • Snapshot publishing: • Release publishing: $ ./gradlew artifactoryPublish $ ./gradlew publishPlugin notifyPluginPortal
  • 36. Plugin portals • Once your packages are published in your Bintray repo, go to https:// bintray.com/grails/plugins and click on “Include my package”. • Grails 3: http://grails.org/plugins.html • Grails 2: http://grails.org/plugins
  • 38. Testing with a profile • You can create a profile and use it as a TCK for your plugin: • Create test apps from that profile. • Apps come with a set of tests. • Use features to test different configurations.
  • 39. Profile descriptor description: Creates a test app for Spring Security REST plugin
 build:
 excludes:
 - org.grails.grails-core
 dependencies:
 compile:
 - "org.grails.plugins:spring-security-rest:${pluginVersion}"
 - "org.grails:grails-datastore-rest-client:5.0.0.RC3"
 testCompile:
 - "com.codeborne:phantomjsdriver:1.2.1"
 - "org.seleniumhq.selenium:selenium-api:2.47.1"
 - "org.seleniumhq.selenium:selenium-firefox-driver:2.47.1" profile.yml.tmpl
  • 40. Feature descriptor description: First configuration of GORM
 dependencies:
 build:
 - "org.grails.plugins:hibernate4:5.0.0.RC2"
 compile:
 - "org.grails.plugins:hibernate4"
 - "org.hibernate:hibernate-ehcache"
 - "org.grails.plugins:spring-security-rest-gorm:${pluginVersion}"
 runtime:
 - "com.h2database:h2" features/gorm1/feature.yml.tmpl
  • 41. Build setup task generateProfileConfig << {
 copy {
 from 'profile.yml.tmpl'
 into '.'
 rename { String fileName -> fileName.replaceAll '.tmpl', '' }
 expand pluginVersion: project.version
 }
 
 file('features').eachDir { feature ->
 copy {
 from "features/${feature.name}/feature.yml.tmpl"
 into "features/${feature.name}/"
 rename { String fileName -> fileName.replaceAll '.tmpl', '' }
 expand pluginVersion: project.version
 }
 }
 }
 
 compileProfile.dependsOn generateProfileConfig
  • 42. Skeleton • Put in the skeleton all your test files and resources. • You can use features to have different sets of tests, resources and configuration. • Define global configuration values in profile’s root skeleton folder.
  • 43. Test them all! for feature in `ls ../spring-security-rest-testapp-profile/features/`
 do
 grails create-app -profile 
 org.grails.plugins:spring-security-rest-testapp-profile:$pluginVersion 
 -features $feature $feature && cd $feature && ./gradlew check && cd ..
 done
  • 44. Use case: the Spring Security REST plugin