skip to content

Metaprogramming Techniques

Ruby code that writes and inspects code at run time: dynamic calls, generated methods, ghost methods, eval contexts, singleton classes and hooks. Interviewers probe when reaching for it is a mistake.

on this pageshow

explore

questions

page 1 of 2

In Ruby, why does obj.methods return dozens of names even for a tiny class, and how do you narrow that list while debugging?

level: juniorimportance: must knowfreq 50%

answer

  1. public and protected only
  2. every ancestor counts
  3. minus Object.instance_methods
  4. grep with a regexp
  5. private_methods for the rest

basics

~10 s

Kernel#methods lists every public and protected method the object responds to, including everything inherited from Object, Kernel and BasicObject. Subtract Object.instance_methods, grep for a pattern, or ask the class for instance_methods(false).

solid answer

~30 s

`obj.methods` returns the names, as symbols, of the public and protected methods reachable from the object: its class, every superclass, every included module and the object's own singleton methods. Most of that list is the shared base: `Object`, `Kernel` and `BasicObject` contribute dozens of names such as `inspect`, `frozen?` and `instance_variable_get`. To see what is specific, subtract the shared part with `obj.methods - Object.instance_methods`, filter with `obj.methods.grep(/total/)`, and `sort` the result. `public_methods` drops the protected ones, while `private_methods` lists what `methods` leaves out, such as helpers marked `private`. None of these lists shows methods that only exist through `method_missing`.

code

ruby · 22 lines
ruby
module Printable
  def print_me = puts(inspect)
end

class Base
  def base_info = "base"
end

class Invoice < Base
  include Printable
  attr_accessor :total
  protected def compare_key = total
  private def recalc = nil
end

inv = Invoice.new
inv.methods.size > 50                          # => true
(inv.methods - Object.instance_methods).sort
# => [:base_info, :compare_key, :print_me, :total, :total=]
inv.methods.grep(/total/)                      # => [:total, :total=] (order may vary)
inv.methods.include?(:recalc)                  # => false: private
inv.private_methods.include?(:recalc)          # => true

go deeper

for a junior

Recall that methods lists public and protected methods from the whole ancestor chain, and that subtracting Object.instance_methods and using grep narrow it quickly.

for a middle

Explain which listing covers which visibility, why ghost methods never appear, and when to switch to the class-side listings.

for a senior

Use these listings as a console exploration tool on unfamiliar objects, and keep them out of production capability checks.

for a principal

Encourage debugging habits that start from introspection in a console before reading source, since it reveals what the running process really loaded.

