Ruby
04 / 07

Metaprogramming & Interview Questions

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
end

Symbol#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   # FrozenError

Interview 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