rubyguides

Object#send

obj.send(symbol, *args) -> obj

Object#send dynamically invokes a method by name at runtime. The method name can be a Symbol or String, and any additional arguments are passed to that method. This is the foundation of Ruby’s metaprogramming capabilities, enabling you to call methods whose names are determined at runtime rather than compile time.

Syntax

object.send(method_name, *arguments)

Parameters

ParameterTypeDefaultDescription
method_nameSymbol or StringThe name of the method to invoke
*argumentsObject[]Arguments to pass to the method

Examples

Basic usage

class Greeter
  def hello(name)
    "Hello, #{name}!"
  end
  
  def goodbye(name)
    "Goodbye, #{name}!"
  end
end

greeter = Greeter.new
greeter.send(:hello, "World")
# => "Hello, World!"

greeter.send("goodbye", "Alice")
# => "Goodbye, Alice!"

The ability to pass either a Symbol or a String as the method name gives send flexibility at the call site. Symbols are idiomatic for hardcoded method names, while Strings are useful when the method name comes from configuration or user input. In either case, the method dispatch works the same way under the hood.

Calling private methods

class Secret
  def reveal
    "The password is 12345"
  end
  
  private
  def hidden
    "This is private"
  end
end

secret = Secret.new
secret.send(:hidden)
# => "This is private"

This ability to bypass visibility is both a feature and a warning. It is useful in testing and framework code where you need to verify internal state, but calling private methods in application code usually suggests the class design should be reconsidered. The standard advice is to use public_send when you want visibility checks and reserve bare send for cases where you genuinely need to reach past the access boundary.

Common Patterns

Dynamic attribute access

class Person
  attr_accessor :name, :age, :email
  
  def initialize(attrs = {})
    attrs.each do |key, value|
      send("#{key}=", value)
    end
  end
end

person = Person.new(name: "Alice", age: 30, email: "alice@example.com")
person.name   # => "Alice"
person.age    # => 30

Dynamic attribute assignment with send is a common pattern in Ruby libraries that accept configuration hashes. The approach keeps the constructor short and avoids repeating a setter call for every attribute. When the attribute names match the hash keys, the code stays readable and the mapping is obvious from the parameter name alone.

Building a DSL

class Builder
  def initialize(&block)
    instance_eval(&block) if block_given?
  end
  
  def method_missing(name, *args)
    puts "Building: #{name} with #{args.inspect}"
  end
end

Builder.new do
  create_user name: "Bob", email: "bob@example.com"
  send_email to: "bob@example.com"
end
# Building: create_user with [{:name=>"Bob", :email=>"bob@example.com"}]
# Building: send_email with [{:to=>"bob@example.com"}]

Errors

  • NoMethodError: Raised if the method does not exist and is not handled by method_missing
  • ArgumentError: Raised if the wrong number of arguments are passed

using send with care

send is powerful because it can call methods whose names are not known until runtime. That makes it a natural fit for metaprogramming and dynamic attribute handling, but it also means the call site can be harder to inspect than an ordinary method call. When the target method name comes from data, the code should be careful about where that data comes from and whether private methods should really be reachable.

In everyday code, send is easiest to trust when the method name is still obvious to the reader and the dynamic part is small. If the call becomes too clever, a direct method call or a small helper may give the same result with less surprise.

That balance is what makes send useful in a codebase without turning every call into a puzzle. It can remove a little repetition and enable a dynamic pattern when the names are predictable, but it should not hide the program’s real behavior. When the code still reads clearly after the dynamic call is added, send is doing its job well.

It also helps to remember that send is often a maintenance tool as much as a metaprogramming tool. It can keep repetitive setter calls short or let a small DSL feel natural, but the resulting code still needs to be easy to debug. If the dynamic dispatch starts to feel opaque, a simpler method call is usually the better long-term choice.

When used sparingly, send can keep a repetitive pattern compact without hiding the real work. That is useful in small helpers and adapters where the method name is already controlled by the program. The key is to keep the dynamic part narrow enough that the reader can still understand the call without chasing extra indirection.

In practice, that usually means the caller knows the method list ahead of time and only the selection changes. That is a much safer shape than letting user input drive an open-ended call. The method stays helpful when it trims repetition, not when it turns the program into a guessing game.

See Also