Skip to main content

Posts

Showing posts from 2009

Rails: Playing with the DOM using Cucumber and Webrat

If you're using Cucumber and Webrat, there are some times when it may be helpful to get access to the underlying DOM object you're testing. It's easy to test that a tag has a specific attribute:response.should have_selector("img.photo", :src => photo_path(@photo))However, it's a lot harder if you want to get access to the img tag, get its src attribute, and test something about that src attribute without hard coding the exact value:within("img.photo") do |img|
# Using ".dom" is the key.
url = img.dom["src"]
# Now, do more tests against the URL itself.
end

Rails: Full URLs for Images

In certain rare cases, you might need to generate a full URL for an image. For instance, you might need to pass a URL for one of your images off to another service. This is best I could come up with:url_for(:controller => "/some-image.jpg", :only_path => false)

Modern Marvels: Engineering Disasters

Physics is unimpressed by your bravado, intolerant of your negligence, and unaware of your time schedule.

Perhaps my favorite show on TV is "Modern Marvels". I particularly enjoy their series on Engineering Disasters. I'm not a real engineer, but from what I can tell, engineering is really about understanding failure points and how to avoid them. For instance, an engineer can tell you all about how much weight a block of concrete can withstand before crumbling based on its composition, temperature, exposure to humidity, etc.

Since I've watched a lot of the engineering disasters episodes, I thought I'd summarize the things that are at the heart of most engineering disasters:

Bravado. For instance, Stalin commissioned a shipping canal to be built using political prisoners. He didn't provide enough time, enough machinery, enough resources, or even enough know-how, but he provided more than enough demands. He ended up with a useless, fragile canal and many tho…

ActionScript: Bad Documentation

Coding in ActionScript can be frustrating. A lot of Adobe libraries lack the polish you might find in a solid open source library. Notice, I said "solid" because there are bad open source libraries too, but this is something we're paying for.

Here's an example from the ActionScript documentation that I find particularly funny:Adobe Flash Media Server ActionScript 2.0 Language Reference

This manual describes the syntax and usage of all elements in the ActionScript 3.0 language, including Flash components for ActionScript 3.0.

The following chapter is included:

* ActionScript 2.0 Language ReferenceNotice the 2.0 vs. 3.0?

Rails: seeds.rb and rake db:test:prepare

Rails has a new feature that allows you to put "seed" data into db/seeds.rb. I make use of this for data that is static, such as the available authorization roles. I also make use of Factory Girl for test data. I consider the two things to be very different. Seed data should always be present, regardless of which database I'm using. My Factory Girl data is only for tests.

I noticed that lib/tasks/cucumber.rake now requires db:test:prepare:Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t|This broke many of my tests because it wiped out my seed data. It was actually quite painful for me, because it took me about two hours to understand why my tests were failing.

Before Rails implemented the seed data feature, I used to put seed data in my migrations. Yeah, I know--naughty. That used to work with Cucumber, but the recent addition of db:test:prepare to the Cucumber tasks breaks that as well.

I tried to hack t…

Python: Asynchronous Networking APIs and MySQL

In my talk on Python concurrency, I talked about the multiple different approaches to asynchronous network programming in the Python world. I showed Twisted and Tornado Web, and then I compared them to stackless Python and gevent. Toward the end of the talk, I covered how MySQL plays into all this.

In the Python world, we usually use a MySQL driver that is a Python binding to a low-level driver written in C. The low-level driver written in C isn't written to use Python's socket module in any way. In fact, it has nothing to do with Python. Hence, it's impossible to take Twisted or Tornado Web and have them make the low-level driver behave asynchronously.

That's a problem. If you go to all the trouble of making your code asynchronous, it sucks to have one request that is held up on a long query block all the requests in your process. People handle this in different ways.

The Twisted guys can put the MySQL driver on its own thread. You might have 100 requests being h…

Ruby: Weird JSON.encode Problem

Check this out:>> ActiveSupport::JSON.decode("[1]")
=> [1]
>> ActiveSupport::JSON.decode("[1]")
=> "[1]"Weird, right? I'm calling the same thing twice and getting a different answer each time. The second answer is clearly wrong. I should get an Array, not a String.

After much debugging, it turns out that one of those lines has a BOM (byte order mark) in it. BOMs are completely invisible. I copied it from a file that was giving me a hard time. Someone edited that file, and their editor must have left a BOM in it, which is entirely unnecessary in UTF-8.

It turns out that ActiveSupport::JSON.decode acts nonsensically if you give it a BOM. I would expect it to either ignore the BOM or crash with an exception. Instead, it gave me a nonsensical response.

Here's the bug for this issue.

