Programando en Ruby

La Guía de los "Programadores Pragmáticos"

Ruby.new



Al principio, cuando escribimos este libro, teníamos un gran plan (éramos jóvenes entonces). Queríamos documentar el lenguaje de arriba a abajo, empezando con las clases y los objetos, y finalizando con los detalles de sintaxis de bajo nivel. En su tiempo, parecía una buena idea. Después de todo, la mayoría de las cosas en Ruby son objetos, así que tenía sentido hablar sobre objetos primero.

O eso pensábamos.

Desafortunadamente, se volvió difícil describir un lenguaje de ese modo. Si no has cubierto las cadenas, las cláusulas if, asignaciones, y otros detalles, es difícil escribir ejemplos de clases. A lo largo de nuestra descripción de arriba a abajo, nos encontramos atravesando detalles de bajo nivel que necesitábamos cubrir para que los ejemplos pudiesen tener sentido.

Así que nosotros trajimos un nuevo gran planta (no nos llaman pragmáticos por nada). Describiríamos Ruby empezando por arriba. Pero antes de hacerlo, añadiríamos un corto capítulo que describiese todas las características comunes del lenguaje utilizadas a lo largo de los ejemplos con el vocabulario especial de Ruby, un tipo de minitutorial para iniciarnos en el resto del libro.

Ruby es un Lenguaje Orientado a Objetos

Vamos a decirlo otra vez. Ruby es un lenguaje genuinamente Orientado a Objetos. Todo lo que manipulas es un objeto, y el resultado de esas manipulaciones son objetos. Sin embargo, muchos lenguajes proclaman lo mismo, y frecuentemente tienen diferentes interpretaciones de que significa orientado a objetos y diferentes terminologías para los conceptos que emplean.

Así que, antes de ir más lejos en los detalles, vamos a echar un breve vistazo a los términos y notaciones que nosotros utilizaremos.

Cuando escribes código orientado a objetos, normalmente estás modelizando conceptos del mundo real en tu código. Durante el proceso de modelado descubrirás categorías de cosas que necesitan ser representadas en código. En una gramola, el concepto de una ``canción'' podría ser como una categoría. En Ruby, definirías una clase para representar cada una de esas entidades. Una clase es una combinación de estados (por ejemplo, el nombre de la canción) y métodos que utilizan esos estados (quizá un método para reproducir la canción).

Una vez que tienes esas clases, querrás crear un número de instancias de cada una. Para el sistema de gramola conteniendo una clase llamada Cancion, tendrías instancias para exitos populares como ``Ruby Tuesday,'' ``Enveloped in Python,'' ``String of Pearls,'' ``Small talk,'' y así. La palabra objeto es utilizada de manera indistinta con instancia de clase (y siendo mecanógrafos perezosos, utilizaremos la palabra ``objeto'' más frecuentemente).

En Ruby, esos objetos son creados mediante la llamada a un constructor, un método especial asociado con un clase. El constructor estándar es llamado new.

song1 = Song.new("Ruby Tuesday")
song2 = Song.new("Enveloped in Python")
# and so on

Estas instancias son ambas derivadas de la misma clase, pero tienen características únicas. Primero, cada objeto tiene un único identificador de objeto (abreviado como object id). Segundo, tu puedes definir variables de instancia, variables con valores que son únicos para cada instancia. Estas variables de instancia contiene el estado de un objeto. Cada una de nuestras canciones, por ejemplo, probablemente tendrán una variable de instancia que contiene el título de la canción.

Dentro de cada clase, puedes definir métodos de instancia. Cada método es un trozo de funcionalidad que puede ser llamado desde dentro de la clase y (dependiendo de las limitaciones de accesibilidad) desde fuera. Estos métodos de instancia a su vez tienen acceso a las variables de instancia del objeto, y por lo tanto al estado del objeto.

Los métodos son invocados mediante el envío de mensajes a un objeto. El mensaje contiene el nombre del método, junto con cualquier parámetro que el pueda necesitar.[La idea de expresar las llamadas a métodos en forma de mensajes viene de Smalltalk.] Cuando un objeto recibe un mensaje, mira dentro de su propia clase por el método correspondiente. Si lo encuentra, el método es ejecutado. Si el método no es encontrado, ... bueno, lo veremos más tarde.

