The new static resources framework

4,488 views

Published on

My talk on the new Resources plugin for Grails, at Groovy Grails Exchange 2010

0 Comments
8 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,488
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
48
Comments
0
Likes
8
Embeds 0
No embeds

No notes for slide

The new static resources framework

  1. 1. The new staticresources framework
  2. 2. About meMarc PalmerPaid to develop WeceemFounder at NoticeLocal and Spotty MushroomOld skool Grails user since 0.4Plugin writing maniac
  3. 3. Resource management Web performance
  4. 4. Eternal caching Minifying Zipping Bundling Load order
  5. 5. Old ProblemsAntJavaBoilerplate codeSpring MVC configCode verbosity
  6. 6. New ProblemsDownload performanceComplex UI from plugins
  7. 7. Your application Admin UICustom CSS Security UICustom JS Scaffolding CSS JS JS +CSS Blueprint jQuery jQuery UI
  8. 8. Optimization hellWho includes resources & when?Is the resource already optimized?Load orderPerformance: I want control
  9. 9. Plugin version conventions help:grails-blueprint-0.9grails-jquery-1.4.3grails-jquery-ui-1.8.2
  10. 10. Resources pluginResource declaration DSLDependency resolutionResource tagsMapping pipelineModular and extensibleProcessed at runtime
  11. 11. Runtime?!
  12. 12. Runtime?!
  13. 13. Only at startup!Works in any environmentCan be bypassedSupports reloadingTiny trade-off
  14. 14. Installing itmarcmacbook:AwesomeApp marc$ grails install-plugin resourcesWelcome to Grails 1.3.1 - http://grails.org/Licensed under Apache Standard License 2.0Grails home is set to: /usr/local/grails-1.3.1Base Directory: /Users/marc/Projects/AwesomeAppResolving dependencies...Dependencies resolved in 1172ms.Running script /usr/local/grails-1.3.1/scripts/InstallPlugin.groovyEnvironment set to developmentInstalling zip ../checkout/Resources/grails-resources-1.0-RC1.zip... ... [mkdir] Created dir: /Users/marc/.grails/1.3.1/projects/AwesomeApp/plugins/resources-1.0-RC1 [unzip] Expanding: /Users/marc/Projects/checkout/Resources/grails-resources-1.0-RC1.zip into /Users/marc/.grails/1.3.1/projects/AwesomeApp/plugins/resources-1.0-RC1Installed plugin resources-1.0-RC1 to location /Users/marc/.grails/1.3.1/projects/AwesomeApp/plugins/resources-1.0-RC1. ...Resolving plugin JAR dependencies ...Executing resources-1.0-RC1 plugin post-install script ...Plugin resources-1.0-RC1 installedmarcmacbook:AwesomeApp marc$
  15. 15. So far so...?Before After
  16. 16. Resource DSLFile: grails-app/config/AwesomeResources.groovymodules = { grailsDefaults { resource url:[dir:css, file:main.css] resource url:[dir:js, file:application.js], disposition:head resource url:[dir:js/prototype, file:prototype.js] } myStuff { dependsOn grailsDefaults resource url:[dir:css, file:branding.css] resource url:[dir:js, file:ui-logic.js] }}
  17. 17. Changes to GSPFile: grails-app/views/index.gsp<html> <head> <title>Welcome to Grails</title> <meta name="layout" content="main" /> <r:use modules="myStuff"/> </head> <body><!-- ... --> </body></html>
  18. 18. What does this page do? What does it need to do it?Explicit resource tags are wrong
  19. 19. Changes to layoutFile: grails-app/views/layouts/main.gsp<html> <head> <title><g:layoutTitle default="Grails" /></title> <r:layoutResources /> <link rel="shortcut icon" href="${r.resource(dir:images,file:favicon.ico)}" type="image/x-icon" /> <g:layoutHead /> </head> <body> <div id="spinner" class="spinner" style="display:none;"> <img src="${r.resource(dir:images,file:spinner.gif)}" alt="Spinner" /> </div> <div id="grailsLogo" class="logo"><a href="http://grails.org"> <img src="${r.resource(dir:images,file:grails_logo.png)}" alt="Grails" border="0" /></a> </div> <g:layoutBody /> <r:layoutResources /> <script type="text/javascript"> oneLineAmazingUI(); </script> </body></html>
  20. 20. Link disposition<head> <r:layoutResources/> <!-- head --></head><body> ... <r:layoutResources/> <!-- defer --></body>
  21. 21. The basic tags<script src="..."><script src="..."> <r:use modules="myStuff"/><link rel="stylesheet" ... /><link rel="stylesheet" ... /><g:resource .../> <r:resource .../><img .../> <r:img .../>
  22. 22. Disposition in DSLFile: grails-app/config/AwesomeResources.groovymodules = { grailsDefaults { resource url:[dir:css, file:main.css] resource url:[dir:js, file:application.js], disposition:head resource url:[dir:js/prototype, file:prototype.js] } myStuff { dependsOn grailsDefaults resource url:[dir:css, file:branding.css] resource url:[dir:js, file:ui-logic.js] }}
  23. 23. Better...Before After
  24. 24. Bundlingmodules = { grailsDefaults { resource url:[dir:css, file:main.css] resource url:[dir:js, file:application.js], disposition:head resource url:[dir:js/prototype, file:prototype.js], bundle: core } myStuff { Cross-module bundle! dependsOn grailsDefaults defaultBundle core resource url:[dir:css, file:branding.css] resource url:[dir:js, file:ui-logic.js] }}
  25. 25. Dependency overridesmodules = { grailsDefaults { defaultBundle core resource url:[dir:css, file:main.css] } myStuff { dependsOn grailsDefaults, blueprint defaultBundle core Text resource url:[dir:css, file:branding.css] resource url:[dir:js, file:ui-logic.js] } overrides { jquery { defaultBundle core } jquery-ui { resource id:js, bundle: core resource id:theme, bundle: core } blueprint { resource id:main, bundle: core } }}
  26. 26. Deferred inline JS<r:script> initUIThatDoesNotSuck();</r:script>
  27. 27. We love the debugDevelopment reloadsTurn it all off: add ?_debugResources=yCache defeat: add ?_refreshResources=yX-Grails-Resources-Original-Src:/bundle-grailsDefaults.js, /js/application.js, /js/prototype/prototype.js
  28. 28. What just happened?Declarative resourcesSingle tag mechanismOptimal, smart orderingResource de-dedupingBundling
  29. 29. Now the fun part
  30. 30. Mapping pipeline Copy to work dir Apply mappers Modify resource Add response handler Update URI
  31. 31. Mapper Artefactsclass TestResourceMapper { def priority = Integer.MAX_VALUE def map(resource, config) { Text def file = new File(resource.processedFile.parentFile, "_${resource.processedFile.name}") assert resource.processedFile.renameTo(file) resource.processedFile = file resource.updateActualUrlFromProcessedFile() }}
  32. 32. Bundling Mapper /js/jquery-ui/jquery-ui.css /css/blueprint/screen.css /css/main.css /bundle_main.css
  33. 33. CSS rewriting Before /css/main.css: body {Both CSS and image background-image: url(../images/bg.png);may be renamed }and/or moved After /bundle_main.css: body { background-image: url(../changed.png); }
  34. 34. Resources Others
  35. 35. Zipped Resourcesgrails install-plugin zipped-resourcesCompresses files to xxx.css.gzKeeps URI the sameSets Content-Encoding: gzip
  36. 36. Cached Resourcesgrails install-plugin cached-resourcesRenames to SHA256 digest of contentsShortens name to base62 encodingFlattens directory structureSets Expires to 1 year
  37. 37. /js/some-bloated-lib/plugin/foo.js/b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9.js /UpLdvv429fMac6FhEx5FMU2F3Cg1yj4HwUGmihFvjYR.js
  38. 38. In production already
  39. 39. Config conventionsgrails.resources.<mapper>.excludes = [...]grails.resources.modules = { ... }grails.resources.debug = truegrails.resources.work.dir = ...grails.resources.uri.prefix = staticgrails.resources.adhoc.patterns = ...
  40. 40. Integration into core DSL Grails Core Dependency resolution Tags Mapping pipelinegrails-resources 2.0 Bundling CSS Rewriting Mapper pluginsgrails-xxxx-resources e.g. zipped-resources
  41. 41. FutureMinifyCSS SpritesSmart image links with auto w & hE.S.P. - externalising inline JSFlavours (content variants)CDN up loaders
  42. 42. Special thanks... Peter Ledbrook Luke Daley Stéphane Maldini Robert Fletcher Burt Beckwith
  43. 43. Q & A and linksSample app:http://bit.ly/awesomeapp1http://bit.ly/awesomeapp2http://grails.org/plugin/resourceshttp://grails.org/plugin/zipped-resourceshttp://grails.org/plugin/cached-resourceshttp://noticelocal.comhttp://www.experienceoz.com.auhttp://www.icescrum.orgAlso check out some unsung plugins:taxonomy, invitation-only, cache-headers,one-time-data, email-confirmation

×