<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Sina Rezaei]]></title><description><![CDATA[Sina Rezaei]]></description><link>https://sinarezaei.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Sina Rezaei</title><link>https://sinarezaei.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 10 Oct 2026 12:10:53 GMT</lastBuildDate><atom:link href="https://sinarezaei.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Getting to Know Yourself: Understanding self in Ruby]]></title><description><![CDATA[self is one of those Ruby keywords that looks easy until you meet it in real code.
You see it in a setter:
self.name = normalized_name

Then in a class method:
def self.from_cents(...)

Then somewhere]]></description><link>https://sinarezaei.hashnode.dev/getting-to-know-yourself-understanding-self-in-ruby</link><guid isPermaLink="true">https://sinarezaei.hashnode.dev/getting-to-know-yourself-understanding-self-in-ruby</guid><category><![CDATA[Metaprogramming ]]></category><category><![CDATA[Ruby]]></category><category><![CDATA[Rails]]></category><dc:creator><![CDATA[Sina Rezaei]]></dc:creator><pubDate>Thu, 08 Oct 2026 13:13:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ac792875f5e138fe84f61ac/04963562-5e2a-4779-ad5c-cafb85c1f968.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><code>self</code> is one of those Ruby keywords that looks easy until you meet it in real code.</p>
<p>You see it in a setter:</p>
<pre><code class="language-ruby">self.name = normalized_name
</code></pre>
<p>Then in a class method:</p>
<pre><code class="language-ruby">def self.from_cents(...)
</code></pre>
<p>Then somewhere inside a DSL or <code>instance_eval</code>, and suddenly the question isn't really:</p>
<blockquote>
<p>What does <code>self</code> mean?</p>
</blockquote>
<p>The better question is:</p>
<blockquote>
<p><strong>Who is</strong> <code>self</code> <strong>right here?</strong></p>
</blockquote>
<p>That small change makes Ruby's object model much easier to follow.</p>
<h2>The setter case is where it starts to matter</h2>
<p>Consider a model-like object:</p>
<pre><code class="language-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} &lt;#{email}&gt;"
  end
end

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

customer.normalize

puts customer.contact
</code></pre>
<p>There is something easy to miss here:</p>
<pre><code class="language-ruby">self.name = name.strip.downcase
</code></pre>
<p>Why not:</p>
<pre><code class="language-ruby">name = name.strip.downcase
</code></pre>
<p>Because those two lines are doing different things.</p>
<p>The second one is a local variable assignment.</p>
<p>The first one calls the <code>name=</code> setter on the current object.</p>
<p>Ruby lets us call the getter without explicitly writing the receiver:</p>
<pre><code class="language-ruby">name
</code></pre>
<p>But assignment creates an ambiguity. When we write:</p>
<pre><code class="language-ruby">self.name = ...
</code></pre>
<p>we are making the receiver explicit.</p>
<p>In other words:</p>
<pre><code class="language-ruby">self.name = "Maya"
</code></pre>
<p>means:</p>
<blockquote>
<p>Call <code>name=</code> on the object I'm currently working with.</p>
</blockquote>
<p>That object is <code>self</code>.</p>
<p>And notice something important.</p>
<p>We didn't create <code>self</code>.</p>
<p>We didn't assign it.</p>
<p>Ruby already knew what <code>self</code> was when <code>normalize</code> started running.</p>
<p>That is the first useful clue.</p>
<p><code>self</code> is less about a special keyword and more about <strong>the object currently receiving the work</strong>.</p>
<h2>Most of the time, Ruby hides it from you</h2>
<p>Take a slightly more realistic object:</p>
<pre><code class="language-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? &amp;&amp; @status == :open
  end

  def mark_as_paid
    @status = :paid
  end

  def notify_customer
    puts "Invoice notification sent to #{@customer}"
  end
