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.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.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.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.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.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.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.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.
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)
- 11
- 20
- 32correct
- 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.