SlideShare ist ein Scribd-Unternehmen logo
1 von 47
Downloaden Sie, um offline zu lesen
The new static
resources framework
About me
Marc Palmer
Paid to develop Weceem
Founder at NoticeLocal and Spotty Mushroom

Old skool Grails user since 0.4

Plugin writing maniac
Resource management


  Web performance
Eternal caching
   Minifying
   Zipping
   Bundling
  Load order
Old Problems
Ant
Java
Boilerplate code
Spring MVC config
Code verbosity
New Problems

Download performance

Complex UI from plugins
Your application


                 Admin UI
Custom CSS                       Security UI


Custom JS                        Scaffolding




   CSS           JS               JS +CSS
  Blueprint         jQuery       jQuery UI
Optimization hell
Who includes resources & when?
Is the resource already optimized?
Load order
Performance: I want control
Plugin version conventions help:

grails-blueprint-0.9
grails-jquery-1.4.3
grails-jquery-ui-1.8.2
Resources plugin
Resource declaration DSL
Dependency resolution
Resource tags
Mapping pipeline
Modular and extensible
Processed at runtime
Runtime?!
Runtime?!
Only at startup!

Works in any environment

Can be bypassed

Supports reloading

Tiny trade-off
Installing it
marcmacbook:AwesomeApp marc$ grails install-plugin resources
Welcome to Grails 1.3.1 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: /usr/local/grails-1.3.1

Base Directory: /Users/marc/Projects/AwesomeApp
Resolving dependencies...
Dependencies resolved in 1172ms.
Running script /usr/local/grails-1.3.1/scripts/InstallPlugin.groovy
Environment set to development
Installing 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-RC1
Installed 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 installed
marcmacbook:AwesomeApp marc$
So far so...?
Before          After
Resource DSL
File: grails-app/config/AwesomeResources.groovy

modules = {
    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']
    }
}
Changes to GSP
File: 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>
What does this page do?

  What does it need to do it?

Explicit resource tags are wrong
Changes to layout
File: 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>
Link disposition

<head>
     <r:layoutResources/> <!-- head -->
</head>
<body>
     ...
     <r:layoutResources/> <!-- defer -->
</body>
The basic tags
<script src="...">
<script src="...">              <r:use modules="myStuff"/>
<link rel="stylesheet" ... />
<link rel="stylesheet" ... />




<g:resource .../>               <r:resource .../>




<img .../>                      <r:img .../>
Disposition in DSL
File: grails-app/config/AwesomeResources.groovy

modules = {
    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']
    }
}
Better...
Before         After
Bundling
modules = {
    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']
    }
}
Dependency overrides
modules = {
    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'
        }
    }
}
Deferred inline JS

<r:script>
    initUIThatDoesNotSuck();
</r:script>
We love the debug
Development reloads

Turn it all off: add ?_debugResources=y

Cache defeat: add ?_refreshResources=y

X-Grails-Resources-Original-Src:
/bundle-grailsDefaults.js, /js/application.js, /
js/prototype/prototype.js
What just happened?
Declarative resources
Single tag mechanism
Optimal, smart ordering
Resource de-deduping
Bundling
Now the fun part
Mapping pipeline
    Copy to work dir



     Apply mappers


       Modify resource


     Add response handler




       Update URI
Mapper Artefacts
class 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()
    }
}
Bundling Mapper
   /js/jquery-ui/jquery-ui.css

   /css/blueprint/screen.css

         /css/main.css




       /bundle_main.css
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);
                     }
Resources   Others
Zipped Resources
grails install-plugin zipped-resources


Compresses files to xxx.css.gz
Keeps URI the same
Sets Content-Encoding: gzip
Cached Resources
grails install-plugin cached-resources

Renames to SHA256 digest of contents
Shortens name to base62 encoding
Flattens directory structure
Sets Expires to 1 year
/js/some-bloated-lib/plugin/foo.js




/b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9.js




         /UpLdvv429fMac6FhEx5FMU2F3Cg1yj4HwUGmihFvjYR.js
In production already
Config conventions
grails.resources.<mapper>.excludes = [...]
grails.resources.modules = { ... }
grails.resources.debug = true
grails.resources.work.dir = ...
grails.resources.uri.prefix = 'static'
grails.resources.adhoc.patterns = ...
Integration into core
                               DSL
     Grails Core        Dependency resolution
                               Tags


                          Mapping pipeline
