Dynamic Dispatch and `method_missing` in Ruby

Explore the power of Ruby's `method_missing` for dynamic method handling and dispatch. Learn to implement, optimize, and use it effectively in your Ruby applications.

8.3 method_missing and Dynamic Dispatch

In the world of Ruby, metaprogramming is a powerful tool that allows developers to write flexible and dynamic code. One of the key features that enable this flexibility is the method_missing hook. This hook allows Ruby objects to intercept calls to undefined methods, providing a mechanism to handle these calls dynamically or delegate them elsewhere. In this section, we will explore how method_missing works, how to implement it, and best practices for using it effectively.

Understanding method_missing

The method_missing method is a special hook in Ruby that is called whenever an object receives a message (method call) it does not know how to handle. By overriding this method, you can define custom behavior for handling undefined method calls.

How method_missing Works

When you call a method on an object, Ruby follows a method lookup path to find the method definition. If Ruby cannot find the method, it calls method_missing on the object, passing the method name and any arguments.

Here’s a simple example to illustrate this:

1class DynamicResponder
2  def method_missing(method_name, *args, &block)
3    puts "You tried to call: #{method_name} with arguments: #{args.inspect}"
4  end
5end
6
7responder = DynamicResponder.new
8responder.unknown_method('arg1', 'arg2')

Output:

You tried to call: unknown_method with arguments: ["arg1", "arg2"]

In this example, the unknown_method is not defined in the DynamicResponder class, so Ruby calls method_missing, which prints the method name and arguments.

Implementing method_missing for Dynamic Methods

method_missing can be used to create dynamic methods that respond to a variety of method calls. This is particularly useful in scenarios where you want to create proxy objects or delegators.

Creating Proxy Objects

A proxy object acts as an intermediary for another object. You can use method_missing to forward method calls to the target object dynamically.

 1class Proxy
 2  def initialize(target)
 3    @target = target
 4  end
 5
 6  def method_missing(method_name, *args, &block)
 7    if @target.respond_to?(method_name)
 8      @target.public_send(method_name, *args, &block)
 9    else
10      super
11    end
12  end
13
14  def respond_to_missing?(method_name, include_private = false)
15    @target.respond_to?(method_name, include_private) || super
16  end
17end
18
19class Target
20  def greet(name)
21    "Hello, #{name}!"
22  end
23end
24
25target = Target.new
26proxy = Proxy.new(target)
27puts proxy.greet("World")  # Outputs: Hello, World!

In this example, the Proxy class forwards method calls to the Target object if it responds to the method. Otherwise, it calls super, which raises a NoMethodError.

Delegating Method Calls

You can also use method_missing to delegate method calls to another object, allowing for flexible and dynamic behavior.

 1class Delegator
 2  def initialize(target)
 3    @target = target
 4  end
 5
 6  def method_missing(method_name, *args, &block)
 7    @target.send(method_name, *args, &block)
 8  end
 9
10  def respond_to_missing?(method_name, include_private = false)
11    @target.respond_to?(method_name, include_private)
12  end
13end
14
15class Worker
16  def perform(task)
17    "Performing #{task}"
18  end
19end
20
21worker = Worker.new
22delegator = Delegator.new(worker)
23puts delegator.perform("task")  # Outputs: Performing task

Performance Implications

While method_missing is a powerful tool, it comes with performance considerations. Each call to method_missing incurs a performance penalty because Ruby has to go through the method lookup process before calling it. Therefore, it’s important to use method_missing judiciously and only when necessary.

Importance of respond_to_missing?

When you override method_missing, it’s crucial to also define respond_to_missing?. This method should return true if the object can respond to the given method name, even if it’s handled by method_missing.

This is important for several reasons:

  • Introspection: Tools like Object#respond_to? rely on respond_to_missing? to determine if an object can handle a method call.
  • Duck Typing: Ruby’s dynamic typing relies on objects responding to methods. Properly implementing respond_to_missing? ensures that your objects behave correctly in a dynamic environment.

Best Practices for method_missing

To prevent unexpected behavior and maintain code quality, follow these best practices when using method_missing:

  1. Use Sparingly: Only use method_missing when necessary. If you can define methods explicitly, do so.
  2. Define respond_to_missing?: Always implement respond_to_missing? alongside method_missing.
  3. Call super: If you cannot handle a method call, call super to allow Ruby to raise a NoMethodError.
  4. Document Behavior: Clearly document the behavior of method_missing in your code to aid understanding and maintenance.
  5. Avoid Complex Logic: Keep the logic in method_missing simple to avoid performance issues and bugs.

Try It Yourself

To get a hands-on understanding of method_missing, try modifying the examples above:

  • Experiment with Arguments: Modify the proxy example to handle methods with different numbers of arguments.
  • Add Logging: Enhance the delegator example to log each method call before delegating it.
  • Create a Dynamic API: Use method_missing to create a class that dynamically generates methods based on a configuration.

Visualizing method_missing and Dynamic Dispatch

To better understand how method_missing fits into Ruby’s method lookup process, let’s visualize it using a sequence diagram.

    sequenceDiagram
	    participant Caller
	    participant Object
	    participant MethodMissing
	
	    Caller->>Object: Call undefined method
	    Object->>MethodMissing: method_missing
	    MethodMissing-->>Caller: Handle method call

Diagram Description: This sequence diagram illustrates the flow of a method call to an undefined method. The Caller attempts to call a method on the Object. Since the method is undefined, the call is intercepted by method_missing, which handles the call.

References and Further Reading

Knowledge Check

  • Question: What is the purpose of method_missing in Ruby?
  • Exercise: Implement a class that uses method_missing to handle undefined methods and logs each call.

Embrace the Journey

Remember, mastering method_missing and dynamic dispatch is just one step in your Ruby journey. As you continue to explore Ruby’s metaprogramming capabilities, you’ll unlock new ways to write flexible and powerful code. Keep experimenting, stay curious, and enjoy the journey!

Quiz: method_missing and Dynamic Dispatch

Loading quiz…
Revised on Thursday, April 23, 2026