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.

Metaprogramming - Eval para classes e instâncias


Hoje falaremos sobre:

  • instance_eval
  • class_eval
  • Blocos
instance_eval

Abre um bloco para instância.

class_eval

Abre um bloco para classe.

Blocos

&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.


Aula Prática

Como de praxe copie os arquivos das aulas anteriores para seguir conforme o padrão.

Pré-requisitos

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.





Próximas Aulas


Metaprogramming - Classes e métodos dinâmicos

Nesta aula iremos aprender como definir classes e métodos em runtime, ...

Metaprogramming - Alias para métodos e atributos

Nesta aula iremos aprender a renomear ou duplicar métodos de forma din...

Metaprogramming - Hooks

Nesta aula iremos aprender os conceitos de hooks, veremos como utiliza...

Metaprogramming - Missings

Nesta aula iremos aprender a utilizar os comandos const_missing e meth...

Instalando Rails

Nesta aula iremos aprender a instalar e configurar o Rails que é um do...

Rails Generators

Nesta aula iremos aprender um pouco sobre os generators do Rails, util...

Rails - Routes

Nessa aula iremos iniciar uma aplicação web do zero, agora passo a pas...

Rails - Partials

Nesta aula iremos aprender como dividir o conteúdo de nossos templates...



Danilo

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.

ELEVE SEUS GANHOS E POTENCIALIZE SUA PERFORMANCE

Receba nossa Ebook de LÓGICA DE PROGRAMAÇÃO

© Didox Business & Technology - CNPJ: 12.127.195/0001-14