grails-resources 2.0         Bundling
                           CSS Rewriting



                           Mapper plugins
grails-xxxx-resources   e.g. zipped-resources
Future
Minify
CSS Sprites
Smart image links with auto w & h
E.S.P. - externalising inline JS
Flavours (content variants)
CDN up loaders
Special thanks...
    Peter Ledbrook
      Luke Daley
   Stéphane Maldini
    Robert Fletcher
    Burt Beckwith
Q & A and links
Sample app:
http://bit.ly/awesomeapp1
http://bit.ly/awesomeapp2

http://grails.org/plugin/resources
http://grails.org/plugin/zipped-resources
http://grails.org/plugin/cached-resources
http://noticelocal.com
http://www.experienceoz.com.au
http://www.icescrum.org


Also check out some unsung plugins:
taxonomy, invitation-only, cache-headers,
one-time-data, email-confirmation

Weitere ähnliche Inhalte

Was ist angesagt?

Hortonworks HBase Meetup Presentation
Hortonworks HBase Meetup PresentationHortonworks HBase Meetup Presentation
Hortonworks HBase Meetup PresentationHortonworks
 
Big data with hadoop Setup on Ubuntu 12.04
Big data with hadoop Setup on Ubuntu 12.04Big data with hadoop Setup on Ubuntu 12.04
Big data with hadoop Setup on Ubuntu 12.04Mandakini Kumari
 
Drupal as a web framework
Drupal as a web frameworkDrupal as a web framework
Drupal as a web frameworkAdam Kalsey
 
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...Codemotion
 
Create a Varnish cluster in Kubernetes for Drupal caching - DrupalCon North A...
Create a Varnish cluster in Kubernetes for Drupal caching - DrupalCon North A...Create a Varnish cluster in Kubernetes for Drupal caching - DrupalCon North A...
Create a Varnish cluster in Kubernetes for Drupal caching - DrupalCon North A...Ovadiah Myrgorod
 
Whirr dev-up-puppetconf2011
Whirr dev-up-puppetconf2011Whirr dev-up-puppetconf2011
Whirr dev-up-puppetconf2011Puppet
 
Think Distributed: The Hazelcast Way
Think Distributed: The Hazelcast WayThink Distributed: The Hazelcast Way
Think Distributed: The Hazelcast WayRahul Gupta
 
Hazelcast
HazelcastHazelcast
Hazelcastoztalip
 
Device Synchronization with Javascript and PouchDB
Device Synchronization with Javascript and PouchDBDevice Synchronization with Javascript and PouchDB
Device Synchronization with Javascript and PouchDBFrank Rousseau
 
Hosting huge amount of binaries in JCR
Hosting huge amount of binaries in JCRHosting huge amount of binaries in JCR
Hosting huge amount of binaries in JCRWoonsan Ko
 
Drupal 8 Cache: Under the hood
Drupal 8 Cache: Under the hoodDrupal 8 Cache: Under the hood
Drupal 8 Cache: Under the hoodPiyuesh Kumar
 
Karine bosch caching-spsbe14
Karine bosch caching-spsbe14Karine bosch caching-spsbe14
Karine bosch caching-spsbe14BIWUG
 
H cat berlinbuzzwords2012
H cat berlinbuzzwords2012H cat berlinbuzzwords2012
H cat berlinbuzzwords2012Hortonworks
 
Introduction to Apache Tajo: Data Warehouse for Big Data
Introduction to Apache Tajo: Data Warehouse for Big DataIntroduction to Apache Tajo: Data Warehouse for Big Data
Introduction to Apache Tajo: Data Warehouse for Big DataGruter
 
Python-CouchDB Training at PyCon PL 2012
Python-CouchDB Training at PyCon PL 2012Python-CouchDB Training at PyCon PL 2012
Python-CouchDB Training at PyCon PL 2012Stefan Kögl
 
Big Data Step-by-Step: Infrastructure 1/3: Local VM
Big Data Step-by-Step: Infrastructure 1/3: Local VMBig Data Step-by-Step: Infrastructure 1/3: Local VM
Big Data Step-by-Step: Infrastructure 1/3: Local VMJeffrey Breen
 
Spring Data MongoDB 介紹
Spring Data MongoDB 介紹Spring Data MongoDB 介紹
Spring Data MongoDB 介紹Kuo-Chun Su
 
