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…



Excelente… me ajudou numa parada aqui. =D
nofxx
Novembro 17, 2008 em 11:17 am