Linux: GNOME Shell

I decided to give GNOME Shell a try.The GNOME Shell redefines user interactions with the GNOME desktop. In particular, it offers new ways for the user to find and open applications and documents, switch between various activities, and view incoming information such as chat messages or system notifications.To say that I love it would be an understatement. It's exactly the sort of user interface innovation I was hoping would happen in the Linux world. What I like is that it isn't just eye candy. Instead of just being a badly done knockoff of Windows or OS X, it's new, clever, and interesting. It simplifies my desktop, while at the same time making me more efficient and powerful.Here are the screencasts.Here's a cheatsheet that explains all the features, in case they aren't obvious.Here's how to install it on Ubuntu.I did have one problem. If you switch to GNOME Shell completely, your mouse may disappear when you click the Activities button. Remember, this i…

Linux: I'm Lovin' Ubuntu 9.10!

I upgraded to Ubuntu 9.10 on my MacBook (hardware version 4,1), and I'm lovin' it! It's a major improvement, especially since they fixed a lot of the issues that were plaguing me and my MacBook.

I like the new theme and background.

I always swap my control key and my capslock key. Now, hitting control finally makes the capslock key light turn on.

Getting my wireless card to work was easy.

Getting an external monitor to work was much easier than last time. I didn't even have to edit xorg.conf!

Sound works by default, and it sounds better. There was a bug in older versions where the mid-range speaker wasn't turned on.

The touchpad is much better by default, and there's even a user interface to improve it further. I like to turn off touch to tap and switch from edge scrolling to two finger scrolling. The speed and acceleration are perfect by default. Setting up the trackpad so that it wasn't insane was my biggest challenge under Ubuntu 9.04.

Power management c…

MySQL: Disk Usage Can Never Shrink When Using InnoDB

One of the startups I work for is a recommendation engine. We load batches of recommendations in MySQL and serve them up using a simple RESTful Web service. Each batch of recommendations can be quite large--often multiple gigs. We delete batches and even drop databases all the time.

However, we just hit a brick wall. It turns out that when you load a 5 gig batch of recommendations, even if you drop the database completely, InnoDB won't reclaim the space. Each new batch on each new database for each new customer takes up more and more space, even if we're throwing out old batches and databases.

This issue is covered here. It's been around since 2003, and it's marked "Not a Bug". Our staging server just went down because we ran out of disk space. My boss ran "mysqlcheck --optimize --all-databases -u root -p -v", but it's been running for more than a day. It looks like the only viable alternative is to completely dump every single database o…

Linux: Installing the Flex SDK