Configuration Management in Drupal 8: A preview (DrupalCamp Alpe Adria 2014)
Configuration Management in Drupal 8: A preview (DrupalCamp Alpe Adria 2014)Configuration Management in Drupal 8: A preview (DrupalCamp Alpe Adria 2014)
Configuration Management in Drupal 8: A preview (DrupalCamp Alpe Adria 2014)Nuvole
 

Was ist angesagt? (20)

Hortonworks HBase Meetup Presentation
Hortonworks HBase Meetup PresentationHortonworks HBase Meetup Presentation
Hortonworks HBase Meetup Presentation
 
Big data with hadoop Setup on Ubuntu 12.04
Big data with hadoop Setup on Ubuntu 12.04Big data with hadoop Setup on Ubuntu 12.04
Big data with hadoop Setup on Ubuntu 12.04
 
Drupal as a web framework
Drupal as a web frameworkDrupal as a web framework
Drupal as a web framework
 
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...
Thijs Feryn - Leverage HTTP to deliver cacheable websites - Codemotion Berlin...
 
Create a Varnish cluster in Kubernetes for Drupal caching - DrupalCon North A...
Create a Varnish cluster in Kubernetes for Drupal caching - DrupalCon North A...Create a Varnish cluster in Kubernetes for Drupal caching - DrupalCon North A...
Create a Varnish cluster in Kubernetes for Drupal caching - DrupalCon North A...
 
Whirr dev-up-puppetconf2011
Whirr dev-up-puppetconf2011Whirr dev-up-puppetconf2011
Whirr dev-up-puppetconf2011
 
Think Distributed: The Hazelcast Way
Think Distributed: The Hazelcast WayThink Distributed: The Hazelcast Way
Think Distributed: The Hazelcast Way
 
Intro To Couch Db
Intro To Couch DbIntro To Couch Db
Intro To Couch Db
 
Hazelcast
HazelcastHazelcast
Hazelcast
 
Device Synchronization with Javascript and PouchDB
Device Synchronization with Javascript and PouchDBDevice Synchronization with Javascript and PouchDB
Device Synchronization with Javascript and PouchDB
 
Hosting huge amount of binaries in JCR
Hosting huge amount of binaries in JCRHosting huge amount of binaries in JCR
Hosting huge amount of binaries in JCR
 
Drupal 8 Cache: Under the hood
Drupal 8 Cache: Under the hoodDrupal 8 Cache: Under the hood
Drupal 8 Cache: Under the hood
 
Karine bosch caching-spsbe14
Karine bosch caching-spsbe14Karine bosch caching-spsbe14
Karine bosch caching-spsbe14
 
H cat berlinbuzzwords2012
H cat berlinbuzzwords2012H cat berlinbuzzwords2012
H cat berlinbuzzwords2012
 
Introduction to Apache Tajo: Data Warehouse for Big Data
Introduction to Apache Tajo: Data Warehouse for Big DataIntroduction to Apache Tajo: Data Warehouse for Big Data
Introduction to Apache Tajo: Data Warehouse for Big Data
 
Python-CouchDB Training at PyCon PL 2012
Python-CouchDB Training at PyCon PL 2012Python-CouchDB Training at PyCon PL 2012
Python-CouchDB Training at PyCon PL 2012
 
Big Data Step-by-Step: Infrastructure 1/3: Local VM
Big Data Step-by-Step: Infrastructure 1/3: Local VMBig Data Step-by-Step: Infrastructure 1/3: Local VM
Big Data Step-by-Step: Infrastructure 1/3: Local VM
 
Spring Data MongoDB 介紹
Spring Data MongoDB 介紹Spring Data MongoDB 介紹
Spring Data MongoDB 介紹
 
Hadoop security
Hadoop securityHadoop security
Hadoop security
 
Configuration Management in Drupal 8: A preview (DrupalCamp Alpe Adria 2014)
Configuration Management in Drupal 8: A preview (DrupalCamp Alpe Adria 2014)Configuration Management in Drupal 8: A preview (DrupalCamp Alpe Adria 2014)
Configuration Management in Drupal 8: A preview (DrupalCamp Alpe Adria 2014)
 

Ähnlich wie The new static resources framework

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
 
Ajax Performance Tuning and Best Practices
Ajax Performance Tuning and Best PracticesAjax Performance Tuning and Best Practices
Ajax Performance Tuning and Best PracticesDoris Chen
 
Build Your Own CMS with Apache Sling
Build Your Own CMS with Apache SlingBuild Your Own CMS with Apache Sling
Build Your Own CMS with Apache SlingBob Paulin
 
