Metaprogramming in Ruby is a powerful feature that allows you to write code that can generate other code at runtime. This can be used to create flexible and dynamic programs. Here are some key concepts and techniques to get started with metaprogramming in Ruby:
Dynamic Method Definition
You can define methods dynamically using define_method
.
class Person
["first_name", "last_name"].each do |method_name|
define_method(method_name) do
instance_variable_get("@#{method_name}")
end
define_method("#{method_name}=") do |value|
instance_variable_set("@#{method_name}", value)
end
end
end
person = Person.new
person.first_name = "John"
person.last_name = "Doe"
puts person.first_name # Output: John
puts person.last_name # Output: Doe
Method Missing
You can intercept calls to methods that do not exist using method_missing
.
class DynamicMethodHandler
def method_missing(method_name, *args, &block)
puts "You called: #{method_name} with arguments: #{args.join(', ')}"
end
end
obj = DynamicMethodHandler.new
obj.some_method(1, 2, 3)
# Output: You called: some_method with arguments: 1, 2, 3
send
Method
You can call methods dynamically using send
.
class Calculator
def add(a, b)
a + b
end
def multiply(a, b)
a * b
end
end
calc = Calculator.new
puts calc.send(:add, 2, 3) # Output: 5
puts calc.send(:multiply, 4, 5) # Output: 20
Class Macros
You can define class-level methods (macros) that modify the behavior of classes.
class MyClass
def self.my_macro
define_method(:greet) do |name|
"Hello, #{name}!"
end
end
end
class User < MyClass
my_macro
end
user = User.new
puts user.greet("Alice") # Output: Hello, Alice!
Open Classes and Monkey Patching
Ruby allows you to reopen existing classes and modify their behavior.
class String
def shout
upcase + "!!!"
end
end
puts "hello".shout # Output: HELLO!!!
Using eval
You can execute Ruby code stored in strings using eval
.
code = "puts 'Hello from eval!'"
eval(code) # Output: Hello from eval!
define_singleton_method
You can define methods on individual objects.
obj = Object.new
obj.define_singleton_method(:greet) do |name|
"Hello, #{name}!"
end
puts obj.greet("Alice") # Output: Hello, Alice!
Hooks for Metaprogramming
Ruby provides hooks that you can override to add behavior when a class or module is modified.
- included: Called when a module is included in another module or class.
- extended: Called when a module is extended in another module or class.
- inherited: Called when a class is subclassed.
module MyModule
def self.included(base)
puts "#{base} included MyModule"
end
end
class MyClass
include MyModule
end
# Output: MyClass included MyModule
Ghost Methods
You can create methods dynamically using method missing and respond_to_missing
?.
class GhostMethod
def method_missing(method_name, *args, &block)
if method_name.to_s.start_with?('say_')
puts "Ghost method: #{method_name}, Args: #{args.join(', ')}"
else
super
end
end
def respond_to_missing?(method_name, include_private = false)
method_name.to_s.start_with?('say_') || super
end
end
ghost = GhostMethod.new
ghost.say_hello('world') # Output: Ghost method: say_hello, Args: world
puts ghost.respond_to?(:say_hello) # Output: true