Using open-source libraries in Apps Script

Tuesday, November 27, 2012 | 11:08 AM

Labels:

JavaScript has long been the de facto choice for client-side web development, but lately it's been catching on server-side as well. While we like to think that Apps Script has contributed to the trend, projects such as Mozilla's Rhino and Node.js have also done a great deal to popularize the concept. As a result, developers have created a wealth of new open-source JavaScript libraries, and in this post we'll talk about how you can leverage them in your Apps Script projects.

Underscore
One library I wanted to use in my scripts was Underscore, which describes itself as "a utility-belt library for JavaScript." It provides a wealth of helper functions that make coding in JavaScript cleaner and more enjoyable. Take, for example, the simple situation where you want to log each value in a range.

// Using plain JavaScript.
for (var i = 0; i < values.length; i++) {
  for (var j = 0; j < values[i].length; j++) {
    Logger.log(values[i][j]);
  }
}

Although writing for loops like this is a common pattern, it's a fair amount of typing and you need to keep track of counter variables that serve little purpose. Underscore provides an each() method that makes the process much simpler.

// Using Underscore.
_.each(values, function(row) {
  _.each(row, function(cell) {
    Logger.log(cell);
  });
});

Passing anonymous functions as parameters takes a little getting used to, but if you've worked with jQuery, the pattern feels familiar.

Underscore also has some great extensions, and Underscore.string provides some useful string manipulation features. My favorite is the ability to use sprintf() notation in JavaScript, which can simplify the process of building complex strings.

// Using plain JavaScript.
var message = "Hello, " + firstName + " " + lastName + ". Your wait time is " + wait + " minutes.";

// Using Underscore.string.
var message = _.sprintf("Hello, %s %s. Your wait time is %d minutes.", firstName, lastName, wait);

Integrating with Apps Script
The simplest way to include the Underscore library in a project would be to paste its source code directly into your script, but this would lead to a lot of duplication if you end up using it in multiple projects. Earlier this year, we released a feature in Apps Script called libraries that allows you to share scripts and include them in other projects. Packaging a JavaScript library like Underscore as an Apps Script library is possible, but requires some helper functions to work correctly.

When Underscore loads, it creates a global variable named "_" that you use to access its functionality. Apps Script specifically prevents the global scope of a library from interfering with the global scope of the script that includes it, so I built a helper function into the library to pass the variable around.

// In the library.
function load() {
  return _;
}

In my script that includes the library, I simply make a call to that function and use the result to set up my own "_" variable.

// In the script that includes the library.
var _ = Underscore.load();

To try my copy of the Underscore library in your own project, use the project key "MGwgKN2Th03tJ5OdmlzB8KPxhMjh3Sh48" and the code snippet above. You can browse the full source code here.

Using it with the HtmlService
Using the code above, I could easily include the library in my server-side Apps Script code, but I also wanted to use these functions client-side in my web app served by the HtmlService. To accomplish this, I created a copy of the Underscore source code, wrapped it in <script> tags, and stored them in Html files (instead of Script files). These snippet files could then be included in my web app's HtmlTemplates using the helper function below.

// In the library.
var FILES = ['Underscore.js', 'Underscore.string.js', 'Init.js'];
function include(output) {
  for (var i = 0; i < FILES.length; i++) {
    var file = FILES[i];
    output.append(
        HtmlService.createHtmlOutputFromFile(file).getContent());
  }
}

This function was called in the web app's HtmlTemplate using the simple code below.

<!-- In the web app that includes the library. -->
<html>
  <head>
    <? Underscore.include(output) ?>
  </head>
  ...

Other libraries
Integrating with Underscore was fairly easy, but trying the same approach with other open-source libraries may be a bit more complicated. Some libraries won't run correctly in the Apps Script environment if they rely on certain capabilities within the browser or Node.js runtime. Additionally, to be served by the HtmlService, the code must pass the Caja engine's strict validation, which many popular libraries don't meet. In some cases, you may be able to manually patch the library to work around these issue, but this usually requires a deep understanding of how the library works.

We hope you're inspired to use Underscore and other open-source libraries in your own work. If you find a library that works great with Apps Script, share it with me on Google+ and I'll help get the word out.


Eric Koleda profile

Eric is a Developer Programs Engineer based in NYC on the Google Apps Script team. He's previously worked with the AdWords API and enterprise content management software.

7 comments:

Kris said...

Cool. Can you suggest a good templating library? I tried using jquery templates but it did not work - I am not sure if it violates Caja's validation.

Arun N said...

I am not too familiar with jQuery's templating engine, but HtmlService has its own that works really well with Apps Script take a look here

Kris said...

I am looking for client side templating functionality - I am using the google.script calls to get the JSON data, and am building the UI via JQuery and JQuery UI - but had to loop through the data to build the UI. Client side templates would come in handy for my task, so was wondering if anyone had recommendations...I should look at the server side templating to see if I can return a fragment from the server and attach it on the client via JQuery DOM manipulation.

Autoblog said...

This is great Eric. Two things I would love to see in Appscript along these lines would be:
1) A JSLint checker so I don't have to debug simple syntax and logic errors in my code. When you're using UiApp, you just get an "Unexpected Error" message which is then a pain to troubleshoot.

2) Support for something like Coffeescript.

3) Make Google Appscript scripts shareable via Google Drive, so we can use our laptop client tools like Textmate/Coffeescript or Eclipse. Changes made on my laptop would appear directly in a program I'm debugging in the cloud. There are hacks where you can use eval(filename) in your code but this doesn't lend itself to online debugging.

Eric Koleda said...

@Kris, Underscore actually contains it's some templating functionality: http://underscorejs.org/#template

@Autoblog, thanks for the feedback, we'll keep those ideas in mind.

Unknown said...

UnderScore/Lodash is a really nice utility library. But `.each()` isn't an appropriate example in this case since `Array.prototype.forEach` is built-in with Google Apps Script which is based on JavaScript 1.8.

Evan R. Murphy said...

I think there's no need to define `.load()` since Underscore ships with `.noConflict()`.