Reopening Ruby Classes

In Ruby the implementation of a class is not closed. In Ruby, it is incredibly easy to add new methods to any existing class at runtime, even core system classes like String, Array, and Fixum. You just reopen a class and add new code. This language feature makes the language incredibly flexible. You might be wondering, why would you need this? Well, take a look at the API documentation of the Java String class. In most development organizations that I have been a part of we have had to write small string libraries that provide far more functionality that provided by the Java String class. The Apache Jakarta Commons Lang project provides a String helper class in StringUtils. StringUtils provides methods such as IsEmpty, Chomp/Chop, and LeftPad. StringUtils. StringUtils is a great class that has saved me from re-inventing the wheel several times, but it is another library to download, configure, learn, and import. Using the StringUtils class is more verbose calling a method on a string.

// Using StringUtils to trim a string.
str = StringUtils.trim(str);
// Calling a trim on a string.
str = str.trim();

Isn’t more concise and easier to read when you invoke a method on a string instead of using StringUtils? Unfortunately, even if an JCP is approved to add your custom string methods to the String class, the whole process is very time consuming. Each incremental release of Java takes years to develop. In Ruby, you can just reopen the String class to add the few methods you feel it lacks. To reopen the Ruby String class we can use the following code:

# reopen the class
String.class_eval do
  # define new method
  def get_size
    # method implementation
    length
  end
end


You can also re-define any method in a given class, without subclassing, just by reopening the class. A mischevious example of this is to re-define the length method to always return 10, just because I couldn’t come up with a better example.

String.class_eval do
  def length
    10
  end
end

This mischievous example, is meant to to make the point that the last implementation wins. The same method can be implemented in a class two or more times. The method that is valid is the one defined last. There is no compiler error or even a warning in Ruby if you duplicate a method in a class.

Perhaps in Perl fashion, Ruby provides more than one way to reopen a class. There is another more familiar way to open a class’ implementation, just define it again!

class String
  def get_size
    length
  end

  def length
    10
  end
end

By looking at the above example I would think that it defines a new String class with just two methods. Ruby is a little smarter than that. Ruby will realized that we already have a String class defined, and it will open the class’ implementation so that we could add the new implementation with the existing class. It is important to reiterate that the above code will not create a new String class, it will just aggregate the new implementation to the existing String class.

The class_eval way of reopening a class comes in handy when extending the class of an object at runtime. Here is a trivial example, lets say we want to add a to_string method to the class of any given object instance. To accomplish this we would need to get the class of the object instance and invoke the class_eval method, like this:

def add_method(obj)
  # Get the class of the object.
  obj.class.class_eval do
    # Add a new method to the class.
    def to_string
      to_s
    end
  end
end

add_method(int_value)
puts int_value.to_string

In the above example, we open the class of the object passed in as a parameter by calling class.class_eval.

In Java, the best way to enhance a class is to extend it through a subclass, but subclass are tightly coupled to the implementor class and come with a associated price. Ruby just seems to have a few more options to enhanced a class, such as mixins and as we have now seen here. It is possible to do byte code manipulation on a Java class to change its behavior after the class has been compiled, in fact Aspect Oriented Programming does this in a way. But Ruby’s way to enhanced a class is so seamless, powerful, and built right into the language itself.

Related posts:

  1. Ruby Language Goodies For Java Developers
  2. Ruby Mixin Tutorial
  3. Introduction to Classes and Objects
  4. Ruby Class Tutorial
  5. ActiveRecord: Ruby on Rails Optional
  6. RubyConf: Open Classes, Open Companies


8 Responses to “Reopening Ruby Classes”

  • NoKarma Says:

    Woah! Are you sure that using SomeClass.class_eval do {some code} end is a good idea?

    Why not simply using this:

    class String
    [some_code]
    end

  • TechKnow Says:

    You would only use the class’ class_eval when you don’t know the type before hand. But if you know that you only want to add new code to a String, then it would be more intuitive to do as you suggest.

  • Tim O'Brien Says:

    Right on, I wrote a whole book about the Commons, but I do think that StringUtils is less of a great utility and more of a sign of huge problems.

    The fact that you have to resort to big static method libraries and that we’re still doing this in 2007 is an embarrassing failure.

  • Gene Says:

    Thanks – I have a question related that I can’t get answered to. In a Rails app, where do I do extensions of String like this? I want the String class to get extended as soon as rails starts up and have my additional string methods be available throughout the rails app.

  • TechKnow Says:

    @Gene – If you want to reopen a Ruby class when Rails startups you might want to create a simple Rails plugin. If you go this route, just reopen the String class, or any class for that matter, in the init.rb file for the plugin.

    Sometime back I mentioned that you can reopen a class in Rails application helper. I wrote that as a way to expend/update models provided by Rails plugins but it will also help you.

  • Learning Scala: Performance Impact of Implicit Conversions « Villane Says:

    [...] languages have ways of doing similar things. JavaScript and Ruby actually let you add methods to existing objects, and these methods will then work everywhere else [...]

  • Find tests more easily in your Rails test.log « I like stuff Says:

    [...] for your Rails project. To me this seems like a nice little example of how Ruby’s open classes can benefit developers (while understandably considered harmful by some). In a language without [...]

  • Josh Cheek Says:

    As of rails 2.x you would put it in an initializer. Make a ruby file in config/initializers and do your monkey patching in there.

Leave a Reply