Metaprogramming with examples

  1. Reflection:

Reflection allows you to obtain information about classes, modules, methods, and variables at runtime.

class Person
  attr_accessor :name, :age
end

person = Person.new
person.name = "Alice"

puts person.class  # Output: Person
puts person.methods  # Output: list of methods available for the object
puts person.instance_variables  # Output: [:@name]

In this example, we use reflection to retrieve information about the person object. We obtain the class name using the class method, the list of available methods using the methods method, and the instance variables using the instance_variables method.

  1. Dynamic method definition:

Ruby allows you to define methods dynamically at runtime using constructs like define_method.

class Person
  attr_accessor :name

  def add_custom_method(method_name)
    define_method(method_name) do
      puts "Custom method #{method_name} called!"
    end
  end
end

person = Person.new
person.add_custom_method(:greet)
person.greet  # Output: Custom method greet called!

In this example, the add_custom_method method dynamically defines a new method based on the given method name. The define_method allows us to generate a method on the fly, and we can then call that method on the person object.

  1. Method missing:

Ruby's method_missing method is called when an undefined method is invoked. By overriding this method, you can intercept method calls and dynamically handle them.

class DynamicHandler
  def method_missing(name, *args)
    puts "Undefined method #{name} called!"
  end
end

handler = DynamicHandler.new
handler.undefined_method  # Output: Undefined method undefined_method called!

In this example, we define a class with the method_missing method overridden. Whenever an undefined method is called on an instance of DynamicHandler, the method_missing method is invoked and prints a message.

  1. Open classes:

Ruby allows you to reopen and modify existing classes at runtime.

class String
  def shout
    upcase + "!"
  end
end

message = "hello"
puts message.shout  # Output: HELLO!

In this example, we reopen the String class and add a new method called shout. This allows us to call shout on any string object, converting it to uppercase and appending an exclamation mark.

  1. eval and instance_eval:

Ruby provides methods like eval and instance_eval that allow you to execute arbitrary code dynamically.

class Calculator
  def initialize(x, y)
    @x = x
    @y = y
  end
end

calculator = Calculator.new(2, 3)
result = calculator.instance_eval("@x + @y")
puts result  # Output: 5

In this example, we use instance_eval to dynamically execute the expression @x + @y within the context of the calculator object. This allows us to access and operate on the instance variables of the object.

These examples showcase different aspects of metaprogramming in Ruby, including reflection, dynamic method definition, method missing, open classes, and code execution at runtime using eval and instance_eval. These features provide the ability to dynamically modify and extend code behavior, making Ruby a flexible and powerful language for metaprogramming.

Last updated