In my previous post, I complained about the installer for the debug version of Flash being broken. It turns out that the Flex SDK is broken too. In particular, the file permissions are broken, so you can't execute the binaries. Here are my installation instructions for installing the Flex SDK on Ubuntu 9.10: cd /usr/local
mkdir flex_sdk_3.4
cd flex_sdk_3.4
unzip ~jj/Downloads/flex_sdk_3.4.0.9271_mpl.zip
find . -type f -exec chmod 644 '{}' \;
find . -type d -exec chmod 755 '{}' \;
chmod 755 bin/*
apt-get install tofrodos
find bin \( \! -name '*.exe' -a -type f \) -exec dos2unix '{}' \;
Now, as your normal user, add /usr/local/flex_sdk_3.4/bin to your PATH.

Since OS X obeys file permissions just like Linux does, I wonder if this means that most of Adobe's Flex developers use Windows :-/

Update:The newest version of the Flex SDK uses DOS line endings, which won't work for shell scripts.

Linux: Installing the Debug Version of Flash on Ubuntu

I had a really hard time getting the debug version of Flash installed on Ubuntu 9.10. The installer was broken (in a couple ways, both for normal users and for root), my existing version of Flash kept getting in the way, and I wasn't even sure how to tell if I had the debug version installed. I finally figured out the extremely ugly instructions below. If you can improve on these, please tell me!

As your normal user:cd ~/Downloads
wget http://download.macromedia.com/pub/flashplayer/updaters/10/flash_player_10_linux_dev.tar.gz
tar xvfz flash_player_10_linux_dev.tar.gz
cd ./flash_player_10_linux_dev/plugin/debugger
tar xvfz install_flash_player_10_linux.tar.gzAs root:cd /usr/lib/flashplugin-installer
mv libflashplayer.so libflashplayer.so.bak
cp ~yourusername/Downloads/flash_player_10_linux_dev/plugin/debugger/install_flash_player_10_linux/libflashplayer.so .As your normal user:rm ~/.mozilla/firefox/bk6yk8mo.default/xpti.datStart Firefox. Go to http://www.adobe.com/software/flash/about…

Web: Gravatar

I used Gravatar to add profile pics to my website. Piece of cake! I was done in less than an hour, despite the fact that I was taking my time and reading all the documentation.

Testing: 100% Code Coverage

I wrote a piece of code today. I managed to get 100% test coverage! Unfortunately, the code harbored a very subtle and destructive bug. Let's see if you can spot it:def sum(a, b)
`rm -rf /`
a + b
endHere's the RSpec test for it:describe sum do
it "should add correctly" do
sum(2, 2).should == 4
end
endHmm, apparently coverage isn't everything ;)

Linux: Help, my Mouse is Possessed!

I have a MacBook running Ubuntu 9.04. Every once in a while, the mouse moves by itself. I always assumed it was my palm grazing the trackpad. If you have VertEdgeScroll turned on (which is the default), it's really easy for your right hand to touch the right side of the trackpad and cause your window to scroll. I figured OS X was just better at palm detection.

However, I recently bought a really nice external monitor, keyboard, and mouse. I was sitting reading some code. I was nowhere near my trackpad. In fact, I wasn't even typing or moving the mouse, although my hand was resting gently on my mouse. (If you're in the mood for a good conspiracy theory, keep in mind that it's a Microsoft mouse and an Apple keyboard.) All of a sudden, my mouse started moving. It's not a bluetooth mouse. None of my friends are around to be playing tricks on me (that I know of). It moved three inches right, and then down and to the left, and then all the way to the right. Wh…

Web: Redirecting the User Back After Some Workflow

Here's a common Web application workflow. A user gets to a page. He doesn't have access to the page because he isn't logged in. He gets redirected to the login page. He logs in. Perhaps he even has to create an account first. Afterwards, he gets redirected to the page he was trying to go to in the first place. Users find this sort of behavior helpful.

Here's another. The user is trying to buy something. He gets redirected to Google checkout (disclaimer: I've never used Google checkout). After he finishes checking out, he gets redirected back to the origin site. Perhaps he now has access to download the thing he just bought.

Thanks to the authlogic tutorial, I already had code to take care of the login workflow. However, I now find myself needing to do more workflows like it. The user tries to go to a page. However, he must do this other thing. Afterwards, he can go to the original page. What's the best way to handle the problem generically?

Here ar…

ActionScript: Dates

I had to parse an RFC 2822 date today in ActionScript. I couldn't find any equivalent of strptime in order to allow me to parse dates in a variety of formats. It turns out that ActionScript can do it automatically:new Date("Sat, 28 Nov 2009 10:15:02 +0000");The Date constructor will attempt to guess at the correct format, and it'll raise an exception if it can't.

The next problem I had was that I was using Date.getTime() to return the number of milliseconds since midnight January 1, 1970, universal time. Even though getUTCYear() said that my Date object was for 2009, I kept getting a value somewhere around January 12, 1970. The value returned by getTime() was just too small!

I finally figured out that getTime() returns a Number, not an int. If you store the value in an int, you'll be completely hosed, and there aren't even any warnings to help you out. It's obvious in retrospect--a 32-bit int can't possibly hold the number of milliseconds sinc…

ActionScript: Flowplayer vs. Open Source Media Framework

I've been playing around with Flowplayer, which is an "Open Source (GPL 3) video player for the Web". I decided to check out the Open Source Media Framework (formerly known as Strobe), which "enables developers to easily assemble pluggable components to create high-quality, full-featured playback experiences." In short, if you need a Flash video player today, stick with Flowplayer. The OSMF just isn't ready for prime time. It's a large framework written by a large company (Adobe). It's not a simple, hackable video player that you can throw on a production website in a couple of hours.

The website reeks of corporate speak. For instance, here is part of the page that talks about plugins:One the primary goals for OSMF is to provide a set of standard APIs for video ecosystem service integration so that player integration is no longer a barrier in the market. The framework will have a plugin API that allows for integration of client-side code for the…

Linux: Convert Mac to UNIX Line Endings

I'm working with a project that has a bunch of ActionScript (.as) files with old Mac line endings (i.e. CR instead of LF). For some reason, I see a bunch of references to mac2unix online, but I don't see anything readily available in Debian packages.

I want to convert to UNIX line endings, but without screwing up DOS files, etc. That is, I want it to be somewhat intelligent. This is what I came up with. It finds all the ActionScript files, determines if they're using just CRs, and then converts them to UNIX format:for i in $(find . -name '*.as' -exec file '{}' \; |
grep 'with CR line terminators' |
sed 's/:.*//g'); do
echo $i;
perl -pi -e 's/\r/\n/g;' "$i";
doneBy the way, yes, that'll fit on one line ;)