Este asunto de metodos y mensajes podría sonar complicado, pero en la práctica es muy natural. VAmos a ver algo sobre llamadas a métodos. (Recuerda que las flechas en los ejemplos de código muestran los valores devueltos pro la correspondiente expresión.)

"gin joint".length » 9
"Rick".index("c") » 2
-1942.abs » 1942
sam.play(aSong) » "duh dum, da dum de dum ..."

Aquí, la cosa antes del punto se llama el receptor, y el nombre después del punto es el método a ser invocado. El primer ejemplo pregunta a una cadena por su longitud, y el segundo pregunta a otra cadena para encontrar la posición de la letra ``c.'' La tercera línea contiene un número calculando su valor absoluto. Finalmente, nosotros le decimos a Sam que nos toque una canción.

Vale la pena darse cuenta aquí de una gran diferencia entre Ruby y otros lenguajes. En Java, encontrarías el valor absoluto de un número mediante la llamada a una función independiente y pasándole ese número. Podrías escribir

number = Math.abs(number)     // Java code

En Ruby, la habilidad de determinar el valor absoluto está integrada dentro del número --ellos se preocupan de los detalles internamente. Tu simplemente envías el mensajes abs al número y le dejas hacer el trabajo.

number = number.abs

Lo mismo se aplica a todos los objetos de Ruby: en C tu escribirías strlen(name), mientras que en Ruby es name.lenght, y así. Esto es parte de lo que queremos decir cuando decimos que Ruby es un genuino lenguaje orientado a objetos.

Algunos Fundamentos de Ruby

A no mucha gente le gusta leer pilas de aburridas reglas de sintaxis cuando se introducen en un nuevo lenguaje. Así que vamos a hacer una trampa. En esta sección nos centraremos en los puntos destacados, las cosas que tenemos que saber si vamos a escribir programas en Ruby. Después, en el Capítulo 18, que comienza la página 199, nos adentraremos en los detalles más sangrientos.

Vamos a comenzar con un sencillo programa en Ruby. Escribiremos un método que devuelve una cadena, añadiendo a esa cadena el nombre de una persona. Entonces invocaremos ese método un par de veces.

def sayGoodnight(name)
  result = "Goodnight, " + name
  return result
end

# Time for bed... puts sayGoodnight("John-Boy") puts sayGoodnight("Mary-Ellen")

Primero, algunas observaciones generales. La sintaxis de Ruby es limpia. No necesitas puntos y comas al final de las sentencias mientras pongas cada sentencia en una línea separada. Los comentarios en Ruby comienzan con un carácter # y llegan hasta el final de la línea. La distribución del código queda a tu elección; la indentación no es significante.

Los métodos se definen con la palabra clave def, seguido por el nombre del método (en este caso, ``sayGoddnight'') y los parámetros del método entre paréntesis. Ruby no utiliza llaves para delimitar los cuerpos de sentencias y definiciones compuestas. En cambio, se finaliza simplemente finalizando el cuerpo con la palabra clave end. El cuerpo de nuestro método es bastante simple. La primera línea concatena la cadena literal ``Goodnight, '' al parametro name y asigna el resultado a la variable local result. La siguiente línea devuelve el resultado al invocador. Hay que darse cuenta que no declaramos la variable result; comienza su existencia cuando la asignamos.

Una vez definido el método, nosotros lo llamados dos veces. En ambos casos le pasamos el resultado al método puts, el cual simplemente muestra su argumento seguido por una nueva línea.

Goodnight, John-Boy
Goodnight, Mary-Ellen

La línea ``puts sayGoodnight("John-Boy")'' contiene dos llamadas a métodos, una a sayGoodnight y la otra a puts. ¿Por qué una llamada tiene sus argumentos entre paréntesis y la otra no? En este caso es simplemente un tema de gusto personal. Las siguientes líneas son equivalentes.

puts sayGoodnight "John-Boy"
puts sayGoodnight("John-Boy")
puts(sayGoodnight "John-Boy")
puts(sayGoodnight("John-Boy"))

Sin embargo, la vida no es siempre tan simple, y las reglas de precedencia pueden hacer difícil saber qué argumento va con cada invocación de método, así que nosotros recomendamos utilizar paréntesis en todos los casos excepto en los más simples.

Este ejemplo también muestra algunos objetos cadena. Hay varias formas de crear objetos cadena, pero probablemente el más común es utilizar cadenas literales: secuencias de caracteres entre comillas simples o dobles. La diferencia entre las dos formas es el total de procesamiento que Ruby hace en la cadena mientras construye el literal. En el caso de las comillas simples, Ruby hace muy poco. Con unas pocas excepciones, lo que tecleas en la cadena literal se convierte en el valor de la cadena.

