Overview
Class and Module Queries provide Ruby's reflection and introspection capabilities, allowing programs to examine class hierarchies, module relationships, and method definitions at runtime. Ruby implements these queries through methods available on Object
, Class
, Module
, and Kernel
that return information about an object's type, its class structure, and available methods.
The core query methods operate on Ruby's object model where every value is an object, every object has a class, and classes themselves are objects. Ruby maintains detailed metadata about class inheritance chains, included modules, and method definitions that these query methods expose programmatically.
class Animal
def speak; end
end
class Dog < Animal
include Comparable
def bark; end
end
dog = Dog.new
dog.class # => Dog
dog.class.superclass # => Animal
dog.class.included_modules # => [Comparable, Kernel]
Class queries examine inheritance relationships and class metadata, while module queries focus on module inclusion and composition. Method queries determine what methods an object responds to and how those methods are defined. Type checking queries verify object relationships and compatibility.
dog.is_a?(Animal) # => true
dog.respond_to?(:bark) # => true
Dog.method_defined?(:speak) # => true
Ruby's query system handles the complexity of eigenclasses, module prepending, and method visibility while providing consistent interfaces for runtime inspection. These queries form the foundation for metaprogramming, testing frameworks, serialization libraries, and debugging tools.
Basic Usage
Class Hierarchy Queries
The class
method returns an object's immediate class, while superclass
traverses up the inheritance chain. Ruby classes form a hierarchy where every class except BasicObject
has a parent class.
class Vehicle; end
class Car < Vehicle; end
class Sedan < Car; end
sedan = Sedan.new
sedan.class # => Sedan
sedan.class.superclass # => Car
Car.superclass # => Vehicle
Vehicle.superclass # => Object
Object.superclass # => BasicObject
BasicObject.superclass # => nil
The ancestors
method returns the complete inheritance chain including included modules, showing the method lookup path Ruby follows when resolving method calls.
module Trackable
def track; end
end
class Vehicle
include Trackable
end
class Car < Vehicle; end
Car.ancestors
# => [Car, Vehicle, Trackable, Object, Kernel, BasicObject]
Module Inclusion Queries
Ruby distinguishes between modules included directly in a class versus those inherited from parent classes. The included_modules
method returns modules mixed into a specific class.
module Fuel
def refuel; end
end
module Electric
def charge; end
end
class HybridCar < Car
include Fuel
include Electric
end
HybridCar.included_modules
# => [Electric, Fuel, Kernel]
Car.included_modules
# => [Kernel]
The include?
method checks whether a class or module appears in the ancestors chain, while <
and >
compare class hierarchy relationships.
HybridCar < Car # => true
Car < Vehicle # => true
HybridCar.include?(Fuel) # => true
Car.include?(Fuel) # => false
Type Checking Queries
Type checking methods determine object compatibility and class relationships. The is_a?
and kind_of?
methods check whether an object is an instance of a class or its ancestors.
hybrid = HybridCar.new
hybrid.is_a?(HybridCar) # => true
hybrid.is_a?(Car) # => true
hybrid.is_a?(Vehicle) # => true
hybrid.is_a?(String) # => false
The instance_of?
method performs strict type checking, returning true only for the exact class match without considering inheritance.
hybrid.instance_of?(HybridCar) # => true
hybrid.instance_of?(Car) # => false
hybrid.instance_of?(Vehicle) # => false
Method Definition Queries
Method queries determine what methods an object can respond to and how those methods are defined. The respond_to?
method checks method availability, including inherited and mixed-in methods.
hybrid.respond_to?(:refuel) # => true
hybrid.respond_to?(:charge) # => true
hybrid.respond_to?(:track) # => true
hybrid.respond_to?(:nonexistent) # => false
Class-level method queries examine method definitions within classes and modules using method_defined?
, private_method_defined?
, and protected_method_defined?
.
class SecureCar < Car
private
def start_engine
authenticate
end
def authenticate; end
end
SecureCar.method_defined?(:start_engine) # => false
SecureCar.private_method_defined?(:start_engine) # => true
SecureCar.method_defined?(:track) # => true
Advanced Usage
Eigenclass and Singleton Method Queries
Ruby creates eigenclasses (singleton classes) for individual objects to hold their unique methods. Eigenclass queries reveal these hidden classes and their method definitions.
class Robot; end
robot = Robot.new
# Define singleton method
def robot.self_destruct
puts "Boom!"
end
# Access eigenclass
eigenclass = robot.singleton_class
eigenclass.instance_methods(false) # => [:self_destruct]
eigenclass.superclass # => Robot
# Check for singleton methods
robot.singleton_methods # => [:self_destruct]
Robot.singleton_methods # => []
Class methods exist as instance methods on the class's eigenclass. Understanding this relationship helps when querying class-level method definitions.
class DatabaseConnection
def self.establish
puts "Connecting..."
end
class << self
def close_all
puts "Closing all connections"
end
private
def validate_config
# validation logic
end
end
end
DatabaseConnection.singleton_class.instance_methods(false)
# => [:close_all, :establish]
DatabaseConnection.singleton_class.private_instance_methods(false)
# => [:validate_config]
Constant Resolution Queries
Ruby's constant lookup follows specific scoping rules. Constant queries examine how constants are resolved within class and module hierarchies.
module Config
DATABASE_URL = "localhost:5432"
class Settings
API_KEY = "secret123"
def self.database_defined?
const_defined?(:DATABASE_URL) # => false (local scope)
end
def self.database_defined_inherited?
const_defined?(:DATABASE_URL, true) # => true (inherited scope)
end
end
end
Config.const_defined?(:DATABASE_URL) # => true
Config::Settings.const_defined?(:API_KEY) # => true
Config.const_get(:DATABASE_URL) # => "localhost:5432"
Config::Settings.constants # => [:API_KEY]
Config.constants # => [:DATABASE_URL, :Settings]
Method Source Location Queries
Ruby tracks where methods are defined, including file paths and line numbers. These queries help with debugging and code analysis tools.
class Analytics
def track_event(name)
# implementation
end
define_method(:track_user) do |user|
# dynamic method definition
end
end
analytics = Analytics.new
# Get method objects
track_event_method = analytics.method(:track_event)
track_user_method = analytics.method(:track_user)
# Query source locations
track_event_method.source_location
# => ["/path/to/file.rb", 42]
track_user_method.source_location
# => ["/path/to/file.rb", 46]
# Query method ownership
Analytics.instance_method(:track_event).owner # => Analytics
Module Prepending Queries
Module prepending alters method lookup order by placing modules before the including class in the ancestors chain. Prepend queries reveal this modified hierarchy.
module Logging
def save
puts "Logging save operation"
super
end
end
class Document
def save
puts "Saving document"
end
end
class AuditedDocument < Document
prepend Logging
end
AuditedDocument.ancestors
# => [Logging, AuditedDocument, Document, Object, Kernel, BasicObject]
# Method resolution follows prepended module first
audited = AuditedDocument.new
audited.save
# Logging save operation
# Saving document
Dynamic Method Query Patterns
Complex applications often need to query method definitions dynamically, filtering by visibility, source, or naming patterns.
class ApiController
def index; end
def show; end
def create; end
private
def authenticate; end
def authorize; end
protected
def current_user; end
end
# Get public action methods
public_actions = ApiController.public_instance_methods(false)
.select { |m| %w[index show create update destroy].include?(m.to_s) }
# => [:index, :show, :create]
# Get private helper methods
private_helpers = ApiController.private_instance_methods(false)
# => [:authenticate, :authorize]
# Find methods matching pattern
auth_methods = (ApiController.private_instance_methods(false) +
ApiController.protected_instance_methods(false))
.select { |m| m.to_s.start_with?('auth') }
# => [:authenticate, :authorize]
Common Pitfalls
Class vs Instance Method Confusion
Ruby distinguishes between class methods and instance methods, but query methods can return confusing results when the distinction isn't clear. Methods defined on classes exist as instance methods of their eigenclass.
class User
def self.find(id) # class method
new
end
def save # instance method
puts "Saving user"
end
end
# Common mistake: expecting instance methods on the class
User.method_defined?(:find) # => false (find is not an instance method)
User.method_defined?(:save) # => true
# Correct approach for class methods
User.singleton_class.method_defined?(:find) # => true
User.respond_to?(:find) # => true
# Instance method queries work on instances
user = User.new
user.respond_to?(:save) # => true
user.respond_to?(:find) # => false
Eigenclass Inheritance Gotchas
Eigenclasses have their own inheritance relationships that don't always match expectations. Singleton methods don't inherit the same way instance methods do.
class Animal
def self.species_count
100
end
end
class Dog < Animal
end
# Class methods are inherited
Dog.species_count # => 100
Dog.respond_to?(:species_count) # => true
# But eigenclass relationships differ
Animal.singleton_class.superclass # => #<Class:Object>
Dog.singleton_class.superclass # => #<Class:Animal>
# Singleton methods don't appear in instance method queries
Dog.method_defined?(:species_count) # => false
# Individual object eigenclasses behave differently
dog1 = Dog.new
dog2 = Dog.new
def dog1.bark_loud
puts "WOOF!"
end
dog1.respond_to?(:bark_loud) # => true
dog2.respond_to?(:bark_loud) # => false
dog1.singleton_methods # => [:bark_loud]
dog2.singleton_methods # => []
Module Inclusion Order Effects
The order of module inclusion affects method lookup and the results of ancestor queries. Later included modules appear earlier in the ancestors chain.
module A
def test; "A"; end
end
module B
def test; "B"; end
end
class Example
include A
include B
end
# B overrides A because it was included last
Example.ancestors # => [Example, B, A, Object, Kernel, BasicObject]
Example.new.test # => "B"
# Prepending reverses this expectation
class PrependExample
include A
prepend B
end
PrependExample.ancestors # => [B, PrependExample, A, Object, Kernel, BasicObject]
PrependExample.new.test # => "B"
Constant Resolution Scope Issues
Constant lookup follows lexical scope first, then inheritance hierarchy. This creates unexpected behavior when constants with the same name exist at different scoping levels.
DATABASE_URL = "global"
module Config
DATABASE_URL = "config"
class Settings
def self.get_url
DATABASE_URL # Returns "config", not "global"
end
def self.get_global_url
::DATABASE_URL # Explicitly reference global constant
end
end
module Nested
def self.get_url
DATABASE_URL # Still returns "config" from Config module
end
end
end
Config::Settings.get_url # => "config"
Config::Settings.get_global_url # => "global"
Config::Nested.get_url # => "config"
# const_defined? behavior varies with inheritance flag
Config::Settings.const_defined?(:DATABASE_URL) # => false
Config::Settings.const_defined?(:DATABASE_URL, true) # => true
Method Visibility and respond_to? Behavior
The respond_to?
method respects method visibility by default, but this behavior can be overridden. Private and protected method queries require understanding visibility rules.
class SecureSystem
def public_action
puts "Public"
end
protected
def protected_helper
puts "Protected"
end
private
def private_operation
puts "Private"
end
end
system = SecureSystem.new
# Default respond_to? respects visibility
system.respond_to?(:public_action) # => true
system.respond_to?(:protected_helper) # => false
system.respond_to?(:private_operation) # => false
# Override visibility check
system.respond_to?(:protected_helper, true) # => true
system.respond_to?(:private_operation, true) # => true
# Class-level method queries ignore visibility context
SecureSystem.method_defined?(:private_operation) # => true
SecureSystem.private_method_defined?(:private_operation) # => true
# Method calls still respect visibility
begin
system.private_operation
rescue NoMethodError => e
puts e.message # private method `private_operation' called
end
Inherited Method Source Confusion
When methods are inherited or included from modules, their source location points to the original definition, not the including class. This can confuse debugging and introspection tools.
module Timestampable
def created_at
@created_at ||= Time.now
end
end
class User
include Timestampable
def name
@name
end
end
user = User.new
# Method sources point to original definitions
user.method(:name).source_location
# => ["/current/file.rb", 123]
user.method(:created_at).source_location
# => ["/path/to/timestampable.rb", 45] # Points to module, not User class
# Owner reflects actual definition location
User.instance_method(:name).owner # => User
User.instance_method(:created_at).owner # => Timestampable
Reference
Object Type Query Methods
Method | Parameters | Returns | Description |
---|---|---|---|
#class |
None | Class |
Returns the object's immediate class |
#is_a?(class_or_module) |
Class or Module |
Boolean |
Tests if object is instance of class/module or ancestors |
#kind_of?(class_or_module) |
Class or Module |
Boolean |
Alias for is_a? |
#instance_of?(class) |
Class |
Boolean |
Tests if object is direct instance of class (no inheritance) |
#respond_to?(method, include_private=false) |
Symbol /String , Boolean |
Boolean |
Tests if object responds to method call |
Class Hierarchy Query Methods
Method | Parameters | Returns | Description |
---|---|---|---|
.superclass |
None | Class or nil |
Returns parent class in inheritance chain |
.ancestors |
None | Array<Class, Module> |
Returns inheritance chain including modules |
.included_modules |
None | Array<Module> |
Returns modules included directly in class |
.<(other) |
Class or Module |
Boolean or nil |
Tests if class is subclass of other |
.>(other) |
Class or Module |
Boolean or nil |
Tests if class is superclass of other |
.include?(module) |
Module |
Boolean |
Tests if module is in ancestors chain |
Method Definition Query Methods
Method | Parameters | Returns | Description |
---|---|---|---|
.method_defined?(symbol) |
Symbol or String |
Boolean |
Tests if public/protected instance method is defined |
.private_method_defined?(symbol) |
Symbol or String |
Boolean |
Tests if private instance method is defined |
.protected_method_defined?(symbol) |
Symbol or String |
Boolean |
Tests if protected instance method is defined |
.public_method_defined?(symbol) |
Symbol or String |
Boolean |
Tests if public instance method is defined |
#singleton_methods(all=true) |
Boolean |
Array<Symbol> |
Returns singleton methods defined on object |
.instance_methods(include_super=true) |
Boolean |
Array<Symbol> |
Returns public and protected instance method names |
.private_instance_methods(include_super=true) |
Boolean |
Array<Symbol> |
Returns private instance method names |
.protected_instance_methods(include_super=true) |
Boolean |
Array<Symbol> |
Returns protected instance method names |
.public_instance_methods(include_super=true) |
Boolean |
Array<Symbol> |
Returns public instance method names |
Singleton Class Query Methods
Method | Parameters | Returns | Description |
---|---|---|---|
#singleton_class |
None | Class |
Returns object's eigenclass |
.singleton_class |
None | Class |
Returns class's eigenclass |
#singleton_methods(all=true) |
Boolean |
Array<Symbol> |
Returns methods defined only on this object |
Constant Query Methods
Method | Parameters | Returns | Description |
---|---|---|---|
.const_defined?(symbol, inherit=true) |
Symbol /String , Boolean |
Boolean |
Tests if constant is defined in scope |
.const_get(symbol, inherit=true) |
Symbol /String , Boolean |
Object |
Returns constant value from scope |
.const_set(symbol, value) |
Symbol /String , Object |
Object |
Defines constant in current scope |
.constants(inherit=true) |
Boolean |
Array<Symbol> |
Returns constant names defined in scope |
.const_missing(symbol) |
Symbol |
Object |
Hook called when constant lookup fails |
Method Object Query Methods
Method | Parameters | Returns | Description |
---|---|---|---|
#method(symbol) |
Symbol or String |
Method |
Returns Method object for instance method |
.instance_method(symbol) |
Symbol or String |
UnboundMethod |
Returns UnboundMethod for class method |
Method#owner |
None | Class or Module |
Returns class/module where method is defined |
Method#source_location |
None | Array or nil |
Returns [filename, line_number] of method definition |
Method#parameters |
None | Array |
Returns parameter information as [type, name] pairs |
Query Method Return Types
Boolean Methods: Return true
, false
, or nil
(for comparison operators when objects aren't comparable)
Array Methods: Return empty arrays when no matches found
Class Methods: Return Class
objects or nil
for BasicObject.superclass
Constant Methods: Raise NameError
when constants not found (except const_defined?
)
Visibility Levels
Level | Description | Query Methods |
---|---|---|
public |
Callable from anywhere | public_method_defined? , public_instance_methods |
protected |
Callable within class hierarchy | protected_method_defined? , protected_instance_methods |
private |
Callable only within defining object | private_method_defined? , private_instance_methods |
Common Query Patterns
# Check if object can handle method before calling
obj.respond_to?(:method_name) && obj.method_name
# Find all methods containing substring
klass.instance_methods.select { |m| m.to_s.include?("save") }
# Get methods defined in class (not inherited)
klass.instance_methods(false)
# Check class relationship
child_class < parent_class
# Verify module inclusion
klass.include?(module_name)
# Get method definition location
obj.method(:method_name).source_location