# Getting to Know Yourself: Understanding self in Ruby

`self` is one of those Ruby keywords that looks easy until you meet it in real code.

You see it in a setter:

```ruby
self.name = normalized_name
```

Then in a class method:

```ruby
def self.from_cents(...)
```

Then somewhere inside a DSL or `instance_eval`, and suddenly the question isn't really:

> What does `self` mean?

The better question is:

> **Who is** `self` **right here?**

That small change makes Ruby's object model much easier to follow.

## The setter case is where it starts to matter

Consider a model-like object:

```ruby
class Customer
  attr_accessor :name, :email

  def initialize(name:, email:)
    @name = name
    @email = email
  end

  def normalize
    self.name = name.strip.downcase
    self.email = email.strip.downcase
  end

  def contact
    "#{name} <#{email}>"
  end
end

customer = Customer.new(
  name: "Maya",
  email: "MAYA@EXAMPLE.COM "
)

customer.normalize

puts customer.contact
```

There is something easy to miss here:

```ruby
self.name = name.strip.downcase
```

Why not:

```ruby
name = name.strip.downcase
```

Because those two lines are doing different things.

The second one is a local variable assignment.

The first one calls the `name=` setter on the current object.

Ruby lets us call the getter without explicitly writing the receiver:

```ruby
name
```

But assignment creates an ambiguity. When we write:

```ruby
self.name = ...
```

we are making the receiver explicit.

In other words:

```ruby
self.name = "Maya"
```

means:

> Call `name=` on the object I'm currently working with.

That object is `self`.

And notice something important.

We didn't create `self`.

We didn't assign it.

Ruby already knew what `self` was when `normalize` started running.

That is the first useful clue.

`self` is less about a special keyword and more about **the object currently receiving the work**.

## Most of the time, Ruby hides it from you

Take a slightly more realistic object:

```ruby
class Invoice
  def initialize(customer, total)
    @customer = customer
    @total = total
    @status = :open
  end

  def close
    return false unless valid_for_closing?

    mark_as_paid
    notify_customer
    true
  end

  private

  def valid_for_closing?
    @total.positive? && @status == :open
  end

  def mark_as_paid
    @status = :paid
  end

  def notify_customer
    puts "Invoice notification sent to #{@customer}"
  end
end
```

Inside `close`, Ruby sees:

```ruby
mark_as_paid
notify_customer
```

You could make the receiver explicit:

```ruby
self.mark_as_paid
self.notify_customer
```

But normally there is no reason to.

The receiver is already there.

It is `self`.

So when Ruby evaluates:

```ruby
mark_as_paid
```

the useful mental model is:

```ruby
self.mark_as_paid
```

The receiver is implicit.

This is one of those Ruby features that makes code pleasant to read, but it can also hide an important detail.

The receiver isn't missing.

**It is invisible.**

And that becomes interesting when we start asking when Ruby changes that context.

## There are scope gates for `self`

This is where I find it useful to stop thinking about every Ruby construct as if it creates a new `self`.

It doesn't.

A block is a good example.

Consider:

```ruby
class Report
  def initialize(rows)
    @rows = rows
  end

  def failed_rows
    failures = []

    @rows.each do |row|
      next unless row[:status] == :failed

      failures << {
        id: row[:id],
        reason: row[:error]
      }
    end

    failures
  end
end
```

Inside the block:

```ruby
@rows.each do |row|
```

`row` is the block's local value.

It is not `self`.

And the block does not automatically replace the surrounding `self`.

So inside that block, `self` is still the `Report` instance.

This gives us a useful mental distinction:

**Not every nested piece of Ruby code is a new** `self` **scope.**

A block can introduce local variables and control flow without automatically changing the receiver.

But structures such as:

```ruby
class ...
module ...
def ...
```

create important boundaries in how Ruby evaluates code and what `self` refers to.

And then there are APIs that deliberately change it.

For this article, I'll call these boundaries **scope gates**. That's a mental model, not an official Ruby term.

The useful idea is simply:

> **A block is not automatically a** `self` **gate. Context-changing constructs and APIs are.**

Once you see that, some Ruby behavior that initially feels inconsistent becomes much easier to predict.

And now we can look at the class side.

## The same keyword can point at the class

Consider a factory-style class method:

```ruby
class Invoice
  def initialize(customer, total)
    @customer = customer
    @total = total
  end

  def self.from_cents(customer, cents)
    new(customer, cents / 100.0)
  end

  def summary
    "#{@customer}: $#{format('%.2f', @total)}"
  end
end

invoice = Invoice.from_cents("Maya", 12500)

puts invoice.summary
```

This line:

```ruby
def self.from_cents
```

can look like special Ruby grammar.

It isn't really a separate universe.

The important part is still the receiver.

While Ruby evaluates the body of the class definition, `self` refers to the `Invoice` class object.

So:

```ruby
def self.from_cents
```

is essentially saying:

> Define `from_cents` on the object currently referenced by `self`.

And in this context, that object is `Invoice`.

That is why this works:

```ruby
Invoice.from_cents(...)
```

The class itself is an object, and that object can receive method calls.

Now compare the two situations:

```ruby
invoice.summary
```

Here the receiver is an `Invoice` instance.

```ruby
Invoice.from_cents(...)
```

Here the receiver is the `Invoice` class object.

Nothing about the keyword changed.

**The context did.**

That distinction is much more useful than memorizing "instance method versus class method" as two unrelated Ruby features.

