Skip to content
agosto 22, 2008 / cassiomarques

Métodos de classe adicionados através de modules

Para implementar algo semelhante à herança múltipla em Ruby usamos modules. Através deles podemos usar o conceito de mix-ins, ou seja, podemos misturar os comportamentos descritos na implementação de diversos modules em uma única classe concreta.
Algo assim:

module Bar
  def metodo_legal_de_bar
  end
end

module Foo
  def metodo_legal_de_foo
  end
end

class FooBar
  include Foo
  include Bar
end

f = FooBar.new
f.metodo_legal_de_bar
f.metodo_legal_de_foo

Simples, muito simples. Acabamos de criar dois modules que definem um método cada. O único detalhe é que estes dois métodos são métodos de instância, ou seja, são mensagens que só podem ser passadas para objetos da classe que inclui o módulo. E se eu quiser criar um módulo que define um método de classe que poderá ser utilizado em diversas outras classes?
Bom, o código abaixo não funciona

module Foo
  def self.um_metodo_de_classe_declararo_em_Foo
  end
end

class Blergh
  include Foo
end  

Blergh.um_metodo_de_classe_declararo_em_Foo
# NoMethodError: undefined method 'um_metodo_de_classe_declararo_em_Foo' for Blergh:Class

Hum… e agora, ferrou? Não caro amigo… Ruby é seu amigo e nada lhe faltará. Todo module possui um método muito interessante chamado included. Na verdade esse método é um “hook”, executado automaticamente sempre que um module é incluído em alguma classe. Ok, e para que serve esse treco? Como eu costumo dizer quando vejo algumas coisas em Ruby, serve para fazer macumba! Você pode criar certos comportamentos muito interessantes escrevendo código dentro do método included, como por exemplo resolver nosso problema de criação de métodos de classe através de modules.
O método included recebe um único argumento: A classe que está incluindo aquele método. Assim:

module Bla
  def self.included(base)
    puts "Acabei de ser incluido pela classe #{base}!"
  end
end

class Blergh
  include Bla
end

e só por fazer um “require” no arquivo que declara a classe Blergh você já vai ver no irb:

cassio@cassio-laptop:~$ irb
>> require 'exemplo_module'
Acabei de ser incluido pela classe Blergh
=> true

Interessante hein? Tudo bem, mas como vamos usar isso para “incrementar” nossa classe e criar novos métodos que podem ser chamados sem uma instância? Usamos o método extend. Este método adiciona os métodos de um module no nível da classe, ao contrário do método include, que adiciona os métodos de um module no nível de instância. A técnica a seguir é amplamente utilizada em Ruby para adicionar métodos de classe a uma classe existente, sendo inclusive amplamente utilizada dentro dos fontes do Rails:

module Bla
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def blabla
    end

    def blibli
    end
  end
end

Aqui foi declarado um module dentro de outro, algo bastante comum. Este module foi chamado de ClassMethods, mas qualquer outro nome poderia ter sido usado. É meio que uma convenção em Ruby chamar de ClassMethods, porque assim qualquer um que ler seu código vai entender que ali dentro estão sendo declarados métodos que serão adicionados às classes que incluirem o module em questão.
Quando o module é adicionado em alguma classe, o método included do module é chamado automaticamente e este por sua vez extende esta classe com os métodos declarados no module ClassMethods. Agora podemos fazer assim:

class Blergh
  include Bla
end

Blergh.blabla
Blergh.blibli

Agora, pensando em escopo: O que significa self dentro dos métodos declarados em ClassMethods? Hum… self é a própria classe que incluiu o module e que agora está chamando o método declararo em ClassMethods.

module Bla
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def blabla
      puts "Chamando o metodo de classe 'blabla' a partir da classe #{self}"
    end
  end
end

class Blergh
  include Bla
end

Blergh.blabla # <= Imprime "Chamando o metodo 'blabla' a partir da classe Blergh"

Com isso dá pra fazer muita macumba…

One Comment

Deixe um comentário
  1. nofxx / nov 17 2008 11:17 am

    Excelente… me ajudou numa parada aqui. =D

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: