Subsecciones

Los Módulos como Espacios de Nombres

Al definir un módulo se crea una constante con el mismo nombre del módulo. El valor de dicha constante es el objeto Module que representa al módulo.

Ejemplo Simple

Say you write a set of the trigonometry functions sin, cos, and so on. You stuff them all into a file, trig.rb, for future generations to enjoy. Meanwhile, Sally is working on a simulation of good and evil, and she codes a set of her own useful routines, including be_good and sin, and sticks them into moral.rb. Joe, who wants to write a program to find out how many angels can dance on the head of a pin, needs to load both trig.rb and moral.rb into his program. But both define a method called sin. Bad news.

The answer is the module mechanism. Modules define a namespace, a sandbox in which your methods and constants can play without having to worry about being stepped on by other methods and constants. The trig functions can go into one module:

Dave Thomas

module Trig
  PI = 3.141592654
  def Trig.sin(x)
   # ..
  end
  def Trig.cos(x)
   # ..
  end
end

# Sample code from Programing Ruby, page 111
module Moral
  VERY_BAD = 0
  BAD      = 1
  def Moral.sin(badness)
    # ...
  end
end
If a third program wants to use these modules, it can simply load the two files (using the Ruby require statement. In order to reference the name sin unambiguously, our code can then qualify the name using the name of the module containing the implementation we want, followed by ::, the scope resolution operator:

$: << '.'
require 'trig'
require 'moral'

y = Trig.sin(Trig::PI/4)
wrongdoing = Moral.sin(Moral::VERY_BAD)

Ejemplo: Codificar y Decodificar Datos Usando Base64

Supongamos que estamos un método para codificar y decodificar datos binarios utilizando la codificación Base64. Puesto que encode y decode son dos nombres muy comunes, para preveer colisiones de nombres definimos nuestros dos métodos dentro de un módulo:

casiano@exthost:~/rubytesting/TheRubyProgrammingLanguage/Chapter7ClassesAndModules/modules$ cat -n base64_mod_self.rb
     1   module Base64
     2    
     3   puts self  # Base64
     4  
     5   DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
     6            'abcdefghijklmnopqrstuvwxyz' +
     7            '0123456789+/'
     8    @x = "hello"
     9            
    10    def self.encode
    11       "self.encode #@x"
    12    end 
    13  
    14    def self.decode
    15      "self.decode #@x"
    16    end 
    17  end
    18  if $0 == __FILE__
    19    puts Base64::DIGITS
    20    puts Base64.encode
    21    puts Base64.decode
    22  end

  1. Note that we define our methods with a self.prefix, which makes them “class methods” of the module.
  2. Outside the Base64 module, this constant can be referred to as Base64::DIGITS.
  3. Inside the module, our encode and decode methods can refer to it by its simple name DIGITS.
  4. If the two methods had some need to share nonconstant data, they could use a class variable (with a @@ prefix), just as they could if they were defined in a class.

[~/local/src/ruby/rubytesting/TheRubyProgrammingLanguage/Chapter7ClassesAndModules/modules]$ cat client.rb 
require 'base64_mod_self'

class A
  def self.tutu
    Base64::encode
  end
end

if $0 == __FILE__
  puts A.tutu
  puts Base64::DIGITS
end

[~/local/src/ruby/rubytesting/TheRubyProgrammingLanguage/Chapter7ClassesAndModules/modules]$ ruby -I. client.rb 
Base64
self.encode hello
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

Espacios de Nombres Anidados

module Base64
  DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

  class Encoder
    def encode
    end
  end

  class Decoder
    def decode
    end
  end

  # A utility function for use by both classes
  def Base64.helper
  end
end
  1. Tenemos dos clases: Base64::encoder y Base64::Decoder. Dentro del módulo las clases pueden referirse la una a la otra y a la constante DIGITS por sus nombres simples, sin cualificar.

  2. Sin embargo, para llamar a Base64.helper deben referirse a él por su nombre completo: Base64.helper.

  3. Las clases pueden también anidarse. Anidar una clase dentro de otra no le da a la clase ningún privilegio especial de acceso a los métodos y variables en la clase mas externa. Su único efecto es en el espacio de nombres de la clase interna.

  4. Cuando escribimos una gema es conveniente anidar las clases y módulos - especialmente aquellas clases que no forman parte de la API pública - para no contaminar los espacios de nombres públicos.

Casiano Rodriguez León 2015-01-07