En el caso de las comillas dobles, Ruby hace más trabajo. Primero, mira sustituciones --secuencias que comienzan con un carácter backslash--- y los reemplaza por valores binarios. El más común de estos es ``\n, que es reemplazado con un carácter de línea nueva. Cuando una cadena contiene una nueva línea, el ``\n fuerza la ruptura de la línea.

puts "And Goodnight,\nGrandma"
produces:
And Goodnight,
Grandma

La segunda cosa que Ruby hace con las cadenas con comillas dobles es la interpolacion de expresiones. Dentro de la cadena, la secuencia #{ expresión } es remplazada por el valor de expresión. Podemos utilizar esto para rescribir el método previo.

def sayGoodnight(name)
  result = "Goodnight, #{name}"
  return result
end

Cuando Ruby construye el objeto cadena, mira del valor actual de name y lo sustituye dentro de la cadena. Expresiones arbitrariamente complejas están permitidas en la construcción #{...}. Como atajo, no necesitas utilizar llaves cuando la expresión es simplemente una variable global, de instancia o de clase. Para más información sobre cadenas, así como otros tipos estándar de Ruby, revisa el Capítulo 5, que comienza en la pagina 47.

Finalmente, podemos simplificar este método algo más. El valor retornado por un método en Ruby es el valor de la última expresión evaluada, así que podemos deshacernos de la sentencia return entera.

def sayGoodnight(name)
  "Goodnight, #{name}"
end

Prometimos que esta sección sería breve. Tenemos que cubrir antes un cuestión más: nombres en Ruby. Para mayor brevedad, utilizaremos algunos términos (como variable de clase) que no serán definidos aquí. Sin embargo, hablando sobre las reglas ahora, estarás por delante en el juego cuando realmente comencemos a discutir variables de instancia y cosas así después.

Ruby utiliza una convención para ayudarse en la distinción del uso de un nombre: el primer carácter de un nombre indica como es utilizado el nombre. Variables locales, parámetros de métodos, y nombres de métodos deben todos comenzar con una letra minúscula o con un carácter de subrayado. Las variables globales comienzan con el signo del dolar ($), mientras que las variables de instancia comienzan con el símbolo de la arroba (@). Las variables de clase comienzan con dos arrobas (@@). Finalmente, los nombres de clase, nombres de módulo y constantes deben comenzar con una letra mayúscula. Ejemplos de diferentes nombres aparecen en la Tabla 2.1 en la página 10.

Siguiendo a este carácter inicial, un nombre puede ser cualquier combinación de letras, dígitos, subrayados (con la excepción de que el carácter que sigue a una signo @ no puede ser un dígito).

Example variable and class names
Variables Constants and
Local Global Instance Class Class Names
name $debug @name @@total PI
fishAndChips $CUSTOMER @point_1 @@symtab FeetPerMile
x_axis $_ @X @@N String
thx1138 $plan9 @_ @@x_pos MyClass
_26 $Global @plan9 @@SINGLE Jazz_Song

Arrays y Hashes

Los arrays y hashes en Ruby son colecciones indexadas. Ambos almacenan colecciones de objetos, accesibles utilizando una clave. En los arrays, la clave es un entero, mientras que en los hashes, la clave puede ser cualquier objeto. Ambos creen si lo necesitan para contener nuevos elementos. Es más eficiente el acceso a los elementos de un array, pero los hashes proporcionan más flexibilidad. Cualquier array o hash puede contener objetos de diferentes tipos; puedes tener un array conteniendo un entero, una cadena, y un número en punto flotante, como veremos en un minuto.

Puedes crear e inicializar un nuevo array utilizando un literal de array ---un conjunto de elementos entre corchetes. Dado un objeto array, puedes acceder a elementos individuales proporcionando un índice entre corchetes, como muestra el siguiente ejemplo.

a = [ 1, 'cat', 3.14 ]   # array with three elements
# access the first element
a[0] » 1
# set the third element
a[2] = nil
# dump out the array
a » [1, "cat", nil]

Puedes crear arrays vacíos tanto utilizando un literal, el array sin elementos o utilizando un constructor de objeto array, Array.new .

empty1 = []
empty2 = Array.new

