The Magic of Class-level Instance Variables
Tags: ruby, programming
Published: 2018-10-02 13:35:44

---
title: The Magic of Class-level Instance Variables
description: Even classes are objects in Ruby. In this edition of Ruby Magic, we'll look at class-level instance variables by creating a module for wrapping an object's methods.
cover_image: https://blog.appsignal.com/images/blog/2018-10/inheritable.jpg
tags: ruby, programming
published: true
canonical_url: https://blog.appsignal.com/2018/10/02/ruby-magic-class-level-instance-variables.html
---

In [a previous Ruby Magic](https://blog.appsignal.com/2018/08/07/ruby-magic-changing-the-way-ruby-creates-objects.html), we figured out how to reliably inject modules into classes by overwriting its `.new` method, allowing us to wrap methods with additional behavior.

This time, we're taking it one step further by extracting that behaviour into a module of its own so we can reuse it. We'll build a `Wrappable` module that handles the class extension for us, and we'll learn all about class-level instance variables along the way. Let's dive right in!

## Introducing the `Wrappable` Module

In order to wrap objects with modules when they are initialized, we have to let the class know what wrapping models to use. Let’s start by creating a simple `Wrappable` module that provides a `wrap` method which pushes the given module into an array defined as a class attribute. Additionally, we redefine the `new` method as discussed in the previous post. 

```ruby
module Wrappable 
  @@wrappers = []

  def wrap(mod)
    @@wrappers << mod 
  end 

  def new(*arguments, &block)
    instance = allocate
    @@wrappers.each { |mod| instance.singleton_class.include(mod) } 
    instance.send(:initialize, *arguments, &block)
    instance
  end 
end 
```

To add the new behavior to a class, we use `extend`. The `extend` method adds the given module to the class. The methods then become class methods. To add a module to wrap instances of this class with, we can now call the `wrap` method.

```ruby
module Logging 
  def make_noise 
    puts "Started making noise"
    super 
    puts "Finished making noise"
  end
end

class Bird
  extend Wrappable 

  wrap Logging
  
  def make_noise
    puts "Chirp, chirp!"
  end
end
```

Let’s give this a try by creating a new instance of `Bird` and calling the `make_noise` method. 

```ruby
bird = Bird.new 
bird.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise
```

Great! It works as expected. However, things start to behave a bit strange once we extend a second class with the `Wrappable` module.

```ruby
module Powered
  def make_noise 
    puts "Powering up"
    super 
    puts "Shutting down" 
  end 
end 

class Machine 
  extend Wrappable 
 
  wrap Powered

  def make_noise 
    puts "Buzzzzzz" 
  end 
end

machine = Machine.new 
machine.make_noise
# Powering up
# Started making noise
# Buzzzzzz
# Finished making noise
# Shutting down

bird = Bird.new 
bird.make_noise
# Powering up
# Started making noise
# Chirp, chirp!
# Finished making noise
# Shutting down
```

Even though `Machine` hasn't been wrapped with the `Logging` module, it still outputs logging information. What’s worse - even the bird is now powering up and down. That can’t be right, can it?

The root of this problem lies in the way we are storing the modules. The class variable `@@wrappables` is defined on the `Wrappable` module and used whenever we add a new module, regardless of the class that `wrap` is used in. 

This get’s more obvious when looking at the class variables defined on the `Wrappable` module and the `Bird` and `Machine` classes. While `Wrappable`  has a class method defined, the two classes don't.

```ruby
Wrappable.class_variables # => [:@@wrappers]
Bird.class_variables # => []
Machine.class_variables # => []
```

To fix this, we have to modify the implementation so that it uses instance variables. However, these aren't variables on the instances of `Bird` or `Machine`, but instance variables on the classes themselves. 

> ## In Ruby, classes are just objects
> This is definitely a bit mind boggling at first, but still a very important concept to understand. Classes are instances of <code>Class</code> and writing <code>class Bird; end</code> is equivalent to writing <code>Bird = Class.new</code>. To make things even more confusing <code>Class</code> inherits from <code>Module</code> which inherits from <code>Object</code>. As a result, classes and modules have the same methods as any other object. Most of the methods we use on classes (like the <code>attr_accessor</code> macro) are actually instance methods of <code>Module</code>.

## Using Instance Variables on Classes

Let’s change the `Wrappable` implementation to use instance variables. To keep things a bit cleaner, we introduce a `wrappers` method that either sets up the array or returns the existing one when the instance variable already exists. We also modify the `wrap` and `new` methods so that they utilize that new method.

```ruby
module Wrappable
  def wrap(mod)
    wrappers << mod
  end

  def wrappers
    @wrappers ||= []
  end

  def new(*arguments, &block)
    instance = allocate
    wrappers.each { |mod| instance.singleton_class.include(mod) }
    instance.send(:initialize, *arguments, &block)
    instance
  end
end
```

When we check the instance variables on the module and on the two classes, we can see that both `Bird` and `Machine` now maintain their own collection of wrapping modules. 

```ruby
Wrappable.instance_variables #=> []
Bird.instance_variables #=> [:@wrappers]
Machine.instance_variables #=> [:@wrappers]
```

Not surprisingly, this also fixes the problem we observed earlier - now, both classes are wrapped with their own individual modules. 

```ruby
bird = Bird.new 
bird.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise

machine = Machine.new 
machine.make_noise
# Powering up
# Buzzzzzz
# Shutting down
```

## Supporting Inheritance 

This all works great until inheritance is introduced. We would expect that classes would inherit the wrapping modules from the superclass. Let’s check if that's the case. 

```ruby
module Flying
  def make_noise
	super
	puts "Is flying away"
  end
end

class Pigeon < Bird
  wrap Flying

  def make_noise
    puts "Coo!"
  end
end

pigeon = Pigeon.new
pigeon.make_noise
# Coo!
# Is flying away
```

As you can see, it doesn’t work as expected, because `Pigeon` is also maintaining its own collection of wrapping modules. While it makes sense that wrapping modules defined for `Pigeon` aren’t defined on `Bird`, it’s not exactly what we want. Let’s figure out a way to get all wrappers from the entire inheritance chain.

Lucky for us, Ruby provides the `Module#ancestors` method to list all the classes and modules a class (or module) inherits from. 

```ruby
Pigeon.ancestors # => [Pigeon, Bird, Object, Kernel, BasicObject]
```

By adding a `grep` call, we can pick the ones that are actually extended with `Wrappable`. As we want to wrap the instances with wrappers from higher up the chain first, we call `.reverse` to flip the order. 

```ruby
Pigeon.ancestors.grep(Wrappable).reverse # => [Bird, Pigeon]
```

> ## Ruby’s <code>#===</code> method</h2>
> Some of Ruby’s magic comes down to the <code>#===</code> (or <em>case equality</em>) method. By default, it behaves just like the <code>#==</code> (or <em>equality</em>) method. However, several classes override the <code>#===</code> method to provide different behavior in <code>case</code> statements. This is how you can use regular expressions (<code>#===</code> is equivalent to <code>#match?</code>), or classes (<code>#===</code> is equivalent to <code>#kind_of?</code>) in those statements. Methods like <code>Enumerable#grep</code>, <code>Enumerable#all?</code>, or <code>Enumerable#any?</code> also rely on the case equality method.
 
Now we can call `flat_map(&:wrappers)` to get a list of all wrappers defined in the inheritance chain as a single array. 

```ruby
Pigeon.ancestors.grep(Wrappable).reverse.flat_map(&:wrappers) # => [Logging]
```

All that's left is packing that into an `inherited_wrappers` module and slightly modifying the new method so that it uses that instead of the `wrappers` method.

```ruby
module Wrappable 
  def inherited_wrappers
    ancestors
      .grep(Wrappable)
      .reverse
      .flat_map(&:wrappers)
  end

  def new(*arguments, &block)
    instance = allocate
    inherited_wrappers.each { |mod|instance.singleton_class.include(mod) }
    instance.send(:initialize, *arguments, &block)
    instance
  end
end
```

A final test run confirms that everything is now working as expected. The wrapping modules are only applied to the class (and its subclasses) they are applied on. 

```ruby
bird = Bird.new
bird.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise

machine = Machine.new
machine.make_noise
# Powering up
# Buzzzzz
# Shutting down

pigeon = Pigeon.new
pigeon.make_noise
# Started making noise
# Coo!
# Finished making noise
# Is flying away
```

## That's a wrap!

Admittedly, these noisy birds are a bit of a theoretic example (tweet, tweet). But inheritable class instance variables are not just cool to understand how classes work. They are a great example that classes are just objects in Ruby.

And we'll admit that inheritable class instance variables might even be quite useful in real life. For example, think about defining attributes and relationships on a model with the ability to introspect them later. For us the magic is to play around with this and get a better understanding of how things work. And open your mind for a next level of solutions. 🧙🏼‍♀️

As always, we’re looking forward to hearing what you build using this or similar patterns. Any ideas? Please leave a comment!. 

_[Benedikt Deicke](http://benediktdeicke.com) is a software engineer and CTO of [Userlist.io](http://userlist.io). On the side, he’s writing a book about building [SaaS applications in Ruby on Rails](http://saasguidebook.com). You can reach out to Benedikt via [Twitter](http://twitter.com/benediktdeicke)._