end
</code></pre>
<p>Inside <code>close</code>, Ruby sees:</p>
<pre><code class="language-ruby">mark_as_paid
notify_customer
</code></pre>
<p>You could make the receiver explicit:</p>
<pre><code class="language-ruby">self.mark_as_paid
self.notify_customer
</code></pre>
<p>But normally there is no reason to.</p>
<p>The receiver is already there.</p>
<p>It is <code>self</code>.</p>
<p>So when Ruby evaluates:</p>
<pre><code class="language-ruby">mark_as_paid
</code></pre>
<p>the useful mental model is:</p>
<pre><code class="language-ruby">self.mark_as_paid
</code></pre>
<p>The receiver is implicit.</p>
<p>This is one of those Ruby features that makes code pleasant to read, but it can also hide an important detail.</p>
<p>The receiver isn't missing.</p>
<p><strong>It is invisible.</strong></p>
<p>And that becomes interesting when we start asking when Ruby changes that context.</p>
<h2>There are scope gates for <code>self</code></h2>
<p>This is where I find it useful to stop thinking about every Ruby construct as if it creates a new <code>self</code>.</p>
<p>It doesn't.</p>
<p>A block is a good example.</p>
<p>Consider:</p>
<pre><code class="language-ruby">class Report
  def initialize(rows)
    @rows = rows
  end

  def failed_rows
    failures = []

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

      failures &lt;&lt; {
        id: row[:id],
        reason: row[:error]
      }
    end

    failures
  end
end
</code></pre>
<p>Inside the block:</p>
<pre><code class="language-ruby">@rows.each do |row|
</code></pre>
<p><code>row</code> is the block's local value.</p>
<p>It is not <code>self</code>.</p>
<p>And the block does not automatically replace the surrounding <code>self</code>.</p>
<p>So inside that block, <code>self</code> is still the <code>Report</code> instance.</p>
<p>This gives us a useful mental distinction:</p>
<p><strong>Not every nested piece of Ruby code is a new</strong> <code>self</code> <strong>scope.</strong></p>
<p>A block can introduce local variables and control flow without automatically changing the receiver.</p>
<p>But structures such as:</p>
<pre><code class="language-ruby">class ...
module ...
def ...
</code></pre>
<p>create important boundaries in how Ruby evaluates code and what <code>self</code> refers to.</p>
<p>And then there are APIs that deliberately change it.</p>
<p>For this article, I'll call these boundaries <strong>scope gates</strong>. That's a mental model, not an official Ruby term.</p>
<p>The useful idea is simply:</p>
<blockquote>
<p><strong>A block is not automatically a</strong> <code>self</code> <strong>gate. Context-changing constructs and APIs are.</strong></p>
</blockquote>
<p>Once you see that, some Ruby behavior that initially feels inconsistent becomes much easier to predict.</p>
<p>And now we can look at the class side.</p>
<h2>The same keyword can point at the class</h2>
<p>Consider a factory-style class method:</p>
<pre><code class="language-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
</code></pre>
<p>This line:</p>
<pre><code class="language-ruby">def self.from_cents
</code></pre>
<p>can look like special Ruby grammar.</p>
<p>It isn't really a separate universe.</p>
<p>The important part is still the receiver.</p>
<p>While Ruby evaluates the body of the class definition, <code>self</code> refers to the <code>Invoice</code> class object.</p>
<p>So:</p>
<pre><code class="language-ruby">def self.from_cents
</code></pre>
<p>is essentially saying:</p>
<blockquote>
<p>Define <code>from_cents</code> on the object currently referenced by <code>self</code>.</p>
</blockquote>
<p>And in this context, that object is <code>Invoice</code>.</p>
<p>That is why this works:</p>
<pre><code class="language-ruby">Invoice.from_cents(...)
</code></pre>
<p>The class itself is an object, and that object can receive method calls.</p>
<p>Now compare the two situations:</p>
<pre><code class="language-ruby">invoice.summary
</code></pre>
<p>Here the receiver is an <code>Invoice</code> instance.</p>
<pre><code class="language-ruby">Invoice.from_cents(...)
</code></pre>
<p>Here the receiver is the <code>Invoice</code> class object.</p>
<p>Nothing about the keyword changed.</p>
<p><strong>The context did.</strong></p>
<p>That distinction is much more useful than memorizing "instance method versus class method" as two unrelated Ruby features.</p>
<h2>Rails makes this even more visible</h2>
<p>You see the same idea constantly in Rails models.</p>
<p>For example:</p>
<pre><code class="language-ruby">class Subscription &lt; ApplicationRecord
  scope :active, -&gt; {
    where(status: :active)
  }

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

  def cancel!
    update!(
      status: :cancelled,
      cancelled_at: Time.current
    )
  end
