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, ...]
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, ...]
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
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]
Also, there are top-level methods to list local_variables
and global_variables
.
# irb session
>> local_variables
# => [:_]
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">
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]
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
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
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.
Top comments (1)
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.