CONTENTS OF THIS SITE

OUR OTHER CONTENTS

RECENT BLOG ENTRIES

RUBY: DRY up your Enumerations

May 25th, 2007 by comment sandi

Using Higher Order Messaging to honestly reveal your intentions.

If you had a collection of stooges (@stooges) and you were looking for Mo, would you rather say:

@stooges.select {|s| s.name == ‘Mo’}

or

@stooges.that.have.name == ‘Mo’

I don’t have to ask you twice, right?

In the first example, the fact that it’s an enumeration overwhelms the intention to find Mo.

In the second example, the most obvious thing is that I want Mo. The fact that @stooges is a collection is obviously not the most important thing. What matters is that I want Mo and example 2 does a much better job of revealing that.

This is not magic. It’s Higher Order Messaging. And trust me, you want it.

I stumbled across HOM on Why’s blog over a year ago. I followed a link from his site to some other site and downloaded ho_enumerable.rb (sorry, I can no longer find the orginal site, but if you know, let me know and I’ll make proper attribution here). I’ve been using HOM with delight ever since.

What is Higher Order Messaging?

Higher order messaging lets you talk to collections in a natural way that reveals instead of conceals your ultimate intentions. Here are more examples.

(To follow along at home, download hom_examples.rb and ho_enumerable.rb).

Given class Stooge:

class Stooge

 attr_reader :name, :hair, :habit

 def initialize(aRow)
  @name   = aRow[0]
  @hair   = aRow[1]
  @habit  = aRow[2]
 end

 def baldish?
  hair.nil?
 end

 def muck_with_name
  name.gsub!(/y/, ‘x’)
 end

 def to_s
  “#<Stooge - name:#{name} hair:#{hair} habit:#{habit}>”
 end
end

and array STUFF

STUFF = [[ 'Mo',    'bowly', 'eye poking' ],
         [ 'Larry', 'curly', 'whomping'   ],
         [ 'Curly',  nil,    'whining'    ]]

I can create a new collection of Stooges by saying

@stooges  = STUFF.as(Stooge)

instead of

@stooges = STUFF.collect {|r| Stooge.new(r)}

I can select all of the stooges that are nearly bald by saying

@stooges.that.are.baldish?

instead of

@stooges.select {|s| s.baldish?}

I can find all of the stooges named ‘Mo’ by

@stooges.that.have.name == ‘Mo’

instead of

@stooges.select {|s| s.name == 'Mo'}

I can build an array of all of their names by saying

@stooges.extract.name

instead of

@stooges.collect {|s| s.name}

I can find out if ‘all’, or ‘any’, stooges ‘are’ or ‘have’ some quality. (Since you’re completely sold now I’m gonna leave the iteration style code to you).

@stooges.all.are.baldish?  # false
@stooges.any.are.baldish?  # true
@stooges.all.are_not.baldish?  # false
@stooges.any.are_not.baldish?  # true
@stooges.all.have.name == ‘Mo’  # false
@stooges.any.have.name == ‘Mo’  # true

I can also order the stooges by some attribute.

@stooges.in_order_of.name
@stooges.in_reverse_order_of.name

And I can perform some operation against every stooge.

@stooges.do.muck_with_name

The ho_enumerable code is very short and delightfully nerdly. You may or may not be interested in looking at it to figure out how it works, but you don’t have to understand the details of implementing HOM to recognize what a great idea this is.

Stick ho_enumerable in your rails/lib directory, require the file and start using HOM. Your code will please you by revealing what it’s doing without drowning you in detail about how it’s doing it. And when your friends have to maintain it, they’ll like you even better than you like yourself.

ps - Thanks to Lori Evans for pouring over the ho_enumerable code with me on the plane on the way home from railsconf. It was more than can possibly be expected of any exhausted human being and she was alarmingly cheerful about it. Thanks.

ˆ Back to top