end
</code></pre>
<p>There are several receivers involved here.</p>
<p>When you call:</p>
<pre><code class="language-ruby">subscription.cancel!
</code></pre>
<p>the receiver is the <code>subscription</code> instance.</p>
<p>Inside <code>cancel!</code>, the call to:</p>
<pre><code class="language-ruby">update!(...)
</code></pre>
<p>is being sent to that instance.</p>
<p>But:</p>
<pre><code class="language-ruby">Subscription.for_customer(customer)
</code></pre>
<p>is sent to the <code>Subscription</code> class object.</p>
<p>And Rails adds another interesting layer with <code>scope</code>.</p>
<p>The block passed to <code>scope</code> is not simply "a new self." The surrounding Ruby context and the way Rails evaluates the scope both matter.</p>
<p>This is why a useful question when reading Rails code is not just:</p>
<blockquote>
<p>What method is this?</p>
</blockquote>
<p>but:</p>
<blockquote>
<p><strong>What object is receiving this method here?</strong></p>
</blockquote>
<p>That question scales much better when the code gets more dynamic.</p>
<h2>Then <code>instance_eval</code> deliberately moves the context</h2>
<p>Now we can make the context change explicit.</p>
<p>Consider a configuration object:</p>
<pre><code class="language-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]
</code></pre>
<p>Why can this block access:</p>
<pre><code class="language-ruby">@values
</code></pre>
<p>directly?</p>
<p>Because <code>instance_eval</code> evaluates the block with the receiver as <code>self</code>.</p>
<p>Before entering the block, <code>self</code> referred to the surrounding context.</p>
<p>Inside:</p>
<pre><code class="language-ruby">config.instance_eval do
</code></pre>
<p><code>self</code> becomes <code>config</code>.</p>
<p>That is a deliberate context change.</p>
<p>And this is where the earlier idea of scope gates becomes useful.</p>
<p>The block itself didn't inherently create a new <code>self</code>.</p>
<p><code>instance_eval</code> did.</p>
<p>Ruby gives the programmer an explicit mechanism for saying:</p>
<blockquote>
<p>Evaluate this code as if this object is the current receiver.</p>
</blockquote>
<p>That is a very different thing from simply passing a block around.</p>
<h2><code>instance_exec</code> takes the same idea one step further</h2>
<p>There is a subtle difference between <code>instance_eval</code> and <code>instance_exec</code> that becomes important when designing DSLs.</p>
<p>Suppose we want to configure an object, but we also want to pass information into the block:</p>
<pre><code class="language-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
</code></pre>
<p>The important part is:</p>
<pre><code class="language-ruby">endpoint.instance_exec(method, path) do |http_method, route|
</code></pre>
<p><code>instance_exec</code> changes <code>self</code> to <code>endpoint</code>, just like the context-changing idea we saw with <code>instance_eval</code>.</p>
<p>But it also lets us pass arguments into the block.</p>
<p>So inside the block we have both:</p>
<pre><code class="language-ruby">self
</code></pre>
<p>pointing at <code>endpoint</code>, and:</p>
<pre><code class="language-ruby">http_method
route
</code></pre>
<p>coming from the outside.</p>
<p>That combination is useful when building expressive APIs.</p>
<p>It lets a DSL control the execution context without losing the ability to feed the block data.</p>
<p>And suddenly a pattern like:</p>
<pre><code class="language-ruby">resource :users do
  method :get
  path "/users"
