61 Ruby interview questions with detailed answers, sorted from basic to advanced. Free to practice, with progress saved automatically.
Ruby is an interpreted, object-oriented language that emphasizes readable syntax and underlies the Rails framework. This section covers classes and modules, blocks and iterators, yield versus explicit block parameters, exception handling with begin, rescue, and ensure, gems, metaprogramming, concurrency and the GVL when sizing Puma, and memory growth in long-running processes.
Ruby (Matz, 1995) is an interpreted, dynamically typed, purely object-oriented language built around "developer happiness" — code reads almost like English. What sets it apart from other languages: - Everything is an object — even 5 or nil (you can write 5.times { ... }),…
String is mutable; each instantiation creates a new heap object. Symbol is immutable; every symbol with the same name is the same object in memory (interned). Use Symbol for hash keys, method names, fixed state tokens (:pending, :active). Use String when text manipulation is needed:…
Objectfreeze makes an object immutable — any attempt to modify it raises FrozenError (Ruby ≥ 2.5). Why use it: prevent accidental mutation of constants, safely use strings as hash keys, save memory (Ruby can cache frozen strings). From Ruby 3.0, string literals are immutable by…
Instance variable (@): belongs to each individual object; every instance has its own copy. Class variable (@@): shared across all instances of the class and its subclasses. Gotcha: @@ is inherited by subclasses — modifying it in a subclass affects the parent class too. Many…
The three levels differ in who may call the method: Level Called from outside Called in a subclass Called on another object of the same class ------------ public Yes Yes Yes protected No Yes Yes private No Yes, no receiver No The easy-to-miss case is…
"If it walks like a duck and quacks like a duck, it's a duck." — Ruby does not check an object's type, only whether it responds to the method being called. report does not care which class output belongs to. The two classes share no…
A block is an anonymous piece of code passed to a method — it is not an object and cannot be assigned to a variable. Written with {} (single-line) or do...end (multi-line). A method calls the block using yield.
Both are Proc objects, but differ on two critical points: 1. Arity checking: - lambda: wrong arg count → ArgumentError. - proc: missing args → nil, extra args → ignored. 2. Return semantics: - lambda: return exits only the lambda. - proc: return exits the…
Enumerable is a mixin module — any class that implements each can include Enumerable and instantly gain ~60 methods: map, select, reject, inject, sortby, groupby, flatmap...
A lazy enumerator defers computation until values are actually needed, and creates no intermediate arrays between steps. It helps with infinite sequences and long pipelines. In the eager version, select returns a complete array before map starts. The lazy version works the other way around:…
A module is a collection of methods and constants — it cannot be instantiated and cannot be inherited from. Two main roles: 1. Namespace — prevent name clashes: Payments::Invoice vs Mailer::Invoice. 2. Mixin — include into classes to share methods. Class Module --------- Instantiation new…
All three mix a module into a class, but they differ in where methods land in the method lookup chain: Keyword Adds methods as Called from --------- include instance methods object instances extend class (singleton) methods the class itself prepend instance methods (higher priority) object…
Comparable lets a class define ordering by implementing just one method, <= (the spaceship operator), and in return it gets: <, <=, ==, , =, between?, clamp. <= returns 0 if equal, a negative number if less, a positive number if greater, and nil if…
Ruby allows reopening any class — including built-ins like String and Integer — to add or override methods. This is known as open classes or monkey patching. Pros: concise DSLs (Rails uses this heavily: 2.days.ago). Cons: silently overrides gem methods → hard-to-trace bugs; Ruby/gem upgrades…
Metaprogramming is writing code that generates or modifies other code at runtime — instead of hardcoding every method, Ruby's built-in tools let you automate it. The simplest example: attraccessor :name auto-generates a getter and setter instead of writing them manually. Key tools: - definemethod —…
When a method is called on an object and not found anywhere in the lookup chain, Ruby calls methodmissing on that object. Required caveats: 1. Always call super in unhandled branches so real NoMethodErrors still propagate. 2. Always override respondtomissing? alongside it — otherwise respondto?…
def is a keyword — the method name must be a static identifier known at write-time. definemethod is a method that accepts a dynamic name (string/symbol), enabling method creation from patterns at runtime. When to use definemethod: - Generate many methods from a pattern (no…
Execution order: 1. begin — potentially risky code. 2. rescue — catches exceptions by type, matched top-to-bottom. 3. else — runs only when no exception occurred. 4. ensure — always runs (close files, release connections). Key note: bare rescue only catches StandardError and subclasses —…
Exception is the root of the entire tree: Why not rescue Exception: it catches SystemExit and Interrupt — Ctrl+C gets swallowed and the process can't be stopped. Always use rescue StandardError (or a specific subclass).
Inherit from StandardError (or a subclass), not directly from Exception. Reason: a rescue without a class only catches StandardError and its subclasses. A class inheriting straight from Exception slips past every bare rescue, just like SystemExit or Interrupt. The base error lets callers pick their…
raise throws an exception. Three forms: retry restarts the begin block from scratch — only valid inside rescue: Three things to remember about retry: - Always cap the attempts: retry never stops on its own; without a counter it loops forever. - State is not…
RubyGems is Ruby's package manager — each library is packaged as a gem. CLI: gem install <name. Bundler manages project dependencies: it locks specific versions into Gemfile.lock, ensuring every environment (dev/staging/prod) uses exactly the same versions. bundle install installs gems and creates/updates Gemfile.lock. Commit Gemfile.lock…
Operator Compares Used for --------- == value (often overridden) general equality eql? value and same class (hash key semantics) 1.eql?(1.0) → false equal? object identity, meaning the same object checking same object === "case equality", each class overrides it when in case expressions "a".equal?("a") is…
Ruby objects are mutable by default (they can be modified after creation). Only objects that are already frozen cannot be changed: - Integer, Float, Symbol, nil, true and false are always frozen by language design, so there is no way to modify them in place.…
each map (also called collect) --------- Returns the original array a new array of the block's results Used for work with outside effects (printing, writing files) transforming, producing new data each just runs the block for every element and returns the original array, so each…
These macros auto-generate getters/setters for instance variables: Macro Generates Use when --------- attrreader :x def x = @x read-only attrwriter :x def x=(v); @x=v; end write-only attraccessor :x both read + write The simplest example of Ruby metaprogramming: the methods are generated as soon as…
Ruby does not call it MRO like Python; it is the ancestors chain, the list returned by Child.ancestors. When you call a method, Ruby walks that list from the start and uses the first method it finds. The order is: the object's singleton class →…
- classeval (alias moduleeval): runs a block with self set to the class, so methods defined inside become instance methods of that class. - instanceeval: runs a block with self set to a specific object, so methods defined inside become singleton methods of that object.…
raise/rescue is an exception handling mechanism — for actual errors. throw/catch is a control flow mechanism — for early exit from nested code without any error. throw is not an exception — no stack trace, none of the overhead of raise. Use it when you…
Bundler uses the pessimistic constraint operator ~ (twiddle-wakka) to balance receiving new patches/minors while avoiding breaking changes: Constraint Meaning Valid range --------- "~ 2.1" = 2.1, < 3.0 any 2.x "~ 2.1.0" = 2.1.0, < 2.2.0 only 2.1.x patches "= 2.1" no upper bound risky…
MRI Ruby (CRuby) has a GIL (also called GVL — Global VM Lock): only one thread can execute Ruby bytecode at a time, even on multi-core hardware. Why it exists: simplifies the garbage collector and C extensions — no per-object locking needed. Impact: - CPU-bound…
Method Returns true when Notes --------- nil? only for nil available on every object empty? string/array/hash has length 0 nil.empty? → NoMethodError blank? nil, false, empty?, or whitespace-only Rails only (ActiveSupport) Practical rule: use nil? when you specifically need nil; use empty? for collections; use…
load require requirerelative ------------ Extension Must include .rb Omit .rb Omit .rb Caching No — reloads every call Yes — loads once Yes — loads once Base path $LOADPATH $LOADPATH Relative to current file When to use each: - require — gems, stdlib (JSON, Net::HTTP...)…
&. (lonely operator / safe navigation, Ruby 2.3+) calls a method only when the receiver is not nil — if nil, returns nil instead of raising NoMethodError. Difference from .try (Rails): - &. is Ruby core — still raises NoMethodError if the method doesn't exist…
& before an object calls toproc on it, then passes the result as a block. Symbol implements toproc to create a block that calls the named method on each element. select(&method(:puts)) would be wrong: puts returns nil, so select keeps nothing and returns []. The…
Both pass self into a block, but they return different values, and that is the only real difference. - tap runs the block and returns the original object, ignoring the block's result. Use it to insert a debug step or a side operation (logging, setting…
Both call the same-named method in the superclass; they differ in the arguments. Bare super automatically forwards all arguments of the current method. super() with empty parentheses calls the superclass with no arguments. Cat.new("Tom", 3) creates a cat with @name = "Tom" and @age =…
Both create a shallow copy — instance variables are copied by reference (nested objects remain the same). Key differences: dup clone --------- Preserves frozen state No — always creates mutable copy Yes — copy is frozen if original is frozen Preserves singleton methods No Yes…
Both create a new name that points to an existing method. alias is a language keyword, while aliasmethod is a method of Module. They agree on the most important point: an alias copies the method at the moment it is declared and does not follow…
respondto?(methodname) — checks whether an object can respond to a method (duck typing). Returns false for private methods by default; pass true as a second arg to include them. isa?(klass) / kindof?(klass) — checks whether an object belongs to a class/module. Both are aliases. Prefer…
Method chaining is calling several methods in a row on the same expression, because each method returns an object that has the next method. The simplest way is to return self. Each call to where, order and limit modifies the object it is called on…
Ruby ships the Singleton module in its standard library — include it to create a class with exactly one instance, thread-safe out of the box: Singleton uses an internal Mutex to ensure instance is created only once even when multiple threads call it simultaneously. Use…
Marshal is Ruby's built-in serialization module: Marshal.dump turns an object (including instances of your own classes) into a byte string, and Marshal.load rebuilds it. Only Ruby can read the format, and it requires the matching class to be loaded in the process. When to use…
Enumerator.new takes a block with a yielder — call yielder << value (or yielder.yield value) to emit each value. Values are only computed when the caller requests them (lazy-compatible). enumfor(:methodname) (alias toenum) is the standard idiom for making an each-style method return an Enumerator when…
Railway-Oriented Programming (ROP) uses the Result pattern: each step returns success or failure, and the chain only continues when the previous step succeeded. Picture two rails: success runs along the main rail through each step, and a single failure switches to the error rail and…
yield invokes the implicit block a method received; &block converts the block into a Proc object so it can be stored, forwarded, or inspected. Differences that matter: - yield allocates nothing. With &block, since Ruby 2.5 merely forwarding it (othermethod(&block)) or calling block.call does not…