72 Ruby Interview Questions and Answers (2026)

Blog / 72 Ruby Interview Questions and Answers (2026)
Ruby interview questions and answers

Ruby still powers a huge share of production web apps and developer tooling, and its focus on expressive, readable code keeps it a favorite for fast-moving teams. Interviewers can tell within minutes whether you actually understand it — walk in shaky on the object model, blocks, or metaprogramming and you will lose the offer to someone who does.

This is your fix: 72 questions with tight, interview-ready answers and code where it counts. They're ordered Junior to Mid to Senior, so you build from fundamentals up to metaprogramming, concurrency, and performance. Work through them and you'll speak Ruby like you mean it.

Q1.
What does 'everything is an object' mean in Ruby, and are there any exceptions?

Junior

In Ruby almost every value is an object: an instance of some class that responds to methods, including things that are primitives in other languages like integers, nil, true, and even classes themselves.

  • Literals are objects: 42.even? works because 42 is an instance of Integer; nil.nil? works because nil is the single instance of NilClass.

  • Classes are objects too: A class is an instance of Class, so you can call methods on it and assign it to variables.

  • The real exceptions:

    • Methods, blocks, and keywords (if, def, while) are not objects by themselves.

    • You can reify some of them: a block becomes an object via Proc, and a method via Object#method, but in raw form they are syntax, not values.

Q2.
What is duck typing and how does Ruby implement it?

Junior

Duck typing means an object's suitability is determined by what methods it responds to, not by its class: "if it walks like a duck and quacks like a duck, it's a duck." Ruby implements this through dynamic method dispatch rather than declared interfaces.

  • Behavior over type: Code only cares that an object responds to the methods it calls, so any object with a each or to_s can be passed in.

  • No interface declarations: Unlike Java/C#, Ruby has no implements; compatibility is checked at call time when the method is dispatched.

  • Tools that support it: respond_to? to check capability, and method_missing to respond dynamically.

  • Trade-off: Flexibility and easy polymorphism, but errors surface at runtime rather than compile time, so tests matter more.

ruby

def serialize(thing) thing.to_json # works for anything that responds to to_json end

Q3.
What is the difference between a Class and a Module? When would you choose one over the other?

Junior

A class is a blueprint for objects that can be instantiated and inherited; a module is a bundle of methods/constants that cannot be instantiated but can be mixed into classes or used as a namespace. Use a class for "is-a" entities and a module to share behavior or organize names.

  • Class:

    • Can be instantiated with .new and supports single inheritance via <.

    • Models a thing with identity and state (a User, an Order).

  • Module:

    • Cannot be instantiated and has no inheritance.

    • Two jobs: a namespace (Math::PI) and a mixin of shared behavior via include or extend.

  • Choosing:

    • Need objects with state and an "is-a" relationship: class.

    • Need to share capabilities across unrelated classes ("has-a" / "can-do"): module mixin.

    • Ruby has single inheritance, so mixins are how it gets multiple-inheritance-like reuse.

Q4.
What is the Enumerable module and why is it so powerful?

Junior

Enumerable is a mixin module that gives any collection class a huge library of iteration methods (map, select, reduce, sort, etc.) in exchange for defining a single method: each.

  • One method buys dozens: Define each and include Enumerable, and you get the whole traversal/transform/query toolkit for free.

  • Consistent API everywhere: Array, Hash, Range, and Set all share the same methods, so knowledge transfers across collections.

  • Functional, chainable style: Methods return new collections you can chain, encouraging declarative code over manual loops.

  • Sorting and comparison: min, max, and sort rely on elements implementing <=>.

ruby

class TodoList include Enumerable def initialize(*items) = @items = items def each(&block) = @items.each(&block) end TodoList.new("a", "b").map(&:upcase) # => ["A", "B"]

Q5.
What is the difference between a Class and an Object in Ruby?

Junior

A class is the template that defines structure and behavior; an object (instance) is a concrete thing created from that template with its own state. The class says what something can do; the object is an actual one that does it.

  • Class:

    • Defines instance methods and the shape of data (instance variables).

    • Created once; used to stamp out many instances.

  • Object:

    • Produced by Class.new; each holds its own copy of instance variable values.

    • Two objects of the same class share methods but have independent state.

  • Ruby twist: A class is itself an object (an instance of Class), so the two concepts are layered, not opposed.

ruby

class Dog # the class def initialize(name) = @name = name end rex = Dog.new("Rex") # an object/instance with its own @name fido = Dog.new("Fido") # a separate object, separate state

Q6.
What is the difference between attr_accessor, attr_reader, and attr_writer?

Junior

They are macros that generate accessor methods for instance variables: attr_reader makes a getter, attr_writer makes a setter, and attr_accessor makes both. They exist to avoid hand-writing repetitive getter/setter methods.

  • attr_reader :x: Generates x returning @x; read-only.

  • attr_writer :x: Generates x= to set @x; write-only.

  • attr_accessor :x: Generates both getter and setter.

  • Practical notes:

    • Default to the least access needed (often attr_reader) to protect internal state.

    • Inside the object, assigning to a writer needs self.x = ... since a bare x = ... creates a local variable.

ruby

class User attr_accessor :name # name and name= attr_reader :id # id only def initialize(id) = @id = id end

Q7.
What values are considered truthy and falsy in Ruby, and how do nil and false differ?

Junior

In Ruby only two values are falsy: false and nil. Everything else is truthy, including 0, empty strings, and empty arrays.

  • Falsy values: Only nil and false evaluate as false in conditionals.

  • Truthy values: Everything else, including 0, "", and [] (unlike many other languages).

  • How nil and false differ:

    • nil is an instance of NilClass representing absence of value; false is an instance of FalseClass representing a real boolean.

    • Use .nil? to test specifically for nil, since both look falsy in an if.

  • Practical tip: To distinguish "missing" from "explicitly false", compare with == or use nil? rather than relying on truthiness.

