Saturday, September 26, 2009

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 here:
http://jjinux.blogspot.com/2009/09/linux-least-bad-synaptics-configuration.html

References:
https://help.ubuntu.com/community/MacBook4-1/Jaunty#Touchpad%20(appletouch)
https://help.ubuntu.com/community/MacBook_Santa_Rosa#Disable%20Touchpad%20While%20Typing
http://linuxwisdom.blogspot.com/2007/07/macbooksynaptics-trackpad-configuration.html
https://help.ubuntu.com/community/MacBook%20Aluminum#Trackpad
-->

<deviceinfo version="0.2">
<device>
<match key="input.x11_driver" string="synaptics">

<!--
You need this turned on in order to run syndaemon. Once you have it turned on, you can run
"synclient -l" to look at your existing settings, and "synclient VAR=VAL" to change settings at runtime.
-->
<merge key="input.x11_options.SHMConfig" type="string">true</merge>

<!-- Disable tapping. My palm always causes the context menu to pop up, and I hate that. -->
<merge key="input.x11_options.TapButton1" type="string">0</merge>
<merge key="input.x11_options.TapButton2" type="string">0</merge>
<merge key="input.x11_options.TapButton3" type="string">0</merge>

<!-- Set this to 30 and 40 if you want to have to press harder. -->
<merge key="input.x11_options.FingerLow" type="string">10</merge>
<merge key="input.x11_options.FingerHigh" type="string">30</merge>

<!-- This has to do with acceleration. It's a matter of taste. -->
<merge key="input.x11_options.AccelFactor" type="string">0.10</merge>
<merge key="input.x11_options.MaxSpeed" type="string">1.2</merge>
<merge key="input.x11_options.MinSpeed" type="string">0.5</merge>

<!--
2 fingers + click = right mouse button
3 fingers + click = middle mouse button
-->
<merge key="input.x11_options.ClickFinger2" type="string">3</merge>
<merge key="input.x11_options.ClickFinger3" type="string">2</merge>

<!-- Set the speed of scrolling. -->
<merge key="input.x11_options.VertScrollDelta" type="string">20</merge>
<merge key="input.x11_options.HorizScrollDelta" type="string">20</merge>

<!--
You might want to disable one or the other of these. Of course, you'll need a multi-touch compatible
touchpad to use TwoFingerScroll.

<merge key="input.x11_options.VertTwoFingerScroll" type="string">false</merge>
<merge key="input.x11_options.HorizTwoFingerScroll" type="string">false</merge>
<merge key="input.x11_options.HorizEdgeScroll" type="string">false</merge>
<merge key="input.x11_options.VertEdgeScroll" type="string">false</merge>
-->
<merge key="input.x11_options.HorizEdgeScroll" type="string">false</merge>
<merge key="input.x11_options.VertEdgeScroll" type="string">false</merge>

<!-- This never seems to work for me, but hopefully one day it might. -->
<merge key="input.x11_options.PalmDetect" type="string">1</merge>
</match>
</device>
</deviceinfo>
Now, use System :: Preferences :: Startup Applications, and create a new startup item "syndaemon -d -t" and then reboot. Read the file above to learn how to tweak these settings at runtime once you've rebooted.

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 virtual desktops. Once you stumble upon a virtual desktop that has a fully maximized application, such as a browser, the two-finger drag just scrolls that window. You can't use the touchpad to get back to the original virtual desktop if there's a maximized window in the way.

As a Vim user, I was pleasantly surprised to find that my touchpad was helpful when using Vim. It's called the "I'm feeling lucky!" feature. Vim has two modes, insert mode and command mode. If you're in command mode and accidentally hit your palm on the touchpad, it'll paste whatever you have in X11's copy-paste buffer into Vim which treats it as random commands. It's even more fun in Vi since Vi doesn't have unlimited undo.

Last of all, my favorite feature is that "I'm feeling lucky!" works with the shell too. If you've highlighted a big block of text, and then accidentally hit your palm on the touchpad while typing in a terminal window, it'll paste all the things you've highlighted and treat them as shell commands. I think that feature is a useful reminder that you should never highlight anything with "rm" in it!

Wednesday, September 23, 2009

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 actually using clickHandlers. Finally, I started looking for just the word "Handlers", and I came across this:
function bindEvents(A) {
var B = window[A + "Handlers"];
$("body")[A](function (F) {
if (A == "click" && handleFBClick(F)) {
return false
}
for (var E = F.target; E; E = E.parentNode) {
if (!E.className) {
continue
}
var C = E.className.match(/\bl_([\w]+)\b/);
if (!C) {
continue
}
var D = B[C[1]];
if (!D) {
continue
}
if (A == "click") {
$(E).blur();
return D($(E), F) ? undefined : false
} else {
D($(E), F);
return
}
}
if (!window.gIphoneMode && A == "click") {
popup.hide();
$.closePopupMenu()
}
})
}
It took a little longer to figure out what it meant.

