Lessons From Hpricot

HTML processing, C parser, yada, yada, yada. What lesson did I actually learn from Hpricot?

Abuse the / operator!

Why, the Lucky Stiff, presents this neat bit of code:

doc = Hpricot.parse(File.read("index.html"))
(doc/:p/:a).each do |link|
  p link.attributes
end

Nice! That's hot. Let's run with this for a moment and add / navigation to hashes.

class Hash
  def /(key)
    self[key]
  end
end

people = {:toph => {:name => "Topher Cyll"}}
people/:toph/:name
  ==> "Topher Cyll"

Pretty cool, huh? Of course, it's not much more concise than using the standard indexing operator.

people[:toph][:name]

But the real win for me is how it looks like path syntax (ala XPath or Unix filenames).

You can also do this for Arrays if you like:

class Array
  def /(key)
    self[key]
  end
end

people = [{:name => "Topher Cyll"}, {:name => "Al Gore"}]
people/1/:name
 ==> "Al Gore"

Of course, it's even better for things that actually naturally use path syntax. I imagine you could make a decent XPath syntax for REXML if you tried. And here's an example of what you could do with the Dir module.

class Dir
  def /(key)
    Dir.new("#{path}/#{key}")
  end
end

root = Dir.new("/")
root/:Users/:toph
# Strings work to
root/"Users"/"toph"

class Dir
  def Dir./(key)
    Dir.new("/#{key}")
  end
end

# Since we probably would use the filesystem root
# a lot, might as well make the module represent
# the root.
Dir/:Users/:toph

I'm not sure I'd use any of the examples I've listed above in production code, but still, it's pretty cool stuff. Before I go, here's something truly horrible, and kind of awesome...

def method_missing(name, *args)
  return name
end

people = {:toph => {:name => "Topher Cyll"}}
people/toph/name
  ==> "Topher Cyll"

By making method_missing return the symbol of the missing method, we can omit the : before the symbol names. But the love of Ruby... don't do it!

posted on: 07/07/2006 | path: /tech