Q8.
What is a Range in Ruby and how is it used?

Junior

A Range represents an interval between a start and end value, created with .. (inclusive) or ... (exclusive of the end). It works with any objects that are comparable and can be sequenced.

  • Two literal forms: 1..5 includes 5; 1...5 stops at 4.

  • Common uses:

    • Iteration: (1..5).each.

    • Membership tests: (1..10).include?(7) or cover?.

    • Slicing: array[2..4], "hello"[0..1].

    • As a case condition: when 1..10.

  • Works beyond integers: Any objects supporting <=> (and succ for iteration), e.g. ('a'..'e') or dates.

  • Endless and beginless ranges: (1..) and (..10) are valid and handy for slicing and comparisons.

Q9.
How do you define a class method versus an instance method in Ruby?

Junior

Instance methods are defined with a plain def and operate on instances; class methods are defined on the class itself (commonly def self.name) and are called on the class without an object.

  • Instance method:

    • Defined with def greet; called on an object: obj.greet.

    • Has access to that instance's instance variables.

  • Class method:

    • Defined with def self.create; called on the class: Klass.create.

    • Often used for factories, helpers, or operations not tied to one instance.

  • Other ways to declare class methods:

    • class << self opens the singleton class to group several class methods.

    • Inside the class body, def self.x works because self is the class there.

ruby

class User def initialize(name) = @name = name def greet # instance method "Hi, #{@name}" end def self.anonymous # class method new("Anonymous") end end User.anonymous.greet # => "Hi, Anonymous"

Q10.
What is the difference between require, require_relative, and load?

Junior

All three load Ruby code, but differ in path resolution and whether they re-run a file. require loads once from the load path, require_relative loads once relative to the current file, and load runs the file every time it is called.

  • require:

    • Searches the load path ($LOAD_PATH) and gems; needs an absolute path or a name it can resolve.

    • Loads a file only once: tracks it in $LOADED_FEATURES and returns false on repeat calls.

  • require_relative:

    • Resolves the path relative to the location of the current source file, not the working directory.

    • Also loads only once; ideal for requiring files within your own project.

  • load:

    • Executes the file every time it is called (no once-only tracking); useful for reloading changed code.

    • Requires the full filename including .rb, and resolves via the load path.

  • Rule of thumb: Use require for gems/libraries, require_relative for project files, and load only when you deliberately need re-execution.

Q11.
What does the safe navigation operator (&.) do and when would you use it?

Junior

The safe navigation operator &. calls a method on a receiver only if it isn't nil: if the receiver is nil it short-circuits and returns nil instead of raising NoMethodError.

  • What it replaces: Turns user && user.address && user.address.city into user&.address&.city.

  • How it differs from .: a&.b only guards against nil, not against other falsey-ish or missing methods.

  • When to use it: Optional values that may legitimately be nil (API results, optional associations).

  • When NOT to use it: Don't chain it everywhere to silence bugs: it can hide a nil that should never occur and mask the real error.

ruby

user&.address&.city # nil if user or address is nil user.address.city # raises NoMethodError if either is nil

Q12.
What is the difference between a Symbol and a String in terms of memory and usage?

Junior

A Symbol is an immutable, interned identifier stored once and reused, while a String is a mutable sequence of characters where each literal usually creates a new object. Use symbols for fixed names/identifiers and strings for text data.

  • Memory: :name referenced 1000 times is the same single object (same object_id); "name" written 1000 times can be 1000 objects (unless frozen/deduped).

  • Mutability: Symbols are immutable and frozen; strings are mutable (<<, upcase!).

  • Comparison speed: Symbol equality is an identity check (fast); string equality compares contents.

  • Usage:

    • Symbols: hash keys, method names, fixed labels/states.

    • Strings: user input, text manipulation, anything that changes or is displayed.

  • Historical note: Symbols are garbage-collected since Ruby 2.2, so dynamically creating them is far less dangerous than it once was.

Q13.
Why would you use freeze on a Ruby object?

Junior

freeze makes an object immutable: any attempt to modify it raises FrozenError. You use it to protect shared state, declare true constants, and gain small performance/memory wins.

  • Enforce immutability: Constants in Ruby aren't truly immutable; CONFIG = {...}.freeze prevents accidental in-place changes.

  • Safe sharing: A frozen object can be shared across threads/objects without fear of one party mutating it underneath another.

  • Performance / memory: Frozen strings can be deduplicated and used as efficient hash keys.

  • Caveat: shallow freeze: freeze only freezes the object itself, not nested objects. A frozen array can still have mutable elements; deep-freeze each element if needed.

ruby

config = { host: "localhost" }.freeze config[:host] = "other" # raises FrozenError arr = ["a"].freeze arr << "b" # FrozenError arr[0] << "x" # works! element isn't frozen

Q14.
Why are symbols often preferred over strings as hash keys?

Junior

Symbols are preferred as hash keys because they are immutable and interned: the same symbol is one shared object, so it hashes consistently and compares quickly, making lookups fast and memory-efficient.

  • Single shared object: :id used as a key everywhere is the same object, while "id" string keys could allocate many objects.

  • Immutable and stable hash: A key's hash must not change. Symbols can't be mutated; a mutable string key could be altered and "lost" in the hash.

  • Faster comparison: Resolving collisions compares keys via identity for symbols rather than char-by-char for strings.

  • Idiomatic: Ruby/Rails conventions (keyword args, options hashes) use symbol keys; { name: "Ann" } is symbol-keyed sugar.

  • Note: String literal keys are auto-frozen when # frozen_string_literal: true is on, narrowing the gap, but symbols remain clearer and conventional.

