1 | 2 | 3 | 4

Ruby en 20 minutos

Ahora vamos a crear y usar un objeto anfitrión:

irb(main):035:0> a = Anfitrion.new("Juan")
=> #<Anfitrion:0x16cac @nombre="Juan">
irb(main):036:0> a.decir_hola
Hola Juan
=> nil
irb(main):037:0> a.decir_adios
Adiós Juan, vuelve pronto.
=> nil

Una vez que el objeto a es creado, nos recuerda que el nombre es Juan. Mmm, ¿y si queremos acceder al nombre directamente?

irb(main):038:0> a.@nombre
SyntaxError: compile error
(irb):52: syntax error
        from (irb):52

No, no podemos.

Por debajo de la piel del objeto

Las variables de instancia se esconden dentro del objeto. No están tan escondidas, las puedes ver cuando inspeccionas el objeto, y hay otras formas de acceder a ellas, pero Ruby es fiel a las buenas costumbres de la programación orientada a objetos manteniendo los datos lo más privados posible.

Entonces, ¿qué métodos están disponibles para los objetos Anfitrion?

irb(main):039:0> Anfitrion.instance_methods
=> ["method", "send", "object_id", "singleton_methods",
    "__send__", "equal?", "taint", "frozen?",
    "instance_variable_get", "kind_of?", "to_a",
    "instance_eval", "type", "protected_methods", "extend",
    "eql?", "display", "instance_variable_set", "hash",
    "is_a?", "to_s", "class", "tainted?", "private_methods",
    "untaint", "decir_hola", "id", "inspect", "==", "===",
    "clone", "public_methods", "respond_to?", "freeze",
    "decir_adios", "__id__", "=~", "methods", "nil?", "dup",
    "instance_variables", "instance_of?"]

Bien. Eso es un montón de métodos. Nosotros sólo definimos dos métodos. ¿Qué es lo que está sucediendo? Bueno, estos son todos los métodos para los objetos Anfitrion, una lista completa, incluyendo los que están definidos en las superclases de Anfitrion. Si queremos listar únicamente los métodos definidos para la clase Anfitrion podemos pedirle que no incluya sus ancestros pasándole el parámetro false, que significa que no queremos los métodos definidos por sus ancestros.

irb(main):040:0> Anfitrion.instance_methods(false)
=> ["decir_adios", "decir_hola"]

Ah, hay más cosas como esa. Veamos a qué métodos puede responder nuestro objeto anfitrión:

irb(main):041:0> a.respond_to?("nombre")
=> false
irb(main):042:0> a.respond_to?("decir_hola")
=> true
irb(main):043:0> a.respond_to?("to_s")
=> true

Así que, sabe decir_hola, y to_s (que significa “convertir algo en un string”, un método que está definido por defecto para todos los objetos), pero no reconoce nombre como un método.

Modificando clases—Nunca es demasiado tarde

¿Pero qué pasa si quieres poder ver o modificar el nombre? Ruby provee una forma fácil para permitir acceder a las variables de un objeto.

irb(main):044:0> class Anfitrion
irb(main):045:1>   attr_accessor :nombre
irb(main):046:1> end
=> nil

En Ruby, puedes volver a abrir una clase y modificarla. Eso no cambia objetos que ya existan, pero afecta a los nuevos objetos que puedas crear. Así que vamos a crear un nuevo objeto y juguemos con su propiedad @nombre.

irb(main):047:0> a = Anfitrion.new("Pedro")
=> #<Anfitrion:0x3c9b0 @nombre="Pedro">
irb(main):048:0> a.respond_to?("nombre")
=> true
irb(main):049:0> a.respond_to?("nombre=")
=> true
irb(main):050:0> a.decir_hola
Hola Pedro
=> nil
irb(main):051:0> a.nombre="Matilde"
=> "Matilde"
irb(main):052:0> a
=> #<Anfitrion:0x3c9b0 @nombre="Matilde">
irb(main):053:0> a.nombre
=> "Matilde"
irb(main):054:0> a.decir_hola
Hola Matilde
=> nil

El uso de attr_accessor determinó que se definan dos nuevos métodos por nosotros, nombre para obtener el valor, y nombre= para modificarlo.

Saludando a todo el mundo, ¡MegaAnfitrion no niega el saludo a nadie!

De todas formas, este anfitrión no es tan interesante, sólo puede trabajar con una persona a la vez. ¿Qué pasaría si tuviéramos alguna clase MegaAnfitrion que pudiera saludar al mundo, a una persona, o una lista completa de personas?

Escribamos esto en un archivo en vez de usar directamente el intérprete interactivo de Ruby IRB.

Para salir de IRB, escribe “quit”, “exit” o simplemente presiona Control-D.

#!/usr/bin/env ruby

class MegaAnfitrion
  attr_accessor :nombres

  # Crear el objeto
  def initialize(nombres = "Mundo")
    @nombres = nombres
  end

  # Decirle hola a todos
  def decir_hola
    if @nombres.nil?
      puts "..."
    elsif @nombres.respond_to?("each")
      # @nombres es una lista de algún tipo,
      # ¡así que podemos iterar!
      @nombres.each do |nombre|
        puts "Hola #{nombre}"
      end
    else
      puts "Hola #{@nombres}"
    end
  end

  # Decirle adiós a todos
  def decir_adios
    if @nombres.nil?
      puts "..."
    elsif @nombres.respond_to?("join")
      # Juntar los elementos de la lista
      # usando la coma como separador
      puts "Adiós #{@nombres.join(", ")}. Vuelvan pronto."
    else
      puts "Adiós #{@nombres}. Vuelve pronto."
    end
  end

end


if __FILE__ == $0
  ma = MegaAnfitrion.new
  ma.decir_hola
  ma.decir_adios

  # Cambiar el nombre a "Diego"
  ma.nombres = "Diego"
  ma.decir_hola
  ma.decir_adios

  # Cambiar el nombre a un vector de nombres
  ma.nombres = ["Alberto", "Beatriz", "Carlos",
    "David", "Ernesto"]
  ma.decir_hola
  ma.decir_adios

  # Cambiarlo a nil
  ma.nombres = nil
  ma.decir_hola
  ma.decir_adios
end

Guarda este archivo como “ri20min.rb”, y ejecútalo con “ruby ri20min.rb”. El resultado debería ser:

Hola Mundo
Adiós Mundo. Vuelve pronto.
Hola Diego
Adiós Diego. Vuelve pronto.
Hola Alberto
Hola Beatriz
Hola Carlos
Hola David
Hola Ernesto
Adiós Alberto, Beatriz, Carlos, David, Ernesto. Vuelvan pronto.
...
...

Hay un montón de cosas nuevas en este nuevo ejemplo en las que podemos echar una mirada más profunda.