Method Resolution in Ruby

While reading the Ruby Pickaxe book I realized that while JavaScript and Ruby are both dynamic languages, they handle method resolution in very different ways. I often use JavaScript as a reference since it’s the dynamic language I’m most familiar with.

A Simple Example in JavaScript

Let’s first look at a simple JavaScript example.

var widget = {}

widget.name = "Wobble"
alert(widget.name)

widget.surname = "Bobble"
alert(widget.surname)

This will run without any problems. If you try to get the value of a property that doesn’t exist JavaScript will simply return undefined. If you try to set a property that doesn’t exist JavaScript will create that property and set the value. JavaScript will complain when you try to invoke a method that doesn’t exist.

If I recall correctly, dynamic objects in C# 4.0 will function the same way – you can add properties to a dynamic object without problems, but invoking a method will raise an error. (It’s been a while since I worked in C#, so don’t quote me on that)

Ruby doesn’t have the concept of properties, so in the following snippet of Ruby code,

widget.name = "Wobble"

Ruby is actually looking for a method called name= and passing the string “Wobble” as a parameter.

A Simple Example in Ruby

Let’s try the same example in Ruby.

class Widget
end

widget = Widget.new
widget.name = "Wobble"
puts widget.name

NoMethodError

As you might expect, Ruby raises an error saying there is no name= method on this object.

There are a couple of ways we can get around this limitation. The simplest is probably to add a name accessor to the class.

class Widget
end

widget = Widget.new
class Widget
  attr_accessor :name
end
widget.name = "Wobble"
puts widget.name

As you can see from the example, Ruby allows us to add accessors for a @name instance variable to the class. The attr_accessor method (also called a Macro) will add the necessary methods to access this attribute – in other words, it’s similar to adding the following two methods:

def name
  @name
end
def name=(val)
  @name = val
end

It’s important to know that the attr_accessor method doesn’t actually create the @name instance variable – it will be created the first time we access it.

Using method_missing

Ruby also allows us to define a method_missing method (also called a Hook) which will be invoked when a method is not found (as you might deduce from the name). It has a pretty simple signature:

def method_missing(name, *args, &block)

So we have the name of the method being called (passed as a symbol), the arguments passed as a splat array, and a block.

We can now put this to use to allow us to dynamically access any instance variable.

class Widget
  def method_missing(name, *args, &block)
    if name =~ /.*=$/      
      instance_variable_set("@" + name.to_s.chop, args[0])
      return args[0]
    end
    if name =~ /.*[^=]$/
      return instance_variable_get("@" + name.to_s)
    end
    raise "I can't figure out what you're trying to do"
  end
end

widget.surname = "Bobble"
puts widget.surname

Conclusion

I definitely wouldn’t advocate using this type of code in a real project, but it’s interesting to see what’s possible.

Happy coding.