Runtime Introspection in Ruby
Amr El-Bakry Aug 2 Updated on Aug 24, 2018
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).
Let's start by asking an object about its non-private
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
We could also ask the object about:
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
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
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
# 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
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...
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.