Skip to main content
โšก Calmops

Instance Methods in Ruby: Definition, Binding, and Singleton Methods

Introduction

In Ruby, methods are the primary way objects communicate. Understanding the different ways to define methods โ€” and the distinction between instance methods, class methods, and singleton methods โ€” is fundamental to writing idiomatic Ruby.

Three Ways to Define Instance Methods

Way 1: Standard def in a Class Body

The most common approach โ€” defines a method available to all instances:

class Foo
  def baz
    "instance method on #{self.class}"
  end
end

Foo.new.baz  # => "instance method on Foo"

Way 2: attr_accessor (Generated Methods)

attr_accessor, attr_reader, and attr_writer generate getter/setter instance methods:

class Foo
  attr_accessor :baz
end

foo = Foo.new
foo.baz = "instance method"
puts foo.baz  # => "instance method"

These are real methods โ€” you can verify:

Foo.instance_methods(false)  # => [:baz, :baz=]

Way 3: Singleton Method on a Specific Object

A singleton method is defined on a single object, not its class. Only that object has the method:

class Foo; end

foo = Foo.new
bar = Foo.new

def foo.greet
  "Hello from a singleton method"
end

foo.greet  # => "Hello from a singleton method"
bar.greet  # => NoMethodError: undefined method 'greet' for #<Foo:...>

The general syntax for singleton methods:

def object.method_name
  # body
end

Where object can be:

  • An object reference (foo)
  • A constant class name (String, MyClass)
  • self (inside a class body โ€” this is how class methods work)

Method Definition Syntax Summary

# Instance method (on all instances)
class Dog
  def speak
    "Woof!"
  end
end

# Class method (singleton method on the class object)
class Dog
  def self.species
    "Canis lupus familiaris"
  end
end

# Singleton method on a specific instance
rex = Dog.new
def rex.fetch(item)
  "#{item} fetched!"
end

Dog.new.speak    # => "Woof!"
Dog.species      # => "Canis lupus familiaris"
rex.fetch("ball") # => "ball fetched!"
Dog.new.fetch("ball")  # => NoMethodError

Inspecting Methods

class Calculator
  def add(a, b); a + b; end
  def subtract(a, b); a - b; end

  private
  def internal_helper; end
end

calc = Calculator.new

# Instance methods defined directly on the class (not inherited)
Calculator.instance_methods(false)
# => [:add, :subtract]

# All public instance methods (including inherited)
calc.public_methods(false)
# => [:add, :subtract]

# Private methods
Calculator.private_instance_methods(false)
# => [:internal_helper]

# Check if an object responds to a method
calc.respond_to?(:add)      # => true
calc.respond_to?(:internal_helper)  # => false (private)
calc.respond_to?(:internal_helper, true)  # => true (include private)

Method Objects

Methods are first-class objects in Ruby. You can get a reference to a method and call it later:

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

g = Greeter.new

# Get a bound method object
m = g.method(:hello)
puts m.class   # => Method
puts m.call("Alice")  # => "Hello, Alice!"

# Pass as a block
["Alice", "Bob", "Charlie"].map(&g.method(:hello))
# => ["Hello, Alice!", "Hello, Bob!", "Hello, Charlie!"]

Unbound Methods

An UnboundMethod is a method detached from its object. You can bind it to another object of the same class:

class Animal
  def breathe
    "#{self.class} is breathing"
  end
end

unbound = Animal.instance_method(:breathe)
puts unbound.class  # => UnboundMethod

dog = Animal.new
bound = unbound.bind(dog)
puts bound.call  # => "Animal is breathing"

Dynamic Method Definition

define_method

define_method creates instance methods dynamically โ€” useful for generating methods from data:

class Report
  METRICS = [:revenue, :cost, :profit, :margin]

  METRICS.each do |metric|
    define_method(metric) do
      instance_variable_get("@#{metric}") || 0
    end

    define_method("#{metric}=") do |value|
      instance_variable_set("@#{metric}", value)
    end
  end
end

r = Report.new
r.revenue = 100_000
r.cost    = 60_000
r.profit  = r.revenue - r.cost
puts r.profit  # => 40000

method_defined? and define_method with Closures

define_method captures the surrounding scope (closure), unlike def:

class EventEmitter
  EVENTS = [:click, :hover, :focus, :blur]

  EVENTS.each do |event|
    define_method("on_#{event}") do |&block|
      @handlers ||= {}
      @handlers[event] = block
    end

    define_method("trigger_#{event}") do
      @handlers ||= {}
      @handlers[event]&.call
    end
  end
end

emitter = EventEmitter.new
emitter.on_click { puts "Clicked!" }
emitter.trigger_click  # => "Clicked!"

Method Visibility

Methods can be public, private, or protected:

class BankAccount
  def initialize(balance)
    @balance = balance
  end

  # Public โ€” callable from anywhere
  def deposit(amount)
    validate_amount!(amount)
    @balance += amount
  end

  def balance
    @balance
  end

  protected

  # Protected โ€” callable from same class or subclasses
  def transfer_to(other, amount)
    @balance -= amount
    other.receive(amount)
  end

  def receive(amount)
    @balance += amount
  end

  private

  # Private โ€” only callable within the class, no explicit receiver
  def validate_amount!(amount)
    raise ArgumentError, "Amount must be positive" unless amount > 0
  end
end

Method Aliasing

Create alternative names for existing methods:

class String
  alias_method :contains?, :include?
end

"hello world".contains?("world")  # => true

Removing Methods

class MyClass
  def temporary_method
    "I'll be removed"
  end

  # remove_method: removes from this class, superclass version still accessible
  remove_method :temporary_method

  # undef_method: completely blocks the method, even from superclass
  # undef_method :temporary_method
end

Practical: Method Hooks

Ruby provides hooks that fire when methods are added or removed:

class Observable
  def self.method_added(method_name)
    return if method_name == :initialize
    puts "Method added: #{method_name}"
  end

  def greet; "hello"; end   # => Method added: greet
  def farewell; "bye"; end  # => Method added: farewell
end

Summary

Pattern Syntax Scope
Instance method def method_name inside class All instances
Class method def self.method_name The class object
Singleton method def object.method_name One specific object
Generated method attr_accessor, define_method All instances
Method object object.method(:name) Callable reference

Resources

Comments