The hunt for a Twitter client

I hadn’t jumped on to the Twitter bandwagon for a while. I’m not much of a conversationalist, nor am I a very sociable. I also tend to stay away from social networks. But I figured I would try Twitter out for a while, mostly because it’s an outlet for short comments. For long articles, I have my blog. For sharing links, I have Google Reader and del.icio.us. I don’t quite have anything for that occasional moment when I want to say, "Hey! A great way to shred mint leaves is to freeze them!"

The question is what client to use. I wanted something free, portable and featherweight (as in lighter than lightweight: no additional memory usage.)

SMS is the classic Twitter channel. But I don’t like being bothered by SMS messages often. Besides, it’s not free. So that’s out.

The next best would be e-mail via my BlackBerry. The problem is, Twitter doesn’t accept tweets via e-mail. So when looking for alternatives, I found Identi.ca, which is even better than Twitter except for the fact that it doesn’t have Twitter’s user base. Anyway, it accepted e-mail, so that was fine.

On the desktop, the browser is the obvious choice. But somehow, going to the Twitter home page and typing out a tweet felt so… Web 1.0. I didn’t fashion installing a client just for tweeting, like Twhirl. The closest was instant messenger software. Since Identi.ca accepts messages via XMPP, I could install Google Talk and send messages via instant messenger.

That worked for a couple of weeks. Then I pulled out. Instant messenger has the disadvantage of making you accessible, and I honestly don’t have the time. Plus, I don’t fancy running apps persistently, not even something as light as Google Talk. So back to square one.

In the meantime, I was having another problem with sending updates via BlackBerry. My corporate mails have a HUGE disclaimer attached to them. Doesn’t make sense to have 140 character message followed by a 940 character disclaimer. I’d have to get rid of those anyway.

After a bit of digging around, I came across mail handlers. I can write a program on my server to handle mails. So I wrote one that strips out the disclaimer and forwards it to my identi.ca e-mail ID. (Now I’ve modified it to use the API.) So that solves my mobile twittering problem.

It also solves my cross-posting problem. I maintain a twitter.com/sanand0 and an identi.ca/sanand0 account and keep them updated in parallel. My mail handler updates the post on both services.

As for the desktop, I have the best solution of all. I use the browser address bar to twitter. I’ve created a keyword search with the keyword "twitter" with is keyed to a URL like http://www.s-anand.net/twitter/%s. So if I say "twitter Some message" on the address bar and press enter, it contacts the server, which updates Identi.ca and Twitter using the API.

Of course, you don’t really need to do that to update Twitter. Just create a keyword search with a keyword "twitter" and a URL http://twitter.com/home?status=%s, and you’re done. Remember: you can create keyword searches in Internet Explorer as well (read how). With this, you can update twitter from the address bar by just typing "twitter your message goes here".


Anyway, that was a long-winded way of saying just two things.

  1. Mail handlers are cool.
  2. Keyword searches let you update Twitter from the address bar using the URL http://twitter.com/home?status=%s

Bound methods in Javascript

The popular way to create a class in Javascript is to define a function and add methods to its prototype. For example, let’s create a class Node that has a method hide().

1
2
3
4
5
6
var Node = function(id) {
    this.element = document.getElementById(id);
};
Node.prototype.hide = function() {
    this.style.display = "none";
};

If you had a header, say <h1 id="header">Heading</h1>, then this piece of code will hide the element.

1
2
var node = new Node("header");
node.hide();

If I wanted to hide the element a second later, I am tempted to use:

3
4
var node = new Node("header");
setTimeout(node.hide, 1000);

… except that it won’t work. setTimeout has no idea that the function node.hide has anything to do with the object node. It just runs the function. When node.hide() is called by setTimeout, the this object isn’t set to node, it’s set to window. node.hide() ends up trying to hide window, not node.

The standard way around this is:

3
4
var node = new Node("header");
setTimeout(function() { node.hide()}, 1000);

I’ve been using this for a while, but it gets tiring. It’s so easy to forget to do this. Worse, it doesn’t work very well inside a loop:

1
2
3
4
for (var id in ["a", "b", "c"]) {
    var node = new Node(id);
    setTimeout(function() { node.hide(); }, 1000);
}

This actually hides node "c" thrice, and doesn’t touch nodes "a" and "b". You’ve got to remember to wrap every function that contains a function in a loop.

1
2
3
4
5
6
for (var id in ["a", "b", "c"]) {
    (function() {
        var node = new Node(id);
        setTimeout(function() { node.hide(); }, 1000);
    })();
}

Now, compare that with this:

1
2
3
for (var id in ["a", "b", "c"]) {
    setTimeout((new Node(id)).hide, 1000);
}

Wouldn’t something this compact be nice?

To do this, the method node.hide must be bound to the object node. That is, node.hide must know that it belongs to node. And when we call another_node.hide, it must know that it belongs to another_node. This, incidentally, is the way most other languages behave. For example, on python, try the following:

>>> class Node:
...     def hide():
...             pass
...
>>> node = Node()
>>> node
<__main__.Node instance at 0x00BA32D8>
>>> node.hide
<bound method Node.hide of <__main__.Node instance at 0x00BA32D8>>

The method hide is bound to the object node.

