Subsecciones


instance_eval y class_eval

Estos dos métodos evaluan de manera similar a eval pero con estas consideraciones:
  1. Evaluan el código en el contexto del objeto o de la clase o módulo especificados
  2. El objeto o módulo es el valor de self durante la evaluación
  3. instance_eval define métodos singleton del objeto (y, por tanto define métodos de clase cuando el objeto es una clase)
    # Use instance_eval to define class method String.empty
    # Note that quotes within quotes get a little tricky...
    String.instance_eval("def empty; ''; end")
    
  4. class_eval define métodos de instancia
    # Define an instance method len of String to return string length
    String.class_eval("def len; size; end")
    
  5. instance_eval y class_eval aceptan un bloque de código como argumento para la evaluación
    String.class_eval {
      def len
        size
      end
    }
    String.class_eval { alias len size }
    String.instance_eval { def empty; ""; end }
    

  6. La sintáxis de instance_eval es:
        instance_eval(string [, filename [, lineno]] ) 
        instance_eval {| | block }
    
    En el caso de la String, los dos últimos argumentos son utilizados para mejorar los mensajes de error
  7. El método class_eval tiene la sintaxis:

    class_eval(string [, filename [, lineno]])
    module_eval {|| block }
    
Veamos un ejemplo:

    class Klass
      def initialize
        @secret = 99
      end
    end
    k = Klass.new
    k2 = Klass.new
    puts k.instance_eval { @secret }   #=> 99
    
    k.instance_eval " def tutu; puts self.inspect; %q{hello}; end"  
    puts k.tutu    # <Klass:0x10016a9f8 @secret=99> 
                   # hello
    begin
      puts k2.tutu   # "tutu" is a singleton method of "k"
    rescue NoMethodError
      puts $!  # undefined method `tutu' for #<Klass:0x10016a958 @secret=99>
    end
    
    Klass.instance_eval {
      def chachi     # A singleton method of a class is a class method!
        self.inspect
      end
    }
    
    puts Klass.chachi # Klass
    

Los dos últimos argumentos class_eval(string [, filename [, lineno]]) son utilizados para mejorar los mensajes de error:

~/Chapter8ReflectionandMetaprogramming$ cat -n class_eval.rb 
     1  class Thing
     2  end
     3  a = %q{def hello() "Hello there!" end}
     4  Thing.module_eval(a)
     5  puts Thing.new.hello()
     6                                     
     7  Thing.module_eval("invalid code", 
     8                    "ficherito.rb",   # Fichero del que se leyo el código
     9                    123               # nº de linea
    10                   )
Ejecución:
~/Chapter8ReflectionandMetaprogramming$ ruby class_eval.rb 
Hello there!
ficherito.rb:123: undefined local variable or method `code' for Thing:Class (NameError)


Creando accessors con class_eval

Sabemos que podemos crear accessors a los atributos de una clase usando attr_accessor, attr_reader y attr_writer. Estas no son palabras reservadas de Ruby sino que se trata de métodos. Veamos como podemos emular su construcción usando class_eval:

MacBookdeCasiano:chapter8ReflectionandMetaprogramming casiano$ cat -n myAccessors.rb 
     1  class Module
     2    def my_attr_reader(*syms) # método de instancia de Module
     3      syms.each { |s|         # esto es, ¡un método de clase!
     4        class_eval %{         # aquí self es el objeto Module
     5                      def #{s}  # estamos definiendo un método de 
     6                        @#{s}   #instancia en la clase "self"
     7                      end
     8                    }
     9      }
    10    end
    11  end
    12  
    13  class Chuchu
    14    my_attr_reader :a, :b
    15  
    16    def initialize(a,b)
    17      @a, @b = a, b
    18    end
    19  end
    20  
    21  x = Chuchu.new(4, "z")
    22  puts x.a    # 4
    23  puts x.b    # z

Casiano Rodriguez León 2015-01-07