Metaprogramming Intro
Ruby is a dynamic, beginner-friendly programming language whose open, flexible object model makes metaprogramming — code that writes and inspects code at runtime — both natural and powerful.
Learn Metaprogramming Intro in our free Ruby course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Ruby course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
By the end of this lesson you'll call methods dynamically with send, generate methods with define_method, and intercept unknown calls with method_missing.
What You'll Learn in This Lesson
1️⃣ Dynamic Calls and Introspection
send invokes a method named by a symbol or string, so you can decide which method to call at runtime. Pair it with introspection — respond_to? , instance_methods , ancestors — to ask objects about themselves.
2️⃣ Generating and Intercepting Methods
define_method creates methods at runtime, so you can generate a family of similar methods from a list — no copy-paste. method_missing catches calls to methods that don't exist, letting an object respond to names it was never explicitly given.
Your turn. Generate getter methods from the keys of a settings hash with define_method . Confirm the TODO , then run it.
Mini-Challenge: A Dynamic Dispatcher
Route a list of command strings to methods using send — the core of how CLIs dispatch input. Run with ruby dispatch.rb .
Common Errors (and the fix)
- send on untrusted input — It can call private methods. Use public_send and validate the name first.
- "undefined method" despite method_missing — Define respond_to_missing? too, so the object reports honestly.
- define_method with def syntax — Pass a block, not def : define_method(:x) { ... } .
- method_missing swallowing typos — A broad method_missing hides mistakes. Be specific or call super for unknown names.
- Overusing metaprogramming — If a plain method works, prefer it. Clever code is harder to debug.
Pro Tips
- 💡 You've already used it: attr_accessor is metaprogramming — it defines methods for you.
- 💡 Prefer public_send: it respects method visibility, unlike send .
- 💡 Inspect at runtime: obj.methods and obj.class.ancestors are great debugging tools.
📋 Quick Reference — Metaprogramming
Concept
Ruby Syntax
Call by name
obj.send(:add, 1, 2)
Safe call by name
obj.public_send(:m)
Has method?
obj.respond_to?(:m)
Generate method
define_method(:m) { }
Intercept unknown
def method_missing(n,*a)
List methods
Klass.instance_methods
Frequently Asked Questions
🎉 Lesson Complete!
- ✅ Metaprogramming is code that writes/inspects code at runtime
- ✅ send calls a method chosen by name (prefer public_send )
- ✅ Introspection asks objects what they can do
- ✅ define_method generates methods from data
- ✅ method_missing intercepts unknown calls — use sparingly
- ✅ Next lesson: Gems & Bundler — use external libraries
Practice quiz
What does metaprogramming mean?
- writing tests
- compiling code
- writing code that writes or inspects code at runtime
- obfuscating code
Answer: writing code that writes or inspects code at runtime. Metaprogramming generates or examines code while the program runs.
What does calc.send(:add, 2, 3) do?
- calls the add method with 2 and 3
- defines add
- raises an error
- returns the symbol :add
Answer: calls the add method with 2 and 3. send invokes a method named by a symbol or string, the same as calc.add(2, 3).
send can accept the method name as:
- only a symbol
- only a string
- only a method object
- a symbol or a string
Answer: a symbol or a string. send takes the name as either a symbol or a string.
Which is safer because it respects method visibility?
- send
- public_send
- __send__
- call
Answer: public_send. public_send will not invoke private methods, unlike send.
What does respond_to?(:add) return when the method exists?
- true
- nil
- false
- the method object
Answer: true. respond_to? returns true when the object has that method.
What does define_method do?
- deletes a method
- calls a method
- creates a method at runtime
- renames a class
Answer: creates a method at runtime. define_method creates methods programmatically, often in a loop.
An advantage of define_method over def is that its body:
- runs faster always
- can close over local variables in scope
- needs no name
- is always private
Answer: can close over local variables in scope. A define_method block closes over surrounding locals; a plain def cannot.
What does method_missing intercept?
- syntax errors
- all method calls
- private methods only
- calls to methods the object does not have
Answer: calls to methods the object does not have. Ruby calls method_missing when you invoke an undefined method.
When overriding method_missing, you should also define:
- initialize
- respond_to_missing?
- to_s
- method_added
Answer: respond_to_missing?. respond_to_missing? keeps respond_to? honest about dynamic methods.
Which familiar feature is itself an example of metaprogramming?
- puts
- require
- attr_accessor
- if/else
Answer: attr_accessor. attr_accessor defines getter/setter methods for you at runtime.
Continue this course
- Previous: File I/O
- Next: Gems & Bundler