Intermediate Ruby - Monkey Patching, Modules

Question Click to View Answer

What does the following code print?

class Dog
  def name
    "Fido"
  end
end

class Dog
  def introduction
    "Hi, my name is #{name} and I like to woof"
  end
end

fido = Dog.new
p fido.introduction
"Hi, my name is Fido and I like to woof"

The Dog class is initially defined with the name() instance method. The Dog class is then reopened and the introduction() instance method is added.

What does the following code print?

class Array
  def say_hi
    "HEY!"
  end
end

p [1, "bob"].say_hi
"HEY!"

The Array class was reopened and the say_hi() method was added for all instances of the Array class.

Add a method to the Array class that applies the String#downcase method to every string object in the Array.

class Array
  def downcase_strings
    self.map do |element|
      element.instance_of?(String) ? element.downcase : element
    end
  end
end

p [1, "BOB", "cRaZy", :cool].downcase_strings # returns [1, "bob", "crazy", :cool]

Refactor the following class with the built-in Ruby shortcut getter method.

class Person
  def initialize(name)
    @name = name
  end

  def name
    @name
  end
end

bob = Person.new("bob")
p bob.name
class Person
  attr_reader :name
  def initialize(name)
    @name = name
  end
end

The attr_reader method provides the "getter" methods for instance variables so they don't need to be defined explicitly. getter methods simply return the value of an instance variable. Instance variable getter methods are common because these helper methods save a lot of typing.

Refactor the following class with the built-in Ruby shortcut setter method.

class Train
  def initialize(sound)
    @sound = sound
  end

  def sound=(sound)
    @sound = sound
  end
end

train = Train.new("choo choo")
p train
train.sound = "chugga chugga"
p train
class Train
  attr_writer :sound
  def initialize(sound)
    @sound = sound
  end
end

The attr_writer method provides "setter" methods for instance variables so the setter methods don't need to be defined explicitly. setter methods update the value of an instance variable.

Refactor the following class with the built-in Ruby shortcut getter / setter method.

class Sky
  def initialize(color)
    @color = color
  end

  def color
    @color
  end

  def color=(color)
    @color = color
  end
end

sky = Sky.new("blue")
p sky.color
sky.color = "pink"
p sky.color
class Sky
  attr_accessor :color
  def initialize(color)
    @color = color
  end
end

attr_accessor provides the getter and setter methods for an instance variable. In other words, attr_accessor provides methods to return the value of the instance variable and update the value of the instance variable.

Use the following code to create an instance of the Cat class.

module Wrapper
  module Container
    class Cat
    end
  end
end
cat = Wrapper::Container::Cat.new

The Cat class is inside the Wrapper and Container modules. The Wrapper and Container modules need to be referenced to instantiate instances of the Cat class.

Discuss how Modules are used to avoid namespace collisions in the following example.

module Machine
  class Crane
    def self.about
      "Equipment for hoisting"
    end
  end
end

module Bird
  class Crane
    def self.about
      "Long neck"
    end
  end
end

p Machine::Crane.about
p Bird::Crane.about

There are two Crane classes with about() methods. One Crane class is namespaced in the Machine module and the other is namespaced in the Bird module. There are no name conflicts because the Crane classes are namespaced in different modules.

What does the following code print? Explain.

module Desk
  COLOR = :brown
end

p Desk::COLOR
:brown

:: notation can be used to access constants in modules.

What does the following code print?

module Table
  TYPE = :wood
  module Speaker
    class Something
      def self.about
        "I like #{TYPE}"
      end
    end
  end
end

p Table::Speaker::Something.about
"I like wood"

Nested modules and classes have access to constants that are defined in parent modules. In this example, the Something class has access to the TYPE constant that is defined in the Table module.

What does the following code print?

module Membrane
  module Layer
    DERP = "not change opinions, despite the facts"
  end

  class Economist
    def self.about
      "We love to #{Layer::DERP}"
    end
  end
end

p Membrane::Economist::about
"We love to not change opinions, despite the facts"

The DERP constant is accessed in the Economist.about method as Layer::DERP. DERP is not defined in Economist (or a parent module of Economist), so it cannot be referred to directly. The DERP constant could also be referred to as Membrane::Layer::DERP, but the Membrane portion of the reference is not necessary because Economist is already wrapped in the Membrane module.

What does the following code print? Explain.

my_object = Object.new
def my_object.blah
  "blah blah blah"
end
p my_object.blah
"blah blah blah"

blah() is a singleton method added to the my_object object. Singleton methods are methods defined for a particular instance of a class, not all instances of the class. In this example, my_object responds to the :blah message, but other instances of the Object class will not respond to the blah message.

What does the following code print? Explain.

class Dog; end
dog = Dog.new
def dog.bark
  "ruff ruff"
end
p dog.bark
"ruff ruff"

dog is an instance of the Dog class and is given a singleton method bark(). The bark() method can be called on the dog instance of the Dog class, but is not available for other instances of the Dog class. The following code raises an error.

fido = Dog.new
fido.bark

List all the singleton methods that are defined for the method_man object.

method_man = Object.new
def method_man.clan
  "wu-tang"
end
method_man.singleton_methods # [:clan]

The singleton_methods() method lists all the singleton methods that are defined for an object.

What does the following code print?

class Dog
  def self.life_purpose
    "serve master"
  end
end

Dog.singleton_methods
[:life_purpose]

Dog is a class, but Dog is also an object because everything in Ruby is an object. Dog is actually a constant that refers to the Dog class. "Class" methods in Ruby are really just singleton methods defined on the class object.

What does the following code print?

class Turtle; end
biggie = Turtle.new
def biggie.beak
  "sharp"
end
smalls = Turtle.new
p "Biggie's singleton methods #{biggie.singleton_methods}
   Smalls' singleton methods #{smalls.singleton_methods}"
"Biggie's singleton methods [:beak]\n   Smalls' singleton methods []"

The biggie object has the :beak singleton method, but the smalls object does not have any singleton methods.

Identify the objects in the following line of code.

random_stuff = [:tall, "dude", 45.23]

random_stuff is a variable that is assigned to an array (instance of the Array class). The array contains the symbol :tall (instance of the Symbol class), the string "dude" (instance of the String class) and the number 45.23 (instance of the Float class).

Identify the object in the following code.

class Bird
end

Bird is a constant that is assigned to the value of the Bird class. The Bird class is an instance of the Class class. Since the Bird class is an instance of a class, the Bird class is an object. The Bird class is a special type of object that is used to create new objects.