Metaprogramming and Class Inheritance in Ruby
MikeKetterling
Posted on December 1, 2021
If your new to the developer world and your coming across Metaprogramming for ruby, chances are you are feeling a bit intimidated by just the sound of it, or maybe excited about the potential power such a concept might have? For me I know it was a little of both, but id have to say mostly I thought about potential use cases for such a concept, and I didn’t even know that much about it yet!
For myself I have always though I embodies that idea that developers are lazy and do not want to code more than they have to, infact I could say I live my life around this sort of philosophy. Efficiency is everything to me, I want to get as much doen in as little as possible so I can spend more time learning something new, getting better at something, or just relaxing. This is where Metaprogramming really comes into play for me.
I know there are concepts out there with other languages that are either exactly the same as Ruby’s Metaprogramming or very similar to it, but I think this Is just the first time for me where I have the dedicated time to really dive into the concept and the base knowledge to explain it and get through some examples in the hope of helping someone else with the concept and the utilization.
In the most basic explanation, Metaprogramming is code that has been developed to write additional code, treat code as their data, and be read, generate, analyze or transform other programs or itself while its running. With this definition alone I hope I’ve peeked some interest, I know this one sentence got me. If we think about the usability of that concept we could potentially cut down on dozens if not hundreds of lines of code, making our applications much smoother, easy to read and follow great DRY principles.
How does Ruby call Methods?
Before we can really get into the thick of Metaprogramming we have to lay down some basics of how methods work in Ruby. The below example is a simple method inside a class called Car. When we call the method #start from our Car class we get an output of “VROOM”, but more importantly is how we get this output. When we call the start method Ruby first will look for the parent of the station_wagon object, but because this station_wagon object is an instance of the Car class, and has the #start method available it will be called on station_wagon.
class Car
def start
"VROOM"
end
end
station_wagon = Car.new
station_wagon.start => "VROOM"
We can even take a look at a more complicated block of code that has some class inheritance
class Vehicle
def start
"VROOM"
end
def speed_up
"SHIFT
end
end
class Sportscar < Vehicle
def how_many_tickets
"Too many"
end
end
corvette = Sportscar.new
corvette.how_many_tickets => "Too many"
corvette.start => "VROOM"
corvette.thiscarisbroken => NoMethodError: undefined method...
Ruby is doing the same operations as before, Ruby checks the Sportscar class for the #how_many_tickets method and because it exists and finds it, the method will be called. The process changes a bit when we call the corvette.start method. It first asks the Sportscar class if it can call the method, but Sportscar does not have that method available. And so because of inheritance it looks up the chain to the Vehicle class. It located the method in the parent class and calls it. When we call the last line: “corvette.thiscarisbroken” no method is located so we are returned an error.
This process can all be boiled down to something like this; Ask the objects parent class if it has a particular method and call on it, if no method is found it will continue to look up the inheritance chain for the method that’s stated, and if it can’t find the method, the method does not exist and an error will be returned.
Since we’ve seen a basic example of class inheritance and in basic metaprogramming, we can start to take a deeper look into inheritance specifically with the Singleton class.
Singleton Class
The Singleton class was designed specifically for single object addition, alteration and deletion it does this by having only one instance of some class. Lets look at the code below.
greeting = "I like to drive fast!"
class << greeting
def greet
"lets go, " + self
end
end
greeting.greet => "lets go, I like to drive fast!"
Breaking this down, we created a variable called “greeting”. Then we call the greeting on the class itself since it is its own instance and by chaining the greet method to it we can return the entire phrase. The notation on the 3rd line is known as the class << self notation.
With the Singleton class, Ruby is adding another class into our inheritance chain. This class is given the methods and changes instead, making it the parent of the object we are working on. So essentially we are adding one extra layer of inheritance and locating method calls. So instead of first asking the objects parent class if the method exists first, we will ask the object if its singleton class can respond to the method if found.
Conclusion
These are just a couple examples of metaprogramming in specific with inheritance and singleton methods. There is a whole lot more to be uncovered with metaprogramming with Ruby though. The have books just on this topic, so if your still interested go out and find more information on it!
Posted on December 1, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.