Proriedades de Objetos e Metaprogramação em Ruby
Autor: Douglas Camata | Publicado: 2010-09-25T23:46:00-07:00

Olá pessoal. Hoje mais cedo, um amigo (Pedro Henrique) me veio perguntar criar propriedades para objetos em Ruby e então eu decidi fazer esse post para ajudar a galera que ta começando com a linguagem. Criar propriedades em Ruby não é muito complicado, mas para a primeira vez exige um pouco de pesquisa.Vamos começar então. Primeiramente espero que você saiba definir uma classe (olha lá hein!). Criaremos uma classe Pessoa que deve receber um nome no momento da criação, por exemplo.[ruby]class Pessoa  def initialize(nome)    @nome = nome  endend[/ruby]A partir dessa classe, se você criar um objeto Pessoa com qualquer nome e tentar acessar a propriedade nome você terá um NoMethodError. Existem duas maneiras de criar essa propriedade nome. Vamos começar pela mais bruta (setter e getter). Se quando você chamou a propriedade nome o Ruby disse que o método não existe, vamos criá-lo:[ruby]class Pessoa  def initialize(nome)    @nome = nome  end  def nome    @nome  endend[/ruby]Desta maneira você já conseguirá criar um objeto da classe Pessoa e acessar sua propriedade nome. Porém ao tentar mudá-la, novamente, será lançado NoMethodError para a função "nome=". Então vamos criá-la.[ruby]class Pessoa  def initialize(nome)    @nome = nome  end  def nome    @nome  end  def nome=(novo_nome)    @nome = novo_nome  endend[/ruby]Ótimo! Agora podemos acessar e modificar a propriedade nome dos objetos criados a partir da classe Pessoa. Mas essa maneira de fazer isso é bem feia e trabalhosa. Existe um "açucar sintático" que torna a criação de propriedades bem mais simples. São as funções attr_reader, attr_write e attr_accessor. A classe Pessoa poderia ser reescrita, com o mesmo funcionamento, da seguinte maneira:[ruby]class Pessoa  attr_accessor :nome  def initialize(nome)    @nome = nome  endend[/ruby]A linha 3 do código tem um funcionamento interessante. Ela transforma o símbolo passado como parâmetro para string, chama a função define_method, que define um método na classe onde ela é chamada. Ela passa então, via bloco de código, instance_variable_get, que por sua vez, recebe uma string como parâmetro e retorna o valor da variável com esse nome na instância da classe onde a função foi chamada. E para permitir a mudança as coisas não mudam muito, porém o método passa para o bloco de código uma variável, que seria o novo valor, e chama a função instance_variable_set que além da variável a ser mudada recebe o seu novo valor. Cabuloso, não?Quanto aos outros dois métodos "mágicos": o attr_reader, como seu nome diz, cria um método de acesso à propriedade e o attr_writer cria um método de escrita.Ai vai uma implementação dos 3 para você entender melhor como funciona:[ruby]def new_attr_reader(attr)  define_method(&quot;#{attr}&quot;) { instance_variable_get(&quot;@#{attr}&quot;) }enddef new_attr_writer(attr)  define_method(&quot;#{attr}=&quot;) { |new_value| instance_variable_set(&quot;@#{attr}&quot;, new_value) }enddef new_attr_accessor(attr)  new_attr_reader(attr)  new_attr_writer(attr)end[/ruby]Isso nos mostra um pouco (?) do poder da metaprogramação em Ruby, que nos permite modificar classes em runtime e adicionar a elas: métodos, propriedades, variáveis... tudo que você pode imaginar! É código que modifica código. Praticamente 80% da facilidade, graça e poder do Ruby On Rails só pode existir graças a essa poderosa ferramenta que vale a pena ser estudada com paciência e atenção.Bom, agora vocês já devem estar experts nisso (ou não). Até a próxima pessoal... e qualquer problema ou dúvida é só comentar! :D