A faithful reader complained this morning that my RSS feed didn't have timestamps. This was a surprise to me, because I had suppressed all memories of RSS after cutting-and-pasting a template together months ago. But he was right: my entries contained only a title, a link, and some text. I tried to figure out how to specify timestamps, and soon realised that RSS is a complete mess with divergent streams of development and lax specifications (and that this wasn't news to anyone who had been paying attention).
I fixed the timestamp problem by adding a pubDate element to each item, but I was forced to change the feed version from 0.91 to 2.0 because pubDate is a channel element (and not an item element) in 0.91. I also decided that I didn't want to keep using RSS any more. Atom is now supported widely, and I decided to switch to it.
There are many reasons to prefer Atom to RSS. The most compelling ones are that the content-type problems of RSS disappear entirely. There is no ambiguity about what MIME type to serve Atom feeds as, and there is no ambiguity about whether the content is text or HTML, or how special characters are escaped. You no longer have to guess whether an entry contains a summary or full text, and relative URIs can be handled sanely. Best of all, Atom has a real specification. (Here's a detailed comparison of Atom 1.0 with RSS 2.0.)
It took me only a few minutes to publish an Atom feed for ams/etc. I am no longer advertising the RSS feed, but it will continue to work for the benefit of people already subscribed to it.
The Economist says that as of August 2010, Indian citizens can visit (approximately) 50 "countries and territories" without a visa.
I really wonder which ones they are.
For the last five years, toroid.org has lived on a server hosted at Hetzner.DE. It has served me very well during that time (with a disk failure the only major problem), but I have finally outgrown its modest hardware configuration (Athlon XP 2000+, 512MB of RAM). The need for a more capable machine was recently made very clear to me when the Yahoo crawler decided to index archives.aox.org, and my machine spent several days swapping things in and out (it worked fine, it was just very slow and the other services suffered).
Two weeks ago, Hetzner's RP39 dedicated server plan (Athlon 64 X2 5600+, 2GB RAM, 2*400GB disk, for €39/month) caught my eye, and I wrote to them to ask about upgrading to it. I had two important questions: could I keep all my existing IP addresses? And could I order an RP39 server with 4GB of RAM installed? Surprisingly, I received no response for a week, despite sending two reminders. When I finally heard from them, I was rather disappointed with the response.
No, I couldn't keep my primary IP address or my IPv6 /64 subnet. I could move the IPv4 /29 subnet for €99 plus €15/month, which was as good as not being able to keep it. No, I couldn't order an RP39 server with 4GB of RAM because… it was no longer available! Sure enough, they had removed RP39 from their order page before responding to my message. The only sensible course of action available was to order an EQ4 server (Intel i7-920, 8GB RAM, 2*750GB disk) for €149 plus €49/month, accept that I would have different (and fewer) IP addresses, and migrate data and services the hard way.
I spent a couple of days being disappointed about the disappearance of RP39, but knew that Hetzner was within their rights to discontinue what was always described as a limited "special offer". The EQ4 cost a little more than I liked, and the migration would be painful, but I ordered one on the 20th of August. Hetzner said they expected it to be online within 24 hours, which I took with a pinch of salt (it took three days for them to bring up my old server). To my surprise, my shiny new EQ4 machine was up and running within two and a half hours from the time of my order!
Now to complete the migration quickly…
When I wrote a page about myself as a birding guide, I asked a number of people for feedback. More than one person suggested that the page would benefit from a photograph (or three) of myself, perhaps with clients. Although I agreed in principle, I couldn't bring myself to do anything about it—adding a mugshot sounded so boring.
I liked Hassath's idea of using a cartoon much better, but even though I could imagine a few suitable cartoons, I had—as usual—no idea how to put them down on paper. So I did nothing, and the weeks passed by.
The other day, I happened to see Rohan Chakravarty's bird cartoons on the KolkataBirds web site. The style wasn't exactly what I had in mind for my own web page, but I liked the illustration, especially the birds' expressions. I knew Rohan in passing from the delhibird mailing list, and I decided to ask him if he would be interested. He was.
I explained the style I wanted: black-and-white to fit the fairly sober mood of the page, more lines than solid areas; something very much like the New Yorker cartoons. Rohan and I discussed a few ideas, and quickly settled on a concept for one wide panel and one square panel, and an approximate price. I told him I was in no particular hurry, and swapped the subject out of my mind.
To my surprise, however, Rohan completed the assignment in no time at all, and the cartoons he sent me the next day were brilliant. I had not expected the first draft to match what was in my head, let alone improve on it, but it did; and I was able to use his illustrations without any changes on my web page, where they look great even a week later.
Rohan's blog is at greenhumour.blogspot.com. Should anyone I know need the services of a freelance illustrator and cartoonist, I know whom to recommend now.
I needed to write a plpgsql function that inserted rows into some tables and returned the generated primary key values to the caller. I had never needed to return multiple values from a function before, and I ran into a couple of new problems along the way.
This was my first attempt:
create function create_widget(name text, colour int) returns record as $$
declare
wid int;
cid int;
ids record;
begin
insert into widgets (name) values (name)
returning widget_id into wid;
insert into widget_colours (widget_id, colour)
values (wid, colour)
returning colour_id into cid;
select wid::int, cid::int into ids;
return ids;
end;
$$ language plpgsql;
When I tried this, I got a syntax error which I eventually understood to mean that my parameters could not have the same names as any columns in the table I was inserting into. Not very surprising, since the inserts look a bit ambiguous even when reading the code. Since parameter names occur only in the function definition and aren't exposed to the caller, I didn't mind working around this by renaming my parameters "p_name" and "p_colour".
What did surprise me was that I had to call the function like this:
select * from create_widget('x', 3) as cw(widget_id int, colour_id int);
In other words, the caller always had to supply a column definition list, and I couldn't (see any way to) specify what I was going to return in the function definition.
I was given two suggestions about how to work around this problem. The first was to use OUT parameters:
create function create_widget(name text, colour int,
widget_id OUT int, colour_id OUT int)
But this approach, predictably enough, had the same problem with the names of parameters conflicting with the names of columns, and I was much less willing to force the caller to retrieve and have to use columns named "r_widget_id" or something similar.
With Postgres 8.4 and above, I learned that I could also declare the function as returning a table, which looked exactly like what I had hoped for:
create function create_widget(name text, colour int)
returns table (widget_id int, colour_id int)
But to my great disappointment, the conflict with the column names persists even in this form.
In the end, I decided to use "p_" prefixed parameter names, return a record, and name the columns in the caller.
A few days ago, I discovered that Mojolicious::Plugin::PoweredBy is enabled by default, and adds an "X-Powered-By: Mojolicious (Perl)" header to all HTTP responses. Although I can change the value of the header, there is no way to suppress it (e.g. by setting the value to undef). Since X-headers are meant for private experiments, and should not be exposed to the Internet (never mind how many people do it anyway), I thought this was poor behaviour.
The solution was to bundle an empty PoweredBy plugin with my app:
package Mojolicious::Plugin::PoweredBy;
use base 'Mojolicious::Plugin';
sub register {
}
1;
I brought this up in #mojo on IRC, suggesting tactfully that the plugin should either not be enabled by default, or be easier to disable (when what I really wanted was to suggest it be removed entirely). The Mojo author disagreed, and said that nobody would enable it if it wasn't enabled by default (which is quite true, and to me suggests that it should not exist at all), and that it was "advertising" for Mojo.
It may be advertising, but I'm not sure it sends the right message.
After our unpleasant experience with trying to have a faulty Exide battery replaced under warranty, Hassath and I didn't want to let the matter slide, and decided to approach the consumer court.
Under the Consumer Protection Act of 1986, a number of quasi-judicial bodies empowered to deal with consumer grievances have been set up at the district and state level, under an apex body in Delhi called the National Consumer Dispute Redressal Commission; in this case, the East Delhi District Consumer Forum was the most appropriate forum for our complaint.
We spoke to someone at the DCF on the phone, and were told to bring two copies of our petition to them the next day. We spent a couple of hours preparing the petition (following an example on the NCDRC web site) and assembling a paper trail to establish our case (warranty cards, reports of the inspection, email correspondence). We stopped by the Post Office to get a blank Postal Order for INR 100 (the filing fee), and submitted all of this documentation to the (very pleasant) officer at the DCF the next afternoon.
He asked us to pick a date for the initial filing, and we decided on the tenth of August—which happened to be when the warranty of our batteries expired. We were told to appear in court at 10:30 on that morning, for the case to be brought before the Forum and admitted (or thrown out).
We went to court this morning, waited around for a while, and were quite surprised to hear my name called first (the invoice for the purchase of the batteries was in my name, so it was my name on the petition). Two of the Forum's three judges in attendance, and they were handling cases in parallel. I was called by the (stern-looking, but very kind) lady judge, and asked to explain the case. She listened to me, reviewed the petition briefly, and told me to add the battery vendor as an opposing party in the case (our petition named only Exide). Fortunately, I had a complete extra set of all the documentation with me, and she allowed me to just scribble an extra name and address on all three copies.
In the end, she assigned a date one month later, and ordered notices to be sent to the two opposing parties. Now we have to turn up in court on the new date, and we'll see what Exide does by way of opposition.
For the first time in many years, I have a new web project to work on (and perhaps it goes without saying that I need to finish it quickly). Much has changed in the world of web development with Perl since I was last seriously involved, and it took me a while to find my bearings.
When I last worked on a major new web project, none of today's popular web development frameworks existed. Tangram was the shiny new ORM (but I can't remember hearing either "ORM" or "CRUD" back then), and ePerl was my favourite out of perhaps half a dozen templating modules on CPAN. Both have long since been forgotten, and I set out to find something to replace them. I had a few very specific ideas about what functionality I wanted, but I suppressed my "not invented here" tendencies and decided to accept any reasonable way to implement them. Read more…
At the end of a hectic and stressful week, Hassath and I spent a pleasant morning at Basai.
While driving to Basai, we saw a fat White-throated Kingfisher trying to kill a very large (and very resilient) insect. It grabbed the bug from a puddle in the road in front of us and smashed it repeatedly on the road; but the unwilling victim kept trying to crawl away, only to be grabbed and subjected to the same harsh treatment again.
A little further on, I almost overlooked my first pair of Greater Painted-snipe in a shallow pond by the road. I've been hearing reports of this species for years, but never had any luck looking for them, so it was a pleasant surprise to get such a nice view.
Other highlights included a lone Whiskered Tern in flight over Basai, an adult and a juvenile Oriental Pratincole, Pheasant-tailed Jaçanas everywhere, some very dark Ruffs, and dozens of Little Egrets—some still with plumes and a patch of purple skin in front of the eye—fighting each other in a flooded paddy field.
Apart from the Ruffs, there were a number of other waders, including Wood, Green, and Marsh Sandpipers, Little Stints, and Redshanks. There was also an unusually large number of Cormorants at Basai—I guess these are the birds that usually feed at Sultanpur, but have been forced away because the lake was drained.
Speaking of which, Sultanpur is still closed.
Our UPS is hooked up to three Exide Powersafe EP65-12 SLA (Sealed Lead Acid) batteries. Normally, that gives us about six hours of backup time for my desktop, monitor, and a few assorted peripherals. It's not often that the mains power is off for so long (less than half a dozen times a year, I'd guess), but that capacity has proven invaluable in the past. For the last few months, however, the UPS has lasted for half an hour at most, even when the batteries were fully charged. Using a multimeter, I found that the voltage across one of the batteries fell rapidly to 10.5V just before the UPS died, while the other two remained above 12V. Since the batteries were still under warranty, I contacted the vendor to ask about having them replaced (which I have had to do in the past)
Unlike last time, the vendor told me to register a complaint with Exide, which I did after some delay due to external circumstances. An engineer was dispatched to visit me a couple of days later, and after testing the system, he agreed with my diagnosis: one battery was bad. He wrote up a report and went on his way after telling me that the replacement should arrive in a few days. Unfortunately, what did arrive the next day was email from his supervisor, saying they couldn't replace the battery because the charging current was "too low". (The mail also said that I didn't have the original invoice for the purchase of the batteries, but that was just the engineer trying to cover his ass after forgetting to ask me for it.) Read more…