skip to content

Introspection APIs

methods, instance_methods(false), instance_variable_get and ObjectSpace.each_object let running code list and read what an object holds. Interviewers use them to test how you debug unfamiliar code.

on this pageshow

explore

questions

6

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, 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, 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, a method on a live object behaves unlike the code you read — how do you find the file and line that actually define it?

level: seniorimportance: should knowfreq 40%

basics

~10 s

Call obj.method(:name).source_location. It returns the file and line of the definition that call would actually run, exposing a patch or prepended module; nil means the method is implemented in C.

open as a page

In Ruby, what does ObjectSpace.each_object(Session) yield and return, and why is its count not an exact number of live sessions?

level: seniorimportance: nice to knowfreq 15%

basics

~20 s

It yields every heap object that is a Session or subclass instance and returns how many it yielded. The count can include unreachable objects the GC has not yet collected, so it is an upper bound, not a live count.

open as a page