A arte de escrever programas que escrevem programas está presente desde o início da ciência da computação, com o objetivo de aumentar a produtividade diminuindo a quantidade de código a ser escrito. O prefixo grego meta está presente na nossa língua, tornando mais fácil o entendimento do conceito por nós brasileiros logo de cara, bastando apenas lembrar das aulas de literatura ou dos livros de Machado de Assis.

Machadão, o rei da Metaliteratura
Metaclasses
Lembra daquela frase que dizia que “em Ruby tu é objeto”? Pois é, vamos olhar o seguinte código:
class Zombie
def self.alive
puts "i am alive"
end
end
Zombie.class # => Class
Zombie.alive # => "i am alive"
z = Zombie.new
z.class # => Zombie
Quando definimos uma classe (no caso a Zombie), uma constante (que em Ruby são escritas com a primeira letra em maiúsculo, lembram?) é criada com o mesmo nome para armazenar um objeto do tipo Class. Assim, quando chamamos um método dessa “constante”, essa mensagem é passada para um objeto Class que o representa.
É interessante frisar que objetos não armazenam métodos, apenas classes fazem isso. Então quando invocamos um método de Zombie, na verdade, este método está armazenado em um objeto do tipo Class.
Vamos brincar mais um pouco:
Zombie.to_s #=> "Zombie"
Ué? De onde esse método saiu se não o definimos?
O que ocorre aqui é que aquele objeto mencionado anteriormente, que representaria uma classe Zombie, recebe no momento de sua criação todos os métodos com o sufixo “self” ( que representam os métodos de classe) e herda todos os métodos da classe Class. Nesse exemplo, to_s é um método da classe Class. Na verdade, a definição de Zombie é uma classe virtual criada dinamicamente para armazenar os métodos da classe. Esses objetos de classe são chamados de Metaclasses.
Resumindo: Uma classe também é um objeto, mas de um tipo bem particular! Mais precisamente, um objeto da classe Class.
Metaprogramação em Ação
Vamoz continuar brincando com a nossa classe Zombie. É possível que criemos variáveis em tempo de execução dentro de apenas uma instância:
z = Zombie.new
z.instance_variable_set("@eyes", 3)
z.eyes # =>NoMethodError: undefined method `eyes' for #
Opa, será que não funcionou o nosso exemplo? Na verdade, sim. Só que não possuímos nenhum método de acesso à propriedade @eyes. Podemos garantir isso, através do seguinte método:
z.instance_variables # => ["@eyes"]
Mas como faríamos para acessar a propriedade @eyes de nosso zumbi? Isso nos leva ao próximo tópico:
Classes Singleton
Primeiramente, esqueça completamente o Design Pattern Singleton . As classes Singleton de Ruby só compartilham o nome com este padrão. Singleton class pode ser resumida como uma classe virtual, aonde um objeto de uma classe já definida pode assumir um comportamento particular, diferente de outros objetos da mesma classe. Vejamos abaixo:
def z.eyes
"I have #{@eyes} eyes"
end
z.eyes # => "I have 3 eyes"
Nesse exemplo nós estamos criando um método apenas para a instância(objeto) “z” da classe Zombie. Agora nosso objeto “z” é um classe Singleton, única, com um comportamento próprio. Podemos perguntar para um objeto se ele possui métodos singleton:
z.singleton_methods #=> ["eyes"]
Certo, vamos pirar mais um pouco na batatinha. Observe este exemplo:
y = Zombie.new
class << y
def brains_eaten
"millions"
end
end
y.singleton_methods # => ["brains_eaten"]
y.brains_eaten #=> "millions"
Essa sintaxe até certo ponto estranha para iniciantes é o jeito Ruby de dizer que você está “abrindo” uma classe singleton do objeto y . Mas as coisas interessantes não param por aí. Podemos fazer Singletons de instâncias de classes como no exemplo a seguir:
class Fixnum
class << self
def desc
puts "another description"
end
end
end
Fixnum.desc # => another description
Ou, para ficar mais familiar, podemos repetir o mesmo exemplo desta maneira, que é bem comum quando manipulamos objetos do ActiveRecord:
class Fixnum
def self.description
puts "i am a number"
end
end
Fixnum.description # => i am a number
O que fizemos foi simplesmente adicionar um método dinamicamente usando linguagem de programação, ou seja Metapgrogramação.
Por último, ficaremos com um idioma clássico do Ruby, que está presente em qualquer brincadeira que se presente de metaprogramação:
class << self; self; end
Esta pequena e bizarra linha, nos retornará uma classe singleton da classe que a utilizar, como demonstrado no exemplo abaixo:
class Screamer
self.module_eval do
define_method :scream do
puts "I am a instance method!! AAAAAAH"
end
end
(class << self; self; end).module_eval do
define_method :scream do
puts "I am a class method! AAAAAAH"
end
end
end
Screamer.scream # => I am a class method! AAAAAAH
Screamer.new.scream #I am a instance method!! AAAAAAH
Por enquanto é só. No futuro retomarei esse assunto com exemplos melhores para tornar tudo mais claro na prática.
Referências e links para se aprofundar no assunto:
Ruby Metaprogramming techniques por Ola Bini
Seeing Metaclasses Clearly por Whytheluckystiff
Understanding Ruby Singleton Classes por Peter Jones.


Comentário(s) deste Post
Existem 3 comentários
Feb 22, 2009 - 6:29 am
Muito maneiro esse assunto, o meu fraco por hora é entender esses conceitos. Como venho de outras linugagens de programação não tão flexíveis como Ruby, realmente preciso irrigar meu cérebro com esses conceitos. Parabéns pelo post.
Feb 26, 2009 - 2:24 pm
[...] a capacidade de Ruby em se moldar e executar códigos dinamicamente. E seguindo a nossa saga pelos conceitos de metaprogramação, vamos explorar um pouco mais um recurso interessantíssimo de Ruby chamado [...]
Sep 8, 2009 - 3:57 am
Parabéns pelo blog. O que me conquistou no ruby foi a metaprogramação, muito poderosa. No seu exemplo poderíamos ir ainda mais longe. Zombie << DeadBody; Zombie.superclass e por ai vai.
o/