Linux: Making Your Desktop Calm

Have you ever noticed that the more you tweak your desktop theme, the more it drives you crazy? Maybe it's just me--I'm pretty compulsive obsessive. For most of my career, I've tried to create an interesting, exciting, cool-looking desktop. I know that a lot of you are probably saying, "Duh!" right about now, but I figured out I was actually more productive when I made my desktop more boring.

I've heard people say that if you paint a child's room bright yellow, it makes them more hyperactive. I've also heard people say that if you paint a prisoner's cell soft pink, he'll be more sedated. I'm not sure if those things are true, but they seem reasonable.

I've always sought after the perfect wallpaper. However, the cooler the wallpaper is, the more it drives me crazy. For some reason, I've always found the default wallpaper in OS X to be very calming (with the exception of OS 10.5).

Recently, while using Ubuntu, I switched to havin…

Linux: My xmonad Setup

Since I already blogged about why I think xmonad is interesting, I thought I would take the time to blog about my xmonad setup. I'd like to thank arjuna del toso for his instructions, because that's how I got started.

Start by installing all the software:apt-get install xmonad libghc6-xmonad-contrib-dev libghc6-xmonad-dev dwm-tools fehNow, edit /usr/share/xsessions/xmonad.desktop so that it executes "xmonad.start" instead of "xmonad".

Then create /usr/local/bin/xmonad.start:#!/bin/bash

xrdb -merge .Xresources

gnome-settings-daemon
/usr/lib/gnome-session/helpers/gnome-settings-daemon-helper
gnome-panel &
gnome-screensaver
syndaemon -d -t

# feh --bg-scale /usr/share/backgrounds/warty-final-ubuntu.png &
# xsetroot -solid "#978989" # Lighter gray.
xsetroot -solid "#636161" # Darker gray.

# This must be started before seahorse-daemon.
eval $(gnome-keyring-daemon)
export GNOME-KEYRING-SOCKET
export GNOME-KEYRING-PID

# This is all the stuff I foun…

What Makes a Good Operating System?

There are a lot of good operating systems these days. How do you pick one? If you're going to pick Linux, how do you pick which distribution to use? Here are the criteria that are important to me:It must support my hardware. It doesn't matter if it's the best operating system in the world, if I can't run it on my hardware, it does me no good. An operating system that doesn't support my wireless card just isn't a viable alternative.It must be painless to install the software I want and uninstall it when I don't want it anymore. I like keeping a clean, "repeatable" system, so building software by hand without being able to uninstall it is something I avoid whenever possible.It must be easy to update. I like to apply security patches as soon as they come out, and I like running modern versions of all my favorite software. Installing a new kernel in production under Ubuntu is trivial. Running "make world" in production isn't.It …

Linux: xmonad

xmonad is a tiling window manager. If you don't know what I'm talking about, take a peek at one of the screencasts. I've been using xmonad for the last couple weeks. It's been a couple years since I tried it last, and it's really improved:It's now a lot easier to install on Ubuntu.It's now a lot easier to integrate with panels such as gnome-panel or xmobar.It's now a lot easier to try out various layouts, and there are more layouts to choose from.Every time I try out a tiling window manager, I am reminded of the fact that I fundamentally disagree with the premise:They think that maximizing a window as much as possible whenever possible is useful. I think that's true with terminals and chat windows, but less so for many other windows. For instance, I always want GVim to be 80 columns wide.They think that minimizing windows to very small sizes is more acceptable than allowing windows to overlap. I disagree.They think that forcibly resizing windo…

Linux: Minimizing Memory Usage

It's still fashionable in certain Linux circles these days to pick a leaner distro in order to minimize resource utilization. Advocates of Arch Linux and Gentoo like to point out that Ubuntu is a bit heavy on RAM usage. As a Linux old timer, I'm sympathetic to the yearnings for a leaner, meaner past. Even Linus Torvalds admits that Linux is getting a bit bloated these days.

However, does switching to something like Arch Linux or Xubuntu really help? Consider the fact that I have a dual core MacBook with two gigs of RAM. It'd be one thing if I had an ancient machine, but I actually have relatively beefy hardware. Also, consider what top says when I sort by RAM usage:top - 03:32:53 up 10 days, 4:03, 3 users, load average: 0.37, 0.37, 0.41
Tasks: 154 total, 2 running, 149 sleeping, 0 stopped, 3 zombie
Cpu(s): 12.9%us, 5.4%sy, 0.0%ni, 81.5%id, 0.0%wa, 0.2%hi, 0.0%si, 0.0%st
Mem: 2026720k total, 1834468k used, 192252k free, 189524k buffers
Swap: 3559984k…

