Getting Started With Google Closure in Sinatra
Recently, we’ve been experimenting with Google Closure which includes both a Javascript library and a compiler to optimize your Javascript. Oddly, the getting started tutorial doesn’t include how to setup a project with Closure. I also ran into some issues with the compiler using advanced optimizations with very standard Javascript code. So, how can you avoid all of these problems I just went through? Read this tutorial! We’ll go through how to setup Closure quick and dirty running in a Sinatra application so you can start exploring. After that, I’ll tell you one of the major pitfalls I happened to fall into on my way to compiling my code with advanced optimizations, a key feature of Closure.
tl;dr; A little impatient, are we? Just go to the git repository.
Step 1. How the #!@% do I setup Closure?
I’m glad you asked. Follow these steps.
1. mkdir godzilla # why not? godzilla will hold your HTML and JS files for experimenting with Closure
2. cd godzilla # I know it’s scary going near godzilla, but be brave
3. gem install sinatra # install the sinatra web server, a lightweight web framework for Ruby
4. echo “require ‘sinatra’” > server.rb # create the file used to run our sinatra server
5. mkdir public # create a public directory for all of our static HTML, JS, and CSS
6. mkdir public/javascripts # holds application javascript files
7. svn checkout http://closure-library.googlecode.com/svn/trunk/ closure-library # grab a copy of Google Closure
8. cp -R closure-library/closure/* public/ # copy the static assets that you need to write Closure JS, also grabbing the Python scripts needed to eventually compile your javascripts
Step 2. Write a very simple Javascript application with Closure.
The application we will be creating simply writes messages to the viewer by appending elements to the document body. The godzilla.test.MessageWriter class is responsible for writing to the DOM.
1. Create a file public/test.html with this source:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<code><!DOCTYPE html>
<html>
<head>
<title>Testing Closure</title>
<!-- Include the Closure base.js file, responsible for bootstrapping the Closure runtime -->
<script type="text/javascript" src="/goog/base.js"></script>
<!-- This is our application-specific javascript file -->
<script type="text/javascript" src="/javascripts/test.js"></script>
</head>
<body onload="execute()">
<h1>Messages</h1>
</body>
</html>
</code> |
2. Create a file public/javascripts/test.js with this source:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<code>// This file provides this namespace to other code
// that may reference this namespace.
// This information is used by the compiler to
// optimize your code.
goog.provide("godzilla.test");
goog.provide("godzilla.test.MessageWriter");
// We want to use some functionality defined in this module.
goog.require("goog.dom");
// A simple object constructor.
// MessageWriter will append messages to our document body with a given prefix
godzilla.test.MessageWriter = function(prefix) {
this.prefix = prefix;
}
// Append a message to our document's body
godzilla.test.MessageWriter.prototype.write = function(message) {
var m = goog.dom.createDom("div", null, this.prefix + message);
goog.dom.appendChild(document.body, m);
}
// Let's write some messages
function execute() {
var writer = new godzilla.test.MessageWriter("Info: ");
writer.write("Mothra is coming!!!");
writer.write("I see your Schwartz is as big as mine.");
}
// Feel free to write more awesome messages
</code> |
Step 3. Watch your uncompiled script run beautifully.
1. ruby server.rb # start up your sinatra server
2. Navigate to http://localhost:4567/test.html # see that your messages made it onto the page
Step 4. Cry when your script fails to compile properly with advanced optimizations.
1. Download the latest compiler jar here: http://closure-compiler.googlecode.com/files/compiler-latest.zip
2. Extract it and move compiler.jar into public/compiler.jar
3. cd public
4. bin/build/closurebuilder.py -root=../closure-library/ -root=javascripts/ -namespace=godzilla.test -output_mode=compiled -compiler_jar=compiler.jar -compiler_flags=”-compilation_level=ADVANCED_OPTIMIZATIONS” > javascripts/test.compiled.js
5. Notice the warnings :(
6. Modify your test.html file to point to the compiled javascript.
1 2 3 4 5 6 7 8 |
<code><!-- Include the Closure base.js file, responsible for bootstrapping the Closure runtime -->
<!-- <script type="text/javascript" src="/goog/base.js"></script> -->
<!-- This is our application-specific javascript file -->
<!-- <script type="text/javascript" src="/javascripts/test.js"></script> -->
<script type="text/javascript" src="/javascripts/test.compiled.js"></script>
</code> |
7. Browse to your page again: http://localhost:4567/test.html
8. Notice that nothing happens, and the Javascript error is “Uncaught ReferenceError: execute is not defined” (at least in Chrome this is what you will see)
9. What happened? Move on to Step 5 to find out.
Step 5. Export your execute function and let Closure know that MessageWriter is a constructor.
1. Add this line to the end of test.js: window["execute"] = execute;
2. Recompile your javascript and note you still have some warnings
3. Navigate to your test page and see that everything is working again! This is because the Closure compiler renames your execute function to a shorter name in order to reduce the size of the final output file. In order for your old references to the function to work, you need to export the function to window.
4. To get rid of the compiler warnings, we need to tell closure that our MessageWriter function is actually a constructor. To do this, add the following lines directly above “godzilla.test.MessageWriter = function(prefix) {”
1 2 3 4 |
<code>/**
* @constructor
*/
</code> |
5. Recompile your javascript, and look… You have no warnings or errors!
Step 6. Eat a cookie and ponder what just happened. Nom nom nom.
Also, after you are done eating your cookie, you could view the git repository.
For a list of compiler warnings that Closure could give you, go to: https://developers.google.com/closure/compiler/docs/error-ref
A final note. If you are looking for a method that you are sure should exist in Closure, but you can’t find it in the API docs (I was looking for a filter method for arrays), then search in Google for it: “google closure array filter”. You may find what you are looking for in the source code, even though I couldn’t find anything on goog.array in the online docs.