Self & Metaclasses in Ruby

Self has confused me for a while now so I decided to get to the bottom of the purpose and meaning of self.

TLDR:

  • At any point in your program, there is one and only one self
  • Self is the current object accesible to you
  • It can also be said that Self is the receiver object of the current method (ex: String is the object in String.send(:”length”))
  • Since objects in Ruby are usually instances of classes, self is usually the class you are in at any moment
  • Using self enables you to call a method on a class (like Person), instead of just one instance of a class (like Laura)
  • There are a couple different ways to call methods on self: ‘self.some_method’ OR ‘class << self’ before a group of methods
  • You might want to use self if you want to call a method on any potential instance of that object (every Person is born on planet earth but not every Person is named Laura), especially if you don’t plan on having more than one instance of an object (maybe you’re creating a game with one Person, one Fish, and one Monkey–I’m not sure why you would do that–and you don’t want to create multiple instances of Person).

Okay, now for the longer explanation.

Metaclasses are so meta

Every object in Ruby has a class. But what you might not know is that every object in Ruby also has an invisible metaclass.

We already know that methods of a class are available to all instances of that class, or object. For example, String is an object that responds to the length method. Therefore, every instance of String also responds to this method. That’s why the output of $ “hello”.length => 5.

Each object’s metaclass can also have methods, but they are only attached to the object itself. So, for example, if we could create a change_to_animal method on String’s metaclass that returned “Now I’m an animal!”. We could call it with String.change_to_animal and the output would be “Now I’m an animal!”, but we wouldn’t be able to call this method on any instances of String. Here’s this somewhat impractical example:

class String
    def self.change_to_animal
         "Now I'm an animal!"
    end
end

String.change_to_animal # => "Now I'm an animal!"
"hello".change_to_animal # => undefined method

While self doesn’t seem very useful so far, it can be with objects you create. You can define methods that each instance of objects respond to independently AND you can create methods that only the object (or class) responds to (so that specifc instance don’t really make a difference).

Example:

class Person
	attr_accessor :first_name, :age

	@count = 0
	
	def initialize(first_name, age)
			@name = first_name
			@age = age
	Person.add_one_person #call the add_one_person method on self
	end

	def say_first_name
		"This person's name is #{@name}"
	end

	def birthday #increment age for one instance
		@age += 1
		"This person's age is now #{@age}"
	end

	def self.say_home
		"All persons are from planet Earth."
	end

	def self.add_one_person #increment number of instances
		@count += 1
	end

	def self.count_persons
		"We've added #{@count} persons."
	end
end

Same example, different syntax:

class Person
	attr_accessor :first_name, :age

	@count = 0
	
	def initialize(first_name, age)
			@name = first_name
			@age = age
	Person.add_one_person #call the add_one_person method on self
	end

	def say_first_name
		"This person's name is #{@name}"
	end

	def birthday #increment age for one instance
		@age += 1
		"This person's age is now #{@age}"
	end

	class << self
		def say_home
			"All persons are from planet Earth."
		end

		def add_one_person #increment number of instances
			@count += 1
		end

		def count_persons
			"We've added #{@count} persons."
		end
	end
end

These examples are the same. I’m showing two different syntaxes for using self. You can use the second syntax of “class << self” when you have a group of methods to call on the object itself.

There are a few cool things going on here. We created instances of Person with each instance having it’s own name and age. We are calling birthday on a specific Person which increments and returns their age. We are also asking for the name of the home that all Persons come from, which is planet Earth. Finally, we are keeping track of how many instances we have created of the Person object by calling the add_one_person method on our object every time we create a new instance of the object.

So there you have it. Now you hopefully understand how self and metaclasses work. Have fun using this Ruby knowledge!

References:

http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/
http://en.wikibooks.org/wiki/Ruby_Programming/Classes_and_objects

Leave a Reply

Your email address will not be published. Required fields are marked *