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:

namespace App\Http\Contollers;

class PagesController extends ApplicationController {
  public function home() {
    return view('home');
  }
}

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:

class Cat {
  public function meow() {
    echo 'meow';
  }
}

// Now I'm going to override the Cat's meow() method

class HungryCat extends Cat {
  public function meow() {
    echo 'roarrrrr!';
  }
}

$cat = new HungryCat;
$cat->meow();

=> 'roarrrrr!'

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.

class Cat {
  public function meow() {
    echo 'meow';
  }
}

class HungryCat {
  /**
   * @var Cat
   */
  private $cat;

  /**
   * Constructor
   * 
   * @param Cat $cat
   */
  public function __construct(Cat $cat) {
    $this->cat = $cat; 
  }

  /**
   * meow() wrapper
   *
   * @return void
   */
  public function meow() {
    // call the wrapped method
    $this->cat->meow();

    echo 'roarrrrr!';
  }
}

$regularCat = new Cat();

// now "wrap" the regular cat
$cat = new HungryCat($regularCat);
$cat->meow();

=> 'meow'
=> 'roarrrrr!'

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.