I've been working on some code that gets a bunch of data from the server (via XHR) and then builds a big table to display it all. The table is customizable and users can choose which columns they want to show/hide. The only problem is that whenever they show or hide a column we have to recreate the entire table (in Javascript with string concatenation) which is slow. Really slow. I've been thinking about ways around that and I've finally got one that works.

In HTML we have TRs, which let us address individual table rows, but not TCs, so we can't easily identify a table column and modify it. To get around this we can artificially group the TD/TH elements that belong in a column using a CSS class. So, for example, our table could look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

  <table id="table" class="">
    <thead>
      <tr>
        <th class="c1">Column 1</th>
        <th class="c2">Column 2</th>
        <th class="c3">Column 3</th>
        <th class="c4">Column 4</th>
        <th class="c5">Column 5</th>
        <th class="c6">Column 6</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="c1">Row1 Col1</td>
        <td class="c2">Row1 Col2</td>
        <td class="c3">Row1 Col3</td>
        <td class="c4">Row1 Col4</td>
        <td class="c5">Row1 Col5</td>
        <td class="c6">Row1 Col6</td>
      </tr>
    </tbody>
  </table>

where the c1 through c6 classes are used to identify our columns. Now that we have a way of identifying a column we can use CSS to toggle it's visibility. We'll define a rule that says a TD or a TH that has the same class as the TABLE element should be hidden, like this:

1
2
3
4
5
6
7
8
9
10
11

  <style type="text/css">
  table.c1 th.c1, table.c1 td.c1,
  table.c2 th.c2, table.c2 td.c2,
  table.c3 th.c3, table.c3 td.c3,
  table.c4 th.c4, table.c4 td.c4,
  table.c5 th.c5, table.c5 td.c5,
  table.c6 th.c6, table.c6 td.c6 {
    display: none;
  }
  </style>

When the table doesn't have any of the classes applied the TD or TH elements get the default display value and are visible. However, if we want to hide column three all we have to do is add the "c3" class to the TABLE element and column three will be hidden. While it might seem a little unintuitive that we add the column class to our table to remove the column, we do that because then we can use 'display: none' instead of defining what the visible display state should be. And why does that matter? Because there are sixteen visible display states and only one hidden state and this way we can let the browser figure out what the right visible display states for TD/TH elements are.

To see this concept in action you can take a look at this simple page I threw together.

My wife and I went for a hike the other day. Before we left I grabbed my camera, my phone, and my iPod. It occurred to me later that I take those three devices with me just about everywhere I go. I'm really looking forward to the day when they converge into one device that performs all three functions well. The iPhone is getting close, but it doesn't have the capacity of my 30GB 5G iPod or the resolution of my Canon SD 450. On the other hand it does have a bigger screen than my camera, iPod, or phone and strongly out performs my RAZR in phone functionality. I'd guess that the second or third generation iPhone will fulfill all three of those roles satisfactorily.

So far I'm not asking for anything radical. However, what I think would make a truly brilliant device would be if Apple stuck a 500GB hard drive in the iPhone and made it work as a backup location for TimeMachine. Then, go one step farther and make the data stored in the TimeMachine backup accessible/viewable - at least the important bits - through the iPod/iPhone interface. Think about it, my iPod already contains a majority of my music, pictures, and movies (which is the bulk of the data on my laptop) and it's accessible - I can look at pics or play music and movies. Why not go one step further and back-up all my data to my iPod and make all the important bits accessible/viewable?

Every time you connect your iPod to your computer you're essentially backing up all your digital entertainment. Then when you plug in your USB/FW drive for use with TimeMachine the same thing is happening, except that it's a different drive and you can't access any of the data stored on the drive without connecting to an OS X machine. I think Apple should modify the iPod/iPhone OS so that it can read TimeMachine backups, stick a 500GB HD in the device, and call it the iPod/iPhone LifeMachine. So maybe the name's not real hot, but I would pay serious coinage for this device (whatever it got called) if Apple released one.

So please, Apple, let me do away with my camera, phone, iPod, and external TimeMachine drive and replace it with an iPod LifeMachine.

I'm not big on New Year's resolutions. First, it seems to me like people who get one chance a year to make a resolution are at a disadvantage compared to those who can introspect at any point in the year and decide that "Starting now I want to ________." Secondly, a resolution seems like a thing that is more often broken than maintained, and once broken left by the wayside until the next opportunity to make (and possibly break) it again. For these reasons I prefer goals. A goal can be set at any time and you never 'break' your goal. You may not have achieved it yet, but that doesn't imply that you've stopped trying or somehow missed your opportunity to achieve it.

