« April 2008 | Main | June 2008 »

May 30, 2008

Deploying branches with Capistrano

Listen to this articleListen to this article

This morning I had occasion to deploy a branch of a git repository to a staging server but hadn't the foggiest idea how. A quick search through the capistrano source code revealed that I could use set :branch "branch_name" in my deploy script. I tried it and it worked. I then figured I would need to make a similar change across all my branches. Of course, I'm a lazy sod and wondered if there wasn't a better way.

If you're not familiar with git, the output of the git branch command is a list of branches with an asterisk marking the one currently checked out on your local machine. For example:

$ git branch
* drupal_authentication
  fragment_caching
  master

So, I figured, what if I just parsed the output and searched for the branch marked as current:

set :branch, $1 if `git branch` =~ /\* (\S+)\s/m

Now I'm able to deploy whatever branch is current on my local machine from a single, shared, deploy script.

May 13, 2008

JavaScript Date Helpers

Listen to this articleListen to this article

It's all the rage these days to have timestamps displayed in words to indicate how long ago some event occurred. You know something like "less than a minute ago" or "about 2 months ago", etc. You'll see plenty of examples on news sites, blog entries, and bug tracking tickets to name but a few.

If you've ever had to build this kind of thing in Rails you'll be familiar with all the Date Helper methods that make the task pretty trivial. The problem is that the result is fixed to whatever the date was when the page was rendered and as a result these timestamps go stale very quickly.

Save refreshing the page every minute--or hour or whatever--just to update the times, I figured what was needed was a little but of client-side action. Rather than send the text in the HTML, I decided to instead send the raw timestamps and have the browser periodically generate the textual representation.

To this end, I blatantly copied two methods from the afore-mentioned Rails helper-- distance_of_time_in_words(from, to), and time_ago_in_words(from)--and, taking some liberties along the way, converted them to JavaScript:

  distance_in_milliseconds = to - this;
  distance_in_minutes = Math.abs(distance_in_milliseconds / 60000).round();

  if (distance_in_minutes == 0) {
    words = "less than a minute";
  } else if (distance_in_minutes == 1) {
    words = "1 minute";
  } else if (distance_in_minutes < 45) {
    words = distance_in_minutes + " minutes";
  } else if (distance_in_minutes < 90) {
    words = "about 1 hour";
  } else if (distance_in_minutes < 1440) {
    words = "about " + (distance_in_minutes / 60).round() + " hours";
  } else if (distance_in_minutes < 2160) {
    words = "about 1 day";
  } else if (distance_in_minutes < 43200) {
    words = (distance_in_minutes / 1440).round() + " days";
  } else if (distance_in_minutes < 86400) {
    words = "about 1 month";
  } else if (distance_in_minutes < 525600) {
    words = (distance_in_minutes / 43200).round() + " months";
  } else if (distance_in_minutes < 1051200) {
    words = "about 1 year";
  } else {
    words = "over " + (distance_in_minutes / 525600).round() + " years";
  }
  
  return words;
};

Date.prototype.time_ago_in_words = function() {
  return this.distance_of_time_in_words(new Date());
};

Now all I do is periodically invoke a function that calls one or other of these methods and updates the text of whatever display element is appropriate. Even better, because the raw timestamps have timezone information in them, the display doesn't suffer from, in my case here in Australia, always being 10 hours out because the server is sitting in the US with a US date/time.

Giving the Anchor tag some Ajax Lov'n

Listen to this articleListen to this article

It seems I'm forever needing to submit links using an XMLHttpRequest rather than the default full-page refresh. One approach commonly used in the Rails community is to render each link with the JavaScript already in place. My preferred approach however is to keep the HTML as free from JavaScript as possible and unobtrusively add behaviour using LowPro.

LowPro already comes with a built-in behaviour for links but sometimes I need something little more complex than simply submitting the request and so I usually end up doing the following:

anchor = ...;
new Ajax.Request(anchor.href, { method: "get", parameters: ... });

Granted that's not a lot of effort but it still felt as though I were repeating myself and that the overall intention of my code was largely obscured by the infrastructure. It then struck me that submitting a form using the Prototype JavaScript framework is almost trivial:

form = ...;
form.request({ parameters: ... });

So I cooked up a version for anchors as well:

Element.addMethods("A", {
  request: function(anchor, options) {
    new Ajax.Request(anchor.href, Object.extend({ method : "get" }, options || {}));
  }
});

Now I can submit links in pretty much the same was as I do forms:

anchor = ...;
anchor.request({ parameters: ... });

I'm wondering what other possibilities might occur were I to add a serialize() method to extract the request parameters.