Ruby
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
91: const_missing and Dynamic Constants
Imagine you're staying at a luxury hotel. You walk up to the concierge and ask for a specific local specialist—say, a vintage watch expert. The concierge doesn't have a watch expert standing right there in the lobby, but instead of simply saying, "Sorry, we don't have one," the concierge says, "One moment," disappears into the back, makes a few phone calls, finds a specialist, and brings them to you.
As far as you're concerned, the hotel "had" the expert. You didn't care that the expert wasn't physically present when you asked; you just cared that the request was fulfilled. In Ruby, const_missing is that concierge. It's a hook that lets you intercept the moment Ruby realizes a constant is missing, giving you a chance to find it, create it, or fake it before the program crashes with a NameError.
Trapping the NameError
Normally, when you reference a constant that hasn't been defined, Ruby gives up immediately. But if you define a method called const_missing inside your class or module, Ruby will call that method first. It passes the name of the missing constant as a symbol, which you can then use to decide what to do.
I've seen this used in some really clever ways, but the most common professional use case is "lazy loading" or creating dynamic proxies. Let's look at a basic implementation so you can see the mechanics:
module MagicBox
def self.const_missing(name)
puts "Whoops! #{name} wasn't here, but I'll make it for you."
# We define the constant on the fly so it's there next time
const_set(name, "I am the value for #{name}")
end
end
puts MagicBox::Greeting # First time: calls const_missing
puts MagicBox::Greeting # Second time: already defined, no const_missing
Building a Dynamic Configuration Loader
To give you a real-world scenario, let's say you're building a system that loads different configuration sets from a folder. Instead of manually requiring every single config file at the start of your app (which slows down boot time), you can use const_missing to load them only when they are actually referenced.
In this example, if you call AppConfig::Database, Ruby will look for a file named database.rb and load it into the module. This is essentially how many "autoloading" mechanisms in older Ruby frameworks used to work.
module AppConfig
def self.const_missing(name)
file_name = name.to_s.downcase
path = "config/#{file_name}.rb"
if File.exist?(path)
require_relative path
# After requiring the file, the constant should now exist.
# We return the constant so the original call succeeds.
const_get(name)
else
# If we can't find the file, we should let the NameError happen
super
end
end
end
I should warn you: be very careful with const_set inside const_missing. If you have a typo in your code and you're automatically creating constants for everything that's missing, you'll never see a NameError. You'll just end up with a bunch of "ghost" constants in your system, and your bugs will become incredibly hard to track down. Always provide a fallback—like calling super—if the dynamic lookup fails.
The Danger of Infinite Loops
One last thing to watch out for. If you call a constant inside your const_missing method that is also missing, you'll trigger const_missing again. If you aren't careful, you'll hit a SystemStackError (stack overflow) very quickly. Always ensure that the logic inside your hook is "safe" and doesn't accidentally trigger another missing constant lookup in a loop.
📋 Practical Task
The Automatic API Endpoint Generator
Your task is to build a module called API that acts as a gateway to various service endpoints. Instead of defining every single endpoint (like Users, Orders, Products) manually, you will use const_missing to generate a "Handler" object on the fly.
Requirements:
- Create a module named
API. - Implement
self.const_missing(name). - Inside the hook, create a simple
Handlerclass (or use a Struct/OpenStruct) that has a method calledcall. - The
callmethod should return a string:"Calling endpoint: [constant_name]". - The
APImodule should useconst_setto store the handler so that subsequent calls to the same endpoint don't trigger the hook again. - If the constant name starts with an underscore (e.g.,
API::_Secret), the hook should callsuperto trigger a standardNameError.
Test your code with:
puts API::Users.call # Should print: Calling endpoint: Users
puts API::Orders.call # Should print: Calling endpoint: Orders
puts API::Users.call # Should NOT trigger const_missing this time
API::_Secret.call # Should raise NameErrorThere are no comments for now.