Ruby se destaca entre muitas linguagens por sua grande flexibidade. Uma importante caracteristica é a extensão de classes. Para exemplificar essa funcionalidade, criaremos um cenário hipotético:

Temos uma string que representa um nome de arquivo. Apartir dessa string, precisamos substituir os caracteres especiais e espaços em branco por underscore “_”. Poderiamos escrever um modulo com um método que atendesse nossa necessidade. Como por exemplo:

1
2
3
4
5
6
7
8
9
10
11
12
module FilenameUtil
def generate_filename(filename)
filename.gsub(/[\s|\W]/, '_')
end
end
include FilenameUtil
filename = "*Hello *there* f1l3nam3 )()"
FilenameUtil.generate_filename filename
# "_Hello__there__f1l3nam3____"

Parece uma solução legal, porém o quanto mais legal não seria se a própria classe String respondesse a esse método? Em Ruby, isso é possível! Podemos extender a classe String e adicionar novos metodos. Más como isso é possível? usaremos um conceito conhecido como monkey patch, onde podemos abrir uma classe e incluir novos metodos customizados. Podemos fazer isso dessa forma:

1
2
3
4
5
6
7
8
class String
def generate_filename
self.gsub(/[\s|\W]/, '_')
end
end
"*Hello *there* f1l3nam3 )()".generate_filename
# "_Hello__there__f1l3nam3____"

Bem mais elegante não acham?

Cuidado para não criar um problema

Ao realizer um Monkey Path, precisamos ter muito cuidado para não reescrever metodos já existentes. Temos que ter o cuidado de verificar se o método já existe e ter certeza de que não estamos o reescrevendo. Vamos a um exemplo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Vamos listar os metodos da classes string que começam com 're'
String.instance_methods.grep /^re/
# [:replace, :reverse, :reverse!, :remove_instance_variable, :respond_to?]
# Vamos usar o metodo 'replace' que substitui o conteudo da string
'Bruno'.replace 'Paulino'
# 'Paulino'
# Agora vamos adicionar um novo método para a classe String
class String
def replace(content)
self.gsub(/#{content}/, content.upcase)
end
end
# Agora usando o nome metodo 'replace'
"Bruno".replace 'un'
# "BrUNo"

Seguindo o exemplo acima, percebemos que o método ‘replace’ não foi adicionado, mas sim substituido por um novo método que criamos.