Extending Classes in PHP and Ruby
Extending Classes is a key part of Object Oriented Programming. It’s a central concept to frameworks in both PHP and Ruby. Take for instance a simple Laravel controller:
And Ruby on Rails:
class PagesController < ApplicationController
def home
render 'home'
end
end
It’s amazing how similar these 2 pieces of code are despite that under-the-hood the languages are so different.
Just from at a glance you can probably figure out that in Ruby speak the symbol <
is the PHP equivalent of extends
.
# in PHP terms, the PagesController _extends_ the ApplicationController
class PagesController < ApplicationController
# methods are defined in here...
end
Extending Unexpected Classes in Ruby
No, from day to day use you can expect the same behavior from class extension in both PHP and Ruby.
The only difference being in Ruby that you can do some otherwise strange things like extending the String
class if you really feel the need to:
class Yarn < String
def unravel
puts 'Watch me unravel'
end
end
yarn = Yarn.new('I am a ball of yarn')
puts yarn
=> 'I am a ball of yarn'
yarn.unravel
=> 'Watch me unravel'
Monkey Patching
In addition to extending any class you’d like, there’s a common practice in Ruby called Monkey Patching. Usually to overwrite functionality in a class, we’d have to extend the original class and override the public or protected method:
The other option is to use the Wrapper pattern and “wrap” the inside functionality. The big difference here is that we cannot override the wrapped classes function but we can extend it.
We can implement both of these patterns in Ruby, including the Wrapper pattern:
class Cat
def meow
puts 'meow'
end
end
class HungryCat
attr_accessor :cat
initialize(cat)
@cat = cat
end
def meow
@cat.meow
puts 'roarrrrr!'
end
end
regular_cat = Cat.new
hungry_cat = HungryCat.new(regular_cat)
hungry_cat.meow
=> 'meow'
=> 'roarrrrr!'
But Ruby has one more trick up it’s dynamic wizard-like sleeve - Monkey Patching.
Monkey Patching is when you override a public method directly. It’s not a possiblity in static languages.
For example, Ruby on Rail’s Active Support has a monkey patch on the Integer
class to make life easier when needing to create dates:
# getting a date in the past is a cinch
last_month = 1.month.ago
# in the future is possible too
in_a_few_weeks = 3.weeks.from_now
This is a result of Monkey Patching the Integer
class. Here’s a simplistic example:
class Integer
def even?
# self in this case is the actual digit
self % 2 == 0
end
def odd?
self % 2 != 0
end
end
2.even?
=> True
2.odd?
=> False
Now we have 2 convience methods for determining if a particular Integer is odd or even.
Great Power Requires Great Discipline
Ruby is known for letting you “cut with sharp knives” as a developer. These sharp knives are empowering, but they can also allow you to shoot your own foot.
Monkey Patching can hurt you in one distinct way:
Loss of Updates
When you Monkey Patch over top of another public method in a class that’s vendored - a.k.a. it’s part of a Gem - then you will not automatically receive the vendor’s updates to that Gem.
It’s up to you when to use Monkey Patching, and it does have it’s uses. Most of the time however it should not be your go to choice.