To do this in Javascript, instead of adding the methods to the prototype, you need to do two things:

  1. Add the methods to the object in the constructor
  2. Don’t use this. Set another variable called that to this, and use that instead.
1
2
3
4
5
6
7
var Node = function(id) {
    var that = this;
    that.element = document.getElementById(id);
    that.hide = function() {
        that.style.display = "none";
    }
};

Now node.hide is bound to node. The following code will work.

8
9
var node = new Node("header");
setTimeout(node.hide, 1000);

I’ve taken to using this pattern almost exclusively these days, rather than prototype-based methods. It saves a lot of trouble, and I find it makes the code a lot compacter and easier to read.

The courage to be honest

Some months ago, I was working with a client who wanted to set up a website with social commerce elements. (That’s Web 2.0 in fancy words.) They only seemed to have a very rough idea of what they wanted, so asked them right at the start of the meeting: "Why do you want social commerce?"

Their answer was interesting, and one that I had not expected. They said, "We want to project the image of an honest an open organisation."

Hmm. Fair enough.

So we went on with the meeting, discussing what they could do with blogs, how commenting would work, and so on. The main thing was to open up the site for the bank’s customers to talk freely.

At some point, one of the client’s team indicated that profanity and abusive comments would need to be filtered out, so moderation becomes important. "We don’t want to become liable for content that is on our site."

Fair point. While discussing that, another chipped in, saying "True. We will also need to monitor negative comments. We don’t want our site to have negative comments about our products."

A brief silence.

Many nods.

And the conversation continued.

I was too stunned to butt in immediately. But after a few minutes, I raised the point about negative comments. "You want to project the image of an honest and open organisation. If you filter out comments that say anything bad about you, how are you going to achieve that?"

They thought for a short while, and someone said: "Yes, the users will probably find out about it."

You can’t project an honest image unless you are honest. That means being honest about the good as well as the bad. Honesty is irrelevant for good news — no one lies about good news. Are you honest when delivering bad news? That requires courage.

This is worrisome. Someone saying "We don’t want negative comments" is a bit of an issue. But also worrying is their reason for why not. I had hoped that it would be "That’s not what an open an honest organisation does". Instead, it was "The users will probably find out about it."

This sort of behaviour stems from insecurity. It’s what keeps us late at work, not want to be the first to leave. It’s what makes us say "Yes" to things we would really rather say "No" to.

I remember a time when we were making slides late in the night. I finished mine quickly, and took printouts for the project leader to review. He word-smithed it on paper, I typed it back in, and took a printout again. (Yeah, he could’ve edited it himself. But…) And when all of that’s done, I’m still waiting, not wanting to "leave the team behind". I’m a team player after all.

It’s like drugs. You want to fit in. Be a team player. If the team’s doing it, you do it too.

These days, I’m the first to leave from work. Sometimes, it’s late when I leave, but I’m always the first to leave. And it hasn’t made any difference. At least not that I can tell.

A lot of the fear is in the mind, frankly.

There also was a time when I couldn’t say "No". When I left BCG, I spoke to a partner during an exit interview about how I wanted to work less hours. He thought the problem was more fundamental.

"Anand, knowing you, you’re the kind of person that will end up working hard no matter where you are. So will the move really make a difference?"

"The difference, James, is that here I’ve set up an expectation of saying ‘Yes’. I’ve gotten into the habit, and I’ve gotten others into the habit. At least in a new place, I’ll have a fresh start and set new expectations."

That’s happened, fortunately. These days, I consistently say ‘No’. With some folks, it’s easy.

"Anand, would you be able to help out with this?"

"No, sorry." (with an "please excuse me" smile on my face.)

Some people still scare me, though. (These are the aggressive Type A personalities that it is my occasional misfortune to come in contact with.) And when it does, I lie.

"Anand, we need this proposal out by Monday. Could you help out over the weekend?"

"Sorry, have plans for the weekend. I’m visiting a cousin at Brighton."

No, I don’t have a cousin at Brighton. I’m just scared to just say, "Sorry". It didn’t matter, though. The fear is real, but it still is only in your mind.

It’s the same with businesses. We’re collectively scared to admit something’s wrong with us, or that we can’t do something. I went for a meeting with a partner recently. The client mistook us for operations consultants. (We’re IT consultants.) So he asked us what operations experience we have. Our response should’ve been "None. We’re IT consultants."

Instead, our response was "Oh, we have several years of experience in the organisation. We’re this, we’re that, we’re great."

I don’t think we’d have been thought less of if we’d said "None". And we were found out in the next meeting anyway.

It’s the same about opening up to negative comments. If somebody makes a negative comment, it’s okay! Not that many people care. Hushing it up makes it worse (like for instance with BA and Virgin recently). Lying about it might work, but only for a while. The real problem with lying not that you might get caught out — it’s that you’ll get into the habit and in the long run, will get caught out.

For me, the best cure for this sort of fear is the firm belief that the world cares a lot less about us than we think. It’s okay to be a loser. No one cares but us. But it’s less okay to be a liar.

Canary Wharf time lapse video

I left my camera near the window of my office at Canary Wharf in time lapse mode on a cloudy day. The video is playing at 60 times normal speed.

Check out the related time lapse videos. They’re stunning. With this one, you can figure out which firms work till late in the night.