Algunas veces crear arrays de palabras puede ser un tormento, con todas esas comillas y comas. Afortunadamente, hay un atajo: %w hace lo que nosotros queremos.

a = %w{ ant bee cat dog elk }
a[0] » "ant"
a[3] » "dog"

Las tablas hash son similares a los arrays. Un literal hash utiliza llaves en vez de corchetes. El literal debe proporcionar dos objetos para cada entrada: uno para la clave, otro para el valor.

Por ejemplo, podrías querer mapear instrumentos musicales con su sección orquestal. Podrías hacer esto con un hash.

instSection = {
  'cello'     => 'string',
  'clarinet'  => 'woodwind',
  'drum'      => 'percussion',
  'oboe'      => 'woodwind',
  'trumpet'   => 'brass',
  'violin'    => 'string'
}

Las tablas hash son indexadas utilizando la misma notación de corchetes que los arrays.

instSection['oboe'] » "woodwind"
instSection['cello'] » "string"
instSection['bassoon'] » nil

Como muestra el ejemplo anterior, una tabla hash, por defecto, devuelve nil cuando es indexado por una clave que no contiene. Normalmente esto es conveniente ya que nil significa false cuando es utilizado en expresiones condicionales. Algunas veces querrás cambiar este comportamiento por defecto. Por ejemplo, si estás utilizando una tabla hash para contar el número de veces que cada clave se repite, es conveniente tener el valor por defecto a cero. Esto se consigue fácilmente especificando el valor por defecto cuando creas una nueva tabla hash vacía.

histogram = Hash.new(0)
histogram['key1'] » 0
histogram['key1'] = histogram['key1'] + 1
histogram['key1'] » 1

Los objetos array y tablas hash tienen un montón de métodos útiles: ver la información que comienza la página 33, y las secciones de referencia que comienzan en la página 278 y 317, para más detalles.

Estructuras de Control

Ruby tiene todas las estructuras de control más habituales, como clausulas if y bucles while. Los programadores Java, C y Perl podrían verse sorprendidos por la falta de llaves delimitando los cuerpos de esas cláusulas. A cambio, Ruby utiliza la palabra clave end que delimita el final de un cuerpo.

if count > 10
  puts "Try again"
elsif tries == 3
  puts "You lose"
else
  puts "Enter a number"
end

Similarly, while statements are terminated with end.

while weight < 100 and numPallets <= 30
  pallet = nextPallet()
  weight += pallet.weight
  numPallets += 1
end

Los modificadores de sentencias en Ruby son utiles atajos si el cuerpo de una sentencia if o while es una única expresión. Simplemente escribe la expresión, seguida de if o while y la condición. Por ejemplo, aquí tenemos una sencilla sentencia if.

if radiation > 3000
  puts "Danger, Will Robinson"
end

Aquí está de neuvo, reescrita utilizando un modificador de sentencia.

puts "Danger, Will Robinson" if radiation > 3000

Similarmente, un bucle while

while square < 1000
   square = square*square
end

se hace más conciso.

square = square*square  while square < 1000

Estos modificadores de sentencia deberían ser familiares para los programadores en Perl.

Expresiones Regulares

La mayoría de las expresiones incorporadas de ruby serán familiares para todos los programadores. La mayoría de los lenguajes tienen cadenas, enteros, numeros en coma flotante, arrays y demás. Sin embargo, hasta que llegó Ruby, el soporte para expresiones regulares era generalmente parte únicamente de los llamados lenguajes de script, como Perl, Python y awk. Eso es una vergüenza: las expresiones regulares, aunque crípticas, son una poderosa herramienta para trabajar con texto.

Libros enteros han sido escritos sobre expresiones regulares (por ejemplo, Mastering Regular Expressions), así que no intentaremos cubrirlo todo en una corta sección. En cambio, observaremos unos pocos ejemplos de expresiones regulares en acción. Encontrarás completa información sobre expresiones regulares a partir de la página 56.

Una expresión regular es una manera simple de especificar un patrón de caracteres que coinciden en una cadena. En Ruby, típicamente creas una expresión regular escribiendo un patrón entre caracteres slash (/patrón). Y, tratándose de Ruby, las expresiones regulares son objetos y pueden ser manipulados como tales.

Por ejemplo, podrías escribir un patrón que compare una cadena conteniendo el texto ``Perl'' o el texto ``Python'' utilizando la siguiente expresión regular.

