Wednesday, April 23, 2008

Prototype DOM Extension Browser Quirks

Documentation is really helpful. Seriously. In fact, anybody who failed to read the section entitled 'Native Extensions' in the article on How Prototype extends the DOM would be surprised to find that much of their code would not work in IE while working fine in many browsers. Until recently, I was one of those people that had not read that article. This is an easy pitfall for developers whose primary development browser is Firefox, or even Opera.

The problem is that Firefox allows adding methods to prototype of native objects, such as HTMLElement. Thus, all DOM extensions are are available on all elements, including dynamically created elements. However, this is not the case with IE. The syntactic shorthand DOM extensions I'm referring to are constructs such as myDiv.getWidth() instead of the longer Element.getWidth(myDiv).

IE does not let anyone touch HTMLElement.prototype, thus DOM extensions are not available by default on elements in IE. To get around this, Prototype has two constructs to enable DOM extensions on an element:
  1. Element.extend('my_div'), or
  2. $('my_div')
Prototype's $ function automatically extends any element passed through it, while still being smart enough to not doubly extend any element. Thus, assuming that you access all elements through $('elem'), you won't have to worry about the Element extensions not being available on a given object.

The simplest, though more annoying solution, is to not use syntactic extensions and use Element methods directly. This means always using Element.doSomething(myElem,...) rather than myElem.doSomething(...). That's not an ideal solution, and we can do better assuming we can remember to extend elements ourselves, which is as simple as calling Element.extend(myElem), or wrapping access to the element in a $ call, such as $(myElem).

There are three sticky situations to watch out for: (1) dynamically created objects, (2) going up or down the DOM chain from an extended element to non-extended elements, and (3) browser objects.

Dynamically Created Elements
In IE, dynamically created objects are not extended by default, unlike Firefox. In Firefox, the following will work:

var myDiv = document.createElement('div');
myDiv.setStyle({width: '50px'});

Note the DOM extension .setStyle({}) is available by default. IE requires an additional step to achieve the same:

var myDiv = document.createElement('div');
Element.extend(myDiv);
myDiv.setStyle({width: '50px'});

It's easy to forget the call to Element.extend(myDiv) when dynamically creating elements, especially because the code will work just fine without that in a few more developer friendly browsers.

Traversing the DOM
Extending an element does not confer that extension upon elements referenced from said element. parentNode is a big one to watch out for. It's really easy to want to do:

$('myDiv').parentNode.hide();

That will work fine in Firefox, but only because all elements are extended by default (as described above). In IE, you'll have to remember to extend the parent element as well:

$($('myDiv').parentNode).hide();

I agree, it is annoying. On the good side, since $ is smart enough not to re-extend a previously extended element, we don't have to worry about browsers that extent all elements by default running into problems from the constructs necessary to make IE behave nicely.

Browser Objects
document is an object, and thus must be extended to enable the syntactic shortcuts. In Firefox, the following will work fine:

document.body.insert(myDiv);

In IE, that will result in the error message, "Object doesn't support this property of method." To make it work in IE, just wrap the call:

$(document.body).insert(myDiv);

On the whole, I like the syntactic shortcuts, but as everyone stresses, you have to test the code in multiple browsers. If my code using Prototype works in Firefox but not IE, these are the first three things I look for. Happy scripting!

Tuesday, April 22, 2008

Beer Quest: 22 Down, 478 to go

For my birthday last weekend, a friend gave me a copy of Michael Jackon's Great Beer Guide to 500 Classic Brews. This Michael Jackson is the beer aficionado, not the singer. And so, I'm going to use the book as a checklist to expand my beer knowledge. I have had 22 of the 500 beers before receiving the book, and #23 is currently in the fridge. I am thoroughly looking forward to the next 478 beers!

Here's the current list of beers in the book that I have tried:

Alaskan Amber
Alaskan Smoked Porter
Bitburger Premium Pils
Boon Geuze
Budweiser Budvar (sold in the US under the name Chechvar)
Fanziskaner Dunkel Hefe-Weissbier
Gorden Biersch Marzen
Gouden Carolus
Guinness Extra Stout
Hoegaarden Witbier
Huyghe Delirium Tremens
Kasteel Bier Donker
Konig Pilsener
Kostitzer Schwarzbier
Newcastle Brown
North Coast Old Rasputin Russian Imperial Stout
Ommegang
Paulaner Hefe-Weizen
Pilsner Urquell
Red Hook ESB
Saison Dupont
Schloss Eggenberg Urbock 23
Singha

