Ruby for the C# developer–Open Classes & method_missing
In the last post we covered the basics of the Ruby language from a C# developers perspective. We left off with a short discussion about one major fundamental difference between Ruby and C#: Mutable Types
In this post I wanted to cover the subject in a bit more depth and explain what Open classes are and what the method_missing method is used for.
Open classes means you can change the definition of any class at any time. Usually this would be done to add behavior to a class. Notice I am saying class here not object. In the classic cookie and cookie cutter example you think of a class as the cookie cutter and the object as the cookie (the instance that the cookie cutter just stamped out). Now you can see why this idea is very foreign to a C# developer. It means classes can be extending at any time. This sounds a bit like extension methods but you will see as we dig deeper that there is more to it in Ruby.
Let’s first look at an example in Ruby:
class String
def blank?
self.size == 0
end
end
This is adding the method named “blank” which returns a boolean to the String class in Ruby. Now that is almost exactly like an extension method. We could get the same behavior in C# like this:
public static class StringExtensions
{
public static Boolean Blank(this string value)
{
return String.IsNullOrEmpty(value);
}
}
But there is another way to add behavior to a class in Ruby. This is via the method_missing method. Since all method calls in Ruby are really just messages there is a chance that a particular object won’t be able to handle the message it is given. In these cases Ruby will call a special “debugging method” if it cannot find a method with the specified name.
What does this mean for you? It means that you have a hook to handle any unknown method that gets called on your object at runtime. This is really cool but may not be totally clear without an example. The 7 Languages in 7 weeks book does about the best example I could think of so I will shamelessly steal that one to illustrate the point.
class Roman
def self.method_missing name, *args
roman = name.to_s
roman.gsub("IV","IIII")
roman.gsub("IX","VIIII")
roman.gsub("XL","XXXX")
roman.gsub("XC","LXXXX")
(roman.count("I") +
roman.count("V") * 5 +
roman.count("X") * 10 +
roman.count("L") * 50 +
roman.count("C") * 100)
end
end
Ok so what the heck is this thing doing? First it is defining the method_missing method on a class called Roman. It is then doing logic in the method_missing method to handle the name of the method that the user tried to call. Let’s look at what the call site would look like
puts Roman.X
puts Roman.XC
Now maybe it makes more sense. You can see someone tried to call a method named “X” on the Roman class. Instead of defining each permutation of possible Romans numerals as their own method we can simple catch the method_missing method and then do string based logic to figure out the method name and convert that to the correct integer value. Make sense? This is a core feature of Ruby and a very valuable concept to understand.
This is part of a larger series of posts inspired by the 7 languages in 7 weeks book. Use the7LangIn7Weeks tag to find the other related posts
Labels: 7LangIn7Weeks, C#, Ruby