Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Mastering Grails 3 Plugins - Greach 2016

1,846 views

Published on


With Grails 3, the plugin development experience changes a little bit compared to that of Grails 2. In this talk, Álvaro (member of the Grails team at OCI, Grails committer and author of several plugins) will cover several topics to understand how plugins work in Grails 3, focusing on best practices.

The session is structured as a set of tips and tricks with code samples in the following areas: modularisation, build system, testing and publishing.

Published in: Software
  • Be the first to comment

Mastering Grails 3 Plugins - Greach 2016

  1. 1. Mastering Grails 3 Plugins Álvaro Sánchez-Mariscal
  2. 2. Álvaro Sánchez-Mariscal Software Engineer Grails Development Team sanchezmariscala@ociweb.com
  3. 3. OCI is the new home of Grails More at ociweb.com/grails
  4. 4. The Basics
  5. 5. 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
  6. 6. 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.
  7. 7. plugin vs. web-plugin
  8. 8. Trim your plugin!
  9. 9. 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!
  10. 10. The burtbeckwith bot
  11. 11. 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!
  12. 12. The minimal plugin • Folder containing: • build.gradle • src/main/groovy with plugin descriptor. • Empty grails-app folder.
  13. 13. 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.
  14. 14. Plugins features
  15. 15. 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.
  16. 16. 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/**/**'
 }
  17. 17. 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.
  18. 18. 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"
 }
  19. 19. 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
 }
 
 }
  20. 20. Enhancing artefacts import grails.artefact.Enhances
 import groovy.transform.CompileStatic
 
 @Enhances(['Controller', 'Service'])
 @CompileStatic
 trait DateSupport {
 
 Date now() {
 return new Date()
 }
 
 }
  21. 21. Modularisation
  22. 22. 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.
  23. 23. Modularisation Monolithic plugin Multi-module plugin
  24. 24. Modularisation • Benefits: • Optional dependencies. • Smaller JAR files. • Build logic reuse.
  25. 25. Modularisation setup • settings.gradle: include ‘myPlugin-core', ‘myPlugin-domain' //etc
  26. 26. 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
 }
 }
  27. 27. 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"
 }
  28. 28. Aggregating Docs task aggregateGroovyDoc(type: Groovydoc) {
 group = JavaBasePlugin.DOCUMENTATION_GROUP
 
 dependsOn subprojects.groovydoc
 source subprojects.groovydoc.source
 destinationDir file("${buildDir}/docs/groovydoc")
 classpath = files(subprojects.groovydoc.classpath)
 groovyClasspath = files(subprojects.groovydoc.groovyClasspath)
 }
  29. 29. Publishing
  30. 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.
  31. 31. Bintray setup
  32. 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. 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. 34. Build setup • Define rootProject.name in settings.gradle. • Define credentials in ~/.gradle/gradle.properties.
  35. 35. Running it • Snapshot publishing: • Release publishing: $ ./gradlew artifactoryPublish $ ./gradlew publishPlugin notifyPluginPortal
  36. 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
  37. 37. Testing
  38. 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. 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. 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. 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. 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. 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. 44. Use case: the Spring Security REST plugin
  45. 45. ¡Muchas gracias! Álvaro Sánchez-Mariscal

×