Wed 28 Sep 2011
Bookmarklets for fun and profit
It’s a pretty common situation for our dev databases here at Rally to get completely wiped for one reason or another. Starting up a local server to experiment with an idea or debug a problem often involves facing a database that is completely devoid of data. The need to quickly generate dummy data is a common one. Rails has the notion of fixtures, for example, and we even have a few tools in place here to help us whip up some quick data. This includes a Jenkins job that can populate a dev database with data from Rally’s subscription, and a Ruby irb environment that allows us to quickly create some artifacts with a little Ruby-fu.
I wasn’t entirely happy with either of these solutions. I wanted something closer to my typical workflow and something that is just dead easy. Ideally I’d love to just click a button on my browser and be rewarded with instant dummy data to hack around with. Enter bookmarklets…
Bookmarklets
Bookmarklets are nothing new, they’ve been around almost as long as JavaScript has. Although most seem to be aimed at fun, or insanity, there are plenty of super useful ones too. Some of my favorites include:
- Firebug Lite — get access to Firebug no matter where you are, no mucking around with extensions (or even Firefox for that matter if that’s your preference)
- waldo.js — Waldo is a great little tool you can use to search just about anything in a page’s DOM. Awesome for debugging.
- Xray — Gives you a visual read out of an element’s CSS settings.
- InstaCalc — Quick little calculator, really handy
Writing a dummy data bookmarklet for Rally development
Rally offers some great tools to help make this task easier. We have our Web Services API which enables users to easily create new artifacts programmatically. Even better is the App SDK which provides — among many other things — a JavaScript wrapping of the Rally Web Service API. I was also able to make use of our custom apps feature to flesh out and test the bookmarklet in a more sane format before scrunching it down into its unreadable bookmarklet state.
There are two general approaches to writing a bookmarklet. Either have the bookmarklet bootstrap a remote script source and execute it, or contain the entire script directly in the bookmark. I actually had to employ both approaches. I needed our sdk.js file to be loaded and ready before I could use the App SDK, but for the actual content of my bookmarklet I needed it to be all inline. This is because I really don’t have a reliable server to store an external script file. Keeping it all inline keeps things a little simpler, at the cost of space constraints (URLs can only be so long)
<html>
<head>
<title>Bookmarklet testing ground</title>
</head>
<body>
<script type="text/javascript">
(function() {
if(location.href.indexOf('localhost') < 0 || location.href.indexOf('/slm') < 0) {
alert('Please only use this in a Rally dev environment');
return;
}
var scriptId = "_bookmarklet_appsdk_";
var script = document.getElementById(scriptId);
var head = document.getElementsByTagName('head')[0];
if(script) {
head.removeChild(script);
}
script = document.createElement('script');
script.type='text/javascript';
script.src='https://rally1.rallydev.com/apps/1.26/sdk.js';
script.id = scriptId;
script.onload = function() {
rally.addOnLoad(function() {
if(rally.sdk.ui.AppHeader) {
rally.sdk.ui.AppHeader.destroy();
}
var rds = new rally.sdk.data.RallyDataSource(),
created = 0,
quantity = 10;
for(var i = 0; i < quantity; ++i) {
rds.create("Defect", {
Name:'dummy defect'+i,
Blocked:(i&1) === 1,
Description: 'Created with bookmarklet at: ' + new Date()
},
function() {
created++;
if(created === quantity) {
Ext4.getBody().highlight();
}
},
function(error) {
alert(error)
}
);
}
});
};
head.appendChild(script);
})();
</script>
</body>
</html>
Obviously that’s an entire webpage, not quite a bookmarklet. This is the code I pasted into a Rally custom app to test it out and make sure it was all good to go. There are few quirks I had to work through to get everything going:
- I added an initial sanity check to make sure I’m really in a Rally dev environment. Generating a bunch of dummy Defects in a production environment is not recommended!
- I needed to load the App SDK’s sdk.js script. This file isn’t loaded by default by Rally but is available on the server whenever a custom app needs it. So I was able to pull it in by dynamically generating a script tag and throwing it on the page. I also destroy any previously created script tags in case this bookmarklet runs more than once. In a normal custom app you’d just add a script tag. But I needed to load it dynamically as that is what the actual bookmarklet will have to do.
- From there I just need to create a RallyDataSource. Since this bookmarklet will always run inside of Rally, I didn’t need to configure the data source. It will default to the currently logged in user’s default workspace and project, which suits my needs just fine. The RallyDataSource accepts success and error callbacks, which is what those two anonymous functions thrown in there are.
- Then just create a bunch of Defects. One added thing I did was have every other defect be blocked, for added flavor. Once the bookmarklet finishes, I have Ext highlight the body of the document for me as a simple indicator of success. Since I’m running in Rally, I know Ext4 is available.
Once I was satisfied I just grabbed the contents of the script tag to become the bookmarklet.
From readable to bookmarklet
This step is pretty easy, you just need to find a good JavaScript compressor. The key here is to fit the entire bookmarklet onto one line, as browsers don’t accept newlines in URLs. I found this compressor to fit the bill just fine.
Once compressed, you end up with a bookmarklet capable string, just append the “javascript:” protocol to the front:
javascript:(function(){if(location.href.indexOf('localhost')<0||location.href.indexOf('/slm')<0){alert('...
Then I set this string as the source of a bookmark in my browser.
Now I can just click my bookmarklets and just wait a second for my handy new dummy data to be available.

