tl;dr Inheriting from BasicObject allows you to build proxy objects that delegate methods like #inspect , #to_s , and #is_a? .

If you're familiar with Ruby, you probably know that Object is the default root of all Ruby objects. So it's probably a top-level class, right? Nope. Object inherits from BasicObject .

Object.superclass #=> BasicObject

In this article, we'll explore how you can use BasicObject to implement proxy objects that delegate all of their methods to another class.

A Delegate Situation

Let's imagine you have a class called Dog :

class Dog attr_reader :name def initialize(name) @name = name end def bark puts 'WOOF!' end end

Now, imagine that you want to call the #bark method whenever any method is called on an instance of Dog .

Surely, you could use inheritance to override every single method like so:

class DogSubclass < Dog def name bark super end end dog = DogSubclass.new('Fido') dog.name # WOOF! #=> "Fido"

Great. That will solve our problem for now. But, what if Dog had hundreds of methods? Would you manually override every single method that Dog defines?

The Proxy Pattern

A more clever way to implement this feature would be to create a proxy. In short, a proxy forwards (or delegates) all method invocations to another object.

class AnnoyingDog def initialize(dog) @dog = dog end private def method_missing(meth, *args, &block) @dog.bark @dog.send(meth, *args, &block) end end fido = Dog.new('Fido') fido.name #=> "Fido" annoying_fido = AnnoyingDog.new(fido) annoying_fido.name # WOOF! #=> "Fido"

When we call annoying_fido.name , Ruby knows that AnnoyingDog doesn't define a #name method. Therefore, #method_missing is dispatched. This is where we can make the dog #bark and delegate the #name method.

Let's try another example:

fido.to_s #=> "#<Dog:0x007fb3528d5a60>" annoying_fido.to_s #=> "#<AnnoyingDog:0x007f998a04ee28>"

We've got two problems. First, AnnoyingDog#to_s returns a different result than Dog#to_s . Second, annoying_fido didn't bark.

As you can see, AnnoyingDog did not invoke #method_missing . This is because AnnoyingDog inherits from Object , which implements #to_s .

AnnoyingDog.instance_methods.include?(:to_s) #=> true

Back to Basics

As of Ruby 2.3, Object implements a total of 56 methods. To the contrary, BasicObject is extremely lightweight, containing just 8 critical methods.

BasicObject.instance_methods #=> [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__, :__binding__]

Now that we've identified the problem, let's try making AnnoyingDog inherit from BasicObject .

- class AnnoyingDog + class AnnoyingDog < BasicObject

And let's try our previous example again:

fido = Dog.new('Fido') fido.to_s #=> "#<Dog:0x007fb9841d9990>" annoying_fido = AnnoyingDog.new(fido) annoying_fido.to_s # WOOF! #=> "#<Dog:0x007fb06284ed28>"

If it barks like a dog, it must be a dog. The #to_s method is no longer defined for AnnoyingDog . Therefore, annoying_fido barks and behaves exactly like fido , but with slightly different behavior.

The Real World

If you're interested in seeing BasicObject in action, take a look at Baby Squeel.