Q15.
What is the difference between to_s and inspect?

Junior

to_s produces a human-readable string for display, while inspect produces a developer-facing, debug-oriented representation that ideally shows the object's internal state.

  • to_s is for users: Used by string interpolation and puts; meant to be clean and readable.

  • inspect is for developers: Used by p, the IRB/console, and arrays printed with p; shows quotes, structure, ivars.

  • Concrete differences: "hi".to_s is hi, but "hi".inspect is "hi" (with quotes); nil.to_s is empty while nil.inspect is "nil".

  • Override both for your classes: Default inspect lists instance variables; a custom one makes debugging much clearer.

Q16.
Explain the difference between public, private, and protected visibility in Ruby.

Junior

Visibility controls who may call a method. public methods can be called by anyone, private methods only from within the same instance (no explicit receiver, with a small exception), and protected methods can be called with an explicit receiver but only from objects of the same class or subclass.

  • public: The default; part of the object's external API, callable from anywhere.

  • private: Callable only without an explicit receiver, i.e. implicitly on self. Modern Ruby allows self.setter= calls as an exception.

  • protected: Callable with an explicit receiver as long as the caller is the same class or a relative; ideal for comparing two instances' internals.

  • Key nuance: Ruby visibility is about the receiver syntax, not the calling location, which differs from many other languages.

ruby

class Account def >(other) = balance > other.balance # ok: protected on same class protected def balance = @balance end

Q17.
Explain how the yield keyword works.

Junior

yield invokes the block that was implicitly passed to the current method, optionally passing it arguments and using its return value. It lets a method hand control back to caller-supplied code.

  • Calls the implicit block: Any block attached to the method call ({ } or do...end) is run where yield appears.

  • Passes and receives values: yield x passes x to the block; the expression evaluates to the block's return value.

  • Guard with block_given?: Calling yield without a block raises LocalJumpError, so check first if the block is optional.

ruby

def each_double(arr) return unless block_given? arr.each { |x| yield x * 2 } end each_double([1, 2]) { |n| puts n } # 2, 4

Q18.
Explain the difference between a block and a Proc.

Junior

A block is an anonymous chunk of code passed to a method; it is not an object on its own. A Proc is a block wrapped in an object that you can store in a variable, pass around, and call multiple times.

  • A block is syntax, not a value: Written with do...end or { }, a method may receive only one block, invoked with yield or a &block parameter.

  • A Proc is a first-class object: Created with Proc.new, proc { }, or lambda; stored, passed, and called with .call.

  • Conversion between them: Prefixing a parameter with & turns a passed block into a Proc inside the method, and & on a Proc turns it back into a block at the call site.

  • Two flavors of Proc: Lambdas check argument count strictly and return only from themselves; non-lambda procs are lenient and return from the enclosing method.

Q19.
Explain the difference between map and each. When would you use map!?

Junior

each iterates purely for side effects and returns the original collection, while map transforms each element and returns a new array of the results. Use map! when you want that transformation applied in place, mutating the original array.

  • each is for side effects: Run code per element (print, accumulate); the block's return value is ignored and each returns the receiver unchanged.

  • map is for transformation: Collects each block return value into a new array of the same length; the original is untouched.

  • map! mutates in place: Replaces each element with the block's result on the original array; use only when mutation is intended and safe.

  • Choosing: Need a new transformed collection: map. Just doing work: each. Want to overwrite the source: map!.

ruby

[1, 2, 3].each { |n| puts n } # => [1, 2, 3] (returns original) [1, 2, 3].map { |n| n * 2 } # => [2, 4, 6] (new array) a = [1, 2, 3] a.map! { |n| n * 2 } # a is now [2, 4, 6]

Q20.
Explain the roles of begin, rescue, else, and ensure in Ruby exception handling.

Junior

These keywords structure exception handling: begin marks the protected region, rescue handles raised errors, else runs only when no error occurred, and ensure always runs for cleanup.

  • begin: Opens the block whose code is monitored for exceptions (often implicit in a method/class body).

  • rescue:

    • Catches matching exceptions; you can specify a class and bind it: rescue ArgumentError => e.

    • Multiple rescue clauses are tried top to bottom; most specific first.

  • else: Runs only if the begin body raised nothing; keeps the no-error path out of the protected region.

  • ensure: Always executes (error or not, even on return): use for closing files, releasing locks, cleanup.

ruby

begin data = File.read(path) rescue Errno::ENOENT => e warn "missing: #{e.message}" else process(data) # only runs if read succeeded ensure log_attempt # always runs end

Q21.
Why is the Enumerable module considered the heart of Ruby, and what is the role of the spaceship (<=>) operator?

Mid

Enumerable is called the heart of Ruby because it provides a single, consistent iteration vocabulary that nearly every collection shares, built on just each. The spaceship operator <=> is what lets those methods order elements: it answers "how do two objects compare?"

  • Why it's central:

    • Idiomatic Ruby favors expressing intent (select, group_by) over hand-written loops.

    • It standardizes behavior across all enumerable types, so the same code reads the same everywhere.

  • The <=> operator:

    • Returns -1, 0, or 1 (or nil if not comparable).

    • sort, min, max, and sort_by all delegate ordering to it.

  • Pairs with Comparable: Define <=> and include Comparable to get <, >, ==, and between? for free.

ruby

class Version include Comparable attr_reader :n def initialize(n) = @n = n def <=>(other) = n <=> other.n end [Version.new(3), Version.new(1)].sort # uses <=>