JavaScript Modules in Practice
JavaScript Modules in PracticeJavaScript Modules in Practice
JavaScript Modules in PracticeMaghdebura
 
Web applications with Catalyst
Web applications with CatalystWeb applications with Catalyst
Web applications with Catalystsvilen.ivanov
 
Building Performance - ein Frontend-Build-Prozess für Java mit Maven
Building Performance - ein Frontend-Build-Prozess für Java mit MavenBuilding Performance - ein Frontend-Build-Prozess für Java mit Maven
Building Performance - ein Frontend-Build-Prozess für Java mit MavenOliver Ochs
 
[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVCAlive Kuo
 
Build Better Responsive websites. Hrvoje Jurišić
Build Better Responsive websites. Hrvoje JurišićBuild Better Responsive websites. Hrvoje Jurišić
Build Better Responsive websites. Hrvoje JurišićMeetMagentoNY2014
 
TurboGears2 Pluggable Applications
TurboGears2 Pluggable ApplicationsTurboGears2 Pluggable Applications
TurboGears2 Pluggable ApplicationsAlessandro Molina
 
Resource registries plone conf 2014
Resource registries plone conf 2014Resource registries plone conf 2014
Resource registries plone conf 2014Ramon Navarro
 
Front end performance optimization
Front end performance optimizationFront end performance optimization
Front end performance optimizationStevie T
 
Django Rest Framework and React and Redux, Oh My!
Django Rest Framework and React and Redux, Oh My!Django Rest Framework and React and Redux, Oh My!
Django Rest Framework and React and Redux, Oh My!Eric Palakovich Carr
 
Resource Registries: Plone Conference 2014
Resource Registries: Plone Conference 2014Resource Registries: Plone Conference 2014
Resource Registries: Plone Conference 2014Rob Gietema
 

Ähnlich wie The new static resources framework (20)

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
 
Ajax Performance Tuning and Best Practices
Ajax Performance Tuning and Best PracticesAjax Performance Tuning and Best Practices
Ajax Performance Tuning and Best Practices
 
Build Your Own CMS with Apache Sling
Build Your Own CMS with Apache SlingBuild Your Own CMS with Apache Sling
Build Your Own CMS with Apache Sling
 
JavaScript Modules in Practice
JavaScript Modules in PracticeJavaScript Modules in Practice
JavaScript Modules in Practice
 
Web applications with Catalyst
Web applications with CatalystWeb applications with Catalyst
Web applications with Catalyst
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
 
Building Performance - ein Frontend-Build-Prozess für Java mit Maven
Building Performance - ein Frontend-Build-Prozess für Java mit MavenBuilding Performance - ein Frontend-Build-Prozess für Java mit Maven
Building Performance - ein Frontend-Build-Prozess für Java mit Maven
 
Death of a Themer
Death of a ThemerDeath of a Themer
Death of a Themer
 
[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC
 
Build Better Responsive websites. Hrvoje Jurišić
Build Better Responsive websites. Hrvoje JurišićBuild Better Responsive websites. Hrvoje Jurišić
Build Better Responsive websites. Hrvoje Jurišić
 
TurboGears2 Pluggable Applications
TurboGears2 Pluggable ApplicationsTurboGears2 Pluggable Applications
TurboGears2 Pluggable Applications
 
Resource registries plone conf 2014
Resource registries plone conf 2014Resource registries plone conf 2014
Resource registries plone conf 2014
 
Front end performance optimization
Front end performance optimizationFront end performance optimization
Front end performance optimization
 
Web-Performance
Web-PerformanceWeb-Performance
Web-Performance
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
 
Django Rest Framework and React and Redux, Oh My!
Django Rest Framework and React and Redux, Oh My!Django Rest Framework and React and Redux, Oh My!
Django Rest Framework and React and Redux, Oh My!
 
Resource Registries: Plone Conference 2014
Resource Registries: Plone Conference 2014Resource Registries: Plone Conference 2014
Resource Registries: Plone Conference 2014
 

The new static resources framework

  • 2. About me Marc Palmer Paid to develop Weceem Founder at NoticeLocal and Spotty Mushroom Old skool Grails user since 0.4 Plugin writing maniac
  • 3. Resource management Web performance
  • 4. Eternal caching Minifying Zipping Bundling Load order
  • 7. Your application Admin UI Custom CSS Security UI Custom JS Scaffolding CSS JS JS +CSS Blueprint jQuery jQuery UI
  • 8. Optimization hell Who includes resources & when? Is the resource already optimized? Load order Performance: I want control
  • 9. Plugin version conventions help: grails-blueprint-0.9 grails-jquery-1.4.3 grails-jquery-ui-1.8.2
  • 10. Resources plugin Resource declaration DSL Dependency resolution Resource tags Mapping pipeline Modular and extensible Processed at runtime
  • 13. Only at startup! Works in any environment Can be bypassed Supports reloading Tiny trade-off
  • 14. Installing it marcmacbook:AwesomeApp marc$ grails install-plugin resources Welcome to Grails 1.3.1 - http://grails.org/ Licensed under Apache Standard License 2.0 Grails home is set to: /usr/local/grails-1.3.1 Base Directory: /Users/marc/Projects/AwesomeApp Resolving dependencies... Dependencies resolved in 1172ms. Running script /usr/local/grails-1.3.1/scripts/InstallPlugin.groovy Environment set to development Installing 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-RC1 Installed 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 installed marcmacbook:AwesomeApp marc$
  • 15.
  • 17. Resource DSL File: grails-app/config/AwesomeResources.groovy modules = { 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'] } }
  • 18. Changes to GSP File: 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>
  • 19. What does this page do? What does it need to do it? Explicit resource tags are wrong
  • 20. Changes to layout File: 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>
  • 21. Link disposition <head> <r:layoutResources/> <!-- head --> </head> <body> ... <r:layoutResources/> <!-- defer --> </body>
  • 22. The basic tags <script src="..."> <script src="..."> <r:use modules="myStuff"/> <link rel="stylesheet" ... /> <link rel="stylesheet" ... /> <g:resource .../> <r:resource .../> <img .../> <r:img .../>
  • 23. Disposition in DSL File: grails-app/config/AwesomeResources.groovy modules = { 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'] } }
  • 25. Bundling modules = { 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'] } }
  • 26. Dependency overrides modules = { 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' } } }
  • 27.
  • 28. Deferred inline JS <r:script> initUIThatDoesNotSuck(); </r:script>
  • 29. We love the debug Development reloads Turn it all off: add ?_debugResources=y Cache defeat: add ?_refreshResources=y X-Grails-Resources-Original-Src: /bundle-grailsDefaults.js, /js/application.js, / js/prototype/prototype.js
  • 30. What just happened? Declarative resources Single tag mechanism Optimal, smart ordering Resource de-deduping Bundling
  • 31. Now the fun part
  • 32. Mapping pipeline Copy to work dir Apply mappers Modify resource Add response handler Update URI
  • 33. Mapper Artefacts class 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() } }
  • 34. Bundling Mapper /js/jquery-ui/jquery-ui.css /css/blueprint/screen.css /css/main.css /bundle_main.css
  • 35. 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); }
  • 36. Resources Others
  • 37. Zipped Resources grails install-plugin zipped-resources Compresses files to xxx.css.gz Keeps URI the same Sets Content-Encoding: gzip
  • 38. Cached Resources grails install-plugin cached-resources Renames to SHA256 digest of contents Shortens name to base62 encoding Flattens directory structure Sets Expires to 1 year
  • 41.
  • 42.
  • 43. Config conventions grails.resources.<mapper>.excludes = [...] grails.resources.modules = { ... } grails.resources.debug = true grails.resources.work.dir = ... grails.resources.uri.prefix = 'static' grails.resources.adhoc.patterns = ...
  • 44. Integration into core DSL Grails Core Dependency resolution Tags Mapping pipeline grails-resources 2.0 Bundling CSS Rewriting Mapper plugins grails-xxxx-resources e.g. zipped-resources
  • 45. Future Minify CSS Sprites Smart image links with auto w & h E.S.P. - externalising inline JS Flavours (content variants) CDN up loaders
  • 46. Special thanks... Peter Ledbrook Luke Daley Stéphane Maldini Robert Fletcher Burt Beckwith
  • 47. Q & A and links Sample app: http://bit.ly/awesomeapp1 http://bit.ly/awesomeapp2 http://grails.org/plugin/resources http://grails.org/plugin/zipped-resources http://grails.org/plugin/cached-resources http://noticelocal.com http://www.experienceoz.com.au http://www.icescrum.org Also check out some unsung plugins: taxonomy, invitation-only, cache-headers, one-time-data, email-confirmation