Ruby Object Model – self


Leia em 1 minuto

Quando você decidiu aprender Ruby, ficou sabendo que tudo é objeto; números, classes, módulos… tudo, sem exceção, é realmente um objeto. Só que isso traz algumas implicações que nem todos conhecem. A ideia é fazer com que você realmente entenda a essência de Ruby em uma série de artigos sobre o Ruby Object Model e Metaprogramming.

Entendendo o self

self será sempre uma referência ao receiver atual e pode ser diferente dependendo do contexto em que você estiver. Por exemplo, quando estamos no namespace global, nosso self será o objeto main. Já em uma classe, nosso self será a própria classe.

puts self
#=> main

class Thing
  puts self
end
#=> Thing

Sempre que executar um método, o Ruby irá verificar se esse método existe no receiver padrão—self— a menos que você especifique-o explicitamente. E, pelo fato de o receiver padrão ser self, você nem precisa especificá-lo se não quiser.

class Thing
  def do_something
  	puts "doing something"
  end

  def do_something_else
    do_something
  end
end

No método do_something_else poderíamos usar self.do_something, mas isso faria com que nosso código apenas ficasse mais poluído. No entando, definir o receiver é uma coisa muito comum e que, você pode até não se dar conta, mas o faz constantemente quando escreve código Ruby.

numbers = [3,1,2]
numbers.sort
#=> [1,2,3]

ou ainda

text = "some string"
text.upcase
#=> "SOME STRING"

Na prática, o receiver é especificado antes do ponto na chamada de métodos, como em numbers.sort ou text.upcase.

Além de ser o receiver padrão, self também é o responsável por armazenar variáveis de instância de um objeto. Veja o exemplo abaixo.

class Person
  def initialize(name)
    @name = name
  end

  def name
    @name
  end
end

john = Person.new("John Doe")
john.name
#=> "John Doe"

A instância da classe Person possui uma única variável de instância associada ao seu objeto—self—que é retornada pelo método name. Analogamente, podemos definir variáveis de instância em qualquer objeto, como classes.

class Person
  def self.count
    @count ||= 0
  end

  def self.count=(increment)
    @count = increment
  end

  def initialize(name)
    @name = name
    self.class.count += 1
  end

  def name
    @name
  end
end

john = Person.new("John Doe")
Person.count
#=> 1

O exemplo acima mostra como variáveis de instância podem ser usadas em contextos diferentes. Primeiro, estamos definindo um contador de instâncias da classe Person, cujo valor será armazenado em @count. Depois, em nossa própria instância, definimos o nome com a variável @name.

Finalizando

No próximo artigo, irei falar sobre metaclasses do Ruby. Até lá!