end
</code></pre>
<p>starts looking less magical.</p>
<p>The DSL designer may be combining:</p>
<ul>
<li><p>a receiver that becomes <code>self</code></p>
</li>
<li><p>a block that defines the configuration</p>
</li>
<li><p>arguments or yielded values</p>
</li>
<li><p>method dispatch through that context</p>
</li>
</ul>
<p>The syntax is the surface.</p>
<p>The context is the machinery underneath.</p>
<h2>This is why Ruby DSLs can feel strange</h2>
<p>Once you start thinking in terms of context, Ruby DSLs become less mysterious.</p>
<p>You might see code that looks almost like configuration:</p>
<pre><code class="language-ruby">resource :users do
  path "/users"
  method :get
  authentication :required
end
</code></pre>
<p>The interesting question isn't only:</p>
<blockquote>
<p>What does <code>resource</code> do?</p>
</blockquote>
<p>It is also:</p>
<blockquote>
<p><strong>What is</strong> <code>self</code> <strong>while this block is being evaluated?</strong></p>
</blockquote>
<p>Maybe the DSL is yielding to a builder object.</p>
<p>Maybe it is using <code>instance_eval</code>.</p>
<p>Maybe it is using <code>instance_exec</code> because it needs both a changed receiver and arguments.</p>
<p>Maybe it is delegating method calls.</p>
<p>The surface syntax can look almost identical while the underlying execution model is very different.</p>
<p>That's why <code>self</code> becomes particularly useful when reading metaprogramming-heavy Ruby.</p>
<p>Instead of getting stuck on the syntax, look for the receiver.</p>
<p>Then look for the context.</p>
<p>Then look for the point where that context changes.</p>
<h2>So, who is <code>self</code>?</h2>
<p>I don't think of <code>self</code> as a definition I need to memorize anymore.</p>
<p>I think of it as a signpost.</p>
<p>When I see:</p>
<pre><code class="language-ruby">self.name = value
</code></pre>
<p>I ask:</p>
<blockquote>
<p>Which object is receiving this assignment?</p>
</blockquote>
<p>When I see:</p>
<pre><code class="language-ruby">def self.build
</code></pre>
<p>I ask:</p>
<blockquote>
<p>What is <code>self</code> while Ruby is evaluating this class body?</p>
</blockquote>
<p>When I see:</p>
<pre><code class="language-ruby">something.instance_eval do
</code></pre>
<p>I ask:</p>
<blockquote>
<p>Did Ruby just change the receiver for this block?</p>
</blockquote>
<p>When I see:</p>
<pre><code class="language-ruby">something.instance_exec(data) do |value|
</code></pre>
<p>I ask two questions:</p>
<blockquote>
<p>What is <code>self</code> now?</p>
</blockquote>
<p>and:</p>
<blockquote>
<p>What data was passed into this context?</p>
</blockquote>
<p>And when I see a Ruby DSL that seems to have methods appearing out of nowhere, I ask:</p>
<blockquote>
<p><strong>Who is</strong> <code>self</code> <strong>here, and where did that context come from?</strong></p>
</blockquote>
<p>That question usually gets me closer to the actual behavior than simply remembering what the keyword means.</p>
<p>Because <code>self</code> isn't really the interesting part.</p>
<p><strong>Context is.</strong></p>
<p>Once you start reading Ruby through that lens, <code>self</code> becomes less of a keyword to memorize and more of a way to locate yourself inside Ruby's object model.</p>
<p>And I'm curious how other Ruby developers approach this.</p>
<p>Do you consciously think about <code>self</code> while reading Ruby code, or has it become one of those things you only notice when the context suddenly changes?</p>
]]></content:encoded></item></channel></rss>