URL: https://github.com/peterbe/tornado-utils/blob/master/tornado_utils/tornado_static.py

This is Part 2 in a series of blogs about various bits and pieces in the tornado-utils package. Part 1 is here

tornado_static

Code is here

This code takes care of two things: 1) optimizing your static resources and 2) bundling and serving them with unique names so you can cache aggressively.

The trick is to make your development environment such that there's no need to do anything when in "debug mode" but when in "production mode" it needs to be perfect. Which files (e.g. jquery.js or style.css) you use and bundle is up to you and it's something you control from the templates in your Tornado app. Not a config setting because, almost always, which resources (aka. assets) you need is known and relevant only to the templates where you're working.

Using UI modules in Tornado requires a bit of Tornado-fu but here's one example and here is another (untested) example:


# app.py

import tornado.web
from tornado_utils.tornado_static import (
  StaticURL, Static, PlainStaticURL, PlainStatic) 

class Application(tornado.web.Application):
   def __init__(self):
       ui_modules = {}
       if options.debug:
           ui_modules['Static'] = PlainStatic
           ui_modules['StaticURL'] = PlainStaticURL
       else:
           ui_modules['Static'] = Static
           ui_modules['StaticURL'] = StaticURL

       app_settings = dict(
           template_path="templates",
           static_path="static",
           ui_modules=ui_modules,
           debug=options.debug,
           UGLIFYJS_LOCATION='~/bin/uglifyjs',
           CLOSURE_LOCATION="static/compiler.jar",
           YUI_LOCATION="static/yuicompressor-2.4.2.jar",
           cdn_prefix="cdn.mycloud.com",
       )

       handlers = [
         (r"/", HomeHandler),
         (r"/entry/([0-9]+)", EntryHandler),
       ] 
       super(Application, self).__init__(handlers, **app_settings)

def main(): # pragma: no cover
   tornado.options.parse_command_line()
   http_server = tornado.httpserver.HTTPServer(Application())
   http_server.listen(options.port)
   tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
   main()

Note! If you're looking to optimize your static resources in a Tornado app you probably already have a "framework" for setting up UI modules into your app. The above code is just to wet your appetite and to show how easy it is to set up. The real magic starts to happen in the template code. Here's a sample implementation:


<!doctype html>
<html>
   <head>
       <title>{% block title %}{{ page_title }}{% end %}</title>
       <meta charset="utf-8">
       {% module Static("css/ext/jquery.gritter.css", "css/style.css") %}
   </head>
   <body>
      <header>...</header>
   {% block content %}
   {% end %}
   {% module Static("js/ext/head.load.min.js") %}
   <script>
   var POST_LOAD = '{% module StaticURL("js/dojo.js", "js/dojo.async.js") %}';
   </script>
   </body>
</html>

What you get when run is a template that looks like this:


<!doctype html>
<html>
   <head>
       <title>My title</title>
       <meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="//cdn.mycloud.com/combined/jquery.gritter.css.style.1313206609.css">
   </head>
   ...

(Note that this will create a whitespace optimized filed called "jquery.gritter.css.style.1313206609.css" in "/tmp/combined")

Have a play and see if it makes sense in your app. I do believe this can do with some Open Source love but so far it works great for me on Kwissle, DoneCal and TooCoolFor.Me

Comments

Your email will never ever be published.

Previous:
Goodies from tornado-utils - part 1: TestClient September 20, 2011 Tornado
Next:
Goodies from tornado-utils - part 3: send_mail September 24, 2011 Tornado
Related by category:
Fastest database for Tornado October 9, 2013 Tornado
How I stopped worrying about IO blocking Tornado September 18, 2012 Tornado
Integrate BrowserID in a Tornado web app November 22, 2011 Tornado
My tricks for using AsyncHTTPClient in Tornado October 13, 2010 Tornado
Related by keyword:
Advanced Closure Compiler vs UglifyJS2 January 20, 2016 JavaScript
Local Django development with Nginx October 11, 2010 Django
Comparing Google Closure with UglifyJS July 10, 2011 JavaScript
The awesomest way possible to serve your static stuff in Django with Nginx March 24, 2010 Django