Subsecciones

Definiendo Clases y Módulos

Las clases y los módulos son instancias de las clases Class y Module. Por tanto, es posible crearlos dinámicamente:

M = Module.new      # Define a new module M
C = Class.new       # Define a new class C
D = Class.new(C) {  # Define a subclass of C
  include M         # that includes module M
}
D.to_s              # => "D": class gets constant name by magic

Cuando un módulo o clase dinámicamente creado es asignado a una constante el nombre de esa constante es usado como nombre del módulo o clase.

~/Chapter8ReflectionandMetaprogramming$ cat -n dynamicclassesandmodules.rb 
     1  Fred = Module.new do
     2    def meth1
     3      "hello #{args.inspect}"
     4    end
     5    def meth2
     6      "bye #{args.inspect}"
     7    end
     8  end
     9  
    10  Klass = Class.new do
    11    include Fred
    12    attr_accessor 'args'
    13  
    14    def initialize(*args)
    15      @args = args
    16    end
    17  end
    18  
    19  puts Fred.to_s     # Fred
    20  puts Klass.name    # Klass
    21  
    22  a = Klass.new(4, 5)
    23  
    24  puts a.meth1     # hello [4, 5] 
    25  puts a.meth2     # bye [4, 5]


Ambitos y los métodos Class.new, Module.new, define_method

  1. El método local_variables del módulo Kernel nos permite acceder a las variables locales visibles en un punto.
  2. Algunos lenguajes permiten a un ámbito interno ver las variables de un ámbito mas externo. Este tipo de visibilidad no ocurre en general en Ruby
  3. Hay tres puntos en los que un programa abandona el ámbito previo y abre un nuevo ámbito (scope gates en al terminología del libro Metaprogramming Ruby [8]):
    1. Definiciones de clase
    2. Definiciones de módulo
    3. Definiciones de método
  4. Las variables globales son visibles desde cualquier ámbito
  5. Las variables de instancia son visibles desde cualquier punto en el que self se refiere al mismo objeto

[~/chapter8ReflectionandMetaprogramming]$ cat -n scopegates1.rb 
 1    v1 = 1                  
 2    puts "%2d"%__LINE__.to_s+' '+local_variables.inspect     # [:v1, :obj]
 3    
 4    class Tutu
 5      v2 = 2                
 6      puts "%2d"%(__LINE__.to_s)+' '+local_variables.inspect # [:v2]
 7    
 8      def my_method
 9        v3 = 3
10        puts "%2d"%__LINE__.to_s+' '+local_variables.inspect # [:v3]
11      end
12    
13      puts "%2d"%__LINE__.to_s+' '+local_variables.inspect   # [:v2] 
14    end
15    
16    obj = Tutu.new
17    obj.my_method        
18    obj.my_method        
19    puts "%2d"%__LINE__.to_s+' '+local_variables.inspect     # [:v1, :obj]
    
Puesto que Class.new, Module.new y define_method usan el bloque que les sigue y los bloques no abandonan el ámbito previo (aunque crean uno nuevo), podemos jugar con estos tres métodos para crear clausuras en las que se comparten variables locales entre clases, métodos y ámbitos. Observe como en esta reescritura del programa anterior cambia la visibilidad:

  v1 = 1                  
  
  Tutu = Class.new do
    v2 = 2                
    puts "%2d"%(__LINE__.to_s)+' '+local_variables.inspect  # [:v2, :v1, :obj]
  
    define_method :my_method  do
      v3 = 3
      puts "%2d"%__LINE__.to_s+' '+local_variables.inspect  # [:v3, :v2, :v1, :obj]
    end
  
    puts "%2d"%__LINE__.to_s+' '+local_variables.inspect    # [:v2, :v1, :obj]
  end
  
  obj = Tutu.new
  obj.my_method        
  obj.my_method        
  puts "%2d"%__LINE__.to_s+' '+local_variables.inspect       # [:v1, :obj]
    

Casiano Rodriguez León 2015-01-07