## What methods actually returns `methods` is defined in `Kernel`, so every ordinary object has it. Its documentation is precise: it returns *the names of public and protected methods* of the object, *including all the methods accessible in the object's ancestors*. The result is an `Array` of `Symbol`s. That definition explains the size. Even a class with one method inherits from `Object`, which mixes in `Kernel`, which sits above `BasicObject`. Every public method along that chain — `inspect`, `dup`, `frozen?`, `is_a?`, `instance_variables`, `tap`, `then` and many more — is part of the answer. The list for a nearly empty object therefore runs to dozens of entries before your own code adds anything. ## The related listings | Call | What it returns | |---|---| | `obj.methods` | public and protected methods from the whole chain | | `obj.public_methods` | public methods from the whole chain | | `obj.protected_methods` | protected methods only | | `obj.private_methods` | private methods, including `Kernel` helpers such as `puts` | | `Klass.instance_methods` | what `methods` would list for an instance, asked of the class | | `Klass.instance_methods(false)` | only the ones defined in `Klass` itself | The class-side listings, and what exactly counts as "defined in the class", matter most when you generate documentation; for debugging, the instance-side ones are usually what you reach for first. ## Narrowing the list 1. **Subtract the shared base.** `obj.methods - Object.instance_methods` removes everything every object has. What remains is the object's own class, its superclasses below `Object` and any modules they include. 2. **Filter by name.** `obj.methods.grep(/price/)` works because `Enumerable#grep` matches each symbol against the regexp. It is the fastest way to answer "is there a method about prices?" in a console. 3. **Sort.** `sort` makes the list readable and stable between runs. 4. **Ask the class.** `obj.class.instance_methods(false)` lists only the methods the object's class defines itself, without superclasses or mixins. ## What the lists never show - **Private methods.** `methods` excludes them by definition. A helper marked `private` shows up only in `private_methods` or `private_instance_methods`. - **Ghost methods.** A name handled by `method_missing` is not a method at all, so no listing contains it, even though a call to it succeeds. That is also why a class with such names should implement `respond_to_missing?`. - **Anything not defined yet.** The list is a snapshot taken at the moment of the call; code loaded later, or methods generated on first use, are not there. ## Instance side versus class side The same information can be requested from the object or from its class, and the two answers differ in one respect: - `obj.methods` includes the object's **singleton methods**, the ones defined on that one object only, because they are part of what it responds to. - `obj.class.instance_methods` describes what *every* instance of the class responds to, so per-object methods are absent. - Both include superclasses and mixins unless you pass `false` to the class-side call. In practice: ask the object when you are debugging one specific value, and ask the class when you want to know what a type offers. A difference between the two lists is itself a clue that something extended or decorated this particular object. ## Checking one name instead of listing all Listing is for exploration. When code needs to know whether a call will work, it asks about the one name it cares about rather than searching an array, and that check has its own rules. ## Mistakes to avoid - Treating `methods` as "the methods this class defines"; it covers the whole chain. - Expecting private helpers to appear in `methods`. - Concluding that a method does not exist because `methods` lacks it, when it is served by `method_missing`. - Subtracting `Object.instance_methods` and expecting superclass and mixin methods to disappear too; only the shared base goes away. - Using `obj.methods.include?(:x)` in production code as a capability check, which is slow and misses private and ghost methods. ## The takeaway `methods` answers "what can I call on this object from outside?" across the whole ancestor chain, which is why it is long. Subtract `Object.instance_methods`, grep and sort, and switch to the class-side listings when you want what one class itself defines.

  • After obj.methods - Object.instance_methods, why do methods from a superclass and an included module still appear?
    The subtraction removes only what every object shares, the methods reachable from `Object`. Methods from superclasses below `Object` and from modules those classes include are not in that set, so they stay. To see only what the object's class itself defines, ask `obj.class.instance_methods(false)`.
  • Does obj.methods include the object's singleton methods?
    Yes. A method defined on that one object is public by default and part of what the object responds to, so `methods` lists it alongside the inherited ones.

Asking obj.methods is like asking a new employee what they can do and receiving the whole company handbook: their own duties plus every policy all staff share. Removing the pages every employee has leaves their department's duties and their own, not only their own.

saying these in an interview costs you the question

  • obj.methods lists only the methods the object's class defines
  • Private helpers appear in obj.methods like any other method
  • A method missing from obj.methods cannot be called on the object
  • obj.methods - Object.instance_methods leaves only the class's own methods
  • obj.methods returns strings that need converting before comparison
open as a page

In Ruby, what does writing `class String` with a new method do when String already exists, and which code sees that method?

level: juniorimportance: must knowfreq 60%

basics

~20 s

Writing class String again reopens the existing String class instead of creating one, so the def adds a method to String itself. Every string in the process, including older strings and strings inside gems, can then call it.

open as a page

In Ruby, how do you call a method whose name is only known at run time, and how do send and public_send differ?

level: juniorimportance: must knowfreq 68%

basics

~20 s

Pass the name as a Symbol or String to send or public_send, with any arguments after it. send calls the method even if it is private or protected; public_send calls only public methods and raises NoMethodError otherwise.

open as a page

In Ruby, what does def gateway.charge(amount) do inside a test, and why do other Gateway instances keep the original charge method?

level: juniorimportance: must knowfreq 55%

basics

~10 s

def gateway.charge defines a singleton method: it is stored in gateway's own singleton class, which Ruby checks before Gateway, so only that object uses it. Every other Gateway instance still finds Gateway#charge.

open as a page

In Ruby, how does define_method differ from def in how the method name is chosen and what the method body can see?

level: middleimportance: must knowfreq 55%

basics

~20 s

def takes a literal name and starts a fresh local scope. define_method takes the name as a Symbol or String value and uses a block as the body, so the body is a closure that sees the defining scope's local variables.

open as a page

In a Ruby block DSL, what changes when the library runs your block with instance_eval instead of yielding a builder object to it?

level: middleimportance: must knowfreq 50%

basics

~20 s

With instance_eval, self inside the block becomes the DSL object, so bare calls like threshold 90 reach it, but the caller's instance variables and helper methods do not. Yielding a builder keeps self and makes calls explicit: r.threshold 90.

open as a page

In Ruby, why should a class that overrides method_missing also define respond_to_missing?, and what breaks without it?

level: middleimportance: must knowfreq 55%