Python: Concurrency

With all the excitement surrounding Tornado Web Server, I'd like to mention that I wrote a great article last year on Python Concurrency, with an emphasis on the various approaches to writing Web servers.

By the way, has anyone played with gevent or Eventlet? Like the proprietary version of stackless Python we used at IronPort, they offer asynchronous networking without the need to use continuation passing style.

Happy Hacking!

My Friend Committed Suicide

He was a brilliant programmer, and he had everything going for him. He was very successful.

I'm crushed because I know I could have helped if only he had given me a chance. He never did.

We in the programming world aren't always the most emotionally balanced. I know of three others who took their lives in the programming world. I've hinted at this before on my Bipolar Lisp Programmer post. To compound matters, our society has been moving away from personal interaction and responsibility for decades, leading to a culture that is toxic.

Mother Theresa said that the greatest poverty that she ever saw was to see people who felt unloved. If your friends are feeling unloved, please reach out to them. We are each far more loved than we think. In the programming world, it's so easy to get caught up in petty struggles, like Pylons vs. Django, Ruby vs. Python, free software vs. open source, Linux vs. pretty much everything else ;) What we forget is that we're all peopl…

Vim: Editing ActionScript

It took me an embarrassingly long time to figure out how to install the ActionScript plugin for Vim. First, download it from here. Put it in ~/.vim/syntax. Now, update your ~/.vimrc file to look something like this:set autoindent
syntax on
filetype plugin on

" Use .as for ActionScript files, not Atlas files.
au BufNewFile,BufRead *.as set filetype=actionscript

" Put these in an autocmd group, so that we can delete them easily.
augroup vimrc
au!
autocmd FileType actionscript setlocal sw=4 sts=4 et
autocmd FileType python setlocal sw=4 sts=4 et tw=72
autocmd FileType ruby setlocal sw=2 sts=2 et
augroup END

Forking a Subversion Repository Using Git the Brutal Way

I work at a company. We use GitHub. There's a project called CoolProject. CoolProject uses Subversion. I want to fork CoolProject for internal use. There's no use submitting our patches back--they wouldn't want them. I want to maintain our own fork, but I also want to keep updated with changes that they make. Since Subversion isn't a distributed revision control system, forking like this and getting updates is hard. If you know of a better solution than the one I've proposed below, please tell me!

Git has support for Subversion. A great video on how to use it is here. The official documentation for git-svn is here. Here's the gotcha: For the sake of simplicity and interoperating with a less-capable system (SVN), it is recommended that all git svn users clone, fetch and dcommit directly from the SVN server, and avoid all git clone/pull/merge/push operations between git repositories and branches. The recommended method of exchanging code between git b…

Ruby: An Introduction to Behavioral Driven Development with RSpec and Cucumber

On October 20, 2009 at 6:30, I'm going to be giving a talk for the East Bay Ruby Meetup Group called "An Introduction to Behavioral Driven Development with RSpec and Cucumber". This is an introduction to behavioral driven development in Rails using Cucumber, RSpec, Webrat, and factory_girl.

For the second time in my life, I finished preparing several days before the actual talk. If you're interested, here are the slides.

Happy testing! :-D

Ruby: Escaping URL Parameters

If you are trying to escape some URL parameters completely separate of Rails' routing infrastructure, URI.escape (AKA URI.encode) is not what you're looking for. In fact, I can't even figure out what problem URI.escape is trying to solve. Consider:irb(main):003:0> require "uri"
=> true
irb(main):004:0> url = "http://foo.com?a=" + URI.escape("a&b+c")
=> "http://foo.com?a=a&b+c"What you're looking for is CGI::escape:irb(main):005:0> require "cgi"
=> true
irb(main):006:0> url = "http://foo.com?a=" + CGI::escape("a&b+c")
=> "http://foo.com?a=a%26b%2Bc"The same thing goes for URI.decode vs. CGI::unescape.

However, if you're trying parse the query string from a complete URL, try this:irb(main):008:0> require "uri"
=> false
irb(main):009:0> require "cgi"
=> false
irb(main):010:0> url = "http://foo.com?a=b+c&d=a&d=b"…

Ruby: Testing Rake Tasks

I wrote a rake task for my Rails project, and I figured I should write some tests for it. The rake task just creates a certain file, hence it's easy to test. I looked for some entries on testing rake tasks using RSpec.

This blog post shows how to launch rake from within your test--in process. I couldn't get it to work because "Rake" wasn't loaded. This blog post shows how to refactor your rake code into a library that can be more easily tested. I decided to shell out to rake instead.

