DEV Community

Cover image for Runtime Introspection in Ruby
Amr El-Bakry
Amr El-Bakry

Posted on • Edited on

7

Runtime Introspection in Ruby

Ruby offers a variety of methods that allows you to ask an object about its capabilities (which messages/methods does it respond to?), its variables and constants, and its backstory (the object's class and ancestors).

Methods Introspection

Let's start by asking an object about its non-private methods:

str = 'a string'
str.methods

# => [:include?, :%, :*, :+, :unicode_normalize, :to_c, :unicode_normalize!, :unicode_normalized?, :count, ...]

Enter fullscreen mode Exit fullscreen mode

methods returns an array of symbols representing the names of the public and protected methods for the receiver string object str.

We could also ask the object about:

  • private_methods
  • public_methods
  • protected_methods
  • singleton_methods

by default, Ruby will look up and list all the methods in the object's class and its ancestor classes and modules. If you want to list methods in the object's class only, you could pass the argument false or nil to any of these methods.

str = 'a string'
str.public_methods(false)

# public methods defined in String only
# => [:include?, :%, :*, :+, :unicode_normalize, :to_c, :unicode_normalize!, :unicode_normalized?, :count, ...]

Enter fullscreen mode Exit fullscreen mode

Sometimes, you'll only need to know if an object knows about a specific method, and that's when we use the aptly-named respond_to?:

str = 'a string'
str.respond_to?(:include?)
# => true
str.respond_to?(:first)
# => false
Enter fullscreen mode Exit fullscreen mode

Variables and Constants Introspection

In the same sense, we can query a class about instance_variables, class_variables, and constants.

class Cat
  @@cats_count = 2

  CATS = ['Luna', 'Milo']

  attr_accessor :name, :age

  def initialize(name)
    @name = name
  end
end

cat = Cat.new('Max')
cat.age = 2

cat.instance_variables
# => [:@name, :@age]

cat.class.class_variables
# => [:@@cats_count]

cat.class.constants
# => [:CATS]
Enter fullscreen mode Exit fullscreen mode

Also, there are top-level methods to list local_variables and global_variables.

# irb session
>> local_variables
# => [:_]

Enter fullscreen mode Exit fullscreen mode

This underscore local variable is actually a very interesting one; it always holds the last evaluated expression:

Cat.new('Milo')
# => #<Cat:0x00000002415440 @name="Milo">
cat = _
# => #<Cat:0x00000002415440 @name="Milo">
Enter fullscreen mode Exit fullscreen mode

The Object's Backstory

Finally, let's ask the object about its class, superclass, and ancestors

cats = ['luna', 'milo']
cats.class
# => Array

cats.class.superclass
# => Object

cats.class.ancestors
# => [Array, Enumerable, Object, Kernel, BasicObject]
Enter fullscreen mode Exit fullscreen mode

ancestors returns a list which includes its receiver (Array) and modules included in Array (Enumerable, Kernel), Array's superclass (Object), and lastly the superclass of Object which is (BasicObject).

But that's not it. There are also more convenient ways to ask about the object's class...

  • is_a?
  • kind_of?
  • instance_of?

this trio of predicate methods could also help us with that query:

cats = ['luna', 'milo']
cats.is_a?(Array)
# => true
Enter fullscreen mode Exit fullscreen mode

One important difference to note here is that instance_of? will only return true if the receiver object is a direct instance of the class and not of a subclass:

class A; end
class B < A; end

b = B.new
# => #<B:0x00000001fac638>
b.kind_of?(A)
# true
b.instance_of?(A)
# false

Enter fullscreen mode Exit fullscreen mode

This was just a glimpse of ruby's introspection capabilities, but there are always very interesting things you can learn about your objects!

This article can also be found on Medium.

Heroku

Simplify your DevOps and maximize your time.

Since 2007, Heroku has been the go-to platform for developers as it monitors uptime, performance, and infrastructure concerns, allowing you to focus on writing code.

Learn More

Top comments (1)

Collapse
 
burdettelamar profile image
Burdette Lamar

You can also use my Ruby gem debug_helper, which helps with printf-style debugging. It displays the principal data from objects, sometimes recursively.

The documentation is over at the GitHub project.

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay