Meta Programming in Ruby compared to PHP's Magic Methods
Meta Programming is this concept of code writing code. It sounds sophisticated and a 10x multiplier on development productivity.
However, in practical development you’ll come to find that you’ll hardly ever have to use it. But it’s still a good thing to know in case a code base you run across uses it.
As an added bonus I’ll show you that PHP can accomplish the same functionality with it’s Magic Methods.
Magic Methods in PHP
Although PHP is a static language, there are a few tricks up it’s sleeve to accomplish dynamic code. By dynamic code I mean code that is not typed by the developer but code that is created at runtime.
PHP offers a slew of Magic Methods that allow us to hook into the Class lifecycle.
You’re already familiar with one such Magic Method - __construct()
. The __construct()
method in a class is what’s commonly referred to as the constructor. This method is magically called when an object is instantiating.
The other Magic Methods PHP offers follow the same syntax, 2 underscores __
followed by the Magic Method name.
Overloading a.k.a. Creating Methods at Runtime
Overloading is the idea of creating publicly accessible methods on a PHP object at runtime. Sound familiar?
We have 2 Magic Methods available to us to create this behavior:
__call()
__callStatic()
They are essentially the same thing, but __callStatic()
is used in a static a.k.a. class method context.
__call()
is automatically invoked if it’s defined on a class and a method called is not defined.
Let’s return to our Cat
:
Now, if we tried to call a method that didn’t exist on Cat
, then PHP would be angry at us:
Creating a Dynamic Method
Since cat’s can and will eat a plethora of things: cheese, tuna, salmon, shoe laces, etc. let’s create a dynamic method that will respond to eat*
:
Now let’s see it in action:
We can now have the cat eat anything:
Creating Dynamic Static Methods
You can create dynamic static methods as well:
Creating Methods Dynamically in Ruby a.k.a. Meta Programming
In a way, Ruby has it’s own set of “Magic Methods” like PHP does. The one we’re going to explore here has the same functionality as PHP’s __call()
.
Like other things in Ruby this method name might be easier to remember. It’s called method_missing
. Just like PHP’s __call()
Magic Method, Ruby’s method_missing
takes the method name as the first argument.
In the example below we’ll recreate what we accomplished with our PHP Cat
class and allow an instance of our Ruby Cat
class to eat*
.
class Cat
def meow
puts 'meow'
end
def method_missing(method)
# replace the 'eat_' in 'eat_cheese' with an empty string
food = method.sub('eat_', '')
puts "nom nom #{food}"
end
end
You may have noticed the minor difference between code style between camelCasing in PHP and underscoring in Ruby means we don’t have to lowercase our dynamic method call here.
Now let’s use our Cat
and give it food dynamically:
cat = Cat.new
cat.eat_tuna
cat.eat_salmon
cat.eat_grass
=> 'nom nom tuna'
=> 'nom nom salmon'
=> 'nom nom grass'
Creating a Dynamic Class Method in Ruby
Just like leveraging method_missing
to populate missing methods in Ruby Objects, we can use the same method at the class level to create dynamic class methods:
class Cat
def meow
puts 'meow'
end
# The only change we made is attaching the method to the class by prefixing it with `self.`
def self.method_missing(method)
# replace the 'eat_' in 'eat_cheese' with an empty string
food = method.sub('eat_', '')
puts "nom nom #{food}"
end
end
Now that we’ve called method_missing
on the class by using self
, we can have our Cat
eat all sorts of things without being instantiated:
Cat.eat_pasta
Cat.eat_ham
=> 'nom nom pasta'
=> 'nom nom ham'
This is just an introductory guide to Meta Programming. The rabbit hole goes deeper but this will give you an idea of how it works if you ever come across it in the wild.
Last Warning
Like PHP, you can get 100% of your work done in a static way with Ruby. There are very few times when you should consider dynamic programming as a choice. For your future self’s santity and anyone else reading your code, I would refrain from trying this unless it’s painful to engineer a solution without it.
And also note that this is just a 1:1 example of dynamic PHP to dynamic Ruby. In reality, Ruby’s meta programming is a very rich subject. There are times when metaprogramming makes sense, but most of the time it’s a knive that doesn’t need to be pulled out the drawer.
Once you finish this series, you’ll be ready to dive deeper into metaprogramming, and I encourage it.