Events are allowed to propagate all the way up to the body. The body has handlers for many types of events such as "click", but they all point to the same event handler function, which is nested anonymously inside bindEvents.

Let's suppose a JavaScript link is clicked. The event will propagate up to the body tag which will try to handle it. The class name for the link is inspected. If it is something like "l_expandcomments", and clickHandlers has a member called expandcomments, then that function is called. There is special code to a) stop further event propagation unless appropriate b) stop the user from double clicking c) do something special for iPhone users.

Hence, they can connect an HTML element with a JavaScript handler just by adding a class such as "l_expandcomments" and adding an "expandcomments" function to clickHandlers. That's convenient and clever. JavaScript's malleable (almost chaotic) nature reminds me more and more of Lisp (which I've been saying for years). Going back to my buddy's original problem, this approach seems like a viable way to reduce the event handler setup time. I wonder what initially lead them to take this approach.

Tuesday, September 22, 2009

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.

Thursday, September 17, 2009

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.

Wednesday, September 09, 2009

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 => true
Now, 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 idempotent. It will just do the right thing if the user tries to
# insert a duplicate record.
#
# Important: if you call this method, then the next time you load the wish
# list, you must reload to clear the cache. For instance:
#
# current_user.wishlist_books(true)
def add_book_to_wishlist(book)
wishlist_books.create! do |list_item|
list_item.book = book
end
rescue ActiveRecord::StatementInvalid => e
raise e unless /Mysql::Error: Duplicate entry/.match(e)
end
Don't worry too much about the associations. I'm just trying to show the code somewhat in context. The important part is the exception that is caught.

Naturally, this is MySQL specific. I don't know the exact thing to look for with PostgreSQL. However, that's okay. I don't mind using MySQL specific features in my models where it makes sense.

Tuesday, September 01, 2009

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 leads to a brittle modularity nightmare that is unmaintainable and unreausable. It's also a sign of a poorly designed API. Just because Ruby lets you do things like that doesn't mean we should be content with poorly designed APIs.

Matz specifically doesn't want to add interfaces to Ruby. However, Yehuda argues that interface-oriented design is a very good thing. Yehuda reminds us that everywhere other than Ruby, coders know that good design means coding to an interface, not to an implementation.

You might have code and unit tests that test that code. You might refactor them both together, and all your tests might pass. However, if you break the API to your code, you've broken everyone else's code that is reliant on that interface. That's why regression testing is important. It doesn't matter how the implementation works--the only thing that matters is that your interface continues to behave as promised.

That's why testing the external interface is so much more important than unit testing the internal implementation. Heavily mocked and stubbed unit tests just aren't as useful as full-stack testing (with, say, Cucumber and Webrat). I personally think there's nothing worse than a controller, model, and a view that are tested individually using mocks and stubs, but which blow up when combined. That's not to say that unit testing isn't useful--it's just that what I would call integration testing or what Yehuda would call regression testing is even more useful. Test what you actually care about--i.e. the external behavior.

"some_object.should respond_to(:some_method)" is not a useful test. If you're not testing what some_method actually does, then why bother, and if you are testing what some_method actually does, then you don't need to test for its existence. I think that "some_object.should respond_to(:some_method)" is sort of like sunbathing--it makes you feel better about yourself and (arguably) makes you look good, but it's not helpful for your overall health and well being.

At the risk of beating a dead horse, when I see tests like "some_object.should respond_to(:some_method)", it really makes me wish I were coding in Haskell. Haskell's type system can enforce interfaces like this without writing boring, do nothing tests. Perhaps that's why so many Ruby coders have switched to Scala.

Yehuta does think that TDD with unit tests is helpful for designing your code, but those tests aren't good as regression tests. After your code is working, then you should write regression tests that cover the things that you actually care about, not the irrelevant implementation details.

Yehuda doesn't have a computer science background. Hence, he is relearning a lot of the lessons that MIT's Structure and Interpretation of Computer Programs has been teaching for decades, and I think that's good. It seems like much of the Rails world has forgotten a lot of the things we've learned about modularity, encapsulation, and good design over the last 30 years. Hopefully, thanks to Yehuda and Merb, that will improve.

It will be interesting to watch the synthesis of good design as taught by SICP with a strong commitment to testing that is popular in the Rails world.