While thinking about the past year I realized that I learned several new programming languages but didn't really accomplish anything significant in any of them. I learned Ruby (and Rails), Python for work, and spent some time looking at Scala (which I really like), and have recently started learning Objective-C and Cocoa. My goal, starting now, is to stick with a language until I've accomplished something significant in the language. I'm not sure what I mean by significant yet - possibly something that can be used by others - but when I accomplish it I'll know.

I met Monsur in April of 2005. I'd flown to New York on a Thursday night for an interview with Xanga. He and Jon met me at the hotel and took me to Times Square Brewery for a late dinner. After forty-eight hours of coding, eating, drinking, and socializing with the team I received a job offer and that was the beginning of my association with Monsur (as well as the rest of the great folks at Xanga).

Over the course of the next year I got to work closely with Monsur on various projects, be there when he got married, see Ted Leo and the Pharmacists, go on recruiting trips, attend PDC, and move over a hundred racked servers in the back of a U-Haul (see the pics). I've no doubt that life would have continued in much the same vein except that I met an amazing girl in California and decided to move across the country and marry her, which led to the close of the Xanga chapter in my life.

I've been blessed thus far in my life that I've never left a job because I didn't like the job - IBM was a great place to work, I have very fond memories (and a groomsman) from my time at Xanga, and Latham & Watkins was very good to me. In addition I can honestly say that I'd welcome to opportunity to work again with many of my past co-workers, which is why I'm really excited that Monsur accepted a job at Google. As one of the first programmers (perhaps the first) at Xanga I never expected Monsur to leave, so when I heard he was interviewing with Google I was shocked. However, I can attest to the fact that sometimes you have to leave something good behind because a new opportunity has arisen, and I'm glad that this new opportunity for Monsur is here at Google.

Congrats Monsur and welcome to Google! It's unlikely that we'll get called upon to move racks of servers here :)

Rhapsody, a subscription music service I've used for the last couple years, makes a lot of their data available through RSS feeds and XML. For a project I'm working on I wanted to parse one of their RSS feeds with Ruby and I thought I'd share how I did it.

The Rhapsody feeds are RSS 2.0 but also include Rhapsody specific elements in the 'rhap' namespace, so it was necessary to use a parser that could be extended to parse these elements. Ruby includes a library for parsing RSS but after reading the source I wasn't sure how to extend it. Fortunately, some searching turned up the Syndication library which the author designed to be easily extended. After doing a 'gem install syndication' you're all set to begin coding.

The Syndication parser works by defining objects that map (roughly) to elements in the RSS document. When the parser is parsing an element it maps element attributes to object attributes with namespace (if any) prepended to the object attribute with an underscore. For example, the attribute rhap:rcid would become rhap_rcid and if an attr_accessor named rhap_rcid existed on the object it would get set to the value of the rhap:rcid attribute. I wanted to extend the Syndication::RSS:Item class so I started out by defining a module called Syndication::Rhapsody::Item that had all the properties I was interested in. Then, with that defined, it was simply a matter of including this module in the Syndication::RSS:Item class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

require 'syndication/rss'

module Syndication
  module Rhapsody
    module Item
      attr_accessor :rhap_rcid
      attr_accessor :rhap_artist
      attr_accessor :rhap_artist_rcid
      attr_accessor :rhap_album
      attr_accessor :rhap_album_rcid
      attr_accessor :rhap_album_art
      attr_accessor :rhap_album_release_date
      attr_accessor :rhap_album_original_release_date
      attr_accessor :rhap_album_type
      
      # Need to override the tag2method defined in class Container because it
      # doesn't deal with tags with dashes in them. Ruby can't handle method
      # names with dashes so we switch to underscores.
      def tag2method(tag)
        return tag.downcase.gsub(/[:-]/, '_') + '='
      end
    end
  end
  
  module RSS
      class Item
        include Rhapsody::Item
      end
    end
end

You'll notice that I also ended up overriding the definition of the tag2method method. This is because Ruby doesn't allow variable names with hyphens (quite sensibly) so the elements in the 'rhap' namespace that had a hyphen in them were getting ignored. To fix that I simply had tag2method substitute an underscore for a hyphen.

With the Syndication::RSS::Item modified it's now simply a matter of creating the parser and reading the feed:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

require 'rss/2.0'
require 'open-uri'
require 'syndication/rhapsody'

class RhapsodyReader
  def initialize(url = 'http://feeds.rhapsody.com/new-releases.rss')
    @url = url
  end

  def read
    parser = Syndication::RSS::Parser.new
    @feed = nil
    open(@url) do |s|
      content = s.read
      @feed = parser.parse content
    end
    
    @feed.items.each do |item|
      puts "Got rcid '#{item.rcid}'"
    end
  end
