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.

Building Grails Plugins - Tips And Tricks

14,964 views

Published on

Published in: Technology, Art & Photos
  • Dating for everyone is here: ♥♥♥ http://bit.ly/2Q98JRS ♥♥♥
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Dating direct: ♥♥♥ http://bit.ly/2Q98JRS ♥♥♥
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Building Grails Plugins - Tips And Tricks

  1. 1. Building Grails Plugins tips and tricks from the wild
  2. 2. About Me • Mike Hugo, Independent Software Developer • http://piragua.com • http://WhenWorksForYou.com • Groovy/Grails since 2007 • Author of several Grails plugins • code-coverage • greenmail • hibernate-stats • build-info • test-template • ????
  3. 3. • Plugins overview • Build your own plugin • testing • modularization • configuration • events • did I mention testing? what’s on the menu for tonight?
  4. 4. App Plugin a plugin is just another grails application, with a few extra files * GrailsPlugin.groovy * Install, Uninstall and Upgrade scripts
  5. 5. Cool hooks into the runtime environment of a running grails app, plus ability to add new ‘artefacts’ and participate in reloading events
  6. 6. Existing Plugins there are a ton of existing plugins from security to Rich UI to searching to...you name it.
  7. 7. common problem - this is one i’ve solved about 6 times. time for a plugin
  8. 8. • grails create-plugin build-status • cd build-status • mkdir test/projects • cd test/projects • grails create-app statusapp gives you a client you can use to test your plugin. why?
  9. 9. development • make a change to the plugin • grails package-plugin • cd test/projects/statusapp/ • grails install-plugin ../../../grails-build-status-0.1.zip • grails run-app • wash, rinse, repeat
  10. 10. In Place Plugins • In the application just created, modify BuildConfig.groovy and add: • grails.plugin.location."build-status" = "../../.." • TADA! You’re now working with an in- place plugin
  11. 11. Add Controller (test) import grails.test.* class BuildInfoControllerTests extends ControllerUnitTestCase { void testIndex() { ! ! controller.index() ! ! ! ! assertEquals('index', renderArgs.view) ! ! assertEquals(['app.version'], renderArgs.model.buildInfoProperties) } }
  12. 12. Add Controller class BuildInfoController { ! static final List infoProperties = ['app.version'] def index = { ! ! render view:'index', model: [buildInfoProperties:infoProperties] ! } }
  13. 13. Add the View <html> <head> <title>Build Info</title> </head> <body> <div> <g:each in="${buildInfoProperties}" var="prop"> <g:if test="${g.meta(name:prop)}"> <tr> <td>${prop}</td><td><g:meta name="${prop}"/></td> </tr> </g:if> </g:each> </div> </body> </html>
  14. 14. Functional Testing • in the app, install the functional test plugin • grails create-functional-test class BuildInfoPageFunctionalTests extends functionaltestplugin.FunctionalTestCase { void testSomeWebsiteFeature() { get('/buildInfo') assertStatus 200 assertContentContains 'app.version' assertContentContains 'app.grails.version' } }
  15. 15. Introduce i18n • i18n allows a form of customization • create a messages.properties in the plugin i18n directory for default values • override it in the app to test to make sure it works
  16. 16. Introducing Config • Allow the client application the ability to add or remove properties to display void testIndex_overrideDefaults(){ mockConfig """ buildInfo.properties.exclude = ['app.version'] buildInfo.properties.add = ['custom.property'] """ controller.index() assertEquals 'index', renderArgs.view def expectedProperties = controller.buildInfoProperties - 'app.version' expectedProperties = expectedProperties + 'custom.property' assertEquals expectedProperties, renderArgs.model.buildInfoProperties }
  17. 17. Modularizing Views • Put contents of views into templates • Allows client to override the default view //view // template <html> <g:each in="${buildInfoProperties}" var=" <head> <g:if test="${g.meta(name:prop)}"> <title>Build Info</title> <tr> </head> <td> <body> <g:message code="${prop}"/> <div> </td> <table> ! ! ! <td> ! <g:render template="info" <g:meta name="${prop}"/> plugin="buildstatus"> </td> </table> </tr> </div> </g:if> </body> </g:each> </html>
  18. 18. Events • We want to capture the start of WAR file being built and log the Date/Time • What events are available? • Search $GRAILS_HOME/scripts for “event” • What variables are available? • binding.variables.each {println it} http://grails.org/doc/latest/guide/4.%20The%20Command%20Line.html#4.3%20Hooking %20into%20Events
  19. 19. test it • grails.test.AbstractCliTestCase • Thank you Peter Ledbrook: http://www.cacoethes.co.uk/blog/ groovyandgrails/testing-your-grails-scripts http://grails.org/doc/latest/guide/4.%20The%20Command%20Line.html#4.3%20Hooking %20into%20Events
  20. 20. import grails.test.AbstractCliTestCase import java.util.zip.ZipFile class CreateWarEventTests extends AbstractCliTestCase { void testCreateWar(){ execute (['war', '-non-interactive']) assertEquals 0, waitForProcess() verifyHeader() Properties props = new Properties() props.load(new ZipFile('target/app-0.1.war'). getInputStream('WEB-INF/classes/application.properties')) assertNotNull props['build.date'] } }
  21. 21. eventCreateWarStart = {warname, stagingDir -> Ant.propertyfile(file: "${stagingDir}/WEB-INF/classes/application.properties") { entry(key: 'build.date', value: new Date()) } }
  22. 22. return the favor • publish your own events to allow clients to hook into plugin workflow eventCreateWarStart = {warname, stagingDir -> event("BuildInfoAddPropertiesStart", [warname, stagingDir]) Ant.propertyfile(file: "${stagingDir}/WEB-INF/classes/application.properties") { entry(key: 'build.date', value: new Date()) } event("BuildInfoAddPropertiesEnd", [warname, stagingDir]) }
  23. 23. Gradle Build • http://adhockery.blogspot.com/2010/01/ gradle-build-for-grails-plugins-with.html • http://www.cacoethes.co.uk/blog/ groovyandgrails/building-a-grails-project- with-gradle • One build file in the plugin that runs tests on all your ‘apps’ in the test/projects directory
  24. 24. in-place plugin caveats • Reloading plugin artifacts doesn’t always work • Grails 1.1.1 • see next slide • Grails 1.2.1 • Plugin controllers lose GrailsPlugin annotation and views cannot be resolved after reloading http://jira.codehaus.org/browse/GRAILS-5869
  25. 25. def watchedResources = ["file:${getPluginLocation()}/web-app/**", "file:${getPluginLocation()}/grails-app/controllers/**/*Controller.groovy", "file:${getPluginLocation()}/grails-app/services/**/*Service.groovy", "file:${getPluginLocation()}/grails-app/taglib/**/*TagLib.groovy" ] def onChange = { event -> if (!isBasePlugin()) { if (event.source instanceof FileSystemResource && event.source?.path?.contains('web-app')) { def ant = new AntBuilder() ant.copy(todir: "./web-app/plugins/PLUGIN_NAME_HERE-${event.plugin.version}") { fileset(dir: "${getPluginLocation()}/web-app") } } else if (application.isArtefactOfType(ControllerArtefactHandler.TYPE, event.source)) { manager?.getGrailsPlugin("controllers")?.notifyOfEvent(event) // this injects the tag library namespaces back into the controller after it is reloaded manager?.getGrailsPlugin("groovyPages")?.notifyOfEvent(event) } else if (application.isArtefactOfType(TagLibArtefactHandler.TYPE, event.source)) { manager?.getGrailsPlugin("groovyPages")?.notifyOfEvent(event) } else if (application.isArtefactOfType(ServiceArtefactHandler.TYPE, event.source)) { manager?.getGrailsPlugin("services")?.notifyOfEvent(event) } } // watching is modified and reloaded. The event contains: event.source, // event.application, event.manager, event.ctx, and event.plugin. } ConfigObject getBuildConfig() { GroovyClassLoader classLoader = new GroovyClassLoader(getClass().getClassLoader()) ConfigObject buildConfig = new ConfigSlurper().parse(classLoader.loadClass('BuildConfig')) return buildConfig } String getPluginLocation() { return getBuildConfig()?.grails?.plugin?.location?.'PLUGIN_NAME_HERE' }
  26. 26. the real deal http://plugins.grails.org/grails-build-info/trunk/ source code available in the grails plugin svn repository, or browse on the web at: http://plugins.grails.org/grails-build-info/trunk/

×