Nesta aula iremos aprender como abrir uma classe, com os comandos instance_eval e class_eval é possível alterar toda a lógica do seu programa.
Hoje falaremos sobre:
Abre um bloco para instância.
Abre um bloco para classe.
&bloco, uso dele é parecido com o callback do JS.
Obs.: Com o instance_eval
, você consegue abrir uma classe, inclusive trabalhar com os dados
privados, já o class_eval
você pode definir dados dentro da classe.
Exemplo de exposição de variáveis privadas com instance_eval.
class teste
def initialize
@nome = "Danilo"
end
end
teste.new.instance_eval{puts @nome}
Exemplo injeção de método na instância com instance_eval.
class teste
end
teste.instance_eval do
def exec
end
end
Exemplo injeção de método na instância com instance_eval.
class teste
end
teste.instance_eval do
def exec
end
end
Exemplo definição de método na classe com class_eval.
class teste
end
teste.class_eval do
def exec
end
end
teste.new.exec
Note que na definição de métodos é necessário utilizar o new
para inicializar a classe, já na
manipulação da instância não é necessário.
Como de praxe copie os arquivos das aulas anteriores para seguir conforme o padrão.
Precisamos da gem byebug
instalada para utilizarmos o debugger
no nosso código.
Estrutura padrão:
require 'byebug'
# Nosso código irá aqui
debugger
O eval
permite rodar uma string como código Ruby.
O código abaixo é um exemplo básico de eval.
require 'byebug'
a = "puts 'danilo'"
debugger
eval(a)
Agora temos uma classe com uma variável de instância.
require 'byebug'
class Teste
def initialize
@variavel = "danilo"
end
end
teste = Teste.new
debugger
puts teste.instance_eval{ @variavel }
Ao testarmos esse código veremos que o instance_eval
consegue pegar o valor da variável, mas
nós não temos nenhum médodo que retorna o valor. Isso pode ser comprovado fazendo na linha de comando após
executar o código com debugger:
teste.methods - Class.methods
Como abrimos um bloco de instance_eval
dentro da classe e passamos a variável de classe nele,
conseguimos acessar o valor da variável em execução assim:
teste.instance_eval{ @variavel }
O código acima deve nos retornar o valor de @variavel
que no caso é "danilo"
.
Lembrando que o bloco de código com {}
pode ser utilizado como do...end
conforme
abaixo:
teste.instance_eval{ @variavel }
# utilizando do...end
teste.instance_eval do
@variavel
end
Nós também podemos criar um método privado que retorna a variável de classe.
require 'byebug'
class Teste
def initialize
@variavel = "danilo"
end
private
def variavel
@variavel
end
end
teste = Teste.new
debugger
puts teste.instance_eval{ @variavel }
Rodando este código, se tentarmos fazer algo como Class.methods
nesta instância ou tentar
acessar o método private receberemos um erro, entretanto o instance_eval
permite acessar até os
métodos privados.
require 'byebug'
class Teste
def initialize
@variavel = "danilo"
end
private
def variavel_metodo
@variavel
end
end
teste = Teste.new
debugger
puts teste.instance_eval{ @variavel }
puts teste.instance_eval{ variavel_metodo }
No código abaixo conseguimos ver a instância de teste que está guardada em memória graças ao
self
.
require 'byebug'
class Teste
def initialize
@variavel = "danilo"
end
private
def variavel
@variavel
end
end
teste = Teste.new
debugger
puts teste.instance_eval{ @variavel }
teste.instance_eval do
puts self
end
Arquivo final:
require 'byebug'
class Teste
def initialize
@variavel = "danilo"
end
def variavel=(valor)
@variavel = valor
end
private
def variavel_metodo
@variavel
end
end
teste = Teste.new
puts teste.instance_eval{ @variavel }
puts teste.instance_eval{ variavel_metodo }
teste.instance_eval do
puts self
end
teste.instance_eval do
def variavel
puts "metodo"
end
def metodo2
puts "metodo"
end
def metodo3
puts "metodo"
end
end
puts teste.variavel
#======================================
class Teste
end
Teste.instance_eval do
def metodo_de_classe
puts "metodo de classe instance_eval"
end
def metodo_de_classe2
puts "metodo de classe instance_eval 2"
end
end
Teste.metodo_de_classe
Teste.metodo_de_classe2
#======================================
class Teste;end
Teste.class_eval do
def metodo_de_instancia
puts "metodo de class_eval"
end
def metodo_de_instancia2
puts "metodo de class_eval 2"
end
end
Teste.new.metodo_de_instancia
Teste.new.metodo_de_instancia2
#======================================
# não funciona
# def String.teste
# "sssdds"
# end
# "danilo".teste
# =============
String.class_eval do
def teste
"sssdds"
end
end
"danilo".teste
# =============
danilo = "danilo"
def danilo.teste2
"sssdds 333"
end
danilo.teste2
# =============
String.instance_eval do
def de_classe
"sss classe dds"
end
end
String.de_classe
# =============
def String.mostrar_nova_string2
"nova string"
end
String.mostrar_nova_string2
# =============
class << String
def ola1
"olha danilo"
end
end
String.ola1
# =============
#======================================
module AtributosDinamicos
def atributo(attr)
class_eval %{
def #{attr}
@#{attr}
end
def #{attr}=(value)
@#{attr} = value
end
}
end
end
class Teste3
extend AtributosDinamicos
atributo :nome
atributo :nome2
atributo :nome3
atributo :nome4
atributo :nome5
end
teste = Teste3.new
teste.nome = "danilo"
teste.nome
#======================================
class ClasseBloco
def metodo1
puts "medodo 1"
end
def metodo2
puts "medodo 2"
end
def fique_a_vontade_em_executar(&bloco)
instance_eval(&bloco)
end
private
def metodo3
puts "medodo 3"
end
end
classe_bloco = ClasseBloco.new
classe_bloco.fique_a_vontade_em_executar do
metodo1
metodo2
metodo3
def metodo_em_bloco
puts "metodo definifo em bloco"
end
end
debugger
classe_bloco.metodo_em_bloco
x = ""
Na próxima aula veremos mais conteceitos de metaprogramação com Ruby.
Nesta aula iremos aprender como definir classes e métodos em runtime, ...
Nesta aula iremos aprender a renomear ou duplicar métodos de forma din...
Arquiteto de software, analista, programador, professor. Danilo criou o projeto torne-se um programador, para passar o seu conhecimento para a nova geração. Com o intuito de ser um bom pai, Danilo trabalha muito motivado para garantir o futuro de sua filha.