Ruby: Metaprogramming & Interview Questions
Metaprogramming Basics
Ruby's metaprogramming capabilities allow code to write code at runtime. This powers Rails magic like has_many, validates, and attr_accessor.
# define_method — create methods dynamically
class User
['admin', 'moderator', 'guest'].each do |role|
define_method("#{role}?") do
self.role == role
end
end
end
user.admin? # true/false
# method_missing — intercept unknown method calls
class DynamicProxy
def initialize(target)
@target = target
end
def method_missing(name, *args, &block)
if @target.respond_to?(name)
@target.send(name, *args, &block)
else
super
end
end
def respond_to_missing?(name, include_private = false)
@target.respond_to?(name) || super
end
end
# open classes — reopen and extend any class
class String
def palindrome?
self == self.reverse
end
end
"racecar".palindrome? # true
# send — call method by name (even private)
user.send(:name)
user.public_send(:name) # raises error if private
# eval, class_eval, instance_eval
String.class_eval do
def shout
upcase + "!!!"
end
endSymbol#to_proc
# Common shorthand using &
[1, 2, 3].map(&:to_s) # ["1", "2", "3"]
["alice", "bob"].map(&:upcase) # ["ALICE", "BOB"]
[1, nil, 2, nil].compact # remove nils
[1, nil, 2, nil].select(&:itself) # same
# Equivalent to:
[1, 2, 3].map { |n| n.to_s }
# tap — for debugging in method chains
user.tap { |u| puts u.inspect }
.update!(name: "Alice")
.tap { |u| puts "Updated: #{u.name}" }Frozen Objects & Performance
# String literals are mutable by default — freeze for safety and performance
str = "hello".freeze
str << " world" # FrozenError
# frozen_string_literal magic comment (Ruby 2.3+)
# frozen_string_literal: true
# All string literals in this file are frozen
# Symbols are always frozen and deduplicated
:hello.frozen? # true
:hello.object_id == :hello.object_id # true
# Object#freeze
arr = [1, 2, 3].freeze
arr << 4 # FrozenErrorInterview Questions
What is the difference between nil, false, and undefined in Ruby? nil and false are falsy; everything else is truthy. Undefined variables raise NameError.
Difference between Symbol and String? Symbols are immutable, interned (same object_id for same value), faster for comparisons and hash keys. Strings are mutable, each literal is a new object.
What does &method(:method_name) do? Converts a method reference to a block — useful for passing existing methods to enumerables: [1,2,3].map(&method(:puts))
Explain Ruby's object model: Everything inherits from BasicObject. Classes are objects (instances of Class). Modules are included in the method lookup chain (ancestors).
What is a Proc vs Lambda? Both are callable objects. Lambda checks argument count and returns from itself. Proc doesn't check arity and return exits the enclosing method.
What is Comparable? A mixin that requires <=> and provides <, <=, >, >=, between?, clamp for free.
How does Ruby handle multiple inheritance? Ruby doesn't have it — uses modules (mixins) included in order. The lookup chain (ancestors) determines which method is found first.
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free