/Perl|Python/

Los slash hacia adelante delimitan el patrón, que consiste en dos cosas que deben coincidir, separados por la barra vertical (``|''). Se pueden utilizar paréntesis dentro de los patrones, como lo harías en expresiones aritméticas, así que puedes escribir patrones como

/P(erl|ython)/

También puedes especificar repetición dentro del patrón. /ab+c/ encaja en una cadena que contiene una ``a'' seguida por una o más ``b'', seguida por una ``c''. Cambia el ``+'' por un ``*'', y /ab*c/ crea una expresión regular que compara una ``a'', cero o más ``b'', y una ``c''.

También puedes comparar uno de un grupo de caracteres dentro de un patrón. Algunos ejemplos corrientes son clases carácter como ``\s'', los cuales encajan un carácter espacio en blanco (espacio, tabulador, nueva linea y demás), ``\d'', que encaja con cualquier dígito, y ``\w'', que encaja con cualquier carácter que pudiera aparecer en una palabra. El carácter``.'' (punto) encaja con cualquier carácter.

Podemos poner todo esto junto para producir algunas expresiones regulares útiles.

/\d\d:\d\d:\d\d/     # a time such as 12:34:56
/Perl.*Python/       # Perl, zero or more other chars, then Python
/Perl\s+Python/      # Perl, one or more spaces, then Python
/Ruby (Perl|Python)/ # Ruby, a space, and either Perl or Python

Una vez que has creado un patrón, parece una lástima no utilizarlo. El operador match ``=~'' puede ser utlizado para comparar una cadena contra una expresión regular. Si el patrón es encontrado en la cadena, =~ devuelve su posición de comienzo, en otro caso devuelve nil. Esto significa que puedes utilizar una expresión regular como condición en una clasula if o while. Por ejemplo, el siguiente fragmento de código escribe un emsenaje si la cadena contiene el texto 'Perl' o 'Python'.

if line =~ /Perl|Python/
  puts "Scripting language mentioned: #{line}"
end

La parte de la cadena que encaja con la expresión regular puede también ser reemplazada con un texto diferente utilizando uno de los métodos de sustitución de Ruby.

line.sub(/Perl/, 'Ruby')    # replace first 'Perl' with 'Ruby'
line.gsub(/Python/, 'Ruby') # replace every 'Python' with 'Ruby'

Tenemos mucho más que decir sobre expresiones regulares y lo haremos a través del libro.

Bloques e Interadores

Esta sección describe brevemente uno de los puntos fuertes de Ruby. Vamos a ver los bloques de código: trozos de código que pueden ser asociados con la invocación de un método, casi como si fuesen parámetros. Esta es una característica de increíble potencia. Puedes utilizar los bloques de código para implementar callbacks (pero son más simples que las clases anónimas internas de Java), para pasar trozos de código (pero son más flexibles que los punteros a funciones de C), y para implementar iteradores.

Los bloques de código son simples trozos de código entre llaves o do...end.

{ puts "Hello" }       # this is a block

do                     #   club.enroll(person)  # and so is this   person.socialize     # end                    #

Una vez que has creado un bloque, puedes asociarlo con una llamada a un método. Ese método pueden entonces invocar el bloque una o más veces utilizando la sentencia de Ruby yield. El siguiente ejemplo muestra esto en acción. Definimos un método que llama a yield dos veces. Entonces lo llamamos, poniendo un bloque en la misma línea, después de la llamada (y después de cualquier argumento al método). [Alguna gente le gusta pensar en la asociación de un bloque con un método como el paso de algún tipo de parámetro. Esto función en un nivel, pero no es realmente toda la historia. Podrías pensar mejor en el bloque y el método como corrutinas, que se transfieren el control entre si.]

def callBlock
  yield
  yield
end

callBlock { puts "In the block" }
produces:
In the block
In the block

Observa como el código en el bloque (puts "In the block") es ejecutado dos veces, una por cada llamada a yield.

Puedes proporcionar parámetros a la llamada a yield: estos parámetros serán pasados al bloque. Dentro del bloque, tu listas los nombres de los argumentos para recibir esos parámetros entre barras verticales (``|'').

  def callBlock
    yield , 
  end

callBlock { |, | ... }

Los bloques de código son utilizados a lo largo de la librería de Ruby para implementar iteradores: métodos que devuelven sucesivos elementos de algún tipo de colección, como los arrays.

