Reason #159 • June 8th, 2026

Dynamic dispatch via Module#const_get

Ruby's Module#const_get method allows us to retrieve a constant by name at runtime:

Ruby
module MyModule
  GREETING = "Hello, world!"

  class MyClass
  end
end

MyModule.const_get("GREETING")
# "Hello, world!"

MyModule.const_get("MyClass")
# MyModule::MyClass
    

Let's recreate the example we introduced two days ago in a way that dispatches via const_get:

Ruby
help = ARGV.delete("--help")
puts "Usage: cli.rb <command> [args...]" and exit if ARGV.empty?

module Commands
  module Greet
    extend self

    def description = "Greet someone"
    def usage = "cli.rb greet "
    def call(name) = puts "Hello, #{name}!"
  end

  module Add
    extend self

    def description = "Add two numbers"
    def usage = "cli.rb add  "
    def call(x, y) = puts x.to_i + y.to_i
  end
end

command_name = ARGV.shift.capitalize.to_sym

unless Commands.constants.include?(command_name)
  abort "ERROR: Unknown command - #{command_name.downcase}"
end

command = Commands.const_get(command_name)

if help
  puts "Command: #{command_name.downcase}"
  puts command.description
  puts "Usage: #{command.usage}"
  exit
end

command.call(*ARGV)
    

With these simple commands, the step up in verbosity from our previous examples may not be worth it, but if our commands were complex enough to require multiple methods, this approach could provide a better organisational structure.

Note the usage of Module#constants to verify that the command is defined on the module before calling const_get. This is important to avoid unsafe access to constants that may not be intended as commands:

Ruby
Commands.constants
# [:Greet, :Add]

# But const_get doesn't enforce namespace locality by default
Commands.const_get("Kernel")
# Kernel

# Passing false as a second argument prevents searching parent namespaces
Commands.const_get("Kernel", false)
# NameError: uninitialized constant Commands::Kernel

# However, we can bypass this by providing an absolute path with ::
Commands.const_get("::Kernel", false)
# Kernel
    

Enjoy using const_get but remember to do so responsibly!

History

Module#const_get and Module#constants shipped in Ruby 1.2 in 1998.

In Ruby 1.9, released in 2007, both methods gained the optional inheritance flag demonstrated above, allowing calls like const_get(:Kernel, false) and constants(false) to avoid searching ancestor namespaces. Around the same time, Module#constants changed from returning strings to returning symbols.

Ruby 2.0, released in 2013, expanded const_get to accept qualified constant paths such as "Foo::Bar::Baz". That made the method more powerful, but also increased the need to validate user-provided input for dynamic dispatch.