SlideShare uma empresa Scribd logo
1 de 26
Baixar para ler offline
Building Grails Plugins
     tips and tricks from the wild
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              •   ????
• Plugins overview
                                  • Build your own plugin
                                   • testing
                                   • modularization
                                   • configuration
                                   • events
                                   • did I mention testing?


what’s on the menu for tonight?
App                                               Plugin




a plugin is just another grails application, with a few extra files
* GrailsPlugin.groovy
* Install, Uninstall and Upgrade scripts
Cool hooks into the runtime environment of a running grails app, plus ability to add new
‘artefacts’ and participate in reloading events
Existing Plugins




there are a ton of existing plugins from security to Rich UI to searching to...you name it.
common problem - this is one i’ve solved about 6 times. time for a plugin
• 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?
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
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
Add Controller (test)
import grails.test.*

class BuildInfoControllerTests extends ControllerUnitTestCase {
    void testIndex() {

!   !   controller.index()
!   !
!   !   assertEquals('index', renderArgs.view)
!   !   assertEquals(['app.version'],
           renderArgs.model.buildInfoProperties)
    }
}
Add Controller

class BuildInfoController {

!   static final List infoProperties = ['app.version']

     def index = {
!   ! render view:'index', model:
           [buildInfoProperties:infoProperties]
!   }
}
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>
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'
      }
  }
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
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
}
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>
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
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
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']
    }

}
eventCreateWarStart = {warname, stagingDir ->
    Ant.propertyfile(file:
      "${stagingDir}/WEB-INF/classes/application.properties") {
        entry(key: 'build.date', value: new Date())
    }
}
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])
}
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
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
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'
}
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/

Mais conteúdo relacionado

Mais procurados

An introduction to maven gradle and sbt
An introduction to maven gradle and sbtAn introduction to maven gradle and sbt
An introduction to maven gradle and sbtFabio Fumarola
 
Gradle - the Enterprise Automation Tool
Gradle  - the Enterprise Automation ToolGradle  - the Enterprise Automation Tool
Gradle - the Enterprise Automation ToolIzzet Mustafaiev
 
The world of gradle - an introduction for developers
The world of gradle  - an introduction for developersThe world of gradle  - an introduction for developers
The world of gradle - an introduction for developersTricode (part of Dept)
 
Micronaut For Single Page Apps
Micronaut For Single Page AppsMicronaut For Single Page Apps
Micronaut For Single Page AppsZachary Klein
 
Scala and Play with Gradle
Scala and Play with GradleScala and Play with Gradle
Scala and Play with GradleWei Chen
 
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...DynamicInfraDays
 
Migrating from Grails 2 to Grails 3
Migrating from Grails 2 to Grails 3Migrating from Grails 2 to Grails 3
Migrating from Grails 2 to Grails 3Michael Plöd
 
Android gradle-build-system-overview
Android gradle-build-system-overviewAndroid gradle-build-system-overview
Android gradle-build-system-overviewKevin He
 
Java(ee) mongo db applications in the cloud
Java(ee) mongo db applications in the cloud Java(ee) mongo db applications in the cloud
Java(ee) mongo db applications in the cloud Shekhar Gulati
 
Faster java ee builds with gradle [con4921]
Faster java ee builds with gradle [con4921]Faster java ee builds with gradle [con4921]
Faster java ee builds with gradle [con4921]Ryan Cuprak
 
An Introduction to Gradle for Java Developers
An Introduction to Gradle for Java DevelopersAn Introduction to Gradle for Java Developers
An Introduction to Gradle for Java DevelopersKostas Saidis
 
Make Your Build Great Again (DroidConSF 2017)
Make Your Build Great Again (DroidConSF 2017)Make Your Build Great Again (DroidConSF 2017)
Make Your Build Great Again (DroidConSF 2017)Jared Burrows
 

Mais procurados (20)

Gradle
GradleGradle
Gradle
 
An introduction to maven gradle and sbt
An introduction to maven gradle and sbtAn introduction to maven gradle and sbt
An introduction to maven gradle and sbt
 
Gradle - the Enterprise Automation Tool
Gradle  - the Enterprise Automation ToolGradle  - the Enterprise Automation Tool
Gradle - the Enterprise Automation Tool
 
Gradle
GradleGradle
Gradle
 
Gradle Introduction
Gradle IntroductionGradle Introduction
Gradle Introduction
 
The world of gradle - an introduction for developers
The world of gradle  - an introduction for developersThe world of gradle  - an introduction for developers
The world of gradle - an introduction for developers
 
Apache Lucene for Java EE Developers
Apache Lucene for Java EE DevelopersApache Lucene for Java EE Developers
Apache Lucene for Java EE Developers
 
Building with Gradle
Building with GradleBuilding with Gradle
Building with Gradle
 
Micronaut For Single Page Apps
Micronaut For Single Page AppsMicronaut For Single Page Apps
Micronaut For Single Page Apps
 
Angular beans
Angular beansAngular beans
Angular beans
 
Scala and Play with Gradle
Scala and Play with GradleScala and Play with Gradle
Scala and Play with Gradle
 
Introduction to gradle
Introduction to gradleIntroduction to gradle
Introduction to gradle
 
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
 
Migrating from Grails 2 to Grails 3
Migrating from Grails 2 to Grails 3Migrating from Grails 2 to Grails 3
Migrating from Grails 2 to Grails 3
 
Android gradle-build-system-overview
Android gradle-build-system-overviewAndroid gradle-build-system-overview
Android gradle-build-system-overview
 
Java(ee) mongo db applications in the cloud
Java(ee) mongo db applications in the cloud Java(ee) mongo db applications in the cloud
Java(ee) mongo db applications in the cloud
 
Micronaut Launchpad
Micronaut LaunchpadMicronaut Launchpad
Micronaut Launchpad
 
Faster java ee builds with gradle [con4921]
Faster java ee builds with gradle [con4921]Faster java ee builds with gradle [con4921]
Faster java ee builds with gradle [con4921]
 
An Introduction to Gradle for Java Developers
An Introduction to Gradle for Java DevelopersAn Introduction to Gradle for Java Developers
An Introduction to Gradle for Java Developers
 
Make Your Build Great Again (DroidConSF 2017)
Make Your Build Great Again (DroidConSF 2017)Make Your Build Great Again (DroidConSF 2017)
Make Your Build Great Again (DroidConSF 2017)
 

Destaque

Reactive Microservice Architecture with Groovy and Grails
Reactive Microservice Architecture with Groovy and GrailsReactive Microservice Architecture with Groovy and Grails
Reactive Microservice Architecture with Groovy and GrailsSteve Pember
 
Application Architectures in Grails
Application Architectures in GrailsApplication Architectures in Grails
Application Architectures in GrailsPeter Ledbrook
 
Burbuja inmobiliaria-financiera
Burbuja inmobiliaria-financieraBurbuja inmobiliaria-financiera
Burbuja inmobiliaria-financieraguestce99ac
 
Burbuja Inmobiliaria Y Financiera
Burbuja Inmobiliaria Y FinancieraBurbuja Inmobiliaria Y Financiera
Burbuja Inmobiliaria Y Financieraguestce99ac
 
Ficha autoevaluacion trabajo final
Ficha autoevaluacion trabajo finalFicha autoevaluacion trabajo final
Ficha autoevaluacion trabajo finalAna Gonzales Pérez
 
Schaumburg Regional Airport Billboard Ads
Schaumburg Regional Airport Billboard AdsSchaumburg Regional Airport Billboard Ads
Schaumburg Regional Airport Billboard Adsmaryjane627
 
Pictures Of Graves Of Prophets & Ahlebaitand
Pictures Of Graves Of Prophets & AhlebaitandPictures Of Graves Of Prophets & Ahlebaitand
Pictures Of Graves Of Prophets & Ahlebaitandzeshan mehmood
 
Vacances D’Hivern
Vacances D’HivernVacances D’Hivern
Vacances D’HivernSEBAS100
 
08年冬训装备总结
08年冬训装备总结08年冬训装备总结
08年冬训装备总结Moohuo
 
Post-Election Health Policy - Impact on Physicians
Post-Election Health Policy - Impact on PhysiciansPost-Election Health Policy - Impact on Physicians
Post-Election Health Policy - Impact on Physiciansguestd63c45
 

Destaque (13)

Reactive Microservice Architecture with Groovy and Grails
Reactive Microservice Architecture with Groovy and GrailsReactive Microservice Architecture with Groovy and Grails
Reactive Microservice Architecture with Groovy and Grails
 
Application Architectures in Grails
Application Architectures in GrailsApplication Architectures in Grails
Application Architectures in Grails
 
Burbuja inmobiliaria-financiera
Burbuja inmobiliaria-financieraBurbuja inmobiliaria-financiera
Burbuja inmobiliaria-financiera
 
Organismo
OrganismoOrganismo
Organismo
 
Burbuja Inmobiliaria Y Financiera
Burbuja Inmobiliaria Y FinancieraBurbuja Inmobiliaria Y Financiera
Burbuja Inmobiliaria Y Financiera
 
Ficha autoevaluacion trabajo final
Ficha autoevaluacion trabajo finalFicha autoevaluacion trabajo final
Ficha autoevaluacion trabajo final
 