## Rails makes this even more visible

You see the same idea constantly in Rails models.

For example:

```ruby
class Subscription < ApplicationRecord
  scope :active, -> {
    where(status: :active)
  }

  def self.for_customer(customer)
    active.where(customer: customer)
  end

  def cancel!
    update!(
      status: :cancelled,
      cancelled_at: Time.current
    )
  end
end
```

There are several receivers involved here.

When you call:

```ruby
subscription.cancel!
```

the receiver is the `subscription` instance.

Inside `cancel!`, the call to:

```ruby
update!(...)
```

is being sent to that instance.

But:

```ruby
Subscription.for_customer(customer)
```

is sent to the `Subscription` class object.

And Rails adds another interesting layer with `scope`.

The block passed to `scope` is not simply "a new self." The surrounding Ruby context and the way Rails evaluates the scope both matter.

This is why a useful question when reading Rails code is not just:

> What method is this?

but:

> **What object is receiving this method here?**

That question scales much better when the code gets more dynamic.

## Then `instance_eval` deliberately moves the context

Now we can make the context change explicit.

Consider a configuration object:

```ruby
class Configuration
  def initialize
    @values = {}
  end

  def [](key)
    @values[key]
  end

  def values
    @values.dup
  end
end

config = Configuration.new

config.instance_eval do
  @values[:environment] = "production"
  @values[:region] = "eu-west"
  @values[:logging] = :verbose
end

puts config[:environment]
puts config[:region]
```

Why can this block access:

```ruby
@values
```

directly?

Because `instance_eval` evaluates the block with the receiver as `self`.

Before entering the block, `self` referred to the surrounding context.

Inside:

```ruby
config.instance_eval do
```

`self` becomes `config`.

That is a deliberate context change.

And this is where the earlier idea of scope gates becomes useful.

The block itself didn't inherently create a new `self`.

`instance_eval` did.

Ruby gives the programmer an explicit mechanism for saying:

> Evaluate this code as if this object is the current receiver.

That is a very different thing from simply passing a block around.

## `instance_exec` takes the same idea one step further

There is a subtle difference between `instance_eval` and `instance_exec` that becomes important when designing DSLs.

Suppose we want to configure an object, but we also want to pass information into the block:

```ruby
class Endpoint
  def initialize
    @settings = {}
  end

  def settings
    @settings
  end
end

endpoint = Endpoint.new

method = :post
path = "/users"

endpoint.instance_exec(method, path) do |http_method, route|
  @settings[:method] = http_method
  @settings[:path] = route
  @settings[:authenticated] = true
end

puts endpoint.settings
```

The important part is:

```ruby
endpoint.instance_exec(method, path) do |http_method, route|
```

`instance_exec` changes `self` to `endpoint`, just like the context-changing idea we saw with `instance_eval`.

But it also lets us pass arguments into the block.

So inside the block we have both:

```ruby
self
```

pointing at `endpoint`, and:

```ruby
http_method
route
```

coming from the outside.

That combination is useful when building expressive APIs.

It lets a DSL control the execution context without losing the ability to feed the block data.

And suddenly a pattern like:

```ruby
resource :users do
  method :get
  path "/users"
end
```

starts looking less magical.

The DSL designer may be combining:

*   a receiver that becomes `self`
    
*   a block that defines the configuration
    
*   arguments or yielded values
    
*   method dispatch through that context
    

The syntax is the surface.

The context is the machinery underneath.

## This is why Ruby DSLs can feel strange

Once you start thinking in terms of context, Ruby DSLs become less mysterious.

You might see code that looks almost like configuration:

```ruby
resource :users do
  path "/users"
  method :get
  authentication :required
end
```

The interesting question isn't only:

> What does `resource` do?

It is also:

> **What is** `self` **while this block is being evaluated?**

Maybe the DSL is yielding to a builder object.

Maybe it is using `instance_eval`.

Maybe it is using `instance_exec` because it needs both a changed receiver and arguments.

Maybe it is delegating method calls.

The surface syntax can look almost identical while the underlying execution model is very different.

That's why `self` becomes particularly useful when reading metaprogramming-heavy Ruby.

Instead of getting stuck on the syntax, look for the receiver.

Then look for the context.

Then look for the point where that context changes.

## So, who is `self`?

I don't think of `self` as a definition I need to memorize anymore.

I think of it as a signpost.

When I see:

```ruby
self.name = value
```

I ask:

> Which object is receiving this assignment?

When I see:

```ruby
def self.build
```

I ask:

> What is `self` while Ruby is evaluating this class body?

When I see:

```ruby
something.instance_eval do
```

I ask:

> Did Ruby just change the receiver for this block?

When I see:

```ruby
something.instance_exec(data) do |value|
```

I ask two questions:

> What is `self` now?

and:

> What data was passed into this context?

And when I see a Ruby DSL that seems to have methods appearing out of nowhere, I ask:

> **Who is** `self` **here, and where did that context come from?**

That question usually gets me closer to the actual behavior than simply remembering what the keyword means.

Because `self` isn't really the interesting part.

**Context is.**

Once you start reading Ruby through that lens, `self` becomes less of a keyword to memorize and more of a way to locate yourself inside Ruby's object model.

And I'm curious how other Ruby developers approach this.

Do you consciously think about `self` while reading Ruby code, or has it become one of those things you only notice when the context suddenly changes?