end

For more information on the Rhapsody web-service you can go here: http://webservices.rhapsody.com/. Like I mentioned above, Rhapsody also makes some of their data available as non-RSS XML and I'll write about using REXML to parse it in the future.

When MarsEdit 2 was released I decided I wanted to try blogging using a client app instead of Mephisto's web interface. However, when I tried connecting to Mephisto using XML-RPC I kept getting errors. This eventually led me to upgrade to the edge version of Mephisto which requires edge rails and while I was at it I also switched to Capistrano 2.1.

Because I'm nowhere near being a Ruby or Rails guru the whole process took me longer than it should have. All the effort was worth it, though, when I was able to make changes on my dev machine and then type 'cap deploy' and have it magically appear on the server hosting this blog. That is truly living the dream.

My goal is to write more frequently and chronicle some of the things I'm tinkering with in my spare time. I also plan on writing up my thoughts on software that I try out, like MarsEdit 2, for instance.

MarsEdit!

This is a test to see how the whole MarsEdit thing works.

It was a real hassle getting xmlrpc working.

1
2
3
4
5
6

class foo
  def init
    puts "Got inited"
  end
end

Code highlighting seems to be a pain in the rear, with no definitive answer on how it's supposed to be done.

Back in February I convinced my dad to let me refer him for the position of Senior Ads Quality Statistician at Google. I knew he was an excellent candidate and would be perfect for the position but I didn't know if anyone would be able to convince Dr. and Mrs. Meyer to pick up and leave the family farm in Illinois for a stint in Souther California. Not surprisingly, Google made an offer. Vanderbilt did too. Then his current place of employment made a ridiculously large counter-offer. After much deliberation he's accepted the Google offer.

Congratulations dad, Google is lucky to get you! It'll be great to work together again as well as having you and mom live a few thousand miles closer :)

During my senior year in college (2001-02) I took CS292 (since renumbered to CS492), a senior design project class. While our project was not the most exciting (we were evaluating which programming languages were well suited for COM development) another group worked on a program for Motorola that would help them debug their Bluetooth implementation. As we were all required to present on our projects, their presentations were my introduction to the Bluetooth communications protocol and all the potential it held.

That was five years ago and my visions of a connected utopia have been slow to materialize. However, several things have happened recently that persuade me there is still hope for the future, the most convincing of which occurred last night when I was out with my mother and father-in-law. As we were walking back to their Prius my father-in-law got a call on his cell phone. It was his brother, calling to finalize some details about the birthday dinner for their mother next week. When we got in the car and my mother-in-law turned it on it was suddenly filled with the sound of Uncle Charlie's voice. Why? The Prius had detected that one of the phones it was paired with was engaged in a call and seamlessly switched it over to the car's audio system. At that point all three of us were able to participate in the conversation - the audio quality was good and we didn't have to talk louder than normal to be heard.

In a similar vein, a couple weeks ago I ordered Aliph Jawbones for my wife and myself so we could talk to family and friends while we're taking care of stuff around the house. Pairing the Jawbone with the phones (Moto Razr and Moto Krzr) is straightforward and the voice quality is good. I'm also impressed by the fact that adjusting the volume on my phone will also adjust the volume in the Jawbone when it's in use. In fact, when I'm using the headset I don't have to hit any buttons on it at all - it acts just like an extension of the cell phone, which is exactly the way I want it to be. When I use bluetooth to pair two devices I don't want to have to manage both devices, I want to manage one and have the other act like an extension of it.

The next several items are all computer related. First, my wife uses her Moto Krzr as an EVDO modem with her MacBook via Bluetooth and has been doing so for the last half-year. While the speed is less than blistering it provides solid performance and lets her connect anywhere she's got a digital signal. I use Bluetooth to sync my contacts between my phone and my MacBook Pro. And finally, earlier this year I was able to use my phone as a modem (paired with my MacBook Pro via Bluetooth) to send a fax from Mail.app.

Even though it's taken a while (at least here in the U.S.) I feel like we're finally reaching a point where Bluetooth is really useful. I have to commend Apple for doing such a good job supporting Bluetooth in their operating system and chastise Verizon for their habit of crippling and disabling various Bluetooth profiles in order to force customers to use their (costly) services instead.

My first internship was with a company called Tellabs that made telecom products - large, powerful boxes that did telephone switching. These big huge boxes generated big huge log files and my task was to write a Perl program that made it easier for engineers to search through and get what they needed from the log files. My computer was an aging Sun something-or-other.