Schaumburg Regional Airport Billboard Ads
Schaumburg Regional Airport Billboard AdsSchaumburg Regional Airport Billboard Ads
Schaumburg Regional Airport Billboard Ads
 
Pictures Of Graves Of Prophets & Ahlebaitand
Pictures Of Graves Of Prophets & AhlebaitandPictures Of Graves Of Prophets & Ahlebaitand
Pictures Of Graves Of Prophets & Ahlebaitand
 
Saint-P
Saint-PSaint-P
Saint-P
 
Vacances D’Hivern
Vacances D’HivernVacances D’Hivern
Vacances D’Hivern
 
08年冬训装备总结
08年冬训装备总结08年冬训装备总结
08年冬训装备总结
 
Post-Election Health Policy - Impact on Physicians
Post-Election Health Policy - Impact on PhysiciansPost-Election Health Policy - Impact on Physicians
Post-Election Health Policy - Impact on Physicians
 
Navigating Complicated Issues for Seniors
Navigating Complicated Issues for Seniors Navigating Complicated Issues for Seniors
Navigating Complicated Issues for Seniors
 

Semelhante a Building Grails Plugins - Tips And Tricks

Gretty: Managing Web Containers with Gradle
Gretty: Managing Web Containers with GradleGretty: Managing Web Containers with Gradle
Gretty: Managing Web Containers with GradleAndrey Hihlovsky
 
Mastering Grails 3 Plugins - GR8Conf EU 2016
Mastering Grails 3 Plugins - GR8Conf EU 2016Mastering Grails 3 Plugins - GR8Conf EU 2016
Mastering Grails 3 Plugins - GR8Conf EU 2016Alvaro Sanchez-Mariscal
 
Mastering Grails 3 Plugins - Greach 2016
Mastering Grails 3 Plugins - Greach 2016Mastering Grails 3 Plugins - Greach 2016
Mastering Grails 3 Plugins - Greach 2016Alvaro Sanchez-Mariscal
 
How to Webpack your Django!
How to Webpack your Django!How to Webpack your Django!
How to Webpack your Django!David Gibbons
 
Introduction to Django
Introduction to DjangoIntroduction to Django
Introduction to DjangoJames Casey
 
Mastering Grails 3 Plugins - GR8Conf US 2016
Mastering Grails 3 Plugins - GR8Conf US 2016Mastering Grails 3 Plugins - GR8Conf US 2016
Mastering Grails 3 Plugins - GR8Conf US 2016Alvaro Sanchez-Mariscal
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenerytoddbr
 
After max+phonegap
After max+phonegapAfter max+phonegap
After max+phonegapyangdj
 
混搭移动开发:PhoneGap+JQurey+Dreamweaver
混搭移动开发:PhoneGap+JQurey+Dreamweaver混搭移动开发:PhoneGap+JQurey+Dreamweaver
混搭移动开发:PhoneGap+JQurey+Dreamweaveryangdj
 
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rulesSrijan Technologies
 
Mastering Grails 3 Plugins - G3 Summit 2016
Mastering Grails 3 Plugins - G3 Summit 2016Mastering Grails 3 Plugins - G3 Summit 2016
Mastering Grails 3 Plugins - G3 Summit 2016Alvaro Sanchez-Mariscal
 
Google App Engine with Gaelyk
Google App Engine with GaelykGoogle App Engine with Gaelyk
Google App Engine with GaelykChoong Ping Teo
 
Google Play Services Rock
Google Play Services RockGoogle Play Services Rock
Google Play Services RockPeter Friese
 
Google Cloud Platform 2014Q1 - Starter Guide
Google Cloud Platform   2014Q1 - Starter GuideGoogle Cloud Platform   2014Q1 - Starter Guide
Google Cloud Platform 2014Q1 - Starter GuideSimon Su
 
Single Page JavaScript WebApps... A Gradle Story
Single Page JavaScript WebApps... A Gradle StorySingle Page JavaScript WebApps... A Gradle Story
Single Page JavaScript WebApps... A Gradle StoryKon Soulianidis
 
Future of Web Apps: Google Gears
Future of Web Apps: Google GearsFuture of Web Apps: Google Gears
Future of Web Apps: Google Gearsdion
 
OpenCms Days 2014 - User Generated Content in OpenCms 9.5
OpenCms Days 2014 - User Generated Content in OpenCms 9.5OpenCms Days 2014 - User Generated Content in OpenCms 9.5
OpenCms Days 2014 - User Generated Content in OpenCms 9.5Alkacon Software GmbH & Co. KG
 

Semelhante a Building Grails Plugins - Tips And Tricks (20)

