The Power of Dynamic Methods

February 14, 2007

Today I was writing some Java code and needed to make methods like this:

@Transient
public String getThingsAsString() {
    if(things != null) {
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for(Thing t: things) {
            if(!first) {
                sb.append(", ");
            } else {
                first = false;
            }
            sb.append(t.getName());
        }
        return sb.toString();
    }
    return null;
}

Basically, I have an object that has a Collection of Things objects, and I need to print the name of each Thing in a comma-separated list. I have several different Collections and so I need to repeat this for each one. I could "simplify" this by creating an interface that all of my different objects like Thing implement, for example calling it NamedEntity. This means that each one of these objects are guaranteed to have a getName() method. After creating that interface and modifying all the object so that they implement it, I would write a utility method to print a NamedEntity as a String. So then each above method would look like:

public String getThingAsString() {
    return NamedEntityUtil.getNamedEntitiesAsString(things);
}

But this still means that I have to create a getXXXAsStringMethod for each collection. I could just call the utility method when I need the string, but the problem is that I need this string within JSP pages, and you can't call the static methods from a JSP without resorting to scriplets. This is especially a pain because it is difficult to mix JSTL and scriptlets.

So what could you do if you need to do this in Rails? Well for starters, you can use one line of Ruby code to do it:

things.collect {|t| t.name}.join(", ")

You could also create one view helper to make the syntax a little cleaner, and then re-use it. Ths view helper method would be:

def join_names(things, sep=", ")
  things.collect {|t| t.name}.join(sep)
end

And look at that, we easily can now optionally pass a different separator. But that's not the point I want to illustrate in this post. Let's say for some reason in Rails we really needed to create various method to print the various collections as strings. Well that would be easy too. We would just put this inside of our class that has a Collection of things:

["things", "other_things"].each do |name|
  define_method("#{name}_as_string") do self.join_names(self.send(name)) end
end

What this ruby magic does is dynamically create methods for each collection. It allows you call methods like this:

whatever.things_as_string
whatever.other_things_as_string

Like I said, you probably wouldn't really do this for this example, but the point here is that with Ruby, you can write code that writes code, and that can be very powerful.

I first heard of this concept in the book Hackers and Painters by Paul Graham. This is the best book on programming I have ever read, and if you haven't read it, go out and buy it and read it. It actually isn't very technical. It doesn't teach you any specific programming techniques, but it's just a great book. My favorite chapter is Why Nerds Are Unpopular.

In a chapter called Beating the Averages, for which the full text is available on Paul Graham's website, like many of the chapters of the book, he talks about how Lisp macros are programs that write programs, and at the time, it sounded like a great idea, but I just didn't get it. He said:

Programs that write programs? When would you ever want to do that? Not very often, if you think in Cobol. All the time, if you think in Lisp.

Code that writes code? When would you ever want to do that? Not very often, if you think in Java. All the time, if you think in Ruby.

Posted in Technology | Tags Hibernate, Ruby, Java, Rails

Comments Disabled