I may not have mentioned it before, but my housemates and I also brew our own beer.  We started last summer and it has been an awesome time.  So far we're brewed an English Brown, an Irish Red, a German Altbier, and an IPA.  Currently underway is a Belgian Triple, and just last night we kegged a Germain Weizenbier.  Next up is a Maibock.  Home-brewing is certainly a lot more work than going to the store and picking up a nice 6-pack, but it's really enjoyable.  Kind of like how steak houses have great food, but sometimes it's way more fun to stand outside in the rain and grill that steak yourself.  So, in addition to tech posts and random thoughts, I think there will be an increased number of posts relating to beer in the future.

Tuesday, April 15, 2008

Parallels Desktop Config Recovery

Do not run OS X Disk Utility's Erase Free Space feature while Parallels Desktop is running. I did that today, and my Parallels VM config file disappeared. Details follow.

Erasing free space is accomplished in a rather interesting way. Disk Utility creates one really huge file to fill up the remaining space on your hard drive, then proceeds to write over that space with 0's as many times as you choose (1-pass, 7-pass, or 35-pass). Thus, at the peak of the process, your system will indicate that your hard drive is full and suggest that you clear space (the free space alert has no awareness of Disk Utilities activities and treats the new large file like any other file). The technique involved is actually a pretty creative way of letting the operating system unconsciously collect all the free space without having to explicitly write a new routine for collecting free space. After all, allocating space for a file is just a matter of finding enough free space for said file.

Anyhow, I had Parallels running while this process was going on and I had forgotten that Parallels requires a certain amount of free space to operate. When the remaining free space dropped below the required amount (in my case 512MB, though it may vary depending on your VM configuration), the VM alerted me and presented two options: clear enough space and continue operating, or shutdown the VM. I chose the latter since I wasn't actually using right then. And that was the final blow...

When I went to restart Parallels later, I was prompted with the screen to install a new VM. WTF Parallels? What happened to the VM I already had installed and have been using for months? After some investigating, I discovered that the config file for my VM was empty. That's right, empty. The file remained, but its contents had disappeared. Not sure why, but it was disappointing to say the least.

The fact that I am a Mac user should suggest that my tolerance for Windows XP installs is rather low, so clearly I didn't want to reinstall the VM... if only I had somebody else's config file...

At this point, I had no idea what the structure of a Parallels VM config file was, but I figured that investigating that would be far quicker than an XP install. Fortunately Arjun also uses Parallels, so he sent over his config file, and I broke out the mad doctor goggles and dove in.

Turns out the VM config file is a simple properties file with [] section headers exactly like a my.cnf MySQL server/client config file. Furthermore, there are very few properties in there that really matter in terms of the VM running or not, the rest just deal with how it runs. Here they are:
App Version = 3.0.5582
...
Disk 0:0 image = winxp.hdd
The first could be problematic because the config file is going to need to list an App Version compatible with the version of Parallels you have installed. For example, if you upgraded to Parallels version 3.x.y from version 2.x.y, the installer reformatted your existing VMs to work with version 3.x.y. If you have version 2.x.y of Parallels, but your config file indicates version 3.x.y, I'd suspect you'd run into some problems...

The other important property there is just a reference to the name of the Parallels .hdd disk image. Since Arjun has the same version of Parallels as me, all I had to change was the name of the disk image.

The only remaining changes were smaller, but I suspect would have caused problems if I hadn't changed them too. Namely, in the [Shared folders] section, there can be references to shared folders, in which case you'll need to change the location to match your file structure. I had the following, but yours may be different.
Folder0 path = /Users/Ryan
...
Folder1 path = /Users/Ryan/Documents
Also, if you remove shared folder mappings, make sure to adjust this property accordingly:
Shared folders count = 2
After making those changes, I fired up Parallels and was back up and running just like before! Overall, it was a remarkably painless process to recover the VM config file if you happen to have lost it. At the very least, those few changes will get your VM back up and running, and then you can use the config GUIs to restore your specific preferences.

As a disclaimer for comments, no, I did not GLOG or RTFM for this, so if it's out there already somewhere, sorry for the duplication. In all, it was probably faster to try than it would have been to find the answer online anyway.