Gretty: Managing Web Containers with Gradle
Gretty: Managing Web Containers with GradleGretty: Managing Web Containers with Gradle
Gretty: Managing Web Containers with Gradle
 
Mastering Grails 3 Plugins - GR8Conf EU 2016
Mastering Grails 3 Plugins - GR8Conf EU 2016Mastering Grails 3 Plugins - GR8Conf EU 2016
Mastering Grails 3 Plugins - GR8Conf EU 2016
 
Mastering Grails 3 Plugins - Greach 2016
Mastering Grails 3 Plugins - Greach 2016Mastering Grails 3 Plugins - Greach 2016
Mastering Grails 3 Plugins - Greach 2016
 
How to Webpack your Django!
How to Webpack your Django!How to Webpack your Django!
How to Webpack your Django!
 
Introduction to Django
Introduction to DjangoIntroduction to Django
Introduction to Django
 
Mastering Grails 3 Plugins - GR8Conf US 2016
Mastering Grails 3 Plugins - GR8Conf US 2016Mastering Grails 3 Plugins - GR8Conf US 2016
Mastering Grails 3 Plugins - GR8Conf US 2016
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenery
 
Grails Advanced
Grails Advanced Grails Advanced
Grails Advanced
 
After max+phonegap
After max+phonegapAfter max+phonegap
After max+phonegap
 
混搭移动开发:PhoneGap+JQurey+Dreamweaver
混搭移动开发:PhoneGap+JQurey+Dreamweaver混搭移动开发:PhoneGap+JQurey+Dreamweaver
混搭移动开发:PhoneGap+JQurey+Dreamweaver
 
Introduction to AngularJS
Introduction to AngularJSIntroduction to AngularJS
Introduction to AngularJS
 
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
 
Mastering Grails 3 Plugins - G3 Summit 2016
Mastering Grails 3 Plugins - G3 Summit 2016Mastering Grails 3 Plugins - G3 Summit 2016
Mastering Grails 3 Plugins - G3 Summit 2016
 
Google App Engine with Gaelyk
Google App Engine with GaelykGoogle App Engine with Gaelyk
Google App Engine with Gaelyk
 
Google Play Services Rock
Google Play Services RockGoogle Play Services Rock
Google Play Services Rock
 
Google Cloud Platform 2014Q1 - Starter Guide
Google Cloud Platform   2014Q1 - Starter GuideGoogle Cloud Platform   2014Q1 - Starter Guide
Google Cloud Platform 2014Q1 - Starter Guide
 
Single Page JavaScript WebApps... A Gradle Story
Single Page JavaScript WebApps... A Gradle StorySingle Page JavaScript WebApps... A Gradle Story
Single Page JavaScript WebApps... A Gradle Story
 
Future of Web Apps: Google Gears
Future of Web Apps: Google GearsFuture of Web Apps: Google Gears
Future of Web Apps: Google Gears
 
Droidcon Paris 2015
Droidcon Paris 2015Droidcon Paris 2015
Droidcon Paris 2015
 
OpenCms Days 2014 - User Generated Content in OpenCms 9.5
OpenCms Days 2014 - User Generated Content in OpenCms 9.5OpenCms Days 2014 - User Generated Content in OpenCms 9.5
OpenCms Days 2014 - User Generated Content in OpenCms 9.5
 

Último

Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?Antenna Manufacturer Coco
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CVKhem
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 

Último (20)

Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 

Building Grails Plugins - Tips And Tricks

  • 1. Building Grails Plugins tips and tricks from the wild
  • 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. • Plugins overview • Build your own plugin • testing • modularization • configuration • events • did I mention testing? what’s on the menu for tonight?
  • 4. App Plugin a plugin is just another grails application, with a few extra files * GrailsPlugin.groovy * Install, Uninstall and Upgrade scripts
  • 5. Cool hooks into the runtime environment of a running grails app, plus ability to add new ‘artefacts’ and participate in reloading events
  • 6. Existing Plugins there are a ton of existing plugins from security to Rich UI to searching to...you name it.
  • 7. common problem - this is one i’ve solved about 6 times. time for a plugin
  • 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. 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. 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. 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. Add Controller class BuildInfoController { ! static final List infoProperties = ['app.version'] def index = { ! ! render view:'index', model: [buildInfoProperties:infoProperties] ! } }
  • 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. 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. 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. 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. 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. 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. 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. 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. eventCreateWarStart = {warname, stagingDir -> Ant.propertyfile(file: "${stagingDir}/WEB-INF/classes/application.properties") { entry(key: 'build.date', value: new Date()) } }
  • 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. 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. 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. 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. 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/