December Code

Ruby OOP interview questions, with answers

Ruby is object-oriented all the way down, and Rails applications are built from classes, so Ruby interviews spend a lot of time on OOP. The questions test Ruby's own rules rather than general theory: how attributes are generated, what self refers to, how visibility works without Java's rules, and how classes can be reopened.

The answers below cover defining classes, attribute methods, self and class methods, visibility, inheritance and super, class-level state and open classes, with code run on Ruby 3.4. Then take the free Ruby diagnostic — ten questions across every Ruby topic in the bank — to see which of these you can explain but not yet predict.

The questions, with answers

  1. 1.How do you define a class with a constructor in Ruby?

    In short: Define initialize, which new calls, and store state in instance variables such as @name that belong to each object.

    Class.new allocates an object and calls its initialize method with the arguments, so initialize is Ruby's constructor. Instance variables, written @name, hold each object's state; they spring into existence when first assigned and read as nil before that. They are always private to the object, so outside code reaches them only through methods. Defining to_s controls how puts and interpolation show the object, and inspect controls what p shows. Class names are constants in CamelCase.

    class Dog
      def initialize(name)
        @name = name
      end
    
      def to_s = "Dog #{@name}"
    end
    puts Dog.new("Rex")
    # Dog Rex
  2. 2.What do attr_reader, attr_writer and attr_accessor do in Ruby?

    In short: They generate methods for instance variables: attr_reader a getter, attr_writer a setter such as title=, and attr_accessor both.

    Because instance variables are never accessible from outside, Ruby classes expose them through methods. Writing getters and setters by hand is repetitive, so these class-level methods generate them: attr_reader :isbn defines isbn, which returns @isbn, attr_writer :title defines title=, which assigns @title, and attr_accessor defines both. They are ordinary method calls that run while the class body is evaluated, and each accepts several names at once, as in attr_accessor :title, :author.

    class Book
      attr_accessor :title
      attr_reader :isbn
    
      def initialize(isbn)
        @isbn = isbn
      end
    end
    b = Book.new("978")
    b.title = "Ruby"
    puts b.title, b.isbn
    # Ruby
    # 978
  3. 3.What does self refer to in Ruby?

    In short: Inside an instance method self is the receiving object; in a class body it is the class, so def self.name defines a class method.

    self is always the current object, and what that is depends on where the code runs. Inside an instance method it is the object the method was called on. Inside a class body, but outside any method, it is the class itself, which is why def self.unit defines a method on the class, a class method, called as Temp.unit. Methods called without an explicit receiver are sent to self. class << self opens the class's singleton class, a block form for defining several class methods or class-level attributes.

    class Temp
      def self.unit = "C"
      def kind = self.class
    end
    puts Temp.unit
    puts Temp.new.kind
    # C
    # Temp
  4. 4.How do private and protected methods differ in Ruby?

    In short: A private method can be called only on self without an explicit receiver; a protected one also on other objects of the same class.

    Methods are public by default. private methods are internal helpers: they can be called only with self as the implicit receiver, never as other.helper. protected methods can be called with an explicit receiver, but only from inside instance methods of the same class or its subclasses, which is exactly what comparisons need: Acct's > method reads other.bal, while outside code calling a.bal gets NoMethodError. private and protected, written alone, apply to every method defined after them, and they also accept method names, as in private :helper.

    class Acct
      def initialize(b)
        @b = b
      end
    
      def >(other)
        bal > other.bal
      end
    
      protected
    
      def bal = @b
    end
    a = Acct.new(5)
    p a > Acct.new(3)
    # true
    a.bal
    # raises NoMethodError
  5. 5.How do inheritance and super work in Ruby?

    In short: class Cat < Animal inherits Animal's methods; super inside an overriding method calls the parent's version of the same method.

    Ruby supports single inheritance, written with <, and every class ultimately descends from Object and BasicObject. A subclass can override any method, and super inside it calls the next implementation up the chain. Bare super passes along the same arguments the current method received, super() passes none, and super(x) passes exactly x, a distinction that matters in initialize. Shared behaviour across unrelated classes is usually expressed with modules rather than deep hierarchies, since a class can include many modules but inherit from one class.

    class Animal
      def speak = "..."
    end
    class Cat < Animal
      def speak = "meow " + super
    end
    puts Cat.new.speak
    # meow ...
  6. 6.What is the difference between a class variable and a class instance variable in Ruby?

    In short: A class variable (@@x) is shared by a class and all its subclasses; a class instance variable (@x in the class body) belongs to one class.

    Class variables, written @@count, are shared across the whole inheritance tree: a subclass reads and writes the same variable as its parent, which often surprises people. A class instance variable is an ordinary instance variable of the class object itself, set with @count in the class body or in a class method. Each class has its own, and subclasses do not inherit its value, so Cafe.count below is nil after Shop.count changed. That independence is why class instance variables, exposed through class << self and attr_accessor, are usually preferred.

    class Shop
      @count = 0
      class << self
        attr_accessor :count
      end
    end
    class Cafe < Shop; end
    Shop.count += 1
    p Shop.count, Cafe.count
    # 1
    # nil
  7. 7.What are open classes and monkey patching in Ruby?

    In short: Any class, including built-ins like String, can be reopened to add or change methods, which is powerful but affects the whole program.

    Writing class String again does not create a new class; it reopens the existing one, and the methods defined are added to every string in the program. Rails's Active Support adds helpers such as 2.days this way. Changing existing methods, monkey patching, is risky, because every library in the process sees the change, and two libraries can patch the same method. Refinements, activated with using, limit a patch to one file or module, and many teams prefer plain helper modules to modifying core classes.

    class String
      def shout = upcase + "!"
    end
    puts "hi".shout
    # HI!

How the diagnostic asks it

One question from the Ruby bank, exactly as a sitting would show it. The bank has 4 on classes & objects and 30 across Ruby.

Classes & Objects · mediumRB-017

What does this Ruby code print?

class Parent
  @@count = 0

  def self.inc
    @@count += 1
  end
end

class Child < Parent; end

Parent.inc
Child.inc
puts Parent.class_variable_get(:@@count)
  1. 11
  2. 20
  3. 32correct
  4. 4It raises NameError: uninitialized class variable in Child

A class variable, prefixed @@, belongs to the class where it is defined and is shared with every subclass: Parent and Child read and write the same @@count. Child.inc runs the inherited inc, which increments that shared variable, so after both calls it is 2. 1 assumes Child has its own copy, which is how class instance variables, a plain @count set at class level, behave, and why they are usually preferred. 0 ignores both calls. There is no NameError, because Child sees Parent's class variables through inheritance.

Measure it

Reading answers tells you what’s true. A diagnostic tells you what you get wrong.

10 Ruby questions across its topics, easy to hard, about fifteen minutes. You get a readiness figure with the arithmetic shown, the topics you missed named, and a practice set sized for today. Free: 1 diagnostic a month and 15 problems a day. No card.

What the readiness test measures · how the score is computed

By Harshit · updated