Wednesday, April 2, 2008

Java Complaints and Introducing the Redemption of Java: J2Free

I'm a big Java fan, so this list is by no means a declaration of war against Java. There are, however, a few things about the language that I find very annoying:

1) Strongly typed. I enjoy types, I really do. Sometimes I even love the strongly typed nature because of the kick ass things that can be done with generics and reflection. Other times, I crave the flexibility of loosely typed languages, such as javascript. I've found that loose typing can lend itself to more elegant solutions.

2) No anonymous functions. If Java treated functions as first-class objects and allowed dynamic creation of functions at runtime, I would be in heaven. Sometimes, I find scenarios where the most elegant, and quickest to implement, solution would make use of an anonymous function. Other times I really wish I could pass functions around as arguments. This would make my life more happy.

3) == vs. .equals() for equality comparison. I'm not a big fan of pointers in C and C++. I prefer array notation in Java, for example. However, C and C++ pointers make equality comparison very easy and clear. Namely, it is quite obvious when you are comparing by memory location and when you are comparing by value. In Java, it is easy for this to escape me momentarily and cause me to spend 5 minutes debugging something so simple as an equality comparison. For example, comparing int primitive data types with == compares by value. However, if you happen to be using the int wrapper class, java.lang.Integer, then equality comparisons with == default to comparison by memory location since Integer is an Object. To compare two Integers by value, you have to use the .equals() method of Integer. I would much prefer if == in Java defaulted to comparison by value, and .equals() (or something like .objectEquals()) was used for memory location comparison. I think it would be more straight forward at least when it comes to consistency with the Java primitive data types.

4) I can't ever find any hosting providers that support Java application servers. This is a pain because I end up setting up all of our servers, taking me away from tasks I find more interesting, challenging, engaging and, in general, worthwhile.

In all fairness, I still think the reasons against Java out there are highly exaggerated. The "experiments" that show Java is less agile, clunkier, and slower to develop all seem to be written by people that don't know Java. The first time you're trying any framework it's confusing as hell, simple as that. Java has some basic configuration required for any web-application. I've been doing it long enough now that I've developed my own framework-ish libraries, which allow me to skip right past all the parts of Java people complain about and jump right into the creation aspect.

On that note, I've begun development of a consolidation of my tools with the help of one of my Co-Founders at Publi.us, Arjun.  We are calling the project J2Free.  It's goal: redemption of Java when compared with frameworks such as Rails & Django.  We started out by thinking up all of the things that annoy us when starting a new Java web-application.  The first step in the J2Free is a smooth process by which users can bypass the annoying crap and jump straight into development.

J2Free itself will utilize J2EE + JPA (with Hibernate as the persistence provider) + MySQL in a classic MVC architecture.  As a disclaimer, I despise JSF with it's massive xml configuration files and odd String return values from controller classes.  So, don't expect any of that here.  J2Free will feature time saving procedures such as auto-generation of an admin section and fun little tidbits such as taglibs for pagination of datasets, templating, caching, and display formatting (Date formatting, quote escaping, etc).

Additionally, J2Free will make it easy to automatically open controller (or subsets of controllers, such as read-only functions) classes via API.  Want an API? "Click. Done."  Since I am a big fan of DWR, I plan on including that same "Click. Done." ease for enabling AJAX via DWR.

Clearly, this is all very early, but we're getting started with planning so now is the best time to throw out ideas!  When complete, we plan on making J2Free available freely and we will be open sourcing the project at some point as well.  Anyway, something to look forward to!

Tuesday, April 1, 2008

Interesting 30-second Snippet for Emailing Websites to Friends

So, one of my co-founders was telling me today about a startup out there that basically is del.icio.us but designed for sending articles to your friends. I realized that I could implement 95% of the "sending articles to your friends" functionality in 30 seconds. The code:

<a href="javascript:location.href='mailto:?subject=Check Out: \'' + document.title + '\'&body=Thought you might like this: ' + window.location.href;">Send to Friends</a>

It's just a Del.icio.us-style bookmarklet that opens your mail client with the current page title in the subject and the link to the current page in the body. Simple, clean, done. Now I can just click that when I want to email a page I'm on to friends.

For simplicity, you can drag this link to your bookmarks bar to add the feature yourself.

E-mail to Friends