a = %w( ant bee cat dog elk )    # create an array
a.each { |animal| puts animal }  # iterate over the contents
produces:
ant
bee
cat
dog
elk

Vamos a ver cómo podríamos implementar el iterador each de la clase Array que hemos utilizado en el ejemplo previo. El iterador each recorre cada elemento en el array, llamando a yield para cada uno. En pseudocódigo, esto podría ser como:

# within class Array...
def each
  for each element
    yield(element)
  end
end

Entonces podrías iterar sobre los elementos del array llamando a su método each y proporcionando un bloque. El bloque sería llamado por cada elemento.

[ 'cat', 'dog', 'horse' ].each do |animal|
  print animal, " -- "
end
produces:
cat -- dog -- horse --

Iguamente, muchas construcciones de bucles están construidas en lenguajes como C y Java son simples llamadas a métodos en Ruby, con el método invocando el bloque cero o más veces.

5.times {  print "*" }
3.upto(6) {|i|  print i }
('a'..'e').each {|char| print char }
produces:
*****3456abcde

Aquí nosotros preguntamos al número 5 que llame al bloque 5 veces, entonces pedimos al número 3 que llame al bloque, pasandole los sucesivos valores hasta que alcance el 6. Finalmente, el rango de caracteres desde ``a'' hasta ``e'' invoca un bloque utilizando el método each.

Leyendo y Escribiendo

Ruby proporciona una completa librería de E/S. Sin embargo, en la mayoría de los ejemplos de este libro nosotros nos apoyaremos en unos pocos métodos. Estaremos alrededor de dos métodos que proporcionan salida. puts escribe cada uno de sus argumentos, añadiendo una carácter de nueva línea después de cada uno. print también escribe sus argumentos, pero sin nueva línea. Ambos pueden ser utilizados para escribir a cualquier objeto E/S, pero suelen escribir a la consola.

Otro método de salida que utilizamos mucho es printf, el cual imprime sus argumentos bajo el control de una cadena de formato (como printf en C o Perl).

printf "Number: %5.2f, String: %s", 1.23, "hello"
produces:
Number:  1.23, String: hello

En este ejemplo, la cadena de formato "Number: %5.2f, String: %s" le dice al método printf que sustituya un número en coma flotante (permitiendo cinco caracteres en total, con dos después del punto decimal) y una cadena.

Hay muchas maneras de leer la entrada en tu programa. Probablemente la más tradicional es utilizando la rutina gets, que devuelve la siguiente línea desde el flujo de entra estándar de tu programa.

line = gets
print line

La rutina gets tiene un efecto secundario: así como devuelve la línea recién leida, también la almacena den la variable global $_. Esta variable es especial, en el aspecto de que es usada como argumento por defecto en algunas circunstancias. Si llamas a print sin argumentos, imprime el contenido de $_. Si escribes una condición if o sencencia while con justamente una expresión regular como condición, esa expresión es validada contra $_. Mientras que esto es visto por algunos purista como barbaridad, estas abreviaturas pueden ayudarte a escribir algunos programas concisos. Por ejemplo, el siguiente programa imprime todas las lineas del flujo de entrada que contienen la palabra ``Ruby.''

while gets           # assigns line to $_
  if /Ruby/          # matches against $_
    print            # prints $_
  end
end

La ``manera Ruby'' de escribir esto sería utilizando un iterador.

ARGF.each { |line|  print line  if line =~ /Ruby/ }

Esto utiliza el objeto predefinido ARGF, que representa el flujo de entrada que puede ser leido por un programa.

Adelante y Arriba

Ya está. Hemos finalizado nuestro paseo a la velocidad de la luz por algunas de las características claves de Ruby. Hemos echado un vistazo a los objetos, metodos, cadenas, contenedores, y expresiones regulares, viendo algunas estructuras de control sencillas, y mirado algunos ingeniosos iteradores. Esperamos que este capítulo te halla dado suficiente munición para ser capaz de atacar el resto del libro.

Es hora de movese, y subir a un nivel superior. A continuación, veremos las clases y objetos, cosas que son a la vez el elemento de más alto nivel en Ruby y los pilares esenciales del lenguaje.


Extraído del libro "Programming Ruby - The Pragmatic Programmer's Guide"
Copyright © 2000 Addison Wesley Longman, Inc. liberado bajo los términos de la Open Publication License V1.0.
La referencia está disponible en: download.