My second internship was with SAIC where I spent the first part of my summer writing code to parse various binary file types and the second part implementing terrain rendering algorithms in C++/OpenGL. My computer was again an aging relic with FPS measurements usually less than one (no textures, just tri-strips and simple shading): an SGI Indigo.

When I started at IBM I was given an older T21 laptop and a decent workstation. By the time I left I had a top of the line T42, a top of the line T41, and a very powerful workstation with a pro-grade graphics card that was unfortunately never utilized. I also had a 19" CRT monitor.

After my first two internships my experience at IBM was a welcome change - finally a place that provided its engineers with really good equipment. From them I learned the value of having the best tools available and three years later when I was interviewing at Xanga one of the things I evaluated them on was the type of equipment their developers had. As it turned out, they gave their devs very good equipment, which came in handy when we played Counter-Strike or Battlefield 2 in the evenings, as well as when programming, of course. It was at Xanga that I was first able to experience a dual-monitor setup. I started with two 19" monitors and graduated to a 24" and a 20" a little later.

While I was at Xanga experiencing my new found screen real-estate wealth I couldn't help but wonder what it would be like to have dual 24" monitors, a curiosity that was satisfied when I started at Google, where dual 24" monitors was the default for engineers. The amount of screen real-estate you get with dual 24" monitors is amazing. I can have Eclipse maximized in one screen and have a large browser window, a terminal, and a text editor on the other screen, like this:

What I hadn't expected, though, was the amount of head turning it would take to shift my gaze from one monitor to the next. And, believe it or not, even with Eclipse maximized on one monitor things still felt cramped when I had two code panes open side-by-side (my default mode of operating). So when the opportunity arose to trade in my 24"s for a 30" monitor I jumped at it. After using the 30" for a couple weeks I think I made the right decision. Eclipse maximized on a 30" monitor is a beautiful thing. I can see two full code panes, stack the Package Explorer view on top of the Outline view and both are still usable. The benefit is also apparent in the Debug perspective when there are even more views, all with useful information, visible at the same time. I can see the call stack, the variables currently in scope, the console, and the editor view is actually usable when you're stepping through code:

Another advantage of the 30" monitor is that I don't have to move my head as much, especially to look at something on my laptop, which I use for email and messaging. A drawback is that I can't see Eclipse and Firefox at the same time so I end up alt-tabbing between them, but it's not as much of a drawback as I had thought it might be. For reference, that's a 15" MacBook Pro on the left and a Lenovo X60 on the right.

That may look like some weird form of ASCII art, but it's really a regular expression that will find all short-from Javadoc comments. You can try it out yourself here.

As far as my ToDo list goes, I've written Java code that will serialize an object to JSON and the deserialization will get implemented when I get a few hours to bang away on it. Once that's working I'd like to make it available somewhere. I've also learned enough Python to do what I need to do. I have friends (well, at least one) who really like Python but my impression of it so far is that it's basically like Javascript for the non-browser world. I'm sure that if I spent more time with the language I'd feel differently, but for now I miss my semicolons and brackets and type declarations.

