Menu
Coddy logo textTech

Extending Objects

Part of the Object Oriented Programming section of Coddy's Ruby journey. Lesson 28 of 57.

extend adds a module's methods to one object instead of to all instances. Extending a class adds them to the class object, so they become class methods:

module Describable
  def describe
    "#{name} has #{instance_methods(false).size} methods"
  end
end

class Dog
  extend Describable

  def bark
    "Woof"
  end

  def sit
    "sits"
  end
end

puts Dog.describe

Output:

Dog has 2 methods

A module used with extend can keep state in instance variables of the class. Each class that extends it gets its own copy, as class instance variables:

module Counted
  def created
    @created ||= 0
  end

  def record
    @created = created + 1
  end
end

class Order
  extend Counted
end

class Invoice
  extend Counted
end

Order.record
Order.record
Invoice.record
puts "#{Order.created} #{Invoice.created}"

Output:

2 1

extend also works on a single object. Only that object gains the methods; other instances of its class stay unchanged, and is_a? is true for the extended object only:

module Shiny
  def polish
    "#{self} is shining"
  end
end

a = "car"
b = "bike"
a.extend(Shiny)
puts a.polish
puts a.is_a?(Shiny)
puts b.respond_to?(:polish)

Output:

car is shining
true
false

The two keywords put the same module's methods in different places. include makes them instance methods of a class, extend makes them methods of the one object that is extended:

class Order
  include Printable    # order.print_me       (every instance)
  extend Counted       # Order.record         (the class itself)
end

item.extend(Shiny)     # item.polish          (only this object)
challenge icon

Challenge

Easy

Complete the module Registry, which classes use with extend. register(name) adds a name to the class's own list, kept in @entries (created on first use), and returns Plugin registered x; a name that is already there returns Plugin already has x. entries returns the list and count its size. Plugin and Theme both extend Registry and keep separate lists.

The supplied code reads lines plugin name or theme name, calls register on the matching class and prints the reply. Finally it prints plugins: a, b (2) and themes: ..., or none (0) for an empty list.

Your code goes in registry.rb, plugin.rb and theme.rb. main.rb holds the supplied input/output code and cannot be edited.

Try it yourself

require_relative 'registry'
require_relative 'plugin'
require_relative 'theme'

# Supplied input/output code: keep it as it is
input = []
while (line = gets)
  input << line.chomp
end
input.each do |line|
  kind, name = line.split
  target = kind == "plugin" ? Plugin : Theme
  puts target.register(name)
end
[["plugins", Plugin], ["themes", Theme]].each do |label, klass|
  list = klass.entries.empty? ? "none" : klass.entries.join(", ")
  puts "#{label}: #{list} (#{klass.count})"
end
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Online Ruby compiler