Home > Software design >  What is the 'self' keyword doing exactly in this Class method?
What is the 'self' keyword doing exactly in this Class method?

Time:03-31

  class Restaurant

  attr_accessor :average_rating, :city

  def initialize(city, name)
    @city = city
    @name = name
    @number_of_ratings = 0
    @sum_of_ratings = 0
  end

  def rate(new_rate)
    @number_of_ratings  = 1
    @sum_of_ratings  = new_rate
    @average_rating = @sum_of_ratings.to_f / @number_of_ratings
  end

  def self.filter_by_city(restaurants, city)
    restaurants.select { |restaurant| restaurant.city == city }
  end
end

The above code is part of a challenge and I kept failing the tests for the #filter_by_city method. I checked the solution and the only difference was the self. prior to the method name. I've tried to understand what self does exactly but it's difficult to understand without context. In this particular class method, what exactly is self doing? I know what the body of the method is doing i.e. the filtering of the restaurants by city, but how does it run exactly?

CodePudding user response:

self is the class Restaurant. def self.method is how you implement a method on the class itself rather than an instance of the class. Restaurant.filter_by_city(...) rather than Restaurant.new.filter_by_city(...).

self changes in Ruby depending on context. Within a method, self is the object the method was called on.

Within the class Restaurant block, and outside of any method, self is the Restaurant object which is a Class object. Everything is an object in Ruby. Everything.

You can also do this by declaring a block where the class is the instance.

class << self
  def filter_by_city(restaurants, city)
    restaurants.select { |restaurant| restaurant.city == city }
  end
end

Normally you'd use this syntax if you have a lot of class methods.

See Self in Ruby: A Comprehensive Overview for more.

CodePudding user response:

When defining a method in ruby you can optionally explicitly define that method's receiver using def <receiver>.<method> syntax instead of plain def <method>

object = Object.new

def object.foo
  :foo
end

object.foo #=> foo

The receiver must either be a singular reference OR an expression (but it must be enclosed by brackets):

a = [Object.new, Object.new]

def (a.first).foo
  :foo
end

def (a[1]).bar
  :bar
end

a[0].foo #=> :foo
a.last.bar #=> :bar
a.first.bar #=> undefined method

When receiver is defined, the method is defined directly on the receiver's singleton class, ignoring the context in which the method is defined:

class A
  o = Obejct.new
  
  def o.foo
  end
end

A.new.foo #=> undefined method

Even though method foo was defined in class A body, it is not available to its instances because of the explicit receiver

self is a ruby keyword returning the current "context". Inside the methods, self is (usually) a receiver of the call, and inside the module self returns that module. So:

module Wrapper
  module SomeModule
    puts self.name
  end
end

will print Wrapper::SomeModule.

This means that:

class A
  def self.foo
  end
end

Is exactly the same as:

class A
  def A.foo
  end
end

So, the method is defined directly on A and can only be called directly on the class as A.foo, rather than on its instances.

  • Related