I have a few programming related things I'd like to do or am in the process of doing and I thought I'd write them down here to keep track of them.

  1. Learn Scala. This is partly motivated by my desire to use the lift framework and partly by interest in the language itself. I used to shudder involuntarily when I heard the word 'functional' (unpleasant flashbacks of YAPL-to-C translators written in LISP in college) but the concept of a functional/OO hybrid language seems like a good way to restore my relationship with my highly-parenthesized brethren.
  2. Write a JSON serializer in Java. I think there are probably a couple different approaches that I can take and I'm curious which is best. I might also try to write a deserializer. This is a project I'm undertaking for informational purposes and not to use the actual code. I might also try to write a Scala implementation just to see how they differ.
  3. Learn Python. This is something I need to do for work. I've heard a lot of good things about Python so I'm looking forward to working with it. This is more of a passive learning since I'll learn as much as I need to know to do what I need to do. Probably not the best way to learn a language, but for now it'll have to suffice.
  4. Write a JavaScript parser that will let me use Java style class and interface definitions and then convert it to JavaScript on the fly. I hate the way JavaScript does classes and member access and I want some way to clearly define what my objects look like and behave like. I would love to be able to write:
    
    package com.joelpm.widgets;
    
    public class WidgetFoo {
      protected var width;
      protected var height;
    
      public WidgetFoo(var width, var height) {
        this.width = width;
        this.height = height;
      }
    
      public var getWidth() { return width; }
      public var getHeight() { return height; }
    
      public var move(var xDist, var yDist) {
        // do move...
      }
    }
    
    
    Instead of having the equivalent JavaScript that might look something like this:
    
    var com = {};
    var com.joelpm = {};
    var com.joelpm.widgets = {};
    
    com.joelpm.widgets.WidgetFoo = function(width, height) {
      this.width = width;
      this.height = height;
    }
    
    com.joelpm.widgets.WidgetFoo.prototype.getHeight = function() {
      return this.height;
    }
    
    com.joelpm.widgets.WidgetFoo.prototype.getWidth = function() {
      return this.width;
    }
    
    com.joelpm.widgets.WidgetFoo.prototype.move = function(xDist, yDist) {
      // do move...
    }
    
    
    Clearly, the JS parser would just convert the much nicer looking Java style class declaration syntax into the equivalent JS syntax and evaluate it, but development would be much nicer and the code would be clearer to both myself and anyone else who has to read it. I know there are JS compilers like Google's GWT that will turn actual Java into JS, but I'm looking for something that doesn't require a compile cycle to pick up changes. Anyway, this is probably something I'll never get around to, but the thought was prompted by a discussion I had yesterday.
  5. In theory I also want to keep working on my RoR picture application. I've wanted an application that would let me upload all my pictures to a directory on my webhost via FTP and then process them all with a web-interface. Once they're all uploaded I want to go to the web-interface, type in the directory name, and click a button. The app should then find all new pictures, load the details (EXIF, size, etc) into a DB, create thumbnails, and create a default album. From there I want to be able to add the photos to custom albums and set privacy. I've got some basic code to read a dir and insert photos into a DB but there's a lot left to do. Recently this has been less important than learning Scala, but I may bounce back and forth between the two.

Looking back over my list I guess I've got enough to keep me busy for a while, and that's not counting the non-programming projects I'd also like to tackle at some point, like setting up my Ubuntu workstation as a wireless router that connects to the internet using my EVDO card. So much to do, so little time...

An old college friend was in town so I stayed up past my bed time getting caught up with him. Before turning in I fired up NetNewsWire so I could subscribe to his blog. I also took a quick peek at Digg and was shocked when I saw the first item about "Ad Text Ideas":

The wizard for generating the ads already existed, but exposing it as a standalone tool (as the "Ad Text Ideas" page) was my starter project at Google. I never thought it would show up on Digg. The link on Digg goes to this page, which describes the functionality.

I got a stack track trace when trying to Capistranize my app that looked like this:

/Applications/Locomotive2/Bundles/standardRailsFeb2007.locobundle/i386/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require': no such file to load -- active_resource (MissingSourceFile) from /Applications/Locomotive2/Bundles/standardRailsFeb2007.locobundle/i386/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require' from /Users/joelmeyer/src/FotoDir/vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:495:in `require' from /Users/joelmeyer/src/FotoDir/vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:342:in `new_constants_in' from /Users/joelmeyer/src/FotoDir/vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:495:in `require' from ./config/../vendor/rails/railties/lib/initializer.rb:160:in `require_frameworks' from ./config/../vendor/rails/railties/lib/initializer.rb:160:in `each' from ./config/../vendor/rails/railties/lib/initializer.rb:160:in `require_frameworks' from ./config/../vendor/rails/railties/lib/initializer.rb:88:in `process' ... 8 levels... from /Applications/Locomotive2/Bundles/standardRailsFeb2007.locobundle/i386/lib/ruby/gems/1.8/gems/capistrano-1.4.1/lib/capistrano/cli.rb:12:in `execute!' from /Applications/Locomotive2/Bundles/standardRailsFeb2007.locobundle/i386/lib/ruby/gems/1.8/gems/capistrano-1.4.1/bin/cap:11 from /Applications/Locomotive2/Bundles/standardRailsFeb2007.locobundle/i386/bin/cap:16:in `load' from /Applications/Locomotive2/Bundles/standardRailsFeb2007.locobundle/i386/bin/cap:16

It turns out that when using Edge rails you need to get the active resource gem for things to work:


gem install activeresource --source http://gems.rubyonrails.org

After getting the gem and it's dependencies installed everything worked.

Last night I created a new Ruby-on-Rails project and got a subversion repository set up for it so that I could hack away on my 'fun project' whenever I had a few minutes. This morning I read about a programming language called Scala that compiles down to Java bytecode and an accompanying web framework called lift that is like Rails but performs 6x faster and is multi-threaded.

I'm greatly disappointed. How's a guy supposed to get into something new if every time he turns around there's something even cooler out there?

For now I'll stay focused on RoR since I don't have a hosting provider that lets me run a JVM, but Scala + Lift looks really cool.