After reading about the differences between kernel.exec, kernel.system, %x{}, and backticks, I decided to just use backticks. The code ended up being fairly simple since it doesn't require much setup:require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe "rake some:task" do
context "subtask" do
it "should create some file containing some stuff" do
begin
`rake some:task:subtask SOME=args`
$?.exi…

Python: How to Blow Up Helicopters Using Pygame

This Thursday, I'm going to be giving a talk for PyGameSF called "How to Blow Up Helicopters Using Pygame". It's a summary of some of the libraries and tricks I used for my two PyWeek entries. I'll be covering topics such as PGU, generator-based animations, and state machine based levels.

For once in my life, I finished preparing several days before the actual talk. If you're interested, here are the slides.

By the way, I used the online service 280 Slides to build my presentation. It was pleasantly simple to use. It felt just a little sluggish compared to desktop software, but for a piece of web-based software, the UI was pretty amazing.

Linux: The Least Bad Synaptics Configuration for a MacBook Running Ubuntu

In my last post, Linux: My Mouse Ate My Homework!, I explained how dangerous it is to have a touchpad configuration that isn't finely tuned when you're running Ubuntu on a MacBook. For some reason, I've always been happy with the touchpad configuration under OS X, but I've never managed to set it up just right under Ubuntu. I spent three hours yesterday reading the synaptics man page (which is very well written, but not always helpful) and tweaking the settings.

In this post, I'm going to document the settings I came up with. I invite you to submit comments if you've found settings that feel more natural and are less likely to cause inadvertent mayhem. I'd be especially interested if you managed to get palm detection working.

First, start by creating /etc/hal/fdi/policy/appletouch.fdi as root:<?xml version="1.0" encoding="ISO-8859-1"?>

<!--
To learn more about this file: man 4 synaptics

Check for updates to this configuration…

Linux: My Mouse Ate My Homework!

One of the biggest challenges with using Ubuntu on a MacBook is getting used to all the "fun features" offered by the Synaptics touchpad, especially if you have big, clumsy hands like I do.

For instance, by tapping two fingers on my touchpad with a blank desktop, I can create a folder named "Untitled Folder". I'm not sure why I would want that, but it's definitely convenient! The problem is that a two finger tap opens up the context menu, and another tap selects the first item in the menu--which is to create a new folder named "Untitled Folder".

Another fun feature is that when I accidentally brush the touchpad with my palm, it'll find the virtual desktop where my browser is running and start scrolling the window. If I try to brush the touchpad in the opposite direction to get back to where I was, it just scrolls the browser in the opposite direction. That's because a two-finger drag on a blank desktop starts flipping through all the vir…

JavaScript: jQuery Event Handling in FriendFeed

My buddy Andreas Schobel and I both use jQuery. He was mentioning to me that his page was getting too big, and setting up all the event handlers was taking too long. He noticed that FriendFeed also uses jQuery but doesn't have this problem. In fact, he couldn't figure out how FriendFeed's click handlers even worked. We decided to investigate.

We started investigating this file. It was minimized, so we ran it through the Javascript Beautifier. This reindents the code. However, it can't deduce the original variable names for local variables. That makes understanding the code a little harder, but it's still possible.

We started looking for instances of the word "click". It looked like the file contained jQuery plugins that made use of the click function, but the main app didn't. We saw this line:var clickHandlers = {};And a bunch of functions like:clickHandlers.expandcomments = function (A) {...};However, we couldn't find any place that was a…

Web: 960 Grid System

I just watched the video for 960 Grid System. What a weird idea: constrain the page to 960 pixels and cut it up into 12 columns. When laying out your page, each piece fits into one or more contiguous columns. It might make more sense if you look at their examples.

Naturally, I knew about YUI Grid, but for some reason, watching the video for 960 Grid System helped me envision how I might actually use it. I've never really been good at super complicated layouts without using tables, but this seems easy enough to use. There's even a version of the CSS that isn't limited to 960 pixels.

Anyway, I'm not saying I'm going to convert all my sites, but I do think it's weird that somehow constraining yourself to fixed numbers like 960 pixels and 12 columns leads to greater flexbiliity. Since I'm a fan of haiku, that kind of makes sense to me.

Apple: Snow Leopard

I upgraded to Snow Leopard today. The upgrader refused to run until I deleted my Linux partition:When trying to install Snow Leopard, some people are having a problem where the installer will not recognize the current boot drive as a valid destination for Snow Leopard. Instead, it will display the drive with a yellow triangle on it, indicating something is wrong with that drive. When the drive is selected, the installer claims the system cannot boot from the drive. From cnet.There's more about the issue here. Once I upgraded, I found out that when you upgrade OS X, you have to install all your MacPorts from scratch. Hence, I didn't get much work done today. On the bright side, I really like one of the new backgrounds! ;)