Q22.
What does self refer to in different contexts within a Ruby program?

Mid

self is the current object: the implicit receiver of method calls and the default for instance variable access. What it points to depends on where the code is executing.

  • Inside an instance method: self is the instance the method was called on.

  • Inside a class/module body (but outside any method): self is the class or module object itself.

  • Inside a class method: self is the class (e.g. defined with def self.foo).

  • At the top level: self is main, a special instance of Object.

  • Why it matters: self.attr = x is needed to call a setter, since a bare attr = x creates a local variable instead.

ruby

class Foo puts self # Foo (class body) def bar self # the instance end def self.baz self # Foo (class method) end end

Q23.
What does respond_to? do and how does it relate to duck typing?

Mid

respond_to? asks whether an object has (and can respond to) a given method by name, returning true or false. It lets you check capability rather than class, which is the essence of duck typing.

  • Basic usage:

    • obj.respond_to?(:each) returns true if obj can receive each.

    • A second argument true also checks private/protected methods.

  • Relation to duck typing:

    • Duck typing cares what an object can do, not what it is: "if it quacks like a duck...".

    • respond_to? lets you branch on behavior instead of using is_a? / class checks, keeping code flexible.

  • Caveat: Methods caught by method_missing won't show up unless you also implement respond_to_missing?.

ruby

def print_all(obj) if obj.respond_to?(:each) obj.each { |x| puts x } else puts obj end end

Q24.
How does Pattern Matching (case...in) differ from a standard case...when statement?

Mid

case...when tests a value against conditions using ===, while case...in (pattern matching, stable in Ruby 3.0) deconstructs a value's structure and can bind parts of it to variables.

  • case...when matches by ===:

    • Each when calls clause === value (class, range, regex, etc.).

    • Good for simple value/type/range branching.

  • case...in matches by structure:

    • Destructures arrays and hashes and binds matched parts to local variables.

    • Supports find patterns, guards (if), and pinning (^) of existing values.

  • Failure behavior differs: case...when returns nil if nothing matches; case...in raises NoMatchingPatternError unless you add an else.

Q25.
Explain pattern matching in Ruby using case...in.

Mid

Pattern matching with case...in checks a value against structural patterns, deconstructing arrays and hashes and binding their parts to variables in one step. It is ideal for processing nested data like JSON or API responses.

  • Array patterns: in [a, b, *rest] matches by shape and binds elements, using * to capture the remainder.

  • Hash patterns: in {name:, age:} matches keys and binds their values to name and age.

  • Extra features:

    • Type checks: in Integer => n.

    • Guards: in [x, y] if x > y.

    • Pin operator: ^expected matches against an existing variable's value.

  • No-match handling: Add else to avoid a NoMatchingPatternError; there is also a one-line => / in form for single matches.

ruby

config = {name: "db", port: 5432} case config in {name: String => n, port: Integer => p} puts "#{n} on #{p}" in {name:} puts "only name: #{name}" else puts "no match" end

Q26.
What is the difference between ==, ===, eql?, and equal?

Mid

All four test equality but at different levels: == is general value equality, === is case/membership equality, eql? is strict value-and-type equality (used by hashes), and equal? is object identity.

  • ==: Value equality, overridden per class. 1 == 1.0 is true.

  • ===: "Case subsumption" used by case/when. For a class it means is_a?, for a Range it means inclusion, for a Regexp it means match.

  • eql?: Stricter value equality used together with hash for Hash keys. 1.eql?(1.0) is false (different types).

  • equal?: Identity: same object (same object_id). Should never be overridden.

ruby

1 == 1.0 # => true 1.eql?(1.0) # => false Integer === 5 # => true (5.is_a?(Integer)) a = "x"; a.equal?(a) # => true "x".equal?("x") # => false (two objects)

Q27.
What is the purpose of the # frozen_string_literal: true magic comment?

Mid

The # frozen_string_literal: true magic comment makes every string literal in that file frozen (immutable), which saves memory by reusing identical strings and prevents accidental mutation.

  • Where it goes: On the first line of the file (after a shebang if present); it is file-scoped.

  • Benefits:

    • Performance/memory: identical literals like "hello" can be deduplicated into one object instead of allocating per evaluation.

    • Safety: mutating a literal raises FrozenError, catching unintended shared-state bugs.

  • Gotcha: If you need a mutable string, build it explicitly with String.new, +"str", or dup.

  • Future direction: Frozen string literals are the planned default in future Ruby, so adding it now eases migration.

Q28.
Hash vs. Array: which is faster for lookups and why?

Mid

For looking up a value by key, a Hash is much faster: it offers roughly O(1) access, while finding a value in an Array by scanning is O(n). Arrays only win when you access by known integer index, which is also O(1).

  • Hash lookup is O(1) average: It hashes the key to compute a bucket, so size barely affects lookup time.

  • Array search is O(n): include? or find must walk elements one by one until a match.

  • Array index access is O(1): arr[5] is instant because it jumps to a memory offset; this is positional, not value-based, lookup.

  • Rule of thumb: Need membership/keyed lookup, use a Hash (or Set). Need ordered, index-addressed data, use an Array.

Q29.
What is the difference between dup and clone in Ruby?

Mid

Both create a shallow copy of an object, but clone preserves more of the original's state: it copies the frozen status and the singleton class, while dup does not.

  • Frozen state: clone copies the frozen status (a clone of a frozen object is frozen); dup always returns an unfrozen copy.

  • Singleton class / singleton methods: clone copies the singleton class (so singleton methods carry over); dup does not.

  • Both are shallow: Instance variables are copied by reference, so nested objects are shared. Use a deep-copy technique (e.g. Marshal) if you need full independence.

  • Customization: Both call initialize_copy; clone accepts freeze: false to override frozen copying.

