Auditando Métodos em Ruby

Mais um da série “Coisas que você nunca quis fazer com Ruby, e tinha medo de perguntar”, embora o título não seja tão correto assim: bem ou mal, eu precisei fazer isso algumas vezes em um ou outro trabalho meu, mas enfim, vamos ao cenário:

Imagine que você está mexendo num código de alguém. O código está com acoplamento muito alto, e antes de refatorá-lo você precisa entendê-lo – e é aí que entra o problema. Ruby, como todos sabemos, não tem uma IDE muito boa, com um debugger muito bom, e para fazer esse processo seria legal se, por exemplo, eu pudesse rodar um comando e ele me retornasse: “Entrei no método X, Entrei no método Y, Saí do método Y”, etc.

Bom, o fato é que é bem simples de fazer isso com Ruby:

Para tal, basta “apenas” reabrir todas as classes que formos auditar, puxar todos os métodos que são implementados naquela classe, e re-escrevê-los de forma que seja informado que aquele método foi chamado. Como não há muito desafio em fazer tal coisa, apresentarei duas formas de fazê-lo.
(more…)

Métodos privados semelhantes a Java/C++ em Ruby

Fazia um tempo que eu não postava uma das bizarrices em Ruby, então vamos lá: esses dias, no grupo de usuários de Ruby, surgiu uma discussão muito boa sobre métodos privados e alto acoplamento. A idéia é mais ou menos assim: em Java (ou C++), ao definir um método público que chama um método privado, se você reimplementar aquele método privado, os métodos públicos herdados não são afetados. Um exemplo vale mais que mil palavras, então:

#include <stdio.h>

class Example {
    public:
    void imprimir() {
        hello();
    }

    private:
    void hello() {
        printf("Hello, world!\n");
    }
};

class Ex2: public Example {
    private:
    void hello() {
        printf("Hello, from Ex2\n");
    }
};

int main(void) {
    Ex2 e;
    e.imprimir(); //Essa linha imprime "Hello, world!"
}

Já no código Ruby equivalente:

class Example 
  def imprimir()
    hello
  end

  private
  def hello
    puts "Hello, world!"
  end
end

class Ex2 < Example 
  private
  def hello
    puts "Hello, from Ex2"
  end
end

Ex2.new.imprimir #Imprime "Hello, from Ex2"

Ou seja, reimplementar métodos privados numa subclasse pode quebrar métodos da superclasse. Embora eu considere isso uma peculiaridade da linguagem e não como um problema de acoplamento, implementação falha, ou qualquer coisa (e até, mais pra frente, pretendo escrever um post sobre o assunto de diferenças entre linguagens de programação além da sintaxe), a discussão inteira me deu uma idéia: como implementar esse comportamento em Ruby?
(more…)

Dá para criar classes abstratas em Ruby?

Ok, aqui vamos nós para mais estudos de como fazer coisas bizarras em Ruby… outro dia, estava olhando para um livro, “Design Patterns in Ruby”, que falava de classes abstratas (tipo Java) e a inexistência delas em Ruby, aonde “Duck Typing” resolve. Mas aí pensei: será que não dá para simular o comportamento de classes abstratas tipo Java, em Ruby?

Para os que não conhecem Java: se você, em Java, declarar uma classe como abstrata, e definir, digamos, dois métodos abstratos, quando esta classe for herdada, você é obrigado a definir estes dois métodos, senão o código sequer compila. Bom, parece então simples, em Ruby: basta criar uma classe, informar, de alguma que ela é abstrata, e então quando ela for herdada, se a classe herdada não definir todos os métodos, lançar uma exceção (digamos, um NoMethodError). Ok, Ruby permite traçar quando uma classe foi herdada com o método “callback” inherited, portanto é relativamente simples saber se a classe foi herdada e se ela implementa os métodos da abstrata, certo?

Bom, na prática… não.
(more…)

Dá para fazer tipagem estática em Ruby?

Bom, resolvi começar uma série – coisas que você NUNCA quis fazer com Ruby, e tinha medo de perguntar. Basicamente, pensei em montar códigos absurdos de coisas que são completamente contra a filosofia da linguagem, ou que pelo menos são muito esquisitas, e publicar aqui os resultados. As regras são simples: os resultados devem ser testáveis (com RSpec, de preferência) e devem ser escritas puramente em Ruby, de preferência sem nenhuma biblioteca auxiliar (e, se for necessário usar, é obrigatório que a biblioteca tenha sido escrita puramente em Ruby também).

Como primeiro da série, vamos simular uma tipagem estática em Ruby. Como é impossível sobrescrever o operador “=” em Ruby, resolvi usar uma função – static – para simular o mesmo comportamento. Para simplificar, vamos meio que definir uma “variável global” com “static.”, e definir que esta terá tipagem estática. Começamos definindo uma função chamada “static”

def static
  Static.instance
end

(more…)