I'm thinking of trading my MacBook on Craigslist for an IBM Thinkpad T61p.

Rails: Handling Duplicate Entries with Unique Indexes

If you add a unique index to a column in a database table, the database will prevent you from having two records with the same value for the given column. Rails has similar functionality at the application level. However, just like with foreign keys, the application isn't in a good position to enforce such a constraint. As soon as you have multiple application servers hitting the same database, it becomes obvious that only the database is in a position to enforce the constraint without suffering from ugly race conditions.

Ok, so how do you let the database enforce the constraint, but still handle duplicate records gracefully? Start with adding the unique index to the database in a migration:add_index :wishlist_books, [:user_id, :book_id], :unique => trueNow, instead of creating the record directly in the controller, call a method in the model:current_user.add_book_to_wishlist(book)In the user model, you might have something like this:# Add a book to the wish list.
#
# This is …

My Take on Writing Code that Doesn't Suck

I just finished watching Writing Code That Doesn't Suck by Yehuda Katz. I think he also could have called it "Writing Tests That Don't Suck". I'm going to write a summary of some of his points (along with some of my own notes). He covers a lot of the things I've been thinking about over the last couple months as a Python coder coding in Rails using behavioral driven development (BDD).

Most Rails programmers agree that tests are good. However, we don't spend enough time considering which tests are the most valuable. In fact, Yehuda points out that a lot of tests are quite useless.

Unit tested code doesn't mean bug free code. In my own experience, I'm often so tired after writing automated tests that I don't have enough mental energy to do solid exploratory testing. Having someone other than the programmer do some serious exploratory testing is necessary.

alias_method_chain is bad. Having a chain of modules monkeypatching the same method le…

Rails: Ratings

I had to add a ratings widget to my app, sort of like Amazon has. I decided to use the jQuery Star Rating Plugin on the front end. That worked out well. I decided to code the back end from scratch. That took longer than I would have expected, but the code is super tight.

Only logged in users can vote. If a user votes again, it should update his existing vote rather than letting him stuff the ballot box. Aside from keeping track of the ratings for each user, I wanted the item itself, i.e. the book, to have a rating_average field. Furthermore, I didn't want rating_average to have to calculate the average rating every time I loaded the page. It should be cached in the same way that Rails can cache the number of children a parent has.

Here's what my schema looks like:class CreateBookRatings < ActiveRecord::Migration
def self.up
execute %{
CREATE TABLE book_ratings (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
book_id INT…

JavaScript: Ajax Notifications with jQuery

If you're using Ajax, you should give hints to the user when a request to the server is being made, and you should tell him if something goes wrong. Gmail does this really well. Here's how I coded it:

First, add this to your layout:<div id="ajaxNotifications"></div>Add some CSS to make it purty:#ajaxNotifications {
position: fixed;
top: 0px;
right: 0px;
background: yellow;
text-color: black;
padding: 5px;
display: none;
}Setup jQuery, jQuery UI, and your own application.js file. This syntax is for Rails, but it's similar in Python:<script src="http://www.google.com/jsapi" type="text/javascript"></script>
<script type="text/javascript">
<% extras = RAILS_ENV == "development" ? "{ uncompressed: true }" : "{}" %>
google.load("jquery", "1.3.2", <%= extras %>);
google.load("jqueryui", "1.7.2", <%= extras %>);
</script>…

Rails: Engine Yard Flex

I'm reading the Engine Yard Flex documentation. It's pretty interesting. Here's a snippet:Each Application or Application Master server is setup to run haproxy on port 80 and then nginx or apache on port 81. Each App server has its own haproxy setup to balance load to all the other App servers so any one App server could become master at any point if the main master fails for any reason. We have an 'ey-monitor' daemon that runs on all the application slave servers and periodically does health checks on the current Application Master server to see if it is still running properly or not. If the App Master fails for any reason then the App slaves will try to take over as master by using a technique called STONITH(shoot the other node in the head). This means that once the master fails, the slaves will wait for a few bad health checks and then the slaves will all race to grab a distributed lock. Whichever slave gets the lock will steal the IP address of the failing m…

JavaScript: Coping with Client Server Conflicts

One of the things that worries me about writing Ajax apps is how easy it is for the client and the server to get out of sync. Open up the app in two different browsers, and it's easy to play havoc. Even if you implement Comet, all you have to do is temporarily lose Internet connectivity to go out of sync.

One way to cope with this problem is by using the "409 Conflict" HTTP status code. If the client makes a request, and the server knows that the client is working with stale state, it can return a "409 Conflict". In my own case, if the client makes an Ajax request, and the server returns a "409 Conflict", the client will reload the current page. It's not perfect, but it's a simple, effect, and easily-implemented strategy.

Here's how to return a "409 Conflict" in Rails:
render :text => "Your browser is out of sync with the server. Please reload the page.",
:status => "409 Conflict"Here's ho…

Rails: Using jQuery via Google's AJAX Libraries API

Adding jQuery to your project via Google's AJAX Libraries API is a piece of cake! I just updated my application layout to include:<script src="http://www.google.com/jsapi" type="text/javascript"></script>
<script type="text/javascript">
<% extras = RAILS_ENV == "development" ? "{ uncompressed: true }": "{}" %>
google.load("jquery", "1.3.2", <%= extras %>);
google.load("jqueryui", "1.7.2", <%= extras %>);
</script>That code loads jquery and jqueryui. I don't have to keep them on my server. I don't have to cache them. It uses the compressed version for production and the uncompressed version in development. Best of all, the user very well might already have that file cached in their browser.

JavaScript: DOM vs. innerHTML, Server-driven vs. Client-driven

What's the best approach to architecting JavaScript, and which frameworks best support that approach? Is it best to build the app mostly on the client like Gmail and Google Maps, or is it better to provide a normal HTML page, but with lots of Ajax mixed in like YouTube? Which approach leads to the fewest bugs when the client and server get out of sync? How does your server respond to Ajax requests? Does it serve up JavaScript code to run, JSON or XML data to digest, or pre-rendered HTML?

In the Rails world, there are all these helper functions that generate JavaScript in your HTML pages. The JavaScript might result in Ajax requests that themselves serve up more JavaScript (via .js.erb or .rjs files). There is also heavy use of innerHTML. The server is in control of the application flow.

In the jQuery world, it's standard to keep the JavaScript separate of the HTML. I think innerHTML use is still very common via the append() method.

It seems like having the client be in co…

Web: More Lost than Ever

I've built Web apps in ASP, PHP, Perl (with mod_perl and Mason), Python, and Ruby. Just in the Python world, I've used Webware, Aquarium (which I wrote), Zope, Plone, Web.py, Pylons, and Django. I've also used ZPT, Cheetah, Myghty, Mako, Genshi, and Django templates. On the JavaScript side, I've used MochiKit, Dojo, and jQuery, and I've read the docs for YUI, Prototype, and Script.aculo.us. I've written Greasemonkey scripts, and read all the XUL documentation. I've used Apache, Nginx, IIS, and I've even written my own Web server. I've done ecommerce sites, configuration dashboards, XML-RPC services, RESTful Web services, and Ajax apps. I do everything from CSS to scalability. I'm even up to date with the latest books: I've read "JavaScript the Good Parts", "Building Scalable Web Sites", "RESTful Web Services", etc.

After a decade of working with the Web, you might think I'd know it like the back of m…

Rails: Dynamic 404s, authlogic, Cucumber, and rescue_from

I'm using Rails, Cucumber, and authlogic. I want my 404 pages to be rendered dynamically; that way, I can use my application-wide layout, which is a lot friendlier for lost users. I want ActiveRecord::RecordNotFound exceptions to be handled by the dynamic 404 page. I want Cucumber tests to verify that everything is working correctly.

Getting everything working together at the same time turned out to be extremely challenging. Cucumber made it hard for me to test my ActiveRecord::RecordNotFound handling. authlogic made it impossible for me to catch ActionController::RoutingError exceptions. Defining ApplicationController#render_optional_error_file as described here conflicted with authlogic and/or Cucumber. After several hours, I finally got it all working. Here's how:

First, I added the following to ApplicationController:# Note, I can't use rescue_from to catch ActionController::RoutingError,
# otherwise authlogic breaks. Hence, I set a default route instead.
rescue_fr…

Rails: authlogic Code Review

I just spent a couple days code reviewing authlogic. First of all, let me say it's good code. Usually, I have a lot to say when I code review someone's code, but this time, I was pretty happy. Here are the corrections I submitted.

Clearly, the author of authlogic knows Ruby a heck of a lot better than I do; I'm still relatively new to Ruby. There were a bunch of idioms, language features, and design decisions that caught my attention as a Python programmer. That's what this blog post is about.

The first thing I noticed is that the code pieces together huge classes by mixing in tons of modules. For instance, in authlogic/session/base.rb:module Authlogic
module Session
# This is the base class Authlogic, where all modules are included. For
# information on functionality see the various sub modules.
class Base
include Foundation
include Callbacks
include Timeout
include Params
include Cookies
include Session
include HttpAut…