ruby

original = "hi".freeze original.clone.frozen? # => true original.dup.frozen? # => false

Q30.
How does memoization work in Ruby, and what does the ||= operator do?

Mid

Memoization caches the result of an expensive computation so it runs once and later calls reuse the stored value. The ||= operator is the idiomatic tool: it assigns only if the variable is currently nil or false.

  • What ||= expands to: a ||= b means a || (a = b): evaluate the right side only when a is falsy.

  • How memoization uses it: @value ||= compute stores the result on first call and returns the cached ivar afterward.

  • The falsy trap: If the real result is nil or false, it recomputes every time. Use defined? or an explicit key check instead.

  • Multi-line bodies: Wrap in begin ... end or use @value ||= (a; b) to memoize a block of work.

ruby

def expensive @expensive ||= begin do_heavy_work end end # Safe for nil/false results: def flag return @flag if defined?(@flag) @flag = compute_flag end

Q31.
What do the tap and then (yield_self) methods do and when would you use them?

Mid

Both come from Kernel and aid method chaining, but they differ in what they return: tap yields the object and returns the same object, while then (aliased yield_self) yields the object and returns the block's result.

  • tap: side effects, returns self: Great for inspecting/logging or mutating an object mid-chain without breaking the chain.

  • then / yield_self: transform, returns block value: Wraps a value in a transformation so you can pipe it through expressions left-to-right.

  • then and nil handling: Combined with the safe-navigation idiom, it helps express "do this only if present" pipelines cleanly.

ruby

# tap: peek/mutate, returns the same object user = User.new.tap { |u| u.name = "Ada" } # then: transform, returns the block result result = "42".then { |s| Integer(s) } * 2 # => 84

Q32.
Explain the difference between include, extend, and prepend.

Mid

All three mix a module into a class, but they differ in where the module lands in the ancestors chain and which methods it affects. include adds instance methods, prepend adds them ahead of the class, and extend adds them as singleton (class-level) methods.

  • include: Inserts the module just above the class in the ancestors chain, so its methods become instance methods (overridable by the class).

  • prepend: Inserts the module below the class, so its methods take precedence and can wrap the original via super (useful for decorators).

  • extend: Adds the module's methods to a single object's singleton class; on a class it makes them class methods.

  • Quick mental model: include = below the receiver in lookup, prepend = above it, extend = onto the object itself.

ruby

module M; def hi; "M"; end; end class C; include M; end class D; prepend M; def hi; "D"; end; end C.new.hi # => "M" (class has none, falls to module) D.new.hi # => "M" (prepend wins over the class method)

Q33.
What does the super keyword do, and how does it differ when called with vs. without parentheses?

Mid

