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.