Enterprise Demo: 1st Search Is Now Faster

Ferret’s what we use as our search engine. It’s the Ruby port of Lucene. We really like it & would recommend it to others.

But a while ago we ran into a problem with Ferret. The index Ferret created would sometimes be corrupted upon system activation. It may have been the version, it may have been the manner of system generation.

We fixed the problem by removing the pre-built index. Instead, the fist search conducted on a new system would trigger a “rebuild” for all content in the system.

This seemed like a good solution at the time, as it guaranteed a reliable demo experience. There was just 1 tiny problem - the “rebuild” took time. And that meant the first user experience with search was by far the slowest.

Well, time passed and we added more content. Which in turn meant the rebuild took longer. Depending on overall system utilization it can take anywhere from 25s to 45s. And that’s a long time for your 1st search!

So, due to popular request, the pre-built Ferret index is back & appears to be copying reliable. 1st search performance has gone from 30s to around 1s.

And to be frank, we should have made this change a lot sooner.. Enjoy the speedier demo experience!

-Dave

p.s. It’s likely the same strategy will be applied to our next Open Source Edition distro.

Coupa Video Blog: No-Click Requisition

Some say the no-click requisition is the coolest feature in Coupa eProcurement. We’re pretty proud of it. Here’s a little intro to the feature and a demo.

Coupa Video Blog: How to Buy Policies

This 5-minute video will show you all about how policies guide users in Coupa eProcurement and how to create policies easily.

Creating a custom dojo package with a custom dojo widget

We have been using the Dojo toolkit right from the beginning, but back then we only used the Editor widget, and only on one page. From there, we gradually added Dojo functionality, with Dialogs on a few more pages, and just recently, Buttons (custom ones at that) on every page! The toolkit has become an integral part of our application, and we realized the hard way that the number of objects downloaded gets really big really quickly if you don’t have a good profile.

There was documentation on profiling Dojo, and documentation on custom widgets, but numerous sources of information and too much code reading was necessary to figure out how to profile Dojo with a custom widget! Hopefully, writing it all down here will benefit those who come after us…

Profiling

Dojo is a very big toolkit. Chances are, your app won’t use half the features, so the toolkit has a small core, dojo.js, that knows how to pull in components as necessary. This makes the initial install trivial. The problem is, as you use more of the toolkit, you are making the user pull down many small files, and this greatly increases latency, not to mention browser caching is defeated in many cases. Dojo’s solution is a concept of profiling, where you define the objects that you need, and bake them into a custom compressed dojo.js file. This way the browser gets all/most of the code in a single, compressed, and cacheable download.

Sources

The first step to creating our own Dojo package, is to download the source:

svn co http://svn.dojotoolkit.org/dojo/tags/release-0.4.1

Conventions

Next, we will write our very own Coupa Button. Dojo gives you some flexibility in defining custom widgets, but we’re Rails developers, so convention triumphs over configuration, and the dojo conventions for custom widgets are:

  1. the widget should be called <you>.widget.<Component>, so in our case, coupa.widget.Button
  2. the code should be placed in release-0.4.1/../<you>/widget/<Component>.js
  3. the HTML/CSS associated with the widget should go in release-0.4.1/../<you>/widget/templates
  4. the images associated with the widget should go in release-0.4.1/../<you>/widget/templates/images

We end up with with this directory structure:

coupa_button_dir.png

Code

The conventions continue into the code for the custom widget. We only wish to change the CSS and the images used by Dojo’s Button class, and by following conventions, we minimize the code we have to write in our Button.js:

dojo.provide("coupa.widget.Button");
dojo.require("dojo.widget.Button");

dojo.widget.defineWidget(
    "coupa.widget.Button",
    dojo.widget.Button,
    {
        templateCssPath: dojo.uri.moduleUri("coupa", "widget/templates/ButtonTemplate.css"),
        inactiveImg: "../coupa/widget/templates/images/coupaButton-"
    }
);

It almost reads like english: we are providing a coupa.widget.Button, and to do that, we need dojo.widget.Button. We define the former as a subclass of the latter, overriding two of the attributes. Short and sweet. Also note the use of the dojo.uri.moduleUri helper made possible by the conventional directory structure.

Profile

We then create a profile release-0.4.1/buildscripts/profiles/coupa.profile.js:

var dependencies = [
    "dojo.widget.Dialog",
    "dojo.widget.DropdownDatePicker",
    "dojo.widget.Button",
    "coupa.widget.Button"
];

dependencies.prefixes = [
    ["coupa", "../coupa"]
];

load("getDependencyList.js");

Again, the minimum information needed to do the job: we want to include these objects, and find them in this path prefix in addition to Dojo’s.

Now, you might have noticed a file called manifest.js in our coupa directory in the screenshot above. This file tells Dojo’s dynamic class loader what components live under a directory, so it knows to look here when it needs these components. We don’t need this file now that our button is baked into dojo.js, so we won’t elaborate on it in this article, but it is good for testing our widgets, in which case we use the minimal dojo.js.

Finally we are ready to generate our very own dojo.js:

cd release-0.4.1/buildscripts
ant -Dversion=0.4.1 -Dprofile=coupa -Ddocless=true release intern-strings

This will read the object list defined in profiles/coupa.profile.js and prepare a release without documentation, including associated HTML/CSS objects.

Deploy

Now we can deploy our new Dojo package:

mkdir $RAILS_ROOT/public/javascripts/dojo
cd ../release/dojo
cp -r dojo.js iframe_history.html src nls $RAILS_ROOT/public/javascripts/dojo

and let’s not forget our custom widget:

cd ../../..
cp -r coupa $RAILS_ROOT/public/javascripts

Why deploy the huge Dojo source tree and our full code structure when all the interesting stuff is already in dojo.js, you ask? By convention, Dojo is deployed with its full source tree, and so should custom extensions. Our images need to be deployed in that location, for one, and our code needs to be in source control somewhere anyway. Deploying them in the standard locations also allows us to debug them by simply substituting in the minimal dojo.js. Lastly, we might want to use a widget, like Editor2, in only a few places, and don’t wish to enlarge our dojo.js with it, in which case we can just leave it out of the profile, and it will be loaded from the source tree.

Instantiate

We’ve come to the very last step, which is, of course, to use our new button:

<div id="do_stuff_button" dojo:type="coupa:Button" onclick="doStuff()">Hit me, Baby!</div>

and for buttons that are inserted into a page through AJAX we need to manually instantiate them:

dojo.widget.createWidget('do_stuff_button')

Conclusion

That’s all for today, folks!

Dojo is not for everyone. In fact, there’s a different library out there for each developer’s needs, it seems. But those whose needs are met by the Dojo toolkit, will no doubt appreciate the efforts of Alex Russell and his team in creating an astoundingly comprehensive set of tools and widgets with incredible consistency and quality, which is all the more amazing given how much time they spend babysitting n00bs like myself on #dojo!