ruby.onl / sorcery

Dynamic Methods: send, define_method, and method_missing

2026-03-04

Call methods by name stored in a variable. Generate methods at runtime. Intercept calls to methods that don't exist. This is Ruby's dynamic dispatch toolkit, and it maps directly to Perl concepts you already know: $obj->$method_name(), symbol table manipulation, and AUTOLOAD.

Part 1: send() - Call Methods by Name

# Dynamic method dispatch method_name = "upcase" "hello".send(method_name) # => "HELLO" # From variable action = "strip" line.send(action) # calls line.strip # With arguments "hello world".send(:split, " ") # => ["hello", "world"]
Like Perl's $obj->$method_name(). Same concept, cleaner syntax.

public_send (The Safer Version)

send can call private methods. public_send respects visibility:
"hello".public_send(:upcase) # works (upcase is public) obj.public_send(:private_method) # NoMethodError (good!) obj.send(:private_method) # works anyway (bypasses private)

Practical: Dynamic Dispatch

# Dispatch based on input (sanitize first!) action = params[:action] if %w~start stop restart~.include?(action) service.send("#{action}_process") end
Always validate the method name before passing it to send. Otherwise you're handing arbitrary method execution to whoever provides the input.

Part 2: send Bypasses Private

class Secret private def hidden "secret data" end end s = Secret.new s.hidden # NoMethodError: private method s.send(:hidden) # => "secret data" (works!)
Perl comparison: Perl has no real privacy enforcement, so everything is always accessible. Ruby's send is like Perl's default behavior.

Part 3: define_method - Create Methods at Runtime

Generate methods programmatically. Like manipulating Perl's symbol table.
# Generate numbered methods (0..9).each do |n| define_method("press_#{n}") do @number = @number.to_i * 10 + n end end # Now press_0 through press_9 exist as methods # Generate accessors for multiple attributes %w~host port user~.each do |attr| define_method(attr) { instance_variable_get("@#{attr}") } define_method("#{attr}=") { |v| instance_variable_set("@#{attr}", v) } end
Ops use case: generate handlers for multiple similar config options, log levels, or service types without copy-pasting the same method ten times.

Part 4: method_missing - Catch Undefined Method Calls

Like Perl's AUTOLOAD. Intercepts calls to methods that don't exist.
class Flexible def method_missing(name, *args) if name.to_s.start_with?("find_by_") field = name.to_s.sub("find_by_", "") find_where(field, args.first) else super # raise NoMethodError for truly missing methods end end # Always implement this alongside method_missing def respond_to_missing?(name, include_private = false) name.to_s.start_with?("find_by_") || super end end obj.find_by_name("Alice") # Works! Handled by method_missing obj.find_by_email("a@b.c") # Also works!
Always implement respond_to_missing? when using method_missing. Without it, obj.respond_to?(:find_by_name) returns false even though the method works. That's a nasty surprise for anyone calling your code.

Part 5: Symbol Interpolation

Symbols support interpolation for dynamic creation:
attr = "name" :"#{attr}=" # => :name= # Quoted symbols for special characters :'class' # :class (reserved word as symbol) :'with spaces' # works! :"method-name" # works!

Created By: Wildcard Wizard. Copyright 2026