Refactoring with Super in Rails

Super, a Simple Yet Effective Solution

Photo by Elias Castillo on Unsplash

Luke is a guest author for ZEAL. We were lucky enough to have Luke intern with us when he was graduating from LEARN academy. Luke is now a developer at Bitwerx, a consultancy based out of Kentucky. At the moment, we are working together on a project for our longtime client PetIQ.

A good refactor results in cleaner, more readable, and often less code. Recently I saw an opportunity to refactor a complex api pattern in a Ruby on Rails application. Unsure of the correct solution and after multiple iterations, I landed on using a Ruby fundamental keyword, super.

What does the Super keyword do?

Way back in my bootcamp days, `super` was taught as a fundamental building block of object oriented coding languages. The use of `super` in ruby directly relates to how object inheritance works.

According to ruby-docs.org, `super`, when “called from a method, searches along the method lookup path (the classes and modules available to the current object) for the next method of the same name as the one being executed.”

Super in action:

Let’s take this simple, overused example:

-- CODE language-ruby --class Animal
  def hello
    puts 'I am an animal.'
  end
end
class Dog < Animal
  def hello
    super
    puts 'woof!'
  end
end

-- CODE language-ruby --dog = Dog.new
Dog.hello
→ 'I am an animal.'

In this example, Class Dog is a child of Class Animal and inherits its methods. By overwriting the method `hello` and calling super within it, the Dog class will then look for a method called `hello` in it’s parent class and call it. This code is too simple to see the full power of `super` so let’s extend this example out a bit. We’ll start with the same two classes, Animal and Dog, but add another called Cat. How original! We’ll need to add an API fetch to retrieve the hello string.

Without Super:

-- CODE language-ruby --class Animal
  def hello
    puts 'I am an animal.'
  end
end

-- CODE language-ruby --class Dog < Animal
  def hello
    puts animals.hello
  end
  def animals
    # api call that returns:
    {
      "data": {
        "animals": {
          "dog":
            {
              "hello": 'woof',
            },
          "cat":
            {
              "hello": 'meow',
            }
        }
      }
    }.data.animals.dog
  end
end

-- CODE language-ruby --class Cat < Animal
  def hello
    puts animals.hello
  end
  def animals
    # api call that returns:
    {
      "data": {
        "animals": {
          "dog":
            {
              "hello": 'woof',
            },
          "cat":
            {
              "hello": 'meow',
            }
        }
      }
    }.data.animals.cat
  end
end

Here we have a lot of duplicated code that can easily be refactored using the super keyword.

With Super:

-- CODE language-ruby --class Animal
  def hello
    puts "I am an animal."
  end
  def animals
    # api call that returns:
    {
      "data": {
        "animals": {
          "dog":
            {
              "hello": 'woof',
            },
          "cat":
            {
              "hello": 'meow',
            }
        }
      }
    }.data.animals
  end
end

The first step is to pull the api call into the Animal class. From there, we can access it via super in the child classes.

-- CODE language-ruby --class Dog < Animal
 def hello
   super
   puts animals.hello
 end
 def animals
   super.dog
 end
end

-- CODE language-ruby --class Cat < Animal
 def hello
   super
   puts animals.hello
 end
 def animals
   super.cat
 end
end

Next we use super to return the data in Animal’s animals method.

-- CODE language-ruby --cat = Cat.new
cat.hello
→ "I am an animal."
→ "meow"
dog = Dog.new
dog.hello
→ "I am an animal."
→ "woof"

Conclusion

There we have it! 

In these examples, we used class inheritance to move an api call and response to a shared parent class and dynamically parsed the data using ‘super’.

That’s it! `Super` is a simple yet effective solution to reduce and refactor code. 

More info on super can be found here:

https://ruby-doc.org/docs/keywords/1.9/Object.html#method-i-super