The new static resources framework

47
The new static resources framework

description

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

Transcript of The new static resources framework

Page 1: The new static resources framework

The new static resources framework

Page 2: The new static resources framework

About me

Marc Palmer

Paid to develop WeceemFounder at NoticeLocal and Spotty Mushroom

Old skool Grails user since 0.4

Plugin writing maniac

Page 3: The new static resources framework

Resource management

Web performance

Page 4: The new static resources framework

Eternal caching

Minifying

Zipping

Bundling

Load order

Page 5: The new static resources framework

Old Problems

Ant

Java

Boilerplate code

Spring MVC config

Code verbosity

Page 6: The new static resources framework

New Problems

Download performance

Complex UI from plugins

Page 7: The new static resources framework

Blueprint jQuery jQuery UI

Your application

CSS JS JS +CSS

Admin UISecurity UI

Scaffolding

Custom CSS

Custom JS

Page 8: The new static resources framework

Optimization hell

Who includes resources & when?

Is the resource already optimized?

Load order

Performance: I want control

Page 9: The new static resources framework

Plugin version conventions help:

grails-blueprint-0.9grails-jquery-1.4.3grails-jquery-ui-1.8.2

Page 10: The new static resources framework

Resources plugin

Resource declaration DSL

Dependency resolution

Resource tags

Mapping pipeline

Modular and extensible

Processed at runtime

Page 11: The new static resources framework

Runtime?!

Page 12: The new static resources framework

Runtime?!

Page 13: The new static resources framework

Only at startup!

Works in any environment

Can be bypassed

Supports reloading

Tiny trade-off

Page 14: The new static resources framework

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

Base 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$

Page 15: The new static resources framework
Page 16: The new static resources framework

So far so...?Before After

Page 17: The new static resources framework

Resource DSL

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'] }}

File: grails-app/config/AwesomeResources.groovy

Page 18: The new static resources framework

Changes to GSP

<html> <head> <title>Welcome to Grails</title> <meta name="layout" content="main" /> <r:use modules="myStuff"/> </head> <body><!-- ... --> </body></html>

File: grails-app/views/index.gsp

Page 19: The new static resources framework

What does this page do?

What does it need to do it?

Explicit resource tags are wrong

Page 20: The new static resources framework

Changes to layout<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>

File: grails-app/views/layouts/main.gsp

Page 21: The new static resources framework

Link disposition

<head> <r:layoutResources/> <!-- head --></head><body> ... <r:layoutResources/> <!-- defer --></body>

Page 22: The new static resources framework

The basic tags

<script src="..."> <script src="..."> <link rel="stylesheet" ... /><link rel="stylesheet" ... />

<r:use modules="myStuff"/>

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

<img .../> <r:img .../>

Page 23: The new static resources framework

Disposition in DSL

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'] }}

File: grails-app/config/AwesomeResources.groovy

Page 24: The new static resources framework

Better...Before After

Page 25: The new static resources framework

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 { dependsOn 'grailsDefaults' defaultBundle 'core' resource url:[dir:'css', file:'branding.css'] resource url:[dir:'js', file:'ui-logic.js'] }}

Cross-module bundle!

Page 26: The new static resources framework

Dependency overrides

Text

modules = { grailsDefaults { defaultBundle 'core' resource url:[dir:'css', file:'main.css'] } myStuff { dependsOn 'grailsDefaults, blueprint' defaultBundle 'core' 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' } }}

Page 27: The new static resources framework
Page 28: The new static resources framework

Deferred inline JS

<r:script>

initUIThatDoesNotSuck();</r:script>

Page 29: The new static resources framework

We love the debugDevelopment 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

Page 30: The new static resources framework

What just happened?

Declarative resources

Single tag mechanism

Optimal, smart ordering

Resource de-deduping

Bundling

Page 31: The new static resources framework

Now the fun part

Page 32: The new static resources framework

Mapping pipelineCopy to work dir

Apply mappers

Modify resource

Add response handler

Update URI

Page 33: The new static resources framework

Mapper Artefacts

Text

class TestResourceMapper {

def priority = Integer.MAX_VALUE def map(resource, config) { def file = new File(resource.processedFile.parentFile,

"_${resource.processedFile.name}") assert resource.processedFile.renameTo(file) resource.processedFile = file resource.updateActualUrlFromProcessedFile() }}

Page 34: The new static resources framework

Bundling Mapper

/js/jquery-ui/jquery-ui.css

/css/blueprint/screen.css

/bundle_main.css

/css/main.css

Page 35: The new static resources framework

CSS rewriting

Both CSS and image may be renamedand/or moved

/css/main.css:

body { background-image: url(../images/bg.png);}

/bundle_main.css:

body { background-image: url(../changed.png);}

Before

After

Page 36: The new static resources framework

Resources Others

Page 37: The new static resources framework

Zipped Resources

Compresses files to xxx.css.gz

Keeps URI the same

Sets Content-Encoding: gzip

grails install-plugin zipped-resources

Page 38: The new static resources framework

Cached Resources

Renames to SHA256 digest of contents

Shortens name to base62 encoding

Flattens directory structure

Sets Expires to 1 year

grails install-plugin cached-resources

Page 39: The new static resources framework

/js/some-bloated-lib/plugin/foo.js

/b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9.js

/UpLdvv429fMac6FhEx5FMU2F3Cg1yj4HwUGmihFvjYR.js

Page 40: The new static resources framework

In production already

Page 41: The new static resources framework
Page 42: The new static resources framework
Page 43: The new static resources framework

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 = ...

Page 44: The new static resources framework

Integration into coreDSL

Dependency resolutionTags

Mapping pipelineBundling

CSS Rewriting

Mapper pluginse.g. zipped-resources

Grails Core

grails-resources 2.0

grails-xxxx-resources

Page 45: The new static resources framework

Future

Minify

CSS Sprites

Smart image links with auto w & h

E.S.P. - externalising inline JS

Flavours (content variants)

CDN up loaders

Page 46: The new static resources framework

Special thanks...

Peter Ledbrook

Luke Daley

Stéphane Maldini

Robert Fletcher

Burt Beckwith

Page 47: The new static resources framework

Q & A and linksSample app:http://bit.ly/awesomeapp1http://bit.ly/awesomeapp2

http://grails.org/plugin/resourceshttp://grails.org/plugin/zipped-resourceshttp://grails.org/plugin/cached-resourceshttp://noticelocal.comhttp://www.experienceoz.com.auhttp://www.icescrum.org

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