super calls the method of the same name in the next ancestor up the method resolution chain. The argument passing differs critically based on whether you use parentheses.

  • super (bare, no parentheses):

    • Forwards the same arguments the current method received, automatically.

    • Reflects the original arguments even if local variables were reassigned (it uses the method's parameters).

  • super() (with empty parentheses): Calls the parent method with no arguments explicitly.

  • super(a, b) (with explicit arguments): Passes exactly the arguments you specify.

  • Follows the ancestor chain: Resolves through ancestors, so it can hit an included module's method, not just a superclass.

ruby

class Base def greet(name) = "Hi #{name}" end class Child < Base def greet(name) name = "X" super # passes original name, not "X" super() # ArgumentError: wrong number of args super(name) # passes "X" end end

Q34.
How are modules used as namespaces in Ruby?

Mid

A module acts as a namespace by wrapping classes, methods, and constants so their fully qualified names are unique and don't collide with code elsewhere.

  • Grouping related code: Define classes/constants inside a module; access them via ModuleName::ClassName.

  • Avoiding name collisions: Two libraries can both define a Parser class if each is namespaced under its own module.

  • Nesting matters for constant lookup: Code written inside the module body sees sibling constants without the prefix.

  • Modules differ from classes here: A namespace module isn't instantiated; it just organizes. (Modules also serve as mixins, a separate role.)

ruby

module Billing TAX = 0.2 class Invoice def tax = TAX # sees sibling constant end end Billing::Invoice.new.tax # => 0.2

Q35.
What is the Comparable module and how does it work with the spaceship operator?

Mid

Comparable is a mixin that gives a class a full set of comparison methods (<, <=, ==, >=, >, between?, clamp) for free, all derived from one method you define: the spaceship operator <=>.

  • You implement <=>: It returns -1 if self is less, 0 if equal, 1 if greater, and nil if not comparable.

  • Comparable derives the rest: Each comparison method calls <=> and interprets its result.

  • Bonus methods: You also get between? and clamp automatically.

ruby

class Version include Comparable attr_reader :num def initialize(num) = @num = num def <=>(other) = num <=> other.num end Version.new(1) < Version.new(2) # => true

Q36.
What is the difference between a Proc and a Lambda?

Mid

Both are callable closures, but lambdas behave more like methods: they check argument counts strictly and their return exits only the lambda, whereas a proc is lenient and its return returns from the enclosing method.

  • Argument checking: Lambdas raise ArgumentError on wrong arity; procs ignore extras and fill missing args with nil.

  • Return behavior:

    • return in a lambda returns from the lambda itself.

    • return in a proc returns from the method that defined the proc (can raise LocalJumpError if that method already returned).

  • Both are Proc objects: lambda? distinguishes them; a lambda is just a Proc with stricter semantics.

ruby

l = ->(x) { return x * 2 } l.call(3) # => 6, ArgumentError if no arg p = Proc.new { |x| x.to_i * 2 } p.call # => 0, missing arg becomes nil

Q37.
Explain how yield works and how it differs from calling a block via &block.call.

Mid

Both run a caller's block, but yield works with an implicit block and is faster, while capturing it as &block turns it into a named Proc object you can store, pass on, or call with block.call.

  • yield uses the implicit block:

    • No need to name it in the parameter list; lighter weight (no Proc object allocated).

    • Cannot be stored or forwarded to another method.

  • &block reifies the block as a Proc:

    • The & in the parameter converts the block into a Proc bound to block.

    • You can pass it onward (other_method(&block)), store it, or check it as a truthy value.

  • Practical guidance: Use yield for the common case; use &block when you must capture or forward the block.

ruby

def with_yield = yield(10) def with_block(&block) block.call(10) # explicit Proc pass_along(&block) # can forward it end

Q38.
What is a closure, and how does Ruby maintain the scope of variables inside a block?

Mid

A closure is a function-like object that captures the variables from the lexical scope where it was defined, keeping them alive even after that scope has returned. In Ruby, blocks, Procs, and lambdas are closures: they remember the binding (local variables, self) in effect when they were created.

  • Captures by reference, not by copy: A block sees and can mutate the same local variables that existed where it was written, so changes persist after the block runs.

  • Lexical (static) scoping: What a block can access is determined by where it is written, not where it is called.

  • The binding keeps variables alive: Ruby stores the surrounding environment in a Binding object attached to the closure, so the variables aren't garbage collected while the closure exists.

  • Blocks don't introduce a new scope for outer variables: Variables defined outside remain visible inside; a variable first assigned inside the block stays local to it.

ruby

def counter count = 0 increment = -> { count += 1 } # closes over `count` increment.call # 1 increment.call # 2 count # 2 (same variable mutated) end

Q39.
When would you use a Proc instead of a block?

Mid

Use a Proc (or lambda) when you need to treat the behavior as data: store it, reuse it, pass several callables, or capture a block now and run it later. A plain block is fine for the common case of passing one piece of code to a single method call.

  • You need more than one callable: A method can take only one block, so pass extra behavior as Proc arguments.

  • You want to reuse the same logic: Assign it to a variable once and pass it to many methods instead of repeating a block.

  • You need to store or defer execution: Keep a callback in an instance variable or collection and invoke it later with .call.

  • You want strict argument or return semantics: Reach for a lambda when you want arity checking and a local return.

ruby

double = ->(x) { x * 2 } [1, 2, 3].map(&double) # reuse the same callable [10, 20].map(&double) # again, no repeated block

Q40.
What does the &:symbol idiom (symbol-to-proc) do, and how does it work under the hood?

Mid

The &:symbol idiom is shorthand for calling a single method on each element: map(&:upcase) is equivalent to map { |x| x.upcase }. It works because & coerces its operand into a block, and Symbol defines to_proc.

  • & triggers to_proc: When you write &object at a call site and the object isn't already a Proc, Ruby calls object.to_proc to build one.

  • Symbol#to_proc builds the proc: It returns a proc that takes a receiver and sends the symbol to it, roughly proc { |x, *args| x.public_send(:upcase, *args) }.

  • Then it becomes the block: That proc is passed to the method as its block, so map yields each element into it.

  • Limitation: It only calls a method with no arguments on each element; needing arguments or multiple operations requires a real block.

ruby

%w[a b c].map(&:upcase) # => ["A", "B", "C"] # conceptually equivalent: upcaser = :upcase.to_proc %w[a b c].map { |s| upcaser.call(s) }

Q41.
How do splat (*) and double splat (**) operators work in method arguments?

Mid

The splat * collects any number of positional arguments into an Array, and the double splat ** collects keyword arguments into a Hash. Both also work in reverse to expand a collection back into arguments at a call site.

  • * in a definition gathers positionals: def f(*args) makes args an Array of all positional arguments.

  • ** in a definition gathers keywords: def f(**opts) makes opts a Hash of all keyword arguments.

  • They also splat at the call site: *array spreads an Array into positional args; **hash spreads a Hash into keyword args.

  • Forwarding everything: Combine *args, **kwargs, &block (or ... in Ruby 2.7+) to pass all arguments through to another method.

ruby

def log(*args, **opts) puts args.inspect # positional -> Array puts opts.inspect # keyword -> Hash end log(1, 2, level: :info) # [1, 2] # {:level=>:info} nums = [1, 2, 3] [*nums, 4] # => [1, 2, 3, 4] (expand)

Q42.
What is the difference between positional arguments and keyword arguments in Ruby methods?

Mid

Positional arguments are matched to parameters by their order, while keyword arguments are matched by name. Since Ruby 3.0 the two are fully separated: a Hash is no longer silently converted into keywords.

  • Positional: order matters: def f(a, b) requires callers to remember which value goes where; easy to mix up with many params.

  • Keyword: name matters: def f(a:, b:) lets callers pass f(b: 2, a: 1) in any order; self-documenting at the call site.

  • Defaults and required-ness: Both can have defaults; a keyword with no default (a:) is required by name.

  • Ruby 3 separation: Passing a Hash where keywords are expected now needs explicit **; positional and keyword args no longer auto-convert.

ruby

def connect(host, port:, timeout: 30) # host is positional; port/timeout are keywords end connect("db.local", port: 5432) connect("db.local", timeout: 5, port: 5432) # order-free keywords

Q43.
Explain the new Data class introduced in Ruby 3.2 and how it differs from Struct.

Mid

Data is a class introduced in Ruby 3.2 for defining simple, immutable value objects. It is conceptually a modern, locked-down Struct: instances cannot be mutated after creation, which makes it safer for representing plain values.

  • Immutable by design: There are no setters; to "change" a value you create a copy with with, e.g. point.with(y: 5).

  • Flexible construction: Instances can be built with positional or keyword arguments via new, with keywords reading clearly.

  • How it differs from Struct:

    1. Struct is mutable (has setters); Data is frozen-like and read-only.

    2. Struct supports index access (s[0]) and acts somewhat like an array; Data focuses on named attributes only.

    3. Data expresses intent: "this is a value object", avoiding accidental mutation bugs.

  • Shared features: Both auto-generate ==, to_h, and accessors, and support a block to add custom methods.

ruby

Point = Data.define(:x, :y) do def distance = Math.sqrt(x**2 + y**2) end p = Point.new(x: 3, y: 4) p.x # => 3 p.distance # => 5.0 p2 = p.with(y: 0) # copy with a change; p is unchanged # p.x = 10 # NoMethodError: immutable

Q44.
When would you use a Struct or Data (Ruby 3.2+) instead of a standard Class?

Mid

Reach for Struct or Data when you mainly need a lightweight value object that bundles a few named attributes, getting accessors, ==, and a readable inspect for free. Use a full class when behavior and invariants dominate the design.

  • Struct: mutable value object:

    • Generates a class with accessors, ==, to_a, and keyword/positional init.

    • Attributes are writable, so use it when state may change after creation.

  • Data (Ruby 3.2+): immutable value object:

    • Built via Data.define(:x, :y); instances are frozen, with no setters.

    • Update with #with(x: 1) which returns a new copy; ideal for thread-safe, hashable values.

  • When a full class wins: Rich behavior, validation in initialization, complex invariants, or deep inheritance hierarchies.

  • Rule of thumb: Immutable data holder, Data; mutable simple bundle, Struct; behavior-heavy, plain class.

ruby

Point = Data.define(:x, :y) p = Point.new(x: 1, y: 2) p.with(y: 9) # => #<data Point x=1, y=9> (new frozen copy) p.x = 5 # NoMethodError: immutable

Q45.
How does inject (reduce) work in Ruby, and how do you use it?

Mid

inject (aliased reduce) folds a collection into a single value by repeatedly applying a binary operation, carrying an accumulator from one element to the next. Each iteration the block returns the new accumulator.

  • How the accumulation flows:

    • The block receives (memo, element) and its return value becomes the next memo.

    • The final memo is the return value.

  • Initial value handling:

    • With an argument, inject(0) starts memo at 0.

    • Without one, the first element becomes the initial memo and iteration starts at the second.

  • Symbol shorthand: inject(:+) applies an operator/method between elements; combine with a seed: inject(1, :*).

  • Gotcha: On an empty collection with no initial value it returns nil; pass a seed to be safe.

ruby

[1, 2, 3, 4].inject(:+) # => 10 [1, 2, 3, 4].inject(10, :+) # => 20 # Build a hash with explicit accumulator %w[a bb ccc].inject({}) { |h, s| h[s] = s.length; h } # => {"a"=>1, "bb"=>2, "ccc"=>3}

Q46.
What is the difference between throw/catch and raise/rescue?

Mid

raise/rescue is for error handling: signaling and recovering from exceptional conditions. throw/catch is a control-flow mechanism (a labeled, non-local jump) for breaking out of deeply nested loops or blocks, not for representing errors.

  • raise / rescue:

    • Raises an Exception object representing a real error condition.

    • Carries a class, message, and backtrace; meant for things that went wrong.

  • throw / catch:

    • Use a symbol (or any object) as a label to jump out: catch(:done) { ... throw :done, value }.

    • Optionally returns a value from the catch block; no error semantics, no backtrace cost.

  • When to choose which:

    • Genuine error or failure: raise.

    • Escaping nested iteration cleanly (rare): throw/catch.

ruby

result = catch(:found) do matrix.each do |row| row.each { |x| throw :found, x if x.negative? } end nil end

Q47.
How do you define a custom exception in Ruby, and why should you inherit from StandardError rather than Exception?

Mid

Define a custom exception by subclassing StandardError (directly or via a domain base class). Inherit from StandardError rather than Exception because a bare rescue only catches StandardError and its descendants, so your error gets handled normally instead of slipping through.

  • How to define one:

    • Subclass and optionally add custom data or a default message.

    • A common pattern: one app-wide base error, with specific errors inheriting from it so callers can rescue broadly or narrowly.

  • Why not Exception:

    • Exception is the root and includes things you should not normally catch: SignalException, NoMemoryError, SystemExit.

    • Catching at that level can swallow Ctrl-C and interpreter shutdown, hiding serious problems.

  • Payoff: rescue => e (which defaults to StandardError) and rescue YourError both behave as expected.

ruby

class AppError < StandardError; end class PaymentError < AppError attr_reader :amount def initialize(amount, msg = "payment failed") @amount = amount super(msg) end end raise PaymentError.new(50, "card declined")

Q48.
What does the retry keyword do in exception handling?

Mid

retry is used inside a rescue clause to re-run the begin block from the top, giving the operation another attempt. It is the standard way to handle transient failures like network timeouts.

  • What it does:

    • Jumps back to the start of the protected block and executes it again from scratch.

    • Only valid within a rescue; using it elsewhere raises a SyntaxError.

  • Must have a stop condition:

    • Without a counter or limit it loops forever on a persistent error.

    • Track attempts and re-raise once exhausted; add a backoff (sleep) for external services.

ruby

attempts = 0 begin attempts += 1 call_flaky_api rescue Net::ReadTimeout retry if attempts < 3 raise # give up after 3 tries end

Q49.
What is 'Monkey Patching' (Open Classes) and why is it considered dangerous?

Mid

Monkey patching exploits Ruby's open classes: any class (even core ones like String or Array) can be reopened at runtime to add or redefine methods. It's powerful but dangerous because changes are global and invisible.

  • Open classes: Reopening a class with class String again merges new methods into the existing class instead of replacing it.

  • Why it's dangerous:

    • Global scope: a patch applied anywhere affects every object of that class across the whole program.

    • Silent collisions: two libraries patching the same method silently clobber each other; last load wins.

    • Hard to debug: the method's definition isn't where the class is declared, so source-jumping fails.

    • Brittle: a future stdlib/gem update can add a real method with the same name and break assumptions.

  • Use sparingly: prefer it for genuine cross-cutting fixes, and isolate patches in clearly named files.

Q50.
How does define_method differ from using the def keyword?

Mid

Both create methods, but define_method is a runtime metaprogramming call that takes a block (a closure), while def is a keyword that defines a method with its own fresh scope.

  • Scope handling:

    • def opens a new scope: local variables outside it are NOT visible inside.

    • define_method takes a block that closes over the surrounding scope, so it can capture local variables.

  • Dynamic naming: define_method accepts a method name as a symbol/string computed at runtime, perfect for generating many methods in a loop.

  • Performance: def is marginally faster (no closure), and is the default for normal code.

ruby

multiplier = 3 class Calc # def can't see `multiplier` define_method(:triple) { |n| n * multiplier } # closure captures it end Calc.new.triple(5) # => 15

Q51.
What is 'Monkey Patching,' and what are the safer alternatives provided by modern Ruby?

Mid

Monkey patching is reopening an existing class to add or change methods at runtime; because the change is global and silent, modern Ruby offers more contained alternatives: refinements, modules with prepend, and plain composition.

  • The core problem: Patches are program-wide and can collide with other gems or future library versions.

  • Safer alternatives:

    1. Refinements: scope a change with refine and activate it only where using is called, so it's lexically contained.

    2. Module + prepend: wrap an existing method and call super, instead of overwriting it, preserving the original.

    3. Composition/decoration: wrap the object in your own class rather than altering the original at all.

ruby

# Wrap, don't clobber, with prepend module Logging def save puts "saving..." super end end class Record prepend Logging def save; end end

Q52.
How does the send method work and when would you use it?

Mid

send invokes a method by its name given as a symbol or string, passing along any arguments and block. It's the dynamic-dispatch tool: useful when the method to call is decided at runtime.

  • How it works:

    • obj.send(:method_name, arg1, arg2) is equivalent to obj.method_name(arg1, arg2).

    • It bypasses access control: send can call private and protected methods.

    • Use public_send to respect visibility.

  • When to use:

    • Dynamic dispatch: choosing a method from a variable, config value, or user-mapped action.

    • Metaprogramming and DSLs where method names are generated.

ruby

action = params[:sort_by] # e.g. "name" records.sort_by { |r| r.public_send(action) }

Q53.
How does the send method work and what are its security implications?

Mid

send calls a method named by a symbol/string at runtime, forwarding args and blocks. Its security concern is that it ignores method visibility (it can invoke private methods), so passing user input to it can expose internals.

  • How it works: obj.send(:name, *args, &block) performs dynamic dispatch equivalent to calling that method directly.

  • Security implications:

    • It bypasses private/protected access control.

    • Passing an untrusted string (e.g. obj.send(params[:method])) lets attackers invoke arbitrary methods like destroy or internal helpers.

  • Mitigations:

    • Prefer public_send, which respects visibility.

    • Whitelist allowed method names before dispatching on user input.

Q54.
Explain the difference between send and public_send.

Mid

Both invoke a method dynamically by name, but send bypasses method visibility while public_send respects it, only calling public methods.

  • send ignores access control: Can call private and protected methods, which is powerful but breaks encapsulation if misused.

  • public_send enforces visibility: Raises NoMethodError when you try to call a private/protected method, matching normal dot-call rules.

  • Rule of thumb: Prefer public_send for dispatching on user/external input; reserve send for deliberate internal access to non-public methods (tests, metaprogramming).

ruby

class Account private def secret = 42 end a = Account.new a.send(:secret) # => 42 a.public_send(:secret) # => NoMethodError (private method)

Q55.
What is the difference between Object, Kernel, and BasicObject?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q56.
How does Ruby's Garbage Collector work?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q57.
What is a singleton class (or eigenclass) in Ruby, and when is it created?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q58.
How does Ruby's method lookup work? Walk me through the ancestors chain.

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q59.
How does constant lookup and scoping work in Ruby?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q60.
Why does Ruby favor mixins over multiple inheritance, and how do modules enable this?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q61.
What are Lazy Enumerators, and in what scenario would they prevent a memory overflow?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q62.
What are the tradeoffs of using method_missing for dynamic dispatch?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q63.
Compare instance_eval and class_eval. When would you use each?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q64.
When would you use method_missing vs. define_method?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q65.
What are Refinements and how do they solve the problems of Monkey Patching?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q66.
Explain the difference between Threads, Fibers, and Ractors. When is each appropriate?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q67.
What is the Global VM Lock (GVL), and how does it impact multi-threaded Ruby applications?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q68.
What is M:N scheduling in Ruby 3.3+, and how does it improve thread performance?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q69.
How does Ruby's Generational Garbage Collector work? Explain the Mark-and-Sweep process.

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q70.
What is YJIT, and how does it optimize Ruby code at runtime?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q71.
How do you ensure thread safety in Ruby?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q72.
How does a Ractor achieve true parallelism in Ruby 3?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.