« November 2005 | Main | January 2006 »

December 28, 2005

A travel search engine that truly rocks

Web applications never fail to surprise me. Even in well-trodden domains like Web search or online travel booking, something comes along every once in a while that makes me sit up and take notice.

Most of my travel is international, for which roundtrip tickets cost over $1000. Right now, there are three ways in which I can book tickets in principle:

a) buy online directly from the website of an airline (let's call these direct tickets)
b) buy from an online travel agent like Orbitz or Expedia (let's call these indirect tickets) or
c) buy from a human airfare consolidator or travel agent.

I almost never use the first option because it shows flights only from one airline and some part of me thinks I might not be getting the best price out there. I have used the second option with for most of my travel bookings. I have been able use to the third option with limited success, pricewise, when online travel agents like Expedia and Orbitz don't give me good enough fares.

With existing online travel agents, I find the user interface to be a bit restrictive for planning my flights. For instance, in my search parameters, I am unable to specify a time window within which I would like to arrive and depart. I have to look at each result and filter out the ones that work with my schedule myself. Moreover, these sites offer fares only for indirect tickets that have been made available to them and do not let the user comparison shop for direct tickets that may be an equivalent or better deal. Because they sell fares, I can only look at the fares they have on sale; this restriction also applies when I buy my tickets from a human travel agent.

I recently heard of kayak.com, an online travel search engine that has blown me over with its feature set and user interface. It acts as a meta-search across websites that offer direct tickets as well as indirect tickets. It does not sell any airfares itself but directs users to fares that it finds from airline websites as well as travel agents. In so doing, it has shown me direct ticket deals that I wouldn't ever have encountered on Travelocity, Orbitz or Expedia. Kayak's user interface employs Ajax skilfully to give a smooth user experience. Kayak also provides Ajaxified sliders, an apposite user interface metaphor for selecting continuums, such as price ranges or time windows for arrival and departure.

The only gripe I have about Kayak is that the outbound links to the actual websites from which you can buy the tickets occasional result in session or cookie-related errors. This doesn't prevent me from purchasing the fare -- it's just an extra step that makes the process a bit less seamless than I would like it. Nonetheless, Kayak has quickly become my favorite travel search engine because of its smart user interface and its coverage of websites from which I would never have thought of buying tickets. I highly recommend checking it out the next time you need to buy flight tickets.

Posted by Vishy at 08:31 PM | Comments (0)

December 20, 2005

Vishy's Indian English Dictionary: load shedding

load shedding. /lOHd·shed·ding/. Load shedding refers to the stoppage of the power supply to places where demand for electrical power exceeds supply. Load shedding is not just a power outage at an arbitrary time, like the North American Blackout of August 2003. It takes the form of systematic cuts in the power supply so that demand and supply can be reconciled a little and so the affected areas can work around it. Sometimes, especially in heavily populated areas, load shedding is instituted during heat waves or cold waves as a temporary matter (sign up or use BugMeNot) to overcome excess demand for power. In other areas, where it's a fact of life, locals get around it by equipping themselves with fossil-fuel powered generators that add to the South Asian Brown Cloud. Hopefully soon, the power and infrastructure situation will iron itself out enough to attract more foreign investors, who, in turn, will only accelerate any improvement in the long term.

Posted by Vishy at 01:16 AM | Comments (0)

December 18, 2005

HOWTO: Get Ruby on Rails to map nonstandard column types correctly into your model

I have been trying my hand at Ruby on Rails, the latest and greatest Web development platform to hit the block. Ruby on Rails is often breathlessly praised by the bloggerati for its 'configuration by convention' philosophy and has quickly become a Web 2.0 buzzword. Heck, some even tout it as the beginnings of a hitherto unspoken conversation about how to keep programmers happy. The Net is fulminating with impressive videos that show you how to build a blog application with Rails in 15 minutes. I find that a lot of this material is directed at absolute beginners; beyond that, anyone who wants to meddle in Rails is presented mostly with a steaming pile of API documentation (much to the credit of the Rails developers, I have rarely seen a better-documented framework). Although I find API documentation useful, I, along with many others, learn a lot better from HOWTOs and tutorials. This HOWTO is written in that spirit.

Rails' wow! moment comes when it successfully generates basic CRUD pages for your data model using a single line of code, via a process known as scaffolding. Based on the data types of the columns in your database table, it successfully figures out how to display and accept input for the values of that column. For example, a varchar column in your database table would result in the automatic generation of a HTML text field, whereas a text/ntext/clob field would result in an HTML textarea. This works more than 80% of the time, because there aren't that many standard column types. However, powerful databases like PostgreSQL allow the database user to define custom types and reference them in table definitions. As you would expect, Rails doesn't map these custom types correctly to user interface elements in the scaffold.

Let's say you're building a Web application that is concerned with some domain object, say a widget. Let's suppose that every widget instance has an attribute, :widget_blick, of a nonstandard data type, blick. The actual representation of blick values in the database is irrelevant. Let's assume though that the database provides TextToBlick and BlickToText functions that convert back and forth between a blick and some textual representation of a blick. After you create the widgets table and use Rails' script/generate model Widget, you'll get something like so in widget.rb

class Widget < ActiveRecord::Base
end
Now, you want each widget instance's :widget_blick attribute to return the textual representation of a blick and also to accept values in this representation and perform translations between this representation and blicks using the functions provided by the database. First you need to override the default accessor and mutator functions for the :widget_blick attribute
class Widget < ActiveRecord::Base
        def widget_blick=(textrepresentation)
           # call TextToBlick
        end

        def widget_blick
          # call BlickToText
        end
end
To call the database-provided functions, each model class derived from ActiveRecord::Base provides a find_by_sql method which nominally returns an object of the same type as the model class. In other words, Widget#find_by_sql would return the result of the database function call wrapped in a Widget instance. All you need to do is to reach inside this Widget instance and grab the underlying value to make your accessor and mutator methods functional. Your code would look something like so
class Widget < ActiveRecord::Base
        def widget_blick=(textrepresentation)
           write_attribute(:widget_blick, 
           Widget.find_by_sql(["SELECT TextToBlick(?) 
as value", textrepresentation]).first.value)
        end

        def widget_blick
          Widget.find_by_sql(["SELECT BlickToText(widget_blick) as value 
FROM widgets WHERE id = ?", id]).first.value
        end
end

To verify that this works, fire up the really handy script/console. You should be able to see the following sequence (type things in at the ">>" prompt. The console's responses are marked with a '=>')

$ ./script/console
Loading development environment
>> w = Widget.find_first
=> #
>> w.widget_blick
=> "BLICK(0, 1)" <-- This is the textual representation
>> w.widget_blick = 'BLICK(999, 1000)'
=> "BLICK(999, 1000)"
>> w.save
=> true
Now, check your database table to see that w's widget_blick property indeed got updated to "BLICK(999, 1000)".

We successfully got our model to print as output and accept as input the textual representation for a blick and convert it into a blick behind the scenes for us. For what it's worth, I found this procedure necessary when getting Rails to talk successfully to PostGIS, which uses PostgreSQL's custom types to implement spatial and geographic extensions to the core PostgreSQL database. This HOWTO was made possible with the help of "argv[0]" on the channel #rubyonrails at Freenode IRC.

Posted by Vishy at 12:00 PM | Comments (1)

How does Playboy support software development?

I was trying to download Eclipse, the much celebrated IDE for Java and other languages. I went to Eclipse.org and went to the download page, where they ask you to select a mirror close to you. And boy, among the list of mirrors was 'Playboy Enterprises, Inc.'! I am in shock. Imagine this purveyor of countless silicone valleys, lending server space to help developers in Silicon Valley and all over the world! Needless to say, now you know from where I downloaded my IDE ;)

Posted by Vishy at 10:39 AM | Comments (0)

December 02, 2005

Don't be evil

A certain major technology company operates by the motto "Don't be evil". They offer several major Web services, though. As the geeks among us know, these millions of Web pages are served up by HTTP daemon processes.

How can a company profess to be not evil and depend on daemons so fundamentally to make money? Perhaps they should call their server processes httpa, for "HTTP angels".

Posted by Vishy at 11:42 PM | Comments (0)