Skip to content
Learni
View all tutorials
Développement Backend

How to Master Ruby Metaprogramming in 2026

Lire en français

Introduction

Ruby metaprogramming allows generating code at runtime, creating powerful DSLs and flexible frameworks. In 2026, it remains essential for modern gems and high-performance applications. This tutorial covers advanced techniques used in Rails and professional libraries. You will learn to manipulate methods dynamically while avoiding maintainability pitfalls.

Prerequisites

  • Ruby 3.3+
  • Solid knowledge of Ruby OOP
  • Experience with gems and Bundler
  • Configured terminal and editor

Dynamic Method Definition

dynamic_methods.rb
class DynamicAPI
  def self.define_endpoint(name, &block)
    define_method(name) do |*args|
      instance_exec(*args, &block)
    end
  end
end

api = Class.new(DynamicAPI)
api.define_endpoint(:users) { |id| "User #{id}" }

instance = api.new
puts instance.users(42)

This technique creates methods on the fly via define_method. It is used in Rails routers. Pay attention to readability and debugging.

Implementing method_missing

proxy.rb
class MethodProxy
  def method_missing(name, *args, &block)
    if name.to_s.start_with?('get_')
      key = name.to_s.sub('get_', '')
      return "Value for #{key}"
    end
    super
  end

  def respond_to_missing?(name, include_private = false)
    name.to_s.start_with?('get_') || super
  end
end

proxy = MethodProxy.new
puts proxy.get_username

method_missing allows intercepting unknown calls. Always implement respond_to_missing for full compatibility with respond_to?.

Singletons and Eigenclasses

singleton.rb
obj = Object.new

class << obj
  def unique_behavior
    "Comportement spécifique à cet objet"
  end
end

puts obj.unique_behavior

def obj.another_method
  "Autre méthode singleton"
end

Singletons allow adding behavior to specific instances without modifying the class. Useful for mocks and configurations.

Refinements for Scoping

refinements.rb
module StringExtensions
  refine String do
    def reverse_words
      split.reverse.join(' ')
    end
  end
end

using StringExtensions

puts "hello world".reverse_words

Refinements limit the scope of monkey-patches. They are essential in 2026 to avoid conflicts in large applications.

Creating a Mini DSL

dsl.rb
class Workflow
  def self.define(&block)
    instance = new
    instance.instance_eval(&block)
    instance
  end

  def step(name, &block)
    (@steps ||= []) << { name: name, action: block }
  end

  def run
    @steps.each { |s| s[:action].call }
  end
end

wf = Workflow.define do
  step(:init) { puts 'Initialisation' }
  step(:process) { puts 'Traitement' }
end
wf.run

instance_eval enables creating readable DSLs. This approach is used in RSpec and builders. Keep the scope controlled.

Best Practices

  • Prefer define_method over eval for security
  • Always document dynamically generated methods
  • Use refinements rather than global monkey-patches
  • Thoroughly test method_missing paths
  • Limit eigenclass depth for readability

Common Errors to Avoid

  • Forgetting respond_to_missing creates bugs with third-party libraries
  • Using eval without sanitization exposes security vulnerabilities
  • Refinements not activated with 'using' have no effect
  • Overloading method_missing without super breaks the inheritance chain

Further Reading

Deepen these concepts with our advanced Ruby courses.