something to do with web development

January 15, 2008

Tree Drawing with the Adjacency List Model

Let's not fake piety, we've all of us stuffed a few hierarchies into the adjacency list model. It's so easy to add parent_id to the end of any table, and the performance issues seem negligible in small data sets. It's only when we're grabbing data to draw the hierarchical tree it represents that we maybe worry about recursion's inherent performance drawbacks. For example, you've probably seen a method like this:

function get_children($parent) {
    $result = mysql_query("SELECT id
        FROM family
        WHERE parent='{$parent}'");
    while($child = mysql_fetch_object($result)) {
        $children[$child->id] = get_children($child->id);
    }
    return $children;
}

One obvious problem is that this function runs a query for every single node it discovers. Another is that the amount of recursion this function produces depends, not on the size of your data set, but on the complexity of the hierarchy your data represents; the more branches your tree has, the deeper its recursion goes. Even if you have a small number of rows such that preorder tree traversal seems overkill, the complexity of your tree may still demand a more efficient artist than get_children. I'd like to propose one alternative for producing a complete tree.

Here's my data: two families of the animal kingdom:

  • Hapalemurinae
    • Hapalemur
      • Hapalemur Alaotrensis
      • Hapalemur Aureus
      • Hapalemur Griseus
    • Prolemur
      • Prolemur Simus
  • Lemurinae
    • Eulemur
      • Eulemur Coronatus
      • Eulemur Fulvus
      • Eulemur Macaco
      • Eulemur Mongoz
      • Eulemur Rubriventer
    • Lemur
      • Lemur Catta
    • Varecia
      • Varecia Rubra
      • Varecia Variegata

To help emphasize the concept I'm promoting, I'm using taxonomic names as IDs and parents in the $family array I'm using to represent this data:

$family = array(
    array('id' => 'Eulemur', 'parent' => 'Lemurinae'),
    array('id' => 'Eulemur Coronatus', 'parent' => 'Eulemur'),
    array('id' => 'Eulemur Fulvus', 'parent' => 'Eulemur'),
    array('id' => 'Eulemur Macaco', 'parent' => 'Eulemur'),
    array('id' => 'Eulemur Mongoz', 'parent' => 'Eulemur'),
    array('id' => 'Eulemur Rubriventer', 'parent' => 'Eulemur'),
    array('id' => 'Hapalemur', 'parent' => 'Hapalemurinae'),
    array('id' => 'Hapalemur Alaotrensis', 'parent' => 'Hapalemur'),
    array('id' => 'Hapalemur Aureus', 'parent' => 'Hapalemur'),
    array('id' => 'Hapalemur Griseus', 'parent' => 'Hapalemur'),
    array('id' => 'Hapalemurinae', 'parent' => ''),
    array('id' => 'Lemur', 'parent' => 'Lemurinae'),
    array('id' => 'Lemur Catta', 'parent' => 'Lemur'),
    array('id' => 'Lemurinae', 'parent' => ''),
    array('id' => 'Prolemur', 'parent' => 'Hapalemurinae'),
    array('id' => 'Prolemur Simus', 'parent' => 'Prolemur'),
    array('id' => 'Varecia', 'parent' => 'Lemurinae'),
    array('id' => 'Varecia Rubra', 'parent' => 'Varecia'),
    array('id' => 'Varecia Variegata', 'parent' => 'Varecia'),
);

This still comes quite close to the result you might expect from a MySQL query.

Here's the recursion-free method I'm proposing:

$branches = array();
$tree = array();

foreach($family as $child) {
    extract($child);

    if(!isset($branches[$id]))
        $branches[$id] = array();

    if(!empty($parent)) {
        if(!isset($branches[$parent]))
            $branches[$parent] = array();
        $branches[$parent][$id] = &$branches[$id];
    }
    else $tree[$id] = &$branches[$id];
}

The results are stored in two arrays: $branches, which contains individual branches of the hierarchy indexed by parent; and $tree, which collects all branches in the context of a complete tree. print_r($tree) and you'll see the whole structure; print_r($branches['Hapalemurinae']) and you'll see only the children of Hapalemurinae.

In the real world, this code would be more complex—each node probably having more properties than its ID—but the underlying principle of assigning nodes to the tree by reference can be used to create a relatively inexpensive serialize()d tree-caching routine for small, complex data structures.

January 6, 2008

new Year()

Here's a modest change for 2008:

Cool things down with Prototype. (Maybe it's time to start seeing other frameworks.)

No I'm not donning the fashionable gang colors of those edgy developers railing against Rails, and barely felt the OMG! filesize('prototype.js') > filesize('yomomma.jpg') feng shui fad, so why am I suddenly dissatisfied? Here's why:

  • I'm not. Prototype's great.

    I'm really not dissatisfied. Prototype's a great framework, now with strong documentation. But the cutlery block's got more than one slot, and I've grown too attached to the chef's knife.

  • OMG! filesize('prototype.js') > filesize('yomomma.jpg')

    Sweet code is all about Qi.

  • It doesn't feel JS

    Same old story: Prototype wants me to believe JavaScript is Ruby! Code blocks! Iterators! Argh! JavaScript is not Ruby! Fzzzftphhh!

  • Events

    Getting better all the time, but still not Prototype's strong point. Example: jQuery's triggerHandler makes me jealous.

  • Event.observe(window, 'load', ...

    Ok, this one's not Prototype's fault, but a change of library will help break the habit. In case you haven't noticed, window.load is my favorite trigger to initialize libraries. It's simple, it's decoupling, it guarantees safe DOM manipulation, and it reminds me to care for the scriptless browsers. So where's the problem?

    It disrupts what I like to call render rhythm; the late trigger causes flickering: DOM manipulation becomes a visible process. The traditional inline <script> approach is ugly, but goes down smooth.

    Just found out that Prototype 1.6 includes document.observe('dom:loaded'..., which is pretty much the same as jQuery's $(document).ready, or MooTools' window.addEvent('domready'... Woohoo!

So, where to now? I mentioned jQuery (commonly known as the other JavaScript framework); I've also batted my eyelashes at DOMAss and base2, and heard nice things about MooTools. Ideally, I'd like to test my knife metaphor: learn to pick a framework best suited to carve a particular project's scope. Even if I become another framework's fiancé, I'll certainly keep current with Prototype's development. It's one of the web's most common frameworks, and common ground is what frameworks are all about.

PS: Digging up Roger's article, I noticed Google Reader now has a search tool. Cool!

January 3, 2008

Rails Is A Ghetto: A Disney Version

My SFW version of Zed Shaw's famous "Rails is a Ghetto" blue comedy rant, in which I completely miss Zed's "insider" jokes and possibly represent his style and vocabulary inaccurately:

Rails Is Monsters Inc

I don't want to use Ruby anymore because some people upset me. I'm going to complain about them here, and they are going to listen to me or not.

Personal Responsibility

It's all my fault.

The General Personality

I think Tom Werner named "Fuzed" after my favorite catchphrase and me, in that order. I find that offensive.

Kevin Clark of Vapor Set didn't like my ideas. I don't like him. Our egos clash. A silly mistake he made in a huff got me thinking: I'm better than all these people.

Tied To The Rails

I like Obie Fernandez. He's a nice chap.

The Stories I Could (and Will) Tell

I've turned down some job offers.

Revenge Of Some Chap Who Got My Name Wrong

Despite my genius, people don't like working with me. Or maybe they mix me up with some Shain chap who's apparently quite a rough employee.

How'd This Happen?

David H. Hansson said he had to restart his rails server up to 400 times a day before fastthread. I know it's a language bug, but still: 400 times a day? That's like once every four minutes. You duped us, Dave, you told us Rails wouldn't hurt.

He could have been using hyperbole. We may never know.

If he duped us, I'm glad he did. I like rails.

Michael K. and Dave Thomas threatened me into not fixing my code for 3 months while cgi_multipart_eof_fix was developed.

DHH Still Rocks More Than You

I like David H. He sent me a calm e-mail explaining that it took 60 processes to reach 400 restarts a day, and expressing his gratitude for my work championing the fixes.

Alright, but I had to champion those fixes. No one believed in the cause until a fix was found.

Then The Ninnies Came

I don't like Michael Koziarski. You probably haven't heard of him, but he writes lots of code.

Some Ruby web frameworks are better than Rails. Because I'm a genius, I totally could have made Rails better, but I don't like the Rails team.

Can you believe Michael Koziarski defended his use of semicolons in requests? He singlehandedly brought down Rails.

The Nitro, Factor, Django, Lua, Python, Lisp, and Mongrel crews are nicer people who put up with me.

Everyone's an idiot. That's why they refuse to hire me.

The Hysteria Of Consultancy

Consultancy firms charge a lot of money. A lot more than they pay their employees.

The chaps at ThoughtWorks are using the RoR buzz to make money:

  1. TW learns RoR.
  2. People pay for TW's services.
  3. Profit.

They aren't really experts. Step 1 only took two weeks. They don't use the RoR and eXtreme Programming seal of quality, so we have nothing to hold them to.

I took over a couple of projects from ThoughtWorks. Nasty code. We did a better job, and guess what happened? We got it done faster with fewer people.

You know what's really bad about ThoughtWorks? They compared RoR to Visual Basic. What silly nonces.

Mingle Rocks Hard, Buy It

Having said that, Mingle is great.

Maybe it's just the code they gave me that sucks. Code which, by the way, doesn't have any comments.

From Industry to Corporate

RoR sold out, man.

Fighting Consulting Firms

Be careful hiring consulting firms. Make sure they aren't as sleazy as I said TW is.

Day Two

I Don't Like Dave Thomas

So Dave Thomas wants to talk to me on the phone. He'll probably tell me to calm down or something. I am calm, Dave!

No matter how much I try to grab his attention with insults and slurs, Dave never has anything nice to say to me. It's like the guy has something personal against me.

Also, have you ever noticed how he's the only guy in the Rails Core chat room?

Also, I hate the first edition of that one Rails book. Models in modules doesn't work. He didn't ask me for help, that's why it's a bad book.

Then I realized all of Ruby's problems can be pinned on Dave Thomas by saying Programming Ruby is a bad book. Get this: it's all about how people can use Ruby to do things. It's as though he wrote it for people who aren't already familiar with the language. Also, I don't like his examples.

Maybe you think I'm nit-picking, but Ruby didn't take off until Rails. That's Dave's fault for writing a bad book.

Dave challenged me at a keynote, even though I was working on something he'd have liked.

Ruby Mailing Lists

I had bad experiences in some Ruby mailing lists, too. I seem to have bad experiences almost everywhere I go.

RailsConf

At some big conferences, people are wasting their time playing some Victorian parlor game from the 80's instead of working instead of paying attention.

Also, smaller conferences are often better. I liked GoRuCo, Rails To Italy, and Lone Star Ruby Conference.

Industry? What Industry?

RoR is not an industry because it has no representation in congress.

Your Boss Doesn't Care

I swear a lot. Demand for Rails cleanup exists.

Free Market

Lying and ineptitude should be condemned. Marx was wrong, the free market will work.

Your industry is silly, but my rant may fix it.

People I Like

These people are cool:

  • DHH
  • Obie Fernandez
  • Bradley Taylor
  • Why The Lucky Stiff
  • NYC Ruby Crew
  • Mongrel Team and Contributors
  • Nitro, Merb, IWOA, Camping and Others
  • 1/2 Of Rails Core (the half I like)
  • Ezra Zygmuntowicz
  • Rubinius and Evan
  • JRuby and Charles
  • Matz himself
  • Anyone else I like

Buy their books and services.

I like Ruby, but the community could use some work.

Zed's Dead

Industry chaps, hear this: when the bubble bursts, remember I told you so.