basics

~20 s

respond_to? and method consult respond_to_missing? for names not in any method table. Without it, a ghost method works when called but respond_to? says false and method(:name) raises NameError, so code that checks before calling skips a working object.

open as a page

In Ruby, how do you write method_missing for a settings object that answers any config key as a method, and why must it call super?

level: middleimportance: must knowfreq 65%

basics

~20 s

Override method_missing to return the stored value when the name is a known key, handle key= writers, and call bare super for every other name. Without super, typos return nil silently instead of raising NoMethodError.

open as a page

In Ruby, how would you auto-register every subclass of a ReportFormat base class, and when is Class#subclasses enough instead of an inherited hook?

level: middleimportance: must knowfreq 40%

basics

~20 s

Override self.inherited(subclass) on ReportFormat, call super, and append the subclass to a registry; it fires at class Csv < ReportFormat, before Csv's body runs. Class#subclasses (Ruby 3.1+) returns only direct, still-alive subclasses, with no extra metadata.

open as a page

In Ruby, an API-docs generator must list only the public methods Widget itself defines — which call gives that, and what does it leave out?

level: middleimportance: must knowfreq 45%

basics

~10 s

Widget.public_instance_methods(false) lists the public instance methods defined in Widget itself. It leaves out superclass and mixin methods, private and protected ones, class methods, and names served only by method_missing.

open as a page

In Ruby, when two gems both reopen Integer to define minutes, which definition wins, and how would you notice the collision?

level: middleimportance: must knowfreq 55%

basics

~20 s

The definition loaded last wins, for every caller in the process: reopening Integer overwrites the single minutes entry in Integer's method table. Ruby raises nothing; only with warnings on (ruby -w) does it print 'method redefined; discarding old minutes'.

open as a page

In Ruby, why are class methods really singleton methods, and what does class << self open inside a class body?

level: middleimportance: must knowfreq 55%

basics

~20 s

A class is an object, so a class method is a singleton method of the class object, stored in its singleton class. class << self opens that singleton class, so def, attr_accessor and private inside it act at class level.

open as a page

In Ruby, calling Report.instance_eval versus Report.class_eval on a class, what is self in each block, and where does a def inside it land?

level: seniorimportance: must knowfreq 55%

basics

~10 s

Both set self to Report. They differ in the default definee: a def inside Report.instance_eval becomes a singleton method, Report.x, while a def inside Report.class_eval becomes an instance method, Report.new.x.

open as a page

In Ruby, how would you generate paid?, shipped? and cancelled? predicate methods on an Order class from a list of its statuses?

level: juniorimportance: should knowfreq 42%

basics

~10 s

Loop over a frozen STATUSES list with each and call define_method(:"#{s}?") { status == s } for every entry. Each iteration's block captures its own s, so every predicate compares against its own status.

open as a page

In Ruby, what is a class macro such as `alert_on :cpu, above: 90` in a class body, and when does it run?

level: juniorimportance: should knowfreq 40%

basics

~20 s

A class macro is an ordinary method called on the class: inside a class body self is the class, so alert_on :cpu, above: 90 calls a class-level method. It runs once, as the class body executes, recording settings or defining methods.

open as a page

In Ruby, when a library runs your configuration block through builder.instance_eval, why do your instance variables and helper methods stop working inside it?

level: juniorimportance: should knowfreq 45%

basics

~20 s

instance_eval sets self to the builder while the block runs, so @ivars and bare method calls resolve against the builder, not your object. Local variables still work because the block stays a closure over its scope.

open as a page

In Ruby, what happens when an object receives a call to a method that no class in its ancestor chain defines?

level: juniorimportance: should knowfreq 50%

basics

~20 s

After lookup fails, Ruby calls method_missing on the receiver with the name as a Symbol, the arguments and the block. BasicObject's default raises NoMethodError, or NameError when the name was a bare identifier that could have been a local variable.

open as a page

In Ruby, which hook methods run when a class is subclassed, gains or loses an instance method, or gains a singleton method?

level: juniorimportance: should knowfreq 30%

basics

~10 s

Ruby calls inherited(subclass) on the parent when a class is subclassed, method_added(name) after an instance method is defined, method_removed or method_undefined after remove_method or undef_method, and singleton_method_added(name) when an object gains a singleton method.

open as a page

In Ruby, what changes when define_method receives a Proc, a lambda or an UnboundMethod as the body instead of a block?

level: middleimportance: should knowfreq 30%

basics

~20 s

A Proc or lambda body is copied and given lambda semantics, so arity is strict and return leaves the method. An UnboundMethod body reuses an existing definition, but only from the same class, an ancestor or a module; otherwise TypeError.

open as a page

In Ruby, what does define_singleton_method do, and when would you use it instead of define_method?

level: middleimportance: should knowfreq 28%

basics

~20 s

define_singleton_method adds a method to one object's singleton class, so only that object gets it. Called on a class object it creates a class method, which makes it the tool for generating class-level method families from a list.

open as a page

In Ruby, why should class_eval and eval with a heredoc string get __FILE__ and __LINE__ + 1, and what do backtraces show otherwise?

level: middleimportance: should knowfreq 30%

basics

~20 s

String code has no file of its own. The file and line arguments set what backtraces, SyntaxError messages and FILE report; without them Ruby 4.0 labels the code (eval at caller.rb:N) and counts lines from 1 inside the string.

open as a page

In Ruby, when is Module#const_missing called, what happens to its return value, and how does Module#const_source_location help find a constant?

level: middleimportance: should knowfreq 25%

basics

~20 s

Ruby calls const_missing(name) on the module where a constant lookup failed; the default raises NameError. An override's return value becomes the expression's value but is not stored, so it runs again next time. const_source_location returns the defining file and line.

open as a page

In Ruby, how does Object.const_get("Billing::Invoice") resolve a namespaced name, and what does Module#name return for a class built with Class.new?

level: middleimportance: should knowfreq 35%

basics

~20 s

const_get splits the string on :: and looks up each segment in the module found so far, raising NameError if one is missing or malformed. Module#name returns nil for an anonymous class until it is assigned to a constant.

open as a page

In Ruby, what do instance_variable_get and instance_variable_set do, and why can instance_variables omit an attribute declared with attr_accessor?

level: middleimportance: should knowfreq 35%

basics

~10 s

They read and write an object's instance variable by name, such as :@total, bypassing its methods. instance_variables lists only variables already assigned, and attr_accessor defines methods without creating the variable.

open as a page

In Ruby 4.0, how do you forward positional arguments, keyword arguments and a block through public_send to a method chosen at run time?

level: middleimportance: should knowfreq 38%

basics

~20 s

Collect and re-pass all three channels: def run(name, *args, **opts, &block) with public_send(name, *args, **opts, &block), or def run(name, ...) with public_send(name, ...). Collecting only *args turns keywords into a positional Hash since Ruby 3.0.

open as a page

In Ruby, what do singleton_class, singleton_class? and Class#attached_object return, and when does attached_object raise TypeError?

level: middleimportance: should knowfreq 25%

basics

~10 s

obj.singleton_class returns the object's singleton class, creating it if needed. singleton_class? says whether a module is one. Class#attached_object, since Ruby 3.2, returns the owning object and raises TypeError on an ordinary class.

open as a page

In CRuby 4.0, why can a method generated with define_method cost more than one written with def, and when does that difference matter?

level: seniorimportance: should knowfreq 30%

basics

~20 s

A define_method body is a closure: it keeps its whole defining scope alive, shares captured variables across calls, and YJIT compiles fewer call shapes for it, such as calls passing a block. Only hot paths, big closures or Ractor code justify rewriting.

open as a page

In a Ruby DSL that accepts any keyword through method_missing, why does a typo like sevrity go unnoticed, and how would you make DSL errors readable?

level: seniorimportance: should knowfreq 35%

basics

~20 s

A catch-all method_missing stores every unknown call, so sevrity :high becomes a setting nobody reads. Define keywords as real methods so typos raise NoMethodError with a did_you_mean hint, validate each rule after its block, and point errors at the user's line.

open as a page

In Ruby, how does Money.class_eval with a heredoc string differ from class_eval with a block for constants, locals and method names?

level: seniorimportance: should knowfreq 35%

basics

~20 s

A string is parsed when class_eval runs, resolves constants inside Money and can interpolate method names into def. A block is parsed with the file, keeps lexical constant lookup and closes over objects, but needs define_method for computed names.

open as a page

In CRuby 4.0, what does answering a hot call through method_missing cost, and how does defining the method on the first miss change that?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Each ghost call pays a failed lookup, a second dispatch to method_missing and the handler's own string checks, and YJIT does not compile such a call site. Defining a real method on the first miss makes later calls ordinary, compilable method calls.

open as a page

showing 1–30 of 41