I made something.
Need to run Internet Explorer on Mac OS X or Linux? Here’s how, without having to purchase a Windows license:
- Download the VPC image you’d like to use. For testing IE6, IE7, and IE8, I find it easiest to download the Windows XP image and make copies of the VHD for each version of IE. We’ll get to that.
- Rename the .exe file you just downloaded to a .rar file, and use any RAR extractor to get the VHD file.
I like to move and rename the VHD file to stay organized, so let’s move the VHD file that was just extracted to ~/VirtualMachines/ie6.vhd. - Start VirtualBox and create a new VM. I like to name my VMs by the version of IE they contain. Go with the default options in the new VM wizard, but when presented with the disc image option, choose to load the VHD file that was downloaded - ~/VirtualMachines/ie6.vhd if you’re following along.
- Boot the new VM. Windows will ask you for a password - it’s “Password1” at the time of this writing. The screen will be tiny when the VM starts. Close the Notepad window that is open and cancel all of the prompts to install device drivers and new hardware.
- The official IE VPC images don’t come with VirtualBox compatible network drivers. Since Micro$oft requires you activate the images over the internet
before using them, you’ll need to install the drivers yourself.
Follow the instructions provided by Rick Harris, or just do this (cribbed from Rick’s instructions):- Download the network drivers here.
- With the IE VM running, go to “Devices > CD/DVD Devices > Choose a Virtual CD/DVD Disk file…” and find the ISO you made in the previous step.
- Once the driver ISO is mounted, click “Start” and then right click on “My Computer” and choose “Properties”. Click on the “Hardware” tab, then open “Device Manager”. Find your non-functioning ethernet card and right click on it and choose “Update driver…”
- In the window that pops up, choose “No, not this time”, then “Install from a list or specific location”. It’ll find the driver in the ISO you just mounted - install the one selected by default. When the installation finishes, you should be connected to the internet.
- Click the key icon in the task bar to start activation. Choose “Yes, let’s active Windows over the internet now”, then “Hell no, I don’t want to register now; let’s just activate Windows” and it should be successful.
- Click the blue star in the task bar and choose “Validation Failure Details (online)” to start the genuine verification process. It will open up a browser window and ask you if you want to install something. Click “Install”. In a few moments, you’ll see a webpage with a green banner that says something like “Welcome to Windows”.
- Once Windows is activated, you can install VirtualBox Guest Additions. This will fix some driver issues and make Windows work better with VirtualBox. Don’t install Guest Additions until you’ve activated Windows, or you’ll have to start over.
- OK, the hard part’s over. Windows will still prompt you to install drivers when you start the VM, but you can safely cancel those prompts.
Now we’ll make copies of the VM for the other versions of IE so that we don’t have to go through the validation process again. Shut down your VM if it’s running, and make 2 copies of your ie6.vhd file - I went with ie7.vhd and ie8.vhd. - VirtualBox needs every disk image to have it’s own UUID - don’t ask me why. After you’ve made copies of ie6.vhd, run the following command on the copies:
VBoxManage internalcommands sethduuid /path/to/image.vhd - You can now configure VMs for the disk image copies that were just made. The IE6 VPC image comes with installers for IE7 and IE8 - just use the installer shortcuts found on the desktop to upgrade each disk image copy you made.
And that’s it! Easier, cheaper, and faster than buying a copy of Windows and trying to install it from scratch on VirtualBox.
After processing form data with a server side language like Ruby, Python, or PHP, it’s common to show some sort of confirmation to the user. Many applications will use an HTTP redirect to send the user to a different URL for the confirmation, in an attempt to prevent the browser from resubmitting the form if the user hits “refresh” or “back”.
This is a good idea, but unfortunately most web programming languages, including Ruby, Python, and PHP, default to an HTTP 302 (Temporarily Moved) redirect. This tells the web browser “the URL you just submitted data to has temporarily moved; you can now find it here.” Some browsers will cache this redirect, causing the form to be resubmitted on a page refresh, despite the different URL. No bueno.
Luckily it’s the little-known 303 redirect to the rescue. From the HTTP/1.1 spec:
10.3.4 303 See Other
The response to the request can be found under a different URI and SHOULD be retrieved using a GET method on that resource. This method exists primarily to allow the output of a POST-activated script to redirect the user agent to a selected resource. The new URI is not a substitute reference for the originally requested resource. The 303 response MUST NOT be cached, but the response to the second (redirected) request might be cacheable.
To use the 303 redirect, just issue a “HTTP/1.1 303 See Other” header followed by a typical “location” header. In PHP, it looks like this:
header(‘HTTP/1.1 303 See Other’);
header(‘location: http://www.mysite.com/some-url/’)
Unlike the 302 redirect, the 303 redirect never gets cached, so users can refresh your form confirmation page with aplomb.
A post I wrote for the ParkWhiz blog:
If you traveled to the recent Super Bowl in Texas, or you just have a strange obsession with all things Super Bowl, you may have heard about the $900 parking spot. But was Super Bowl parking really that expensive?
As a primary parking providers for Super Bowl XLV, ParkWhiz had a bunch of data lying around, so I decided to do some digging. The answer is, no, of course you don’t have to pay $900 to park at the Super Bowl. But you can, and some people did.
How many?

Not many. The median price for a parking spot at the Super Bowl was $104. While the area directly surrounding the stadium had relatively few places to park, there are several office parks within a mile. There was cheap parking to be had, as long as you didn’t mind a little walking.

In fact, the price dropped exponentially as parking spots got further from the stadium. Land available for parking increases exponentially as you get further from the stadium, and this chart shows that the increased competition had a strong effect on price. Supply and demand still applies, even at the Super Bowl.
Over half of the attendees waited until the week before the game to line up a parking spot.

The chart spikes on 1/25, when the teams playing in the Super Bowl were decided. There’s a dip over the weekend, and then things pick back up, with the most spots being reserved on the day of the game.
Most people reserved a spot from their computer, but the weekend of the game we saw a spike in reservations from mobile devices.

This most likely reflects fans who traveled to game and left their laptops at home.
Could these people have saved money by securing their spot earlier? This chart would suggest not:

But this chart is misleading. It shows the average price of the spots being reserved. Remember that sales volume was relatively low up until 1/26. The high average price early on was caused by people who knew they were going to the game regardless of which teams were playing. That indicates that these attendees are a little less price-sensitive than most.

In fact, the fans that waited until the last week to buy parking spots paid the highest prices. There were some last minute discounts on the mid-range parking spots, but the average fan would have saved by acting sooner rather than later.
So back to our original question. The average price paid for a parking spot for Super Bowl XLV was $124, quite a bit more than the average $36 you’d pay for a spot at a Cowboys home game. However, Super Bowl tickets were selling for an average of $3676 according to StubHub, a whopping 2298% markup over the $183 (according to SeatGeek) you’d pay for a ticket to a regular season game.
It almost makes the parking sound like a bargain.
PS, check back on February 21 to see if Daytona 500 parking experiences similar fluctuations.
This topic came up recently in a conversation I had over email, and I thought I’d share here.
Let’s say you have some latitude/longitude coordinate pairs, and you’d like to find the distance between them. The simplest way would be to use the Pythagorean Theorem. The calculations would become inaccurate as points moved away from the equator, however, because the Pythagorean Theorem doesn’t take into the account the curvature of the Earth.
Instead use the Haversine formula to calculate distances between latitude/longitude pairs. Here’s a PHP function that returns the SQL code for the Haversine:
Use it like this:
MySQL is smart enough to only compute the distance once, so there isn’t a performance hit for using the distance in the WHERE clause.
The lat/lng pairs need to be in radians, not degrees. You’ll probably use it passing in constants for lat/lng A, and column names for lat/lng B. I typically store precomputed lat/lng pairs in radians along with the usual degrees so that they don’t have to be recomputed every time I need a distance.
The 20924640 numerical constant causes this function to return the distance as measured in feet. Use 6367.45 for km or 3978 for miles.
You don’t have to put it in a separate PHP function, but I find that easier than writing out the SQL every time I need a distance. It’s possible to create a MySQL stored procedure that will do this calculation, but stored procedures can interfere with some database replication setups and didn’t really offer any performance benefit, so I like to stick with generating the SQL in PHP.
This is a pretty basic geo information function, but it will run on most of the hosting environments you’ll encounter. If you need something more powerful, check out PostGIS or SimpleGeo.

Gap clothes have never fit me particularly well so I’d normally ignore a 25% off sale, but the novelty of the Foursquare tie-in caught my attention.
Eventually the novelty of Foursquare will wear off; some say it already has. I don’t know about you, but I’ve had very few of the serendipitous “hey my friend just checked in at a bar around the corner; let’s go!” experiences that were the initial selling point to consumers. I rarely remember to check in these days, and collecting badges, mayorships, and points lost their thrill long ago.
But I don’t think this matters to Foursquare’s long-term success. The game was just a Trojan horse - a way for the service to collect a critical mass of users. It doesn’t matter that people grow tired of the game mechanics and stop checking in to bars after a couple months. That’s not the point.
Foursquare’s actual goal is to be the Google Analytics of the real world. By tying promotions to check-ins, businesses can easily track the effectiveness of their marketing campaigns. This is worth a LOT of money. Rather than flying blind or making guesses based on sales numbers after the fact, brick-and-mortar businesses can hone their marketing the same way that web businesses do. Eventually someone will close the loop by tying Foursquare check-ins to actual purchases, and business will be able to do real-world conversion tracking. CPC (Cost-per-conversion) revolutionized advertising on the web; imagine the impact when that model can be used everywhere else.
I’m hardly the first person to think this, but it’s only recently that Foursquare has demonstrated that they’re really moving in this direction. Should be interesting to see how Facebook’s inevitable location product alters this market.
This past Tuesday, ParkWhiz was crawled by a bot that was doing 10-20 requests per second, despite a 1s crawl-delay setting in the robots.txt file for the domain (you do have a robots.txt file, right?). I ended up blocking the bot’s IP address using iptables, but that’s hardly a long-term solution. So how should you defend a site against overly-aggressive bots?
Turns out nginx makes this really simple – just use the limit_req_zone module. Set a rate limit, like 2 request per second or 30 requests per minute, and nginx will make sure no individual website visitor exceeds that limit. A “burst” setting can be used to provide some flexibility, allowing a visitor to make a number of requests in rapid succession before imposing the rate limit.
By default, nginx will attempt to slow down the visitor by delaying responses once the limit is reached. The “nodelay” option can be used to just return a HTTP 503 error instead. I think this is preferable, since it lets the offending visitor know something is wrong, rather than just making the site seem slow.
These are the settings I used for ParkWhiz:
Here’s the important part: put the “limit_req” directive in the location block that passes requests to your app server (mongrel, php-fpm, etc). If you put it in the server block, it will count all requests as part of the rate limiting, including images and other static files. A visitor will hit the rate limit simply by downloading the assets of a single page. We only want to limit requests to the app server.
Try it out; go to http://www.parkwhiz.com and ctrl-click a link a bunch of times in rapid succession.
I recently refreshed the form styling at ParkWhiz, and I’m really happy with how the buttons turned out. You can find a live demo with the CSS code here and the gradient background image is here.

The CSS is a little hairy, but there’s no extra markup; just use a button tag:
You can add an icon to the button by wrapping the inner text in a <span> and using some extra CSS. (That’s why a <button> tag is used instead of an <input> tag.)

The button looks as intended in Firefox, Chrome, Safari, and Opera. IE doesn’t support rounded corners and IE6 doesn’t support the transparent PNG used to create the 3rd look, but it degrades gracefully with the use of some IE-specific CSS.
I just spent the better part of a day getting MySQL replication running between 2 servers. Turns out replication doesn’t play nice with custom stored procedures, and fails with this helpful error message:
Somehow Googling the error text didn’t turn up any useful info. Am I really the first person to run into that problem?
Earlier this week I decided move an old self-hosted WordPress blog into Tumblr and discovered that there really weren’t any tools to import your existing WordPress content into your new Tumblog. So I built one.
wp2tumblr.py (right click and “Save as” to download)
Download an XML export of your blog by logging in to WP-admin and going to Tools -> Export. Then run the wp2tumblr.py script — instructions will be printed when you run it. The script adds some artificial delays to avoid overloading Tumblr’s API.
I wrote it in python for fun but am no means a python expert, so if you see something in the code that makes you say WTF, send me an email and show me what I’m doing wrong.
Update: wp2tumblr is now also on GitHub.

