Anda di halaman 1dari 134

Versin 0.

22

Temario:
Temario:
1) Motivacin
Ruby
2) Setup
Instalando Ruby en OSX
Instalando RVM y ruby
Introduccin a RVM
3) Introduccin a Ruby
El Intrprete
Entrada y salida de datos
Introduccin a tipos de datos
Tipos de lenguaje
Tipos de datos frecuentes.
Preguntas
4) Operadores
Operadores aritmticos
Operadores de comparacin
Operadores de asignacin
Operadores lgicos
Preguntas
5) Trabajando fuera del intrprete.
Creando scripts
Analicemos el resultado
Preguntas:
6) Condiciones y loops
Condicionales
Ciclos

While
Until
For
Ejercicios para resolver
Break
Ciclos anidados.
Desafos
Preguntas
7) Mtodos
Definiendo un mtodo
Llamando una funcin
Parmetros
Mtodos con parmetros valores por defecto
El scope de las variables
Variables globales vs locales
Mtodos recursivos
Preguntas
8) Arrays
Creando arrays
ndices
Contando elementos
Los arrays como contenedores
Mutabilidad en los arrays
Operaciones funcionales sobre los arrays
Select
Inject
Group_by
Contando elementos con group by
Ordenando arreglos
Los arrays como conjuntos

Los arrays como una pila (o stack)


Arrayception (arrays dentro de arrays)
Preguntas
9) Hashs
Creando un hash
Agregando y cambiando elementos a un hash
Removiendo elementos de un hash
Iterando un hash
Convirtiendo un hash en un arreglo.
Convirtiendo un arreglo en un hash
Preguntas
10) Objetos en ruby
Requisitos
Para que sirven los objetos
Los tres conceptos ms importantes
Identidad, Estados y Comportamientos
Mtodos de instancia
Getters y Setters

Preguntas
11) Objetos y arreglos
Ejercicios resueltos
Preguntas
12) Bloques, Procs y Lambdas
Bloques
Procs
Lambdas
13) Archivos

Preguntas

14) Manejo de errores


Introduccin a manejo de errores
Manejando las excepciones
Manejo de errores y archivos
Desafo de Puntos y lneas
Preguntas
15) Expresiones regulares
Patrones
16) Scrapping
Introduccin a Mechanize
Navegando por los tags
Navegando en google
Lyrics
Desafo
Preguntas
17) Objetos avanzado
Self y main
Llamados implcitos vs explcitos
Mtodos pblicos privados y protegidos.
Mtodos protegidos
Herencia
Clase, Superclase, y tipo
Herencia de estados, mtodos y super
Herencia de los mtodos privados y protegidos
18) Mdulos y Mixins
Mdulos vs clases
Mdulo en archivo externo
Includes
Extends
Herencia + includes

Clases dentro de los mdulos


Mdulos dentro de los mdulos
Desafio
Preguntas
19) Testing
Mi primer test
Qu testear?
TDD
BDD
Ejercicios prcticos

1) Motivacin
Este es el segundo libro de la coleccin de Fullstack de Desafo Latam, el libro fue creado con el propsito de
ayudar a los alumnos de desafiolatam a apoyar sus clases, y entender a profundidad como funciona Ruby, para
posteriormente sacarle todo el provecho a Ruby on Rails.
Este libro incluye todos los conceptos claves de ruby, desde las instrucciones bscias, hasta el manejo de bloques,
procs y objetos. Este libro fue escrito y pensado para aquellas personas que ya tengan bases en pensamiento
analtico y resolucin de problemas bsicos de programacin, si no es tu caso puedes empezar con el primer
libro de la coleccin.

Ruby
Ruby fue creado por Yukihiro Matsumoto, tambin conocido como Matz, dentro de las caracterstica tiene:
Sintaxis simple
Programacin orientado a objeto (classes, methods, objects)
Caractersticas especiales de orientacin a objetos (Mix-ins, mtodos singleton methods, etc)
Sobrecarga de operadores
Manejo de errores
Iteradores
Closures
Colector de basura
Carga dinmica
Multiplataforma (Unices, Windows, DOS, OSX, OS/2, Amiga, )

2) Setup
La instalacin de ruby depende del sistema operativo, tanto en OSX como en Linux se recomienda instalar RVM
(Ruby Virtual Machine), el cual permite tener instalada diferentes versiones de ruby en el sistema sin conflicto, lo
que es muy til a la hora de trabajar en diversos proyectos.
Se desaconseja trabajar con Windows, si bien ruby funciona bien en windows muchas componentes (gemas) no
funcionan, pero dentro del alcance de este libro no habr problemas con ningn sistema operativo.

Instalando Ruby en OSX


Previo a otras instalaciones necesitaremos el command line tools, para eso:

xcode-select --install

Se puede encontrar ms informacin en la pgina oficial: http://railsapps.github.io/xcode-command-linetools.html

Instalando RVM y ruby


La pgina oficial de RVM contiene las instrucciones de instalacin, que son preferibles a las que siguen debido a
que estas cambian en el tiempo.

\curl -sSL https://get.rvm.io | bash -s stable --ruby

Existen otros programas similares a RVM, los otros dos ms famosos son Rbenv y CHruby. Para el propsito de
este libro es posible usar cualquiera, pero para el siguiente libro de la coleccin, el libro de Rails se ocupar RVM
para automatizar el deployment con Capistrano as que es mejor instalar RVM desde ya.

Introduccin a RVM
Con RVM instalado (es necesario cerrar la consola y abrirla de nuevo despus de instalarlo)
rvm list muestra todas las versiones de ruby que tienes en el sistema rvm install 2.3.1 instala

la versin 2.3.1 de ruby rvm use 2.3.1 marca como default la versin 2.3.1 de ruby.
Finalmente podemos saber si la versin que tenemos de ruby ocupando:

ruby -v

La versin de ruby que utilizaremos en este libro es la 2.3.1.

3) Introduccin a Ruby
El Intrprete
La forma ms rpida de empezar a trabajar con Ruby es con el intrprete, para entrar a el debemos abrir la
terminal y escribir

irb

Dentro del intrprete ya no podemos escribir comandos bash como cd o ls en su lugar debemos escribir
mtodos y operadores de ruby por ejemplo podemos escribir 2+2 y ver como output 4
Para salir del intrprete debemos escribir exit y con eso volveremos a la terminal bash .

Introduccin a variables
Podemos almacenar valores en variables simplemente asignndolos, ejemplo a = 2
Con los valores asignados podemos realizar las mismas operaciones que en otros lenguajes, como en el siguiente
caso:

1
2
3

a = 2
b = 3
a + b

En el intrprete cuando realizamos cualquier operacin obtenemos el output inmediatamente, pero esto no
suceder cuando creemos nuestros programas fuera del intrprete como normalmente se hace.

Recuperando la ltima lnea


En el intrprete podemos recuperar el resultado de la ltima lnea ejecutada con _ , esto es exclusivamente
para el intrprete.

1
2
3
4
5

a
b
a
c
#

= 2
= 3
+ b
= _
=> 5

Entrada y salida de datos


Con ruby podemos mostrar datos ocupando la instruccin puts y capturar datos ocupando la instruccin gets, de
esta forma:

1
2

a = gets
puts a

Antes de mostrar el valor, la consola quedar bloqueada hasta que nosotros ingresemos una secuencia de
caracteres, ya sea nmeros o letras.

El problema de gets como lo podemos ver en la imagen es que tambin captura el salto de lnea que uno ingresa
al presionar la tecla enter.
Para evitar este problema podemos ocupar el mtodo chomp:

1
2

a = gets.chomp
puts a

Introduccin a tipos de datos


En ruby existen distintos tipos de datos, aunque todo son objetos as que lo correcto sera decir tipos de objeto,
cada uno de estos tipos se comporta de forma distinta, y este es el tema que abordaremos a continuacin.
En ruby podemos saber de que tipo es un objeto con el mtodo .class

a = gets.chomp

2
3

puts a.class
# String

En este caso veremos que el resultado es string sin importar si ingresamos un nmero o una secuencia de
caracteres. Ahora si probamos con un nmero, obtendramos:

1
2

b = 2
puts a.class

Veremos que b es del tipo de dato Fixnum, o sea un nmero, hemos descubierto algo importante que 2 no es lo
mismo que "2" y por lo mismo no podemos sumarlos.

2+"2"

1
2
3
4

TypeError: String can't be coerced into Fixnum


from (irb):5:in `+'
from (irb):5
from /Users/gonzalosanchez/.rvm/rubies/ruby-2.2.3/bin/irb:15:in `<main>'

Antes de ahondar sobre los distintos tipos de datos es necesario que hagamos un pequeo de repaso de los tipos
de lenguaje de programacin para que podemos entender mejor como funciona ruby.

Tipos de lenguaje
Languages de tipo esttico vs dinmicos
En ruby los tipos de datos se revisan en tiempo de ejecucin, o sea dinmico, esto quiere decir que si hay una
operacin que sume datos que no son sumables, como por ejemplo "2" + 2 no lo sabremos hasta que se
lea exactamente esa lnea, pero adems tiene otra ventaja, en algunos lenguajes como C (de tipado esttico) uno
tiene que definir el tipo de variable, por ejemplo:

int c = 5;

Luego cambiar el valor que contiene c a algo que no sea un nmero entero causara un error, incluso tampoco
podramos decir especficamente que ahora c va a contener una variable de otro tipo, en cambio en los lenguajes
dinmicos como ruby se puede hacer:

1
2

c = 5
c = "hola"

Independiente de que sea mucho ms fcil para un programador trabajar de esta forma, tambin hace el cdigo
ms propenso a errores, pero para evitar varios de estos errores hay otra caracterstica muy potente que tiene
ruby, llamada duck typing.

Strong Typing vs Weak Typing


Otra forma de catalogar los lenguajes adems de esttico o dinmico es segn el tipo de conversin que hacen
sobres sus datos, los lenguajes de programacin pueden ser de tipeo fuerte, dbil o tipo pato.
En tipado fuerte no se permite conversin automtica de los tipos de datos, se asume que si hay se debe a un
error del programador.
En lenguajes de tipado dbil como Javascript las conversiones son automticas lo que muchas veces genera
errores, como por ejemplo al sumar "1" + 1, javascript lo convierte a "11"
Ruby es un lenguaje de un tercer tipo, que recibe el peculiar nombre de tipo pato, cul es la idea de esto?, bueno
si algo se comporta como pato, o sea nada como pato, y hace cuac como pato entonces es un pato, esto implica
que no importa el tipo de dato, lo que importa es el comportamiento que tiene definido, y nosotros podemos
definir y redefinir comportamientos, de esta forma podemos crear operaciones a nuestra necesidad sobre
cualquier tipo de objetos, esta es una de las grandes bellezas del lenguaje de Ruby.

Tipos de datos frecuentes.


En ruby todo es un objeto por lo mismo no existen los tipos de datos nativos como en otros lenguajes, sin
embargo si existen tipos de datos incluidos con todo lo necesario para trabajar. Los ms frecuentes son:
String
Fixnum y Bignum
Float
NilClass

TrueClass, FalseClass
Array
Symbol
Hash
Time

String
Los strings son cadenas de texto como "hola mundo". Ruby diferencia los identificadores (o sea los nombres de
las variables) de los strings porque estos ltimos estn rodeados de comillas dobles o comillas simples.

1
2
3
4

b
a
c
d

=
=
=
=

5
b
"b"
'b'

Cunto vale a y cuanto vale c?, si no te qued clara la idea prubalo en el intrprete y obtendrs los resultados.

Interpolacin
La interpolacin consiste en mostrar el valor de una variable dentro de un string como en el siguiente caso:

1
2
3

edad = 30
puts "tienes #{edad} aos"
# tienes 30 aos

El mensaje mostrara que tienes 30 aos, la interpolacin slo funciona sobre comillas dobles y no funciona
sobre comillas simples.

1
2
3

edad = 30
puts 'tienes #{edad} aos'
# tienes #{edad} aos

Fixunum and Bignum


Los detalles que aprenderemos a continuacin no son imporantes para programar en ruby pero nos ensearn
algunas caractersticas interesantes del lenguaje ruby.
Fixnum son aquellos nmeros enteros que son capaces de almacenar un Integer, el tamao final depende de la
arquitectura del computador, pero en una mquina de 64 bits el nmero ms alto que se puede almacenar es
(2 62) 1, sin embargo en caso de exceder ese valor el tipo de dato se convertir automticamente en un
Bignum, en otras palabras no tenemos que preocuparnos de excedernos overflow como en otros lenguajes.
Un ejercicio interesante es probar esto en el intrprete, para eso escribiremos:

1
2

(2**62 - 1).class
(2**62).class

En el primer caso obtendremos Fixnum en el segundo Bignum, si en ambos obtienes bignum es muy posible
que la arquitectura de tu computador sea de 32 bits.
Como detalle cabe destacar que los parntesis son necesarios porque el .class tiene precedencia por sobre la
operacin, entonces sin los parntesis en el primer caso haramos .class de 1 que es un Fixnum y luego no lo
habramos podido restar de 2**62, en el segundo sin los parntesis habramos sacado el .class de 62 y tratado de
elevar 2 a Fixnum operacin que claramente no tiene sentido.

Float
Float son los valores con decimales, o sea por ejemplo:
2.0
3.14
1.5
Los nmeros flotantes son representaciones inexactas de los nmeros reales y por lo mismo se debe tener
precaucin al compararlos a escala muy pequeas.
En ruby la divisin no exacta de dos nmeros enteros no da como resultado un nmero decimal en lugar de eso
da como resultado otro entero. Ejemplo: al dividr 4 / 5 esperaramos como resultado 8, pero en su lugar
obtenemos cero.
Hay dos formas de resolver el problema:

La primera es agregando un .0, por ejemplo 4.0 / 5 o 4 / 5.0 nos darn como resultado 0.8
La segunda es transformando el dato con el mtodo .to_f, ejemplo 4.to_f / 5 , esta segunda forma es la
que hay que utilizar cuando nuestros valores estn dentro de variables.
La divisin de un float con un entero o de un entero con un float si da un float como resultado.

La clase nil o (nilclass)


A diferencia de muchos otros lenguajes populares existe un objeto para los valores nulos, este es nil, lo bueno de
que los valores nulos sean un objeto es que podemos hacer cosas como esta:

1
2

a = nil
a.nil?

En cualquier otro lenguaje habramos obtenido un error del tipo que a no es un objeto y por lo tanto no tiene el
mtodo .nil?, ruby lo soporta. Como resultado del experimento anterior obtendremos el valor booleano true,
pero se preguntamos por un valor no nulo, como por ejemplo 2.nil? obtendremos como resultado false.
no confundir con undefined, una variable ser undefined mientras no se haya definido.

TrueClass y FalseClass
Los objetos true y false sirven para representar valores booleanos, son tiles para representar estados, veamos un
ejemplo bsico:

1
2
3

if true
puts "hola"
end

En muchos lenguajes existe una conversin implcita entre el 0 y el falso y el 1 y verdadero, pero en ruby son
cosas distintas.

1
2

1 == true # Veremos que es falso


0 == false # Veremos que tambin es falso

En ruby todo es un objeto, true y false son instancias de las clases TrueClass y FalseClass respectivamente. Esto
significa que si preguntamos por la clase de true obtendremos TrueClass y si lo hacemos por la clase de false
obtendremos FalseClass.

true.class # TrueClass

Array []
Los arrays de ruby son contenedores que permiten agregar mltiples datos y de diversos tipos, los datos dentro
pueden accedidos secuencialmente o por su ndice. ejemplo:

1
2

a = [1,2,3,4,5, "hola"]
puts a[0]

Los ndices van desde cero hasta n - 1. Acceder a un indice ms all del rango tendr como efecto la devolucin
de nil

puts a[10] # => nil

Para acceder a todos los datos secuencialmente ocuparemos el .each

1
2
3

a.each do |i|
puts a[i]
end

Los arrays puedes ser recorridos de otras formas, y son un captulo completo de este libro que abordaremos ms
adelante.

Smbolos :simbolo
Los smbolos son tipos de datos similares a los strings pero estn optimizados para ocupar menos memoria. los
smbolos empiezan con :

1
2

a = :hola
puts a

Para qu sirven?
En muchas ocasiones no nos interesa ocupar el string para operar sobre el, en algunas ocasiones lo ocupamos
para denotar el estado de una variable, por ejemplo supongamos que tenemos un semforo que puede tener tres
estados, y estos pueden ser "rojo", "amarillo" o "verde"`, en ese caso conviene utilizar smbolos y simplemente
guardar el estado como smbolo, ejemplo semforo = :amarillo
Si el array almacena posibles estados entonces tambin podemos aplicar la misma lgica

1
2

estados_semaforo = [:verde, :amarillo, :rojo]


semaforo1 = estados_semaforo[0]

Hash {}
Los hash son otro tipo de contenedor que lugar de indexarse por un nmero se indexan por una clave. Ejemplo:

1
2

a = {"clave1" => "valor1", "clave2" => "valor2"}


# => {"clave1"=>"valor1", "clave2"=>"valor2"}

Para acceder a los valores de un hash debemos hacerlo por la clave, como en el siguiente ejmplo:

1
2

a["clave1"]
# => "valor1"

A los hash se les suele llamar diccionarios porque funcionan de la misma forma, para poder obtener el
significado de una palabra la bscas por el ndice.
La sintaxis de los hash es clave => valor , donde la clave puede ser un string o smbolo y el valor puede
ser cualquier objeto, o sea cualquier tipo de dato ya sea una array o un hash.

Hash y Smbolos

Mencionamos que los hashs pueden utilizar smbolos como claves, para lograrlo podemos utilizar una de las dos
siguientes sintaxis.

1
2

a = {:smbolo1 => "valor1", :smbolo2 => "valor2"}


#=> {:smbolo1=>"valor1", :smbolo2=>"valor2"}

O podemos utilizar la siguiente:

1
2

a = {"clave1": "valor1", "clave2": "valor2"}


# => {:clave1=>"valor1", :clave2=>"valor2"}

Time
La clase Time nos permite generar instancias de tiempo, Time.now nos devuelve una instancia con la hora local
del sistema, luego podemos movernos en el tiempo sumando y restando segundos.

1
2
3

hora = Time.now
puts hora + 60
puts hora + 3600

Preguntas
Cul es la diferencia entre un smbolo y un array?
Cul es la diferencia entre un array y un hash?
En que consiste el duck typing?
Para que sirve la interpolacin?
Es lo mismo un string con doble comilla y que uno de comillas simples?
Cmo ruby diferencia un string de una variable?
Es lo mismo a = 2, que a = "2"?
Es lo mismo a = "b", que a = b?
Cul es la diferencia entre Bignum y Fixnum?
Qu es una IDE?

Cmo se puede correr un script directamente desde sublime?

4) Operadores
En ruby los operadores son parte del comportamiento de los objetos, estn definidos dentro de las clases como
veremos ms adelante y son necesarios para sobre los distintos tipos de datos existen.
Los operadores bsicos se dividen en estos cuatro tipos:
1. Operadores aritmticos
2. Operadores de comparacin
3. Operadores de asignacin
4. Operadores lgicos

Operadores aritmticos
Los operadores aritmticos son aquellas instrucciones que permiten operar sobre los distintos tipos de datos,
principalmente aplican a nmeros, pero algunos objetos tienen sus propias definiciones de estos
comportamientos
Operator

Nombre

Ejemplo

Resultado

Suma

2+3

Resta

2-3

-1

Multiplicacin

3*4

12

Divisin

3/4

Mdulo (resto)

3%4

**

Potencia

2 ** 4

16

Operadores de comparacin
Los operadores de comparacin son aquellos que comparan dos valores y obtienen como resultado un valor
booleano.
Operator
==

Nombre
Igual a

Ejemplo

Resultado

2 == 2

true

==

Igual a

2 == 2

true

!=

Distinto a

2 != 2

false

>

Mayor a

3>4

false

>=

Mayor o igual a

3 >= 3

true

<

Menor a

4<3

false

<=

Menor o igual a

3 <= 4

true

Operadores de asignacin
Los operadores de asignacin tienen como resultado cambiar la variable a la izquierda de la expresin, por
ejemplo al hacer a = 2 la variable a se le asigna el valor 2.
Operator

Nombre

Ejemplo

Resultado

Asignacin

a=2

a toma el valor 2

+=

Incremento y
asignacin

a += 2

a es incrementado en dos y asignado el valor


resultante

-=

Decremento y
asignacin

a -= 2

a es reducido en dos y asignado el valor


resultante

*=

Multiplicacin y
asignacin

a *= 3

a es multiplicado por tres y asignado el valor


resultante

/=

Divisin y asignacin

a /= 3

a es dividido por tres y asignado el valor


resultante

Operadores lgicos
Los operadores de comparacin son aquellos que comparan dos valores que reciben el nombre de operandos y
obtiene como resultado un valor booleano.
Operator

Nombre

Ejemplo

Resultado

and

false and
true

devuelve true si ambos operandos son true, en este ejemplo se


devolvera false

false or

devuelve true si al menos una de los operando es verdad, en

true

este ejemplo sera false.

or

or

true

este ejemplo sera false.

not

no

not false

true

Preguntas
Que se entiende con que el operador es parte del comportamiento de un objeto? Cul es la diferencia entre un
operador aritmtico y uno de asignacin? Qu significa la expresin a += 1 ?

5) Trabajando fuera del intrprete.


Creando scripts
El intrprete no es la nica forma de trabajar, tambin podemos crear nuestros scripts en un archivo con nuestro
editor de texto favorito. Dos buenas alternativas son Atom y Sublime Text 3
El editor de texto nos entrega pocas herramientas comparado con una IDE, pero son ms que suficientes para
trabajar en Ruby, en cualquier caso si en algn momento se necesitan ms herramientas, existe una IDE muy
potente en el mercado llamada Ruby Mine.
Para crear un script podemos hacerlo desde la opcin archivo nuevo del editor o desde la lnea comando con el el
comando touch.

touch prueba.rb

Es importante es que el archivo tenga extensin .rb que es la extensin de ruby.

Como primera prueba dentro del script vamos a pedir al usuario dos datos y los vamos a sumar.
Para eso dentro del archivo pondremos:

1
2
3
4
5

puts "ingrese el primer valor"


a = gets.chomp
puts "ingrese el segundo valor"
b = gets.chomp
puts a + b

Para probar el script abriremos la carpeta donde est nuestro script utilizando bash y dentro de ella podemos
ejecutar el script escribiendo:

ruby prueba.rb

Luego ingresamos un valor, enter, luego otro valor, enter y veremos como resultado la concatenacin de ambos
nmeros en lugar de las suma.
Existe otra forma de correr los archivos en ruby ocupando el build de sublime directamente, como lo muestra la
siguiente imagen.

Para eso primero debemos seleccionar el build sistema que corresponde y lugar debemos seleccionar la opcin
build o el shortcut que ah aparece.

Analicemos el resultado
Por qu obtuvimos una concatenacin?
Porque gets.chomp devuelve un string, y ruby a travs de su Duck Typing interpreta la suma de dos strings como
la concatenacin.
Entonces cmo hacemos que de la suma? Fcil, transformamos los tipos de datos ocupando el mtodo .to_i

1
2
3
4
5

puts "ingrese el primer valor"


a = gets.chomp.to_i
puts "ingrese el segundo valor"
b = gets.chomp.to_i
puts a + b

En el cdigo anterior tambin aprendimos otra leccin interesante, los mtodos de los distintos objetos se
pueden ir concatenado, puesto gets.chomp devuelve un string entonces podemos aplicar los mtodos de strings
al resultado. Ahondaremos ms en este tema en el futuro.

Antes de avanzar al siguiente captulo deberas ser capaz de contestar estas preguntas, si no te recomiendo que
vuelvas a leerlo y lo contestes

Preguntas:
Cul es la diferencia entre el comando de bash irb y el de ruby?
Qu significa que los mtodos se puedan concatenar?
Cul es la relacin entre concatenacin de mtodos y concatenacin de strings?
Se puede utilizar _ para repetir la ltima lnea cuando estamos fuera del intrprete?

6) Condiciones y loops
Condicionales
Al igual que en casi todos los lenguajes de programacin, es posible ramificar el cdigo basado en condiciones if
y else, por ejemplo:

1
2
3
4
5
6
7

puts "Qu edad tienes?"


edad = gets.chomp.to_i
if (edad > 18)
puts "Eres mayor de edad"
else
puts "Eres menor de edad"
end

El cdigo dentro del if slo se ejecuta si se cumple la condicin, el cdigo dentro del else slo se ejecuta si no se
cumple.
En ruby tambin es posible invertir las operaciones utilizando la instruccin unless.

1
2
3
4
5
6
7

puts "Qu edad tienes?"


edad = gets.chomp.to_i
unless (edad < 18)
puts "Eres mayor de edad"
else
puts "Eres menor de edad"
end

Otra forma de invertir las condiciones es ocupando el operador lgico de not, para no sobrecomplicarnos lo
correcto es siempre escribir las condiciones en positivo, puesto que las expresin como yo no, no lo hara son
difciles de leer y no siempre quieren decir lo que dicen, escribiendo las expresiones en positivo evitaremos
muchos problemas.
Una pregunta interesante es por que es importante tener una instruccin unless cuando es tan fcil negarla con
un not, ejemplo if not(edad < 18) La razn es porque el objetivo de Ruby es que se lea como si fuese
ingls.
En ruby tambin es posible utilizar versiones cortas de la expresin if y unless de la siguiente forma:

result = "eres mayor de edad" if edad > 18


puts results

1
2

A este tipo de expresiones se les llama inline por estar en la misma lnea.

elsif
la instruccin elsif permite capturar la opcin en caso de que no se cumpla el if anterior.

1
2
3
4
5
6
7
8
9

puts "ingresa un nmero"


a = gets.chomp.to_i
if a > 10
puts "tienes ms de 10 aos"
elsif a >= 20
puts "tienes ms de 20 aos"
else
puts "eres menor de 10 aos"
end

Se debe tener mucho cuidado que utilizar dos ifs no es lo mismo que utilizar if y elseif, analicemos el siguiente
caso:

1
2
3
4
5
6
7
8
9
10

puts "ingresa un nmero"


a = gets.chomp.to_i
if a > 10
puts "tienes ms de 10 aos"
end
if a >= 20
puts "tienes ms de 20 aos"
else
puts "eres menor de 10 aos"
end

En este caso si la persona tiene 11 aos, mostrar que tiene ms de 10 y luego mostrar que es menor de 10 aos
y si la persona tiene 20 o ms entonces mostrar que tiene ms de 10 aos y ms de 20 aos.

Case
Cuando son muchos los posibles casos, se recomienda ocupar la instruccin case.

1
2
3
4
5
6
7

a = 10
case
when (a > 1 and a < 10)
puts "Estoy entre 1 y 10"
when a >= 10
puts "a es mayor que 10"
end

La instruccin Case puede ser utilizada de dos formas, con parmetro o sin, ya revisamos el primer caso, ahora
veamos el segundo.

1
2
3
4
5
6
7
8
9
10

case a
when 1..10
puts "Estoy entre 1 y 10"
when 11
puts "Soy 11, o sea ms que 10"
when String
puts "No soy un nmero soy un string"
else
puts "Ups, no se que hacer con #{a}"
end

Gracias al duck typing de ruby la instruccin case se convierte en una herramienta bastante poderosa para
manejar diversas situaciones.

Operador ternario.
El operador ternario es una variante del if que permite asignar un valor u otro dada una condicin.
la lgica es la siguiente:
si_es_cierto ? resultado_cierto : resultado falso.
llevado a ruby sera:

a == 5 ? b = 1 : b = 2

Lo anterior se le, b tendr el valor 1 si a vale 5, en caso contrario tendr el valor 2.

Ciclos
Los ciclos son instrucciones que permiten repetir una o un conjunto de instrucciones, aunque siempre debemos
tener cuidado de que no sea un nmero infinito de veces.

Times
La forma ms fcil de repetir una instruccin o grupo de instrucciones es con la funcin times

1
2
3

5.times do
puts "repitiendo"
end

Adems si queremos mostrar el nmero de la repeticin podemos hacerlo utilizando una variable.

1
2
3

5.times do |i|
puts "repitiendo: #{i}"
end

Tanto times como muchas de las instrucciones que veremos a continuacin reciben bloques, estos son tan
importantes en ruby que tienen su propio captulo en este libro, pero por ahora es importante entender que
todos los bloques tienen dos posibles sintaxis.
La primera es con do y end como vimos previamente, y la segunda es entre llaves

5.times { |i| puts "repitiendo #{i}" }

Desafio

Utiliza la instruccin times para contar en reversa de 100 a 1


Utiliza la instruccin times para contar todos los nmeros pares hasta 100

While
while permite repetir una instruccin mientras se cumpla una condicin, la sintaxis del while es:

1
2
3

while (condicion) do
end

Mientras la condicin sea verdadera todo lo que est dentro del bloque se repetir.
while tiene una sintaxis alternativas al igual que el if

codigo while condition

Y funciona de la misma forma, al igual que en el caso anterior el cdigo se ejecutar mientras la condicin sea
verdadera.
La instruccin while no puede recibir un bloque :(

Until
Until es la versin negativa del while, la idea al igual que unless es evitar ocupar el operador not que hace ms
difcil leer ciertas expresiones (ademas como operador lgico sigue reglas que en ciertas ocasiones puede ser
difcil de entender, como por ejemplo !a y !b = !(a o b)
La sintaxis es:

1
2
3

until (condicion) do
end

Al igual que while tambin puede ser usada en una sola lnea

pedir_password until password == "secreto"

For
for es una instruccin que nos permite iterar sobre rangos y arreglos, por ejemplo:

1
2
3

for i in 0..10
puts i
end

Para un arreglo tenemos varias opciones parar iterarlo, principalmente se dividen en sobre los ndices, los
arreglos, y ambos simultneamente.

Por ndice
1
2
3
4

a = [9,3,5,7,1,2,3]
for i in 0..a.length
puts a[i]
end

Por elemento
1
2
3
4

a = [9,3,5,7,1,2,3]
for element in a
puts element
end

Recordar que element en este caso es simplemente una variable que sirve para iterar por sobre los valores de a,
no es una palabra reservada.

Utilizando each

1
2
3
4

a = [9,3,5,7,1,2,3]
a.each do |element|
puts element
end

Iterando sobre ndice y elemento con each


1
2
3
4

a = [9,3,5,7,1,2,3]
a.each_with_index do |element, indice|
puts "el elemento #{element} en la posicin #{indice}"
end

Ejercicios para resolver


Ejercicios bsicos
Repetir
Repetir 10 veces "voy a aprender ruby"
Mostrar cien nmeros al azar.
Hint:

1
2

prng = Random.new
prng.rand(100)

Mostrar todos los divisores del nmero 840


con while
con for
con times

Cambiando de do-while a while


Dado el cdigo

1
2
3
4

begin
puts "escriba si para continuar"
input = gets.chomp
end while(input != "si")

Transformarlo utilizando nicamente while

El problema de 3n + 1
La conjetura de collatz dice que para cualquier nmero positivo entero:
Si el nmero es par se divide en dos Si el nmero es impar se multiplica por 3 y se le suma 1.
Si ahora tomamos el nmero resultante y aplicamos el mismo criterio sucesivamente esta secuencia tarde o
temprano el nmero resultante ser 1.
Ejemplo
10 => 5 => 16 => 8 => 4 => 2 => 1

Lo que debes hacer es crear un mtodo que permita mostrar toda la secuencia a partir de un nmero ingresado
por el usuario.

Bsqueda binaria
Utilizando exclusivamente ifs y while es posible construir un pequeo juego donde el ordenador adivina un
nmero que tu piensas entre 1 y 1000 en un mximo de diez intentos.
Escoges un nmero, La idea ahora es partir buscando en la mitad del arreglo, por ejemplo en la posicin 500, si
el jugador dice que es mayor, buscaremos en la 750, luego si dice que es menor buscaremos en 675, y as
sucesivamente hasta encontrar el nmero

Break

La instruccin break es capaz de romper un ciclo, la podemos ocupar directamente o dentro de una condicin,
ej:

1
2
3
4
5
6

10.times do |i|
puts i
break
end

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

i = 0
while (i < 100)
puts i
if i > 5
break
end
i = i + 1
end

#
#
#
#
#
#
#
#

0
1
2
3
4
5
6
=> nil

Ciclos anidados.
Es perfectamente posible anidar ciclos, o sea escribir un ciclo de otro, esto tiene varias utilidades, desde las ms
obvias como repetir un ciclo n veces hasta mostrar todos los elementos que existen dentro de una matriz.
Supongamos que por algn motivo queremos generar la secuencia:

1
2
3
4

0
1
1
1

0
1
2
3

5
6
7
8
9
10
11
12

1
2
3
4
5

1
1
1
2
2
2
.
5

4
5
6
1
2
3
.
5

6.times do |i|
6.times do |j|
puts "#{i} #{j}"
end
end

Desafos
Similar al ejercicio anterior genera todas las combinaciones de 4 nmeros posibles del nmero cero al diez.
Genera todas las secuencias de nmeros posibles entre tres nmeros tales que el nmero de la izquierda
siempre sea menor o igual que el de la derecha
ej:
111
112
11.
1 1 10
1 2 2 # el trmino 1 2 1 no podra aparecer
Antes de avanzar al siguiente captulo deberas ser capaz de contestar estas preguntas, si no te recomiendo que
vuelvas a leerlo y lo contestes

Preguntas
Por qu es bueno escribir siempre las condiciones en sentido positivo?
Cul es la diferencia entre if y unless?
De un ejemplo de un if inline?
Cul es la diferencia entre while y until?

7) Mtodos
En muchos lenguajes existe la posibilidad de agrupar cdigo y darle un nombre para reutilizarlo a voluntad, a
esto se le llaman funciones.
En lenguajes como en ruby no existen las funciones, pero existe algo muy similar llamado mtodos y sirven para
exactamente lo mismo construir

Definiendo un mtodo
El mtodo ms bsica que podemos definir:

1
2

def nombre_metodo()
end

Dentro de la definicin de un mtodo existe: - def (para indicar que se va a definir una funcin) - el nombre - los
parmetros (que van dentro de los parntesis) - ej: metodo(x, y) - el retorno
Ejemplo de un mtodo ms complejo:

1
2
3

def perimetro(r)
return 2 * Math.pi * r
end

Llamando una funcin


Luego el llamado consiste en utilizar el nombre y pasar los parmetros indicados.

perimetro(10)

De esta forma uno define una funcin y luego la ocupa varias veces.

Retorno implcito

En ruby el retorno de la funcin es siempre la ltima lnea, por lo tanto podramos escribir el cdigo anterior de
la siguiente forma y sera exactamente lo mismo.

1
2
3

def perimetro(r)
2 * Math.pi * r
end

En el caso de no estar especificado el retorno devolver nulo.


Debemos ser cuidadoso por que por error podramos hacer que un mtodo devolviese nulo u otro valor no
esperado, miremos el siguiente ejemplo:

1
2
3

def foo(x)
true if x > 18
end

En este caso el mtodo foo devolver true si x es mayor de 18, pero en lugar de devolver falso si es menor,
devolver nulo, luego no es lo mismo nulo que falso y tendremos problemas con nuestro cdigo, una mejor
solucin sera:

1
2
3
4
5
6
7

def foo(x)
if x > 18
true
else
false
end
end

Otro motivo por el cual tenemos que tener mucho cuidado con el retorno implcito es que agregando un puts en
la ltima lnea para saber el valor de una funcin romperamos el retorno, imaginemos este caso:

1
2
3
4
5
6
7
8

def foo(x)
if x > 18
true
else
false
end
puts x
end

El mtodo puts muestra en pantalla el valor de x, pero luego devuelve nil, as es como funciona puts y otros
mtodos para mostrar en pantalla, eso no tiene nada de malo, el problema es que los mtodos devuelven el valor
de la ltima lnea, o sea en este caso se devolver nil y nuestro mtodo dejar de funcionar.
Antes de pasar a otro tema veamos una forma ms corta y ms elegante de implementar el mismo mtodo.

1
2
3

def foo(x)
x > 18
end

Parmetros
Un mtodo puede recibir diversos parmetros, los parmetros son variables que puede recibir un mtodo.
Ejemplo:

1
2
3
4
5
6

def mostrar_valores(a,b)
puts a
puts b
end
mostrar_valores(5,8)

Dentro del mtodo mostrar_valores la variable a tendra el valor 5 y b el valor 8


Los parmetros que se le pasan a un mtodo no tienen porque llamarse igual que los que recibe.

1
2
3
4
5
6
7
8

def mostrar_valores(c,d)
puts c
puts d
end
a = 5
b = 8
mostrar_valores(a, b)

En el momento del llamado se asignan los valores, o sea, cuando se hace el llamado mostrar_valores(a,b) ruby lo

que hace es remplazar eso por mostrar_valores(5,8), luego dentro del mtodo lo que hace es hacer c = 5, d = 8,
el orden siempre se conserva.

Mtodos con parmetros valores por defecto


Los mtodos pueden tener valores por defecto, esto les permite asumir el valor si el argumento no es pasado.

1
2
3

def foo(a, b = 5)
puts b
end

Si llamamos a foo como foo(8) entonces ruby lee que a es el valor 8, como b no fue pasado lo toma como
5 y eso es lo que muestra en pantalla.

El scope de las variables


Cuando se entra a un mtodo, se define un nuevo ambiente. Este ambiente es nuevo, dentro de el se pueden
definir variables que no afecten a las de afuera, pero no son la misma variable, son una variable distinta definida
en este ambiente, sucede lo mismo cuando pasamos parmetros a la funcin, al pasarle a y b estamos generando
una copia de esos valores en este ambiente nuevo, si los modificamos ac dentro ese cambio no suceder afuera

Veamos un ejemplo de esto:

1
2
3
4
5
6
7

def foo()
a = 7
end
a = 5
foo()
puts a

Cunto vale a en el momento que se muestra en pantalla?


Vale 5 y la razn es porque a toma el valor 5, luego se llama al mtodo foo, donde a a se le asigna el valor
7, pero esta a es nueva variable, est asignada en otro espaci y al terminar el mtodo y volver al espacio original
su valor vuelve al original.
Veamos otro ejemplo, ahora con parmetros

1
2
3
4
5
6

def foo(a, b)
puts b
puts a
return
foo(2,5)

Qu muestra foo? 2 y 5 o 5 y 2?
La respuesta correcta es 5 y 2 debido a que cuando llamamos a foo, a toma el valor 2 y b toma el valor 5, luego
mostramos b primero.
Un ejemplo ms, ahora lidiando con el tema del ambiente y la sobreescritura de variables

1
2
3
4
5
6
7
8

def foo(a, b)
puts b
puts a
return
a = 2
b = 5
foo(b,a)

Qu se muestra en pantalla?
El llamado a foo en la lnea 8 se hace con los valores 5,2, o sea foo(5,2), luego dentro de foo a tendra el valor 2 y
b el valor 5, y como el orden se muestra al revs se muestra primero 5 y luego 2.

Variables globales vs locales


Las variables globales son variables que existen en todos los mbitos, pero son una psima prctica de
programacin, porque hacen muy fcil romper un programa por error, pero de todas formas necesitamos saber
identificarlas en ruby (aunque nunca las vayamos a utilizar)
En ruby las variables globales llevan un $, por ejemplo $z= 5
luego

1
2

defined? $x
# => "global-variable"

Mtodos recursivos
Los mtodos recursivos son mtodos que se llaman as mismo, son muy tiles para resolver problemas de
inteligencia artificial, de investigacin de operaciones, de bsqueda o problemas matemticos, en el contexto de
una aplicacin en Rails muy rara vez se utilizan.
En matemticas suelen existir expresiones del tipo:
f (n) = f (n 1) + 10

lease que el siguiente termino de la funcin se calcula como el termino anterior + 10 o sea:
f (2) = f (1) + 10 f(1) = f(0) + 10

Este tipo de expresiones son muy fciles de modelar con funciones recursivas.

1
2
3

def fun(x)
fun(x - 1) + 10
end

Lo anterior termina en un ciclo infinito, de hecho el error obtenido ser


SystemStackError: stack level too deep y esto se debe a que no termina nunca, pero
adems debemos recordar algo del principio del captulo, por cada funcin ruby deja espacio para guardar las
variables en un mbito especial, este espacio se llama stack.
Para evitar el error basta con poner un punto de trmino, por ejemplo digamos que la funcin cuando llegue a
f(1) termina.

1
2
3
4
5
6
7

def fun(x)
puts "con x = #{x}"
if x > 1
resultado = fun(x - 1) + 10
end
resultado || 0
end

Entonces si llega al final del cdigo y no ha terminado devuelve el resultado y cuando llega al ltimo nmero
devuelve cero.
Veamos otra operacin, una famosa, fibbonacci, esta serie parte:
1 1 2 3 5 8 13 21 35
En la serie de fibbonacci todos cada nmero de la serie viene dado por la suma de sus dos nmeros anteriores, o
sea f(n) = f(n-1) + f(n-2), y el primer trmino f(0) es 1 y el segundo trmino o sea f(1) tambin es 1.

1
2
3
4
5
6
7
8

def fibo(x)
if x > 2
resultado = fibo(x - 1) + fibo(x - 2)
else
return 1
end
resultado
end

Luego podemos saber cul es el sexto trmino de la serie simplemente escribiendo.

puts fibo(6)

Preguntas
Cul es la diferencia entre una variable global y una local?
Qu es un mtodo?
Cul es la diferencia entre un mtodo y una funcin?
Cmo se hace para que un mtodo tenga valores para los parmetros por defecto?
Importa el orden de los parmetros en un mtodo?
Existen las funciones en ruby?
Qu se entiende por scope?
Todas los mtodos devuelven valores? Cules no?
Qu es un mtodo recursivo?
Qu es el stack?
Cul es la diferencia entre break y return?
En qu consiste el error SystemStackError: stack level too deep ?

8) Arrays
Creando arrays
Ya tuvimos un primer acercamiento a los array, pero en este captulo aprenderemos a sacarle provecho.
Hay dos formas de crear un array, la primera es con los [], la segunda es ocupando la clase Array
Ejemplos:

1
2

a = [1,2,3,4]
# => [1, 2, 3, 4]

El constructor del array es contraintuitivo, puede recibir uno o dos parmetros, si slo hay un parmetro se crea
un array vaco con la cantidad de elementos indicadas por el parmetro:

1
2

a = Array.new(4)
# => [nil, nil, nil, nil]

Y cuando hay dos parmetros el segundo es un elemento que se repite n veces, donde n es el primer parmetro

1
2

a = Array.new(3,0)
# => [0,0,0]

ndices
Los arrays tienen dos partes, una son los elementos, o sea el contenido dentro del array, la otra es el ndice, y
permite acceder al elemento que est dentro, los ndices van de cero hasta n - 1, donde n es la cantidad de
elementos del arreglo.
O sea en un arreglo que contiene 5 elementos, el primer elemento est en la posicin cero, y el ltimo en la 4.

1
2

a = Array.new(3,0)
# => [0,0,0]

3
4
5
6

a[2]
# => 0
a[3]
# => nil

Los ndices tambin se pueden utilizar con nmeros negativos y de esta forma referirse a los elementos desde el
ltimo al primero.

1
2

a = [1,2,3,4,5]
a[-1] # => 5

Contando elementos
Podemos contar la cantidad de elementos dentro de un array con el mtodo .count o con el mtodo .length

1
2
3
4

a.count
# => 5
a.length
# => 5

Los arrays como contenedores


Ya hemos ocupado los arrays como contenedores, un array puede contener un nmero indeterminado de
cualquier cantidad de objetos dentro, aunque si existe un lmite que viene dado por la cantidad de memoria
disponible del computador.
Cuando uno asigna un array a una variable la variable no contiene el contenedor, contiene una direccin al
contenedor, y esta diferencia es importante especialmente cuando intentemos copiarlo.

1
2
3
4

a = ["hola", "yo", "soy", "un", "arreglo"]


b = a
b[0] = "chao"
puts a[0]

Cunto vale a[0]?

Tiene el mismo valor que b[0] porque no cambiamos ni a ni b, cambiamos uno de los elementos adonde ambas
variables apuntaban.

Mutabilidad en los arrays


Esto se conoce como mutabilidad, un objeto es mutable si consecuencia de sus operaciones su estado puede
cambiar, un objeto es inmutable si no cambia, ahora este enfoque tan purista no existe en ruby, uno puede
programar los objetos de forma de que cambien o que devuelvan uno nuevo, por eso se habla exclusivamente de
mtodos mutables o de mtodos inmutables, y un objeto es mutable si contiene al menos un mtodo mutable.
Cmo los arrays tienen mtodos que pueden cambiarlo entonces son mutables, y lo ms peligroso es la copia por
los mencionado previamente
Cmo podemos copiar entonces un arreglo, sin que sea una referencia?
Con el mtodo .dup

1
2
3
4

b = a.dup
b[0] = "probando el cambio"
a.inspect
b.inspect

Lo importante de los arrays como contenedores es saber manipularlos, hay dos formas de acceder a los datos
dentro de un array, secuencialmente y aleatoriamente
Lo forma secuencial consiste en ver los datos en orden, ya vimos previamente dos de esas formas.

Con for
1
2
3
4

a = [1,2,6,1,7,2,3]
for i in a:
puts a[i]
end

Con each

1
2
3
4

a = [1,2,6,1,7,2,3]
a.each do |i|
puts i
end

.each es un mtodo muy interesante que realiza dos cosas, al primera es aplicar todas las operaciones definidas
dentro del bloque, la segunda es devolver el arreglo original.

1
2
3

b = a.each do |i|
puts i**2
end

Ahora b contiene los mismos valores de a y por explicado anteriormente si cambiamos algn valor de b cambiar
el valor de a.

Operaciones funcionales sobre los arrays


Adems de la iteracin con each hay cuatro formas muy interesante de operar sobre los datos de un array.
map o collect
select o reject
inject
group_by

Map
La primera es map, o collect, ambas son exactamente iguales. Map sirve para aplicar una operacin a cada
elemento del array, y devuelve un array con las operaciones aplicadas, ejemplo:

1
2
3
4
5

a = [1,2,3,4,5,6,7]
b = a.map do |e|
e * 2
end
# => [2, 4, 6, 8, 10, 12, 14]

O en su forma express:

1
2

a = [1,2,3,4,5,6,7]
b = a.map {|e| e * 2}

map y collect son exactamente lo mismo, no hay ninguna diferencia entre ocupar uno u otro.

1
2

a = [1,2,3,4,5,6,7]
b = a.collect {|e| e * 2}

Select
La segunda operacin funcional muy til sobre array es select, select permite sacar de un arreglo todos los
elementos que no cumplan un criterio, select al igual que map devuelve un array nuevo sin modificar el original

1
2
3
4

a = [1,2,3,4,5,6,7]
b = a.select{ |x| x % 2 == 0} # seleccionamos todos los pares
# => [2,4,6]

Hay funcin que hace lo contrario a select, se llama reject y lo que hace es eliminar a todos los elementos del
arreglo que no cumplan con el criterio.

1
2

b = a.reject{ |x| x % 2 == 0}
# => [1, 3, 5, 7]

.select y .reject no estn solo limitados a nmeros, por ejemplo supongamos que tenemos un arreglo que tiene
nmeros y palabras y queremos seleccionar solo las palabras.

a = [1,"hola", 2, "aprendiendo", 3, "ruby"].select{|x| x.class == String

Inject

Inject permite operar sobre todos los resultados pero en lugar de devolver un arreglo, devuelve un nico valor
con el resultado de las operaciones, por lo mismo en el bloque hay que pasar dos variables, la que itera y la que
guarda el resultado.

b = a.inject(0){|x, sum| sum += x}

Ms adelante en este libro estudiaremos los procs los procs son perfectos sustitutos para los bloques y funcionan
muy bien en conjunto con las operaciones como map, filter e inject.
Gracias a los procs y al mtodo .to_proc de ruby es posible utilizar lo siguiente:

1
2

b = a.inject(&:+) #sumatoria de todos los elementos


c = a.inject(&:*) #productoria de todos los elementos

Group_by
.group_by permite agrupar los elementos de un array acorde al criterio especificado en el bloque, a diferencia de
los otros mtodos group_by devuelve un hash donde la llave es el criterio de aceptacin en el grupo y el valor es
un array con los elementos que cumplen con la condicin.
Para ejemplificar agrupemos todos los nmeros dentro de un array acorde si son pares o no.

1
2

a.group_by {|x| x % 2 == 0}
# => {false=>[1, 3, 5, 7], true=>[2, 4, 6]}

Un array puede contener elementos de muchos tipos, as que podramos agruparlo acorde al tipo con:

a.group_by {|ele| ele.class}

Contando elementos con group by


Utilizando la misma idea podramos tambin contar los elementos de cada tipo dentro de un arreglo. si tenemos
un arreglo como a = [1,1,3,2,6,8,9,2] podramos agruparlo en funcin de cada elemento con:

1
2

a.group_by{|x| x}
# => {1=>[1, 1], 3=>[3], 2=>[2, 2], 6=>[6], 8=>[8], 9=>[9]}

Nos devuelve un hash donde el key es el elemento y en el value aparece un elemento por cada repeticin, ahora
el siguiente paso sera contar cada uno de esos, para eso ocuparemos un map, para devolver un arreglo nuevo que
tenga el elemento y la cantidad de repeticiones.

1
2

a.group_by{|x| x}.map{|k,v| [k,v.count]}.to_h


# => {1=>2, 3=>1, 2=>2, 6=>1, 8=>1, 9=>1}

Ordenando arreglos
Es posible ordenar todos los elementos dentro de un arreglo utilizando .sort

1
2
3

a = [2,82,1,5,6]
a.sort
# => [1, 2, 5, 6, 82]

Pero si dentro de nuestro arreglo hubiese un tipo de datos que no fuera compatible con la comparacin, como
por ejemplo un string, entonces obtendramos un error.

1
2
3

a = [2,82,1,5,6, "90"]
a.sort
ArgumentError: comparison of Fixnum with String failed

Esto lo podemos corregir ocupando sort_by, en lugar de sort, el mtodo sort_by recibe un bloque o proc que
nos permite explicar como comparar.

Desordenando arreglos
1
2

a.shuffle
[5, 1, 6, 82, 2]

Los arrays como conjuntos


Es posible hacer operaciones de conjunto sobre los array
Para esta seccin tendremos dos arreglos, a y b

1
2

a = [1,2,3]
b = [3,4,5]

Suma de elementos
Al conjunto a se le suman los elementos del conjunto b

1
2

a + b
# [1,2,3,3,4,5]

Diferencia de elementos
Al conjunto a se le quitan los elementos del conjunto b

1
2

a - b
# [1, 2]

Unin
Los elementos del conjunto a o los elementos del conjunto b

1
2

a | b
# [1,2,3,4,5]

Intereseccin
Los elementos del conjunto a y los elementos del conjunto b

1
2

a & b
# [3]

Los arrays como una pila (o stack)


Las pilas son una abstraccin bien potente para la resolucin de algunos problemas, consiste en olvidarse que el
array puede ser accedido aleatoriamente (a travs del ndice) y que estos solo se pueden acceder por el final.
Para eso ocuparemos slo 2 mtodos:

Push
a un array podemos agregarle un dato al final ocupando el mtodo .push

1
2
3

a = [0,1,3,4]
a.push(5)
# => [0,1,3,4,5]

Es posible hacer lo mismo ocupando el operador <<

1
2

a << 5
# => [0,1,3,4,5]

Pop
.pop es un mtodo que nos permite obtener y sacar el ltimo elemento de un array

1
2

a = [0,1,3,4,5]
a.pop

3
4
5

# => 5
a.inspect
# => 0,1,3,4

Arrayception (arrays dentro de arrays)


Los arrays pueden contener arrays dentro, iterarlo es el mismo proceso que itarar uno unidimensional.

1
2
3
4
5
6

super_array = [[1,2,3],[4,5,6],[7,8,9]]
super_array.each do |array|
array.each do |ele|
puts l
end
end

Es posible aplanar un array multidimensional para convertirlo en un array normal ocupando el mtodo .flatten

1
2

super_array.flatten
# => [1, 2, 3, 4, 5, 6, 7, 8, 9]

El ltimo punto que tenemos que cubrir es particularmente delicado, en ruby no existe un deep dup (en rails si
existe), por lo que debemos ser extra cuidadoso al copiar arrays multidimensionales.

1
2
3
4
5
6
7
8

super_array = [[1,2,3],[4,5,6],[7,8,9]]
new_array = super_array.dup
new_array[0] = [11,12,13]
super_array.inspect
[[1,2,3],[4,5,6],[7,8,9]]
new_array.inspect
[[11,12,13],[4,5,6],[7,8,9]]

hasta ah vemos que se comporta segn lo esperado, pero que pasa si en lugar de haber cambiado un arreglo
dentro hubisemos cambiado un elemento dentro dentro del arreglo.

1
2

super_array = [[1,2,3],[4,5,6],[7,8,9]]
new_array = super_array.dup

3
4
5
6

new_array[0][0] = "esto se cambiar en ambos arrays"


super_array.inspect
new_array.inspect

Cmo podemos copiarlo sin que pase eso?

new_array = super_array.map { |array| array.dup }

o de forma ms corta

new_array = super_array.map(&:dup)

Preguntas
Cul es el peligro al copiar una variable que contiene un arreglo?
Cul es el problema que pueda ocurrir al copiar un arreglo multidimensional?
Qu hace el mtodo dup?
Cmo se puede saber el largo de un array?
Qu devuelve el mtodo group_by?
Cul es la principal diferencia entre .each y .map?
Cul es la diferencia entre map y collect?
Que recibe como parmetro el mtodo .inject?
Qu devuelve el mtodo inject?
Cul es la diferencia entre select y reject?
Si a = [1,2,3,4,5] y b = ["hola", 1,2,5 ]
Qu devuelve a + b?
Qu devuelve a - b?
Es lo mismo a - b que b - a?
Qu devuelve a & b?
Qu devuelve a | b?

Qu devuelve el mtodo pop?


Qu hace el mtodo flatten?

9) Hashs
Los hash son contenedores en el cual sus elementos en lugar de ser accedidos por un ndice se acceden por una
clave.

Creando un hash
Hay dos formas de crear un hash

con Hash.new
1

a = {}

Tambin podemos crear un hash que ya venga con algunos valores.

hash_con_valores = {a:5, b:"hola"}

la segunda es con :

a = Hash.new

La primera es la comnmente utilizada.


La clave en los hashs es nica, al igual que el ndice en los array, pues si hubiesen dos iguales no podramos
rescatar el valor

1
2
3

a = {clave1:5, clave1:7}
(irb):94: warning: duplicated key at line 94 ignored: :clave1
=> {:clave1=>7}

Se suele utilizar smbolos como claves ya que estos no pueden ser cambiados y no tendra ningn sentido
cambiar una clave, ya que en ese caso sera otra clave.

Podemos acceder a los valores utilizando la clave, en este caso a[:clave]

Agregando y cambiando elementos a un hash


Supongamos que tenemos un hash definido como:

a = {llave1: 5}

Para agregar un elemento a un hash basta con utilizar una clave nueva.

1
2
3

a[:llave2] = 7
a["llave2"] = 9
puts a

El output mostrado por el puts sera:

{:llave1=>5, :llave2=>7, "llave2"=>9}

Hay que tener muchos cuidado con los claves en los hash, pues los smbolos y los strings son cosas
distintas.

Por ejemplo en el caso anterior en lugar de remplazar el valor de la :llave2 agregamos una nueva llave llamada
"llave2"

Removiendo elementos de un hash


Podemos eleminar una llave con su valor del hash ocupando el mtodo .delete

1
2
3

a = {k1: 5, k2:7}
a.delete(:k1)
puts a

Cuando borremos la clave debemos ser concientes de si es un smbolo o un string.

Iterando un hash
los hash tiene clave y valor por lo tanto ocuparemos dos variables, una contendr la clave y la otra el valor.

1
2
3
4

a = {k1: 5, k2:7, k3: 9, k4: 11, k5:1}


a.each do |k, v|
puts "la clave es #{k}, el valor es #{v}"
end

Convirtiendo un hash en un arreglo.


Es muy simple convertir un hash en un arreglo, simplemente tenemos que llamar al mtodo to_a.
Miremos el siguiente ejemplo:

1
2
3

require 'pp'
a = {k1: 5, k2:7}
pp a.to_a

Ocupamos la libreria pp pretty print para mostrar el arreglo, pero la transformacin del hash en un arreglo se
logra nicamente con el mtodo .to_a

Convirtiendo un arreglo en un hash


Si tenemos un arreglo del tipo [[1,2],[3,4]] lo podemos convertir a hash con el mtodo .to_h ah el elemento de
cada subarreglo se convierte en la clave y el resto en el valor.

1
2

[[1,2],[3,4]].to_h
# => {1=>2, 3=>4}

Si tenemos ms elementos podemos agruparlos, la parte de izquierda es la clave as que va sola, la parte de al
derecha.

1
2

[[1,[2,3]],[2,[3,4]]].to_h
# => {1=>[2, 3], 2=>[3, 4]}

Preguntas
Puede un hash tener dos claves iguales?
Puede un hash tener dos claves iguales pero una es un smbolo y la otra un string?
Cuntas variables se necesitan para iterar un hash?
Qu caractersticas tiene que tener un arreglo para convertirlo en hash?
Cmo se itera un hash?

10) Objetos en ruby


Requisitos
Entender:
Funciones
Paso de parmetros
Return
Variables locales / globales
Para muchos programadores es complicado aprender a programar con objetos debido a la gran cantidad de
conceptos que se introducen simultneamente, pero a mi parecer es porque simplemente la teora est explicada
bajo la metfora de la receta, o del esqueleto y realmente no aplica a la situacin.
En este captulo aprenderemos una mejor forma de entender los objetos junto a todos los conceptos bsicos.

Conceptos bsicos
Algunos conceptos que vamos a aprender en este captulo:
Clase
Objeto
Instancia
Herencia
Mtodo de clase
Mtodo de instancias
Atributos
Getters y Setters
Self

Para que sirven los objetos

En ruby todo es un objeto, los nmeros son objetos del tipo Fixnum, y existen objetos de diversos tipos, como
por ejemplo: los Strings, los Array, y tanto True como False son objetos.
Los objetos nos permiten crear nuestros propios tipos de datos y nuestras propias operaciones para resolver los
problemas que necesitemos, adems nos permiten reutilizar el cdigo que hagamos para resolver problemas
similares.

Los tres conceptos ms importantes


El primer paso es entender los tres conceptos claves, todo el resto derivar de ah:
Clase -> Instanciar -> Objeto
Un objeto es un conjunto de propiedades y comportamientos, por ejemplo una mesa tiene 4 patas (eso es una
propiedad) y la capacidad de golpear dedos del pie (eso es un comportamiento), se dice que est encapsulado
porque si destruyes una mesa, eso no afecta a las otras mesas del mundo.
Se suele decir que las clases (la forma de definir objetos) son recetas, o son un esqueleto, el secreto para
comprender bien la programacin orientada a objetos es entender a las clases como un molde o una fbrica que
sirve para construir esos objetos, por ejemplo un molde de una pieza de lego y a los objetos como productos de
este molde, o sea los legos.

Entonces tenemos la clase que es un molde de legos, que es capaz de imprimir objetos lego, los cuales tienen un
identificador (por ejemplo un nmero de serie), propiedades como el color y tamao; y comportamientos como
acoplarse y causar dolor infinito si uno los pisa.
En ruby para definir una clase:

1
2

class MoldeLego
end

Para crear un objeto a partir de ese molde hay que instanciarlo, para instanciar basta con utilizar el nombre de la
clase con el mtodo new, o sea:

lego1 = MoldeLego.new

Si lo anterior lo hacemos en el intrprete, obtendremos como Output


=> #<MoldeLego:0x007ff7e0836b90>

Resumen hasta ahora:


Las clases son un molde o fabrica que uno programa, y a travs del mtodo new creas instancias que utilizas para
resolver diversos problemas y mantener ordenado tu cdigo.

Identidad, Estados y Comportamientos


El identificador
En ruby todos los objetos tienen un identificador nico, el lego que creamos previamente tiene uno, este se
puede capturar utilizando el mtodo object_id con:

lego1.object_id

Recordar que en ruby el uso de parntesis en los mtodos es opcional, por lo que
lego1.object_id es lo mismo que lego1.object_id()

Como resultado obtendremos 70351300158900 , u otro nmero de similares caractersticas.


Nuestro objeto lego ya tiene un identificador, pero todava no le hemos agregado ni comportamiento ni
propiedades, aunque si tiene algunas que vienen por defecto.

Comportamientos
Ahora el secreto para entender bien esta parte es que tanto el molde (la clase) como el objeto pueden tener
comportamientos. Cuando los comportamientos son del molde se llaman mtodos de clase, cuando lo son del
objeto se llaman mtodos de instancia.
En este captulo abordaremos principalmente los mtodos de instancia.
Para agregar un mtodo de instancia, (o sea un comportamiento de un objeto) lo hacemos dentro de la
definicin de la clase.

class Vehiculo

2
3
4
5

def encender()
puts "rrmrmmmrmrmrm"
end
end

Los estados
Para guardar estados de un objeto ocuparemos variables de instancia, esta se distinguen porque empiezan con @

1
2
3
4
5
6
7
8
9
10
11

class Vehiculo
def encender()
@encendido = on
end
def apagar()
@encendido = off
end
def estado()
@encendido
end
end

Ahora con esta clase de vehculo podemos crear todos los que queramos y cada uno tendr un estado encendido
independiente de los otros.
Ejemplo

1
2
3
4
5

a1 = Vehiculo.new
a2 = Vehiculo.new
a1.apagar
a1.estado
a2.estado

Podemos ver que mientras uno de los estados es prendido, el otro es apagado.

Mtodos de instancia
1
2

class MoldeLego
def initialize()

3
4
5

@size = 1 # las variables de instancia se definen con una @


end
end

Cada vez que creamos un lego este tendr tamao 1,

1
2

lego1 = MoldeLego.new
# Este Lego ahora tiene tamao 1

Podemos ver que size es una variable de instancia utilizando:

lego1.instance_variables

Pero no podemos acceder a esta porque los estados internos de los objetos estn protegidos, o sea si hacemos
lego1.size obtendremos un error.

Resumen hasta ahora:


Los objetos son instancias de una clase, cuando se utilizar la expresin clase.new entonces es cuando se instancia
el objeto y automticamente se llama al constructor, todo lo definido dentro del constructor se ejecuta.
Los constructores sirven principalmente para darle valores iniciales a las variables de instancias.
Un objeto adems puede tener mltiples variables y comportamientos cuando le pertenecer a la instancia los
llamamos mtodos o variables de instancia y cuando le pertenecen a la clase les llamamos mtodos o variables de
clase.

Getters y Setters
Volviendo a nuestro ejemplo, si queremos rescatar el tamao tenemos un problema, no se puede acceder
directamente a los estados internos de un objeto debido al principio de encapsulamiento, para acceder a los
estados tenemos que crear getters y setters, o sea mtodos para obtener los estados (getters) y mtodos para
cambiarlos (setters), as que nuestro MoldeLego quedara as:

1
2
3

class MoldeLego
def initialize()
@size = 1 # las variables de instancia se definen con una @

4
5
6
7
8
9
10
11

end
def getSize()
return @size
end
def setSize(new_size)
@size = new_size
end
end

Para usarlo, lo haramos de la siguiente forma:

1
2
3

lego1 = MoldeLego.new # Este Lego ahora tiene tamao 1


lego1.setSize(5)
lego1.getSize()

Exista una mejor forma de definir los getters y setters, que es hacerlo de tal manera que pareciera que estamos
trabajando directamente sobre el atributo.
o sea:

1
2
3
4
5
6
7
8
9
10
11

class MoldeLego
def initialize()
@size = 1 # las variables de instancia se definen con una @
end
def size()
return @size
end
def size=(new_size)
@size = new_size
end
end

De esta forma podemos ocupar el objeto as:

1
2
3
4

lego1 = MoldeLego.new # Este Lego ahora tiene tamao 1


lego1.size # devuelve 1, pues es el tamao original
lego1.size = 5
lego1.size # devuelve 5, ya que lo cambiamos

Ahora en ruby es posible definir de forma simultnea la variable, los getters y setters a travs del attr_accessor:

1
2
3
4
5
6

class MoldeLego
attr_accessor :size
def initialize()
@size = 1 # las variables de instancia se definen con una @
end
end

Y para ocuparlo sera exactamente que la forma anterior, ahora volvamos a la idea de acoplar:

1
2
3
4
5
6
7
8
9
10
11
12

class MoldeLego
attr_accessor:size
# Pasamos un valor al constructor
# para poder crear legos de otros tamaos
# en caso de que se omita es de tamao 1
def iniatilize(size_orig = 1)
@size = size_orig
end
def acoplar(otro_lego)
return MoldeLego.new(self.size + otro_lego.size) # devolvemos un lego de la s
end
end

Cmo se usa?

1
2
3

lego1 = MoldeLego.new
lego2 = MoldeLego.new
lego3 = lego1.acoplar(otro_lego)

Todava podemos hacer una mejor ms, en ruby al igual que otros lenguajes se puede redefinir una operacin, en
este caso vamos a redefinir la suma (slo para los legos):

1
2
3
4
5

class MoldeLego
attr_accessor :size
# Pasamos un valor al constructor
# para poder crear legos de otros tamaos
# en caso de que se omita es de tamao 1

6
7
8
9
10
11
12
13
14

def initialize(size_orig = 1)
@size = size_orig
end
def +(otro_lego)
# devolvemos un lego de la suma del tamao de los otros legos
MoldeLego.new(@size + otro_lego.size)
end
end

Gracias a eso ahora podemos simplemente escribir:

1
2
3

lego1 = MoldeLego.new
lego2 = MoldeLego.new
lego3 = lego1 + lego2

Antes de avanzar vamos ver un mtodo de clase ms, este lo tienen todos los objetos y se llama to_s y permite
mostrar el objeto como si fuera un string, por ejemplo si mostramos un lego, ocupando:

puts lego3

Veramos en pantalla algo como: #<MoldeLego:0x007f91f920a4f8>


El objeto automticamente se convierte en string para poder ser mostrado, pero nosotros podemos sobreescribir
el mtodo to s.

1
2
3
4
5
6
7
8
9
10
11
12
13
14

class MoldeLego
attr_accessor :size
# Pasamos un valor al constructor
# para poder crear legos de otros tamaos
# en caso de que se omita es de tamao 1
def initialize(size_orig = 1)
@size = size_orig
end
def +(otro_lego)
# devolvemos un lego de la suma del tamao de los otros legos
MoldeLego.new(@size + otro_lego.size)
end

15
16
17
18
19

def to_s
"soy un lego de tamao #{@size}"
end
end

De esta forma al realizar las mismas operaciones anteriores

1
2
3
4

lego1 = MoldeLego.new
lego2 = MoldeLego.new
lego3 = lego1 + lego2
puts lego3

Veramos que lego3 es mostrado como: soy un lego de tamao 2

Mtodos y atributos de clases


Qu pasara si quisiramos contar cuantos legos se han creado?
Eso no lo podemos hacer con una variable de instancia, necesitamos que sea el molde cuente, (algo as como
imprimir el nmero de serie en el producto), o sea es una propiedad de la clase, estas se llaman formalmente
variables de clase, en ruby se crean con 2 @@:

1
2
3
4

class MoldeLego
attr_accessor :size
@@cuenta_legos = 0
end

A diferencia de la variables de instancias estas no requieren definirse en el constructor, pues existen para la clase,
independiente de si existen legos o no.
Ahora si quisiramos contar cada vez que se crea un lego nuevo, podramos hacerlo as:

1
2
3
4

class MoldeLego
attr_accessor:size
@@cuenta_legos = 0
def iniatilize(size_orig = 1)

5
6
7
8

@size = size_orig
@@cuenta_legos += 1
end
end

Si quisiramos obtener las variables de clase toparamos con el mismo problema de encapuslamiento, tenemos
que definir getters y setters para estas, ahora lamentablemente la sintaxis en ruby no es tan bonita, se hace as:

1
2
3
4
5
6
7
8
9

class MoldeLego
class << self; attr_accessor :cuenta_legos end
attr_accessor:size
@@cuenta_legos = 0
def iniatilize(size_orig = 1)
@size = size_orig
@@cuenta_legos += 1
end
end

Slo una cosa est quedando en el tintero, Cmo crear mtodos de clase ms all de los getters y setter?
Para eso vamos a ocupar self, para este ejemplo voy a definir un getter de la forma no tan bonita para la cuenta
legos, sera as:

1
2
3
4
5
6

class MoldeLego
class << self; attr_accessor :cuenta_legos end
def self.getCuentaLegos()
return @@cuenta_legos
end
end

De los legos al molde


Una de las curiosidades que tienen los lenguajes orientados a objetos es que desde las instancias podemos
rescatar las propiedades de la clase, pero desde las clase no podemos saber cuales son las propiedades de las
instancias.
En la metfora del lego podemos saber incluso de que color era el molde que lo fabric sin embargo desde el
molde no podemos saber de que color o cuantos legos se han acoplado a menos que llevemos especficamente la
cuenta como en el caso del nmero de serie.

Para rescatar la clase a partir de la instancia basta con hacer:

lego.class.cuentaLegos

Lo anterior es exactamente lo mismo que:

MoldeLego.cuentaLegos

Es importante no confundir esta metfora con la herencia, esta ltima es una caracterstica de los objetos que les
permite obtener los mtodos y propiedades de sus padres, pero este tema lo abordaremos posteriormente.

Ejemplo con los nmeros complejos


Los nmeros complejos son una forma de anotar coordenadas dentro de un plano cartesiando (son mucho ms
que eso, pero est fuera del mbito de esta gua)

las coordenadas con complejos las podemos describir de la forma x + i * y, donde x es la coordenada horizontal,
e i*y es la coordenada vertical, siguiendo el dibujo y ocupando notacin compleja el punto A estara en -5, 3i, el
punto B en 6, 5i
Ruby ya tiene una forma de manejar nmeros complejos con la clase Complex, pero para ilustrar la utilizacin de
objetos nosotros crearemos la clase Z

Para nuestra clase necesitamos dos coordenadas, que llamaremos a y b, esas sern las variables de nuestra clase
que crearemos a continuacin:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

class Z
attr_reader :a, :b
def initialize(a, b)
@a = a
@b = b
end

def +(z)
if z.class == Fixnum
Z.new(@a + z, @b)
else
Z.new(@a + z.a, @b + z.b)
end
end
def to_s
return "#{@a} + #{@b}i"
end
end

a = Z.new(2,3)
b = Z.new(2,3)
a + b

Preguntas
Cul es la diferencia entre una clase y un objeto?
Qu hace el operador new?
Qu significa instanciar?
Para qu sirven los getters?
Qu hace def x=(x)?
Qu hace attr_reader :x?

Si existe la clase Monster, que devuelve Monster.class?


Monster tiene mtodos de instancia?
Si a = Monster.new, que devuelve a.class? el tipo de a, que es Monster
Cul es la diferencia entre un mtodo de clase y uno de instancia?
Puede un mtodo de instancia modificar una variable de clase?
Puede un mtodo de clase modificar una variable de instancia?
def self.super_metodo, es un mtodo de instancia o de clase?
cul es la diferencia entre @x y @@x?
Se puede acceder una variable local desde afuera de una clase?
Se puede acceder una variable de instancia desde afuera de una clase?
Se puede acceder una variable de clase desde afuera de una clase? El

11) Objetos y arreglos


Ejercicios resueltos
Fraccin
Vamos a crear el objeto fraccin, la fraccin tiene dos parmetros, el numerador y el denominador.

1
2
3
4
5
6

class Fraccion
def initialize(x,y)
@x = x
@y = y
end
end

Los alumnos tendrn que reescribir la suma y la resta para que se respete la suma siempre y cuando sea la misma
base (no es necesario implementar mnimo comn mltiplo u simplificar los resultados), en caso de que sea
distinta base se debe devolver un arreglo con las fracciones
ejemplo:
1 1
2
+ =
4 4
4

pero:
1 1
1 1
+ =[ , ]
4 3
4 3

El mtodo de suma debe recibir una fraccin o un arreglo con fracciones.


20 minutos, el alumno que resuelva el ejercicio debe pasar a la pizarra.
Respuesta:

1
2
3

class Fraccion
attr_reader :num, :denom

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

def initialize(num, denom)


@num = num
@denom = denom
end
def +(fraq)
if fraq.class == Fixnum
Fraccion.new(@num + fraq * @denom, @denom)
elsif @denom == fraq.denom
Fraccion.new(@num + fraq.num, @denom)
else
[Fraccion.new(@num, @denom), Fraccion.new(fraq.num, fraq.denom
end
end
def to_s
"#{@num} / #{@denom}"
end
end

Alumnos y notas
Crear la clase alumno, cada alumno tiene un arreglo de calificaciones (enteros) y un nombre
Crear un arreglo con al menos 4 alumnos, cada alumnos.
Se pide:
Calcular el promedio de notas del curso
Encontrar al alumno que tiene le promedio de notas ms alto y devolver su nombre.
Revisin del mtodo inject. Sumatoria, Concatenacin de nombres
Solucin:

1
2
3
4

class Student
def initialize(name, grades)
@name = name
@grades = grades

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

end
def average
@grades.inject(&:+) / @grades.count.to_f
end
end
students
students
students
students

= []
<< Student.new("Efran", [9, 5, 7, 8])
<< Student.new("Martn", [10, 10, 10, 10])
<< Student.new("Gonzalo", [3, 5, 2, 3])

averages = students.map {|x| x.average}


puts "el promedio ms alto es: #{averages.sort.last}"
puts "el promedio del curso es: #{averages.inject(&:+) / averages.count

Zombies
Crear el objeto Zombie con coordenadas x,y y un mtodo para caminar
Crear el objeto Personas, las personas no se mueven pero si tienen posicin x,y
Los zombies si estn a una coordenada de distancia se comen a la persona y esta es convertida en zombie.
Para hacerlo ms sencillo en cada iteracin cada zombie recibir un arreglo con todas las persona verificar si se
cumplen las condiciones para ser comido y devolver a la persona devorada, con eso hay que sacarla del arreglo

1
2
3
4
5

#hint:
class Zombie
def eat(personas)
personas.each {}
# tambin es posible ocupar delete_if

Se pide: Crear 10 zombies, y 5 personas Correr el programa hasta que todas las personas sean devoradas en el
apocalipsis. Mostrar cuantas iteraciones requiri el proceso
Solucin temporal

class Zombie

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

attr_reader :x, :y, :my_id


@@id = 1
def initialize(x,y)
@x = x
@y = y
check_borders
@my_id = @@id
@@id += 1
end
def check_borders()
@x = 5 if @x > 5
@x = 0 if @x < 0
@y = 5 if @y > 5
@y = 0 if @y < 0
end
def walk()
move_x = Random.rand(3) - 1
move_y = Random.rand(3) - 1
@x += move_x
@y += move_y
check_borders
puts "#{@my_id} Braaaains #{x}, #{y}"
end
def eat(personas)
personas.each do |p|
if @x - p.x <= 0
end
end
end
# Creo 5 zombies
zombies = []
5.times do
x = Random.rand(10)
y = Random.rand(10)
zombies << Zombie.new(x,y)
end
# Los zombies van a caminar 20 veces
20.times do

47
48
49
50
51

zombies.each do |z|
z.walk
end
puts "--------"
end

Baraja de cartas
Se pide construir la clase carta, que debe guardar la pinta y el nmero de la carta.
Se pide construir un arreglo con todas las cartas, para conformar la baraja
Tambin se debe construir la clase mano, esta tiene 5 cartas siempre, y debe haber un mtodo que reciba la
baraja, y permite poner la primera carta de la mano al final de la baraja y la primera carta de la baraja al final de la
mano.
Despus de hacer el intercambio se debe barajar hint, ocupar el mtodo shuffle del array.

Batalla naval
Se pide:
Hacer una diagrama con objeto de las componentes
Un diagrama posible es Flota
Cada flota tiene naves y estado donde est viva si tiene al menos una nave funcional.
Cada nave tiene partes y tamao (donde tamao es la cantidad de partes) y estado que indica si est
hundida o no. Las naves son de distintos tamaos, (eso se refleja en la cantidad de componentes que
tienen), las naves pueden estar alineadas verticalmente o horizontalmente, pero no diagonalmente ni nada
con forma de L o cosas raras.
Cada parte tiene un set de coordenadas y un indicador que muestra si est golpeada (si ya le han dado o
no), cuando todas las partes son golpeadas la nave queda marcada como hundida
Un jugador controla una flota.
No pueden haber dos partes distintas en las mismas coordenadas

El juego solo debe simular al jugador 1


Al empezar el juego se sitan al azar las naves (solo de la flota enemiga) (cuidar la restriccin de que dos
partes no pueden ocupar las mismas coordenadas)
luego en el main loop se pide un input, el input debe ser un par de coordenadas, o, la letra m para mostrar el
mapa, o la letra q para terminar el juego
Al mostrar el mapa se mostraran las naves enemigas y los puntos donde has atacado (para debbugging)
Si es un par de coordenadas revisar contra la flota contraria, si hay una parte hundirla, si todas las partes de la
nave estn hundidas hundir la nave, si todas las naves enemigas estn hundidas ganar el juego.

Preguntas
Cmo podemos agregar un mtodo de instancia a todos los arreglos?
Cmo podemos crear un contador de instancias?
Si creramos una librera de funciones matemticas, sera mejor ocupar mtodos de clase o de instancia?
Si creramos una clase para manejar una baraja de cartas, sera mejor ocupar mtodos de clase o de
instancia?
Si una clase tiene definido dentro mtodos de clase, puede tener mtodos de instancia?
Cmo se define un mtodo de clase?
Se puede definir una variable de clase en el constructor?
Si se tiene un arreglo con n objetos, cmo se puede hacer para aplicarle un mtodo a cada uno.?
Cul es la diferencia entre inject y map?

12) Bloques, Procs y Lambdas


Bloques
Los bloques son funciones annimas, tcnicamente son cdigos que se le pueden pasar como input a otra
funcin.
Los bloques se declaran entre brackets, {} o, utilizando do y end.
Ejemplo

1
2
3
4
5
6
7
8
9

a = [1,2,3,1]
# bloque con brackets
a.each {|x| x + 1}
# bloque con do
a.each do |x|
x + 1
end

Adems los bloques son closures, o sea, lo que pasa dentro del bloque queda dentro del bloque, si declaramos
una variable dentro de un bloque fuera del bloque esta no existe.

1
2
3
4
5
6
7

a = [1,2,3,1]
a.each do |x|
x + 1
z = 1
end
puts z
# => NameError: undefined local variable or method `z' for main:Object

No todos los bloques son exactamente iguales, algunos aceptan inputs, como en el caso anterior con el
parmetro x y otros no los aceptan, adems algunos devuelven informacin y otros no, son muy parecidos a los
mtodos en este mbito, pero funcionan en conjunto.
Ahora para mayor entendimiento vamos a crear nuestro primer mtodo que acepta un bloque.

1
2
3

def mi_bloque()
yield "respuesta"
end

Si intentamos llamar al mtodo mi_bloque obtendremos:

#=> LocalJumpError: no block given (yield)

Pero si lo llamamos con un bloque:

1
2

mi_bloque() {}
# => nil

Y si le pasamos un parmetro:

1
2

mi_bloque() {|x| x}
# => "respuesta"

Una cosa muy interesante que tienen los bloques es que cuando haces un mtodo no le tienes que decir que va a
recibir un bloque, ruby esperar encontrar el yield para ejecutarlo.
Probmoslo:

1
2
3
4
5

def mi_bloque2()
return "soy un bloque" # y no tengo yield
end
mi_bloque2 {|x| x = 0; puts x} #si se ejecutara deberamos ver un cero
# => "soy un bloque"

Pero no vemos el cero porque no se ejecuta, y cul es el motivo?, la respuesta es que no hay un yield en el
mtodo.
Y si lo hubiese?

def mi_bloque()

1
2
3
4
5
6
7
8

yield "hola"
return "soy un bloque"
end
mi_bloque {|x| x = 0; puts x} #si se ejecutara deberamos ver un cero
# => 0
# => "soy un bloque"

Entonces, para qu sirve?


El mtodo each es un claro uso de yield, nosotros podramos crear nuestro propio mtodo each utilizndolo.
Para eso vamos a abrir la clase Array que ya existe y vamos a crear el mtodo mi_each.

1
2
3
4
5
6
7
8
9

class Array
def mi_each
i = 0
while i < self.size
yield(self[i])
i+=1
end
end
end

Aqu vemos algo nuevo, yield acepta parmetros, ese parmetro es lo que devuelve, entonces en este caso est
devolviendo el array en el ndice i.
Para probarlo, necesitaramos hacer:

1
2

a = [1,2,3,4]
a.mi_each {|x| puts x}

Obtendremos como resultado:

1
2
3
4
5

#
#
#
#
#

1
2
3
4
=> nil

Ahora el each de ruby devuelve el array completo al final, eso lo podemos lograr devolviendo self.

1
2
3
4
5
6
7
8
9
10

class Array
def mi_each
i = 0
while i < self.size
yield(self[i])
i+=1
end
self
end
end

Procs
Tambin es posible guardar un bloque en una variable utilizando procs, utilizando el mtodo mi_each que
creamos previamente podramos hacer:

1
2
3

proc = Proc.new { |x| puts x }


a = [1,2,3,4]
a.mi_each(&proc)

Adems como bonus podemos ejecutar el contenido dentro de un proc ocupando el mtodo call.
ejemplo:

1
2
3

p = Proc.new {|x| x = x*2}


p.call(5)
# 10

Diferencias entre Procs y bloques:


1. Los procs son objetos y los bloques no
2. Una funcin puede recibir slo un bloque como argumento, pero puede recibir todos los procs que requiera.
Ejemplo:

1
2
3
4
5
6
7
8
9

def mi_bloque(proc1, proc2)


proc1.call(5)
proc2.call(10)
end
p1 = Proc.new {|x| x = x*2}
p2 = Proc.new {|x| x = x*4}
mi_bloque(p1, p2)

la funcin mi_bloque devolver 40, porque el ltimo en ejecutarse es proc2 y proc2 devuelve x multiplicado
por 4.

Lambdas
Existe otro tipo de proc llamado lambda, el cual se utiliza poco y por el mismo motivo no ahondaremos, las
diferencias con el proc son sutiles, mientras el proc no revisa los parmetros, lambda si lo hace

1
2
3
4
5
6
7
8
9
10

p = Proc.new {|a, b| puts a**2+b**2 } # => #<Proc:0x3c7d28@(irb):1>


p.call 1, 2 # => 5
p.call 1 # => NoMethodError: undefined method `**' for nil:NilClass
p.call 1, 2, 3 # => 5
l = lambda {|a, b| puts a**2+b**2 } # => #<Proc:0x15016c@(irb):5 (lambda)>
l.call 1, 2 # => 5
l.call 1 # => ArgumentError: wrong number of arguments (1 for 2)
l.call 1, 2, 3 # => ArgumentError: wrong number of arguments (3 for 2)

Una diferencia importante entre proc y lambda es que manipulan de manera distinta la palabra return. Mientras
un proc rompe la ejecucin en ese punto del cdigo, una lambda no

1
2
3
4
5
6

def test_proc
Proc.new { return "se regresa del bloque de proc"}.call
return "se regresa del metodo"
end
def test_lambda

7
8
9
10
11
12

lambda { return "se regresa del bloque lambda"}.call


return "se regresa del metodo"
end
puts test_proc #=> "se regresa del bloque de proc"
puts test_lambda #=> "se regresa del metodo"

13) Archivos
Leyendo archivos
Ruby tiene la Clase File que permite abrir, leer, escribir, rebobinar y cerrar archivos.
Para abrir un archivo como lectura:

1
2

file = File.new("archivo.txt","r")
buffer = file.read

y listo ya trajimos todo el contenido del archivo como un string a memoria en una variable llamada buffer.

Guardando archivos
Si quisieramos ahora guardar esta informacin en otro archivo podemos hacer:

1
2
3

output_file = File.new("archivo2.txt","w")
output_file.write(buffer) # o cualquier string
output_file.close

Es importante cuando estemos guardando informacin en archivos que hagamos el close, si no existe la
posibilidad de que perdamos la informacin.

Procesando un archivo CSV


Un archivo csv es una archivo donde los datos vienen separados por una coma, (aunque hay ciertas variantes
donde vienen separado por un punto y coma, o por una tabulacin), adems un archivo CSV puede venir con
cabecera (nombre de las columnas) o puede venir sin ellas.
No es necesario que el archivo tenga la extensin CSV, lo que si es importante es que el Supongamos que el
contenido de archivo.txt es el siguiente:

1
2
3
4

Alumno1,
Alumno2,
Alumno3,
Alumno4,

10, 7, 10
10, 8, 10
9, 9, 10
0, 5, 9

Vamos a abrir el archivo y separarlo por saltos de lnea, de esa forma vamos a quedar con un arreglo final donde
la lnea cero corresponde a la primera lnea del archivo y as sucesivamente.

1
2
3

file = File.new("archivo.txt","r")
buffer = file.read
lines = buffer.split("\n")

Ahora dependiendo de lo que queramos hacer podramos trabajar directamente con el arreglo, pero una forma
muy cmoda de trabajar es mapear los datos a un objeto.

1
2
3
4
5
6

class Alumno
def initialize(name, n1, n2, n3)
@name = name
@notas = [n1,n2,n3]
end
end

Ahora el truco es crear un arreglo de alumnos, entonces el cdigo quedara:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

class Alumno
def initialize(name, n1, n2, n3)
@name = name
@notas = [n1,n2,n3]
end
def to_s
"Alumno #{@name} con notas #{@notas[0]}, #{@notas[1]}, #{@notas
end
end
alumnos = []
file = File.new("secreto","r")
buffer = file.read

16
17
18
19
20
21

lines = buffer.split("\n")
lines.each do |l|
data = l.split(',')
alumnos << Alumno.new(data[0], data[1], data[2], data[3])
end
puts alumnos

Preguntas
Cmo se declara un bloque, un Proc y un Lambda?
Si ejecutamos el mtodo each de un Array , de qu manera podemos enviar un Proc?
Cul es la diferencia entre un bloque y un Proc?
Cules son la principales diferencias entre Proc y Lambda?
Qu expresin requiero declarar dentro del cuerpo de un mtodo ( def ... ) para ejecutar el bloque
que se enva como argumento o parmetro?
Qu clase dentro del API de Ruby nos permite manipular archivos?
Qu mtodo podemos aplicar para leer un archivo?
Cuntos niveles y cules son los niveles de acceso que contamos para el manejo de archivos?
Qu tipo de dato se obtiene despus de la lectura de un archivo?
Cul es la expresin que representa el salto de lnea?
Para qu sirve el mtodo split de la clase String?
Si ejecutamos la sentencia open("libro.txt") y el archivo no existe en nuestro sistema, qu
recibimos al ejecutarla?
Qu mtodo sirve para cerrar el flujo de informacin cuando escribimos/creamos un archivo?
Qu clase tenemos en Ruby que representa carpetas o folders de nuestro filesystem?

14) Manejo de errores


Introduccin a manejo de errores
Es muy comn creer que cuando hay un error es culpa de uno, de algo que uno est haciendo mal, pero los
errores en ruby son parte del diseo del lenguaje, o sea podemos manipularlos y salir a salvo de ellos.
Todo parte con el famoso raise

raise 'An error has occured'

Obtendremos como resultado:

1
2
3
4

raise 'An error has occured'


RuntimeError: An error has occured
from (irb):1
from /Users/gonzalosanchez/.rvm/rubies/ruby-2.2.2/bin/irb:11:in `<main>'

Si observamos el error veremos que dice RuntimeError, hay varios tipos de errores.

Pero errores ms o errores menos es cmo utilizamos esto para nuestro beneficio, bueno el manejo de errores
tiene dos utilidades.
1. Ser capaz de interceptar los errores.
2. Ser capaz de levantar errores.
En un captulo anterior habamos creado la clase Z para nmeros complejos, ahora vamos a aprovecharla

1
2

class Z

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

attr_reader :a, :b
# attr_writer :a, :b
# attr_accessor :a, :b
def initialize(a, b)
@a = a
@b = b
end

def +(z)
if z.class == Fixnum
Z.new(@a + z, @b)
else
Z.new(@a + z.a, @b + z.b)
end
end
def to_s
return "#{@a} + #{@b}i"
end
end

En esta clase tenemos la capacidad de sumar nmeros complejos, an cuando uno de los valores no fuera
complejo, pero que pasa si nos pasan un string, vamos a obtener un error, porque va a intentar sumar la parte a y
un string no tiene un mtodo .a, pero eso no lo tiene por que saber la persona que ocupe esta librera.
La solucin es indicar que el tipo de datos es incorrecto.

1
2
3
4
5
6
7
8
9
10
11
12
13
14

class Z
attr_reader :a, :b
# attr_writer :a, :b
# attr_accessor :a, :b
def initialize(a, b)
@a = a
@b = b
end

def +(z)
if z.class != Fixnum or x.class != Z

15
16
17
18
19
20
21
22
23
24
25
26
27

raise ArgumentError, 'Argument is not complex or fixnum '


if z.class == Fixnum
Z.new(@a + z, @b)
else
Z.new(@a + z.a, @b + z.b)
end
end
def to_s
return "#{@a} + #{@b}i"
end
end

Ahora al probarlo obtendremos el siguiente error:

1
2
3
4
5

2.2.2 :006 > Z.new(2,3) + "hola"


ArgumentError: Argument is not complex or fixnum
from /Users/gonzalosanchez/complex2.rb:15:in `+'
from (irb):6
from /Users/gonzalosanchez/.rvm/rubies/ruby-2.2.2/bin/irb:11:in `<main>'

El cual es mucho ms claro para alguien que est ocupando nuestro cdigo.

Manejando las excepciones


Las excepciones no son el punto final de un programa, las podemos manejar, todos los errores que ocurran
dentro de un bloque begin los podemos rescatar con un rescue.

1
2
3
4
5
6
7

begin
raise 'Soy un error'
rescue
puts 'pero fui salvado'
end
puts 'y ahora el programa corre de forma normal'
end

Debemos de tener cuidado de no caer en el antipatrn de meter todo nuestro cdigo dentro de un rescue, los
errores que lanzamos tienen que ser coherentes con nuestra aplicacin, por ejemplo como lo hicimos en el caso
de los nmeros complejos.
Es mejor utilizar las excepciones con casos especficos, como sera por ejemplo:

1
2
3
4
5
6
7

begin
# rescue Excepcion_especifica
# else # Otras excepciones
# end

El else no es una buena prctica, no debemos capturar excepciones genricamente.

Manejo de errores y archivos


En esta ocasin vamos a abrir un archivo, pero no tendremos los permisos, para eso desde bash vamos a crear el
archivo

touch secreto

Le cambiaremos los permisos para asegurarnos de que no tenemos acceso a el:

chmod 000 secreto

Y luego lo intentaremos abrir desde ruby

file = File.new("secreto","r")

Lo que nos dar el siguiente error:

1
2

Errno::EACCES: Permission denied @ rb_sysopen - secreto


from (irb):1:in `initialize'

3
4
5

from (irb):1:in `new'


from (irb):1
from /Users/gonzalosanchez/.rvm/rubies/ruby-2.2.2/bin/irb:11:in `<main>'

Cmo nos recuperamos de un error as?, con begin y rescue.

1
2
3
4
5

begin
file = File.new("secreto","r")
rescue
puts "El archivo no ha podido ser abierto"
end

Pero si el archivo no ha podido ser abierto como procedemos? RE: No lo hacemos


Ejemplo de funcin parar abrir y procesar archivo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

def read_students(filename)
file = File.new(filename,"r")
rescue
puts "El archivo no ha podido ser abierto"
return nil
end
buffer = file.read
lines = buffer.split("\n")
alumnos = []
lines.each do |l|
data = l.split(',')
alumnos << Alumno.new(data[0], data[1], data[2], data[3])
end
return alumnos
end

Qu ganamos?, ahora el programa no se cae, pero tambin perdimos la capacidad de ver el error, y esto es
importante para que otros programadores (o el usuario) puedan hacer uso de la informacin. Para evitar esto
vamos a capturar la excepcin en una variable para luego mostrarla.

1
2

def read_students(filename)
begin

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

file = File.new(filename,"r")
rescue Exception => e
puts "El archivo no ha podido ser abierto debido a #{e}"
return nil
end
buffer = file.read
lines = buffer.split("\n")
alumnos = []
lines.each do |l|
data = l.split(',')
alumnos << Alumno.new(data[0], data[1], data[2], data[3])
end
return alumnos
end

Desafo de Puntos y lneas


Construir la clase puntos, y una clase lnea que est compuesta de dos puntos.
Crear las lineas a partir de un archivo que viene con el siguiente formato:

1
2
3
4

2,3
1,1
2,2
2,2

4,5
4,4
2,5
2,8

Incluir un mtodo para la clase lnea que permite preguntar si es una linea vertical, o una lnea horizontal y un
mtodo que permita calcular el largo.

1
2
3
4
5
6
7
8
9
10

class Linea
...
def horizontal?
...
end
def vertical?
...
end

11
12

end

Luego crear un mtodo que permita mostrar todas las lneas horizontales y todas las verticales.

Preguntas
Qu es una excepcin?
Qu ocurre con el flujo de la ejecucin de un programa en el momento que se lanza una excepcin?
Qu expresin nos permite lanzar una excepcin manualmente?
Cules son las partes que conforman un bloque que maneja una excepcin?
Cuntos rescue podemos declarar dentro de un bloque begin ... end ?
De qu manera obtenemos el objeto de la excepcin generada si cae en un rescue ?
Para qu sirve el bloque ensure ... end ?
Cul es la diferencia entre escribir un texto entre comillas simples y otra entre comillas dobles?
Qu se muestra en la terminal despus de ejecutar el siguiente bloque de cdigo?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

begin
puts "trying to execute process..."
raise NameError, "wrong name of process!"
else
rescue StandardError => e
puts "process must end because: #{e}"
begin
raise "Exiting..."
rescue Exception => e
puts "can't exit yet"
end
rescue NameError => e
puts "process must end because: #{e}"
ensure
puts "closing opened stream stuff"
end

15) Expresiones regulares


Las expresiones regulares son una tcnica de bsqueda de strings, gracias a ellas con un patrn que uno define
puede encontrar uno o ms resultados dentro de un texto.
Principalmente sirve para operaciones de buscar y remplazar.
Para utilizarlas uno define un patrn (esta es la parte difcil)
luego lo compila (no siempre es necesario)
finalmente lo utilizas para buscar y remplazar.
Entonces
patron.match "texto"
En ruby sera:

/hol/.match('hola')

Si hay match obtendremos => # pero si no lo hubiese obtendramos nil.

Patrones
Tiene vocales?
RegEx no solo nos permite buscar palabras exacta, nos permite buscar patrones

1
2
3
4
5
6

/[aeiou]/.match "zrk"
nil
/[aeiou]/.match "zerg"
#<MatchData "e">
/[aeiou]/.match "murcielago"
#<MatchData "u">

Los patrones que estn entre [] son uno o el otro, o sea se el patron es que tenga la letra a, o la e, o la i, o la o, o

la u.

Tiene nmeros?
Es posible saber si un texto tiene nmeros ocupando:

1
2
3
4

/[0-9]/.match "yo no tengo"


#nil
/[0-9]/.match "yo tengo 1"
#<MatchData "1">

el signo guin permite realizar rangos siempre y cuando se est dentro de los corchetes []

Tiene dos dgitos consecutivos?


Todo lo que est dentro de los corchetes es un o, y nosotros necesitamos un y, para eso lo que tenemos que
hacer es repetir el patrn

1
2
3
4

/[0-9][0-9]/.match "yo no cumplo por que tengo 1 digito"


# nil
/[0-9][0-9]/.match "yo cumplo por que tengo 20 digitos consecutivos"
#<MatchData "20">

Una patente de vehculos?


Ests cambian dependiendo del pas, pero en muchos son letras - nmero o viceversa.
Supongamos que PAC-201 fuera una patente vlida.

1
2

patron_patente = /[A-Z][A-Z][A-Z]-[0-9][0-9][0-9]/
patron_patente.match "QTZ-410"

En este ejercicio aprendimos dos cosas ms, una es que las expresiones regulares se pueden guardar en variables,

la segunda es que el - fuera de los [] funciona literalmente, no implicar un rango.

10, 100, 1000 o 10000


Con regex podemos hacer match de un nmero o de una palabra a pasar de no saber cuantos caracteres (o
dgitos) tiene.
Para eso ocuparemos dos caracteres especiales nuevos, el asterisco (*) y el signo de interrogacin (?)
el + implica que el patron aplicado debe aparecer 1 o ms veces
el * implica que el patron aplicado puede aparecer 0 o ms veces
el ? implica que el patron aplicado puede aparecer 0 o 1 vez

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

/[0-9][0-9]+/.match "10"
# => #<MatchData "10">
/[0-9][0-9][0-9]+/.match "10"
# => nil
/[0-9][0-9]+/.match "100"
# => #<MatchData "100">
/[0-9][0-9]+/.match "1000"
# => #<MatchData "1000">
/[0-9][0-9]*/.match "10"
# => #<MatchData "10">
/[0-9][0-9][0-9]*/.match "10"
# => #<MatchData "10">

Finalmente el caracter que todo lo puede ser, el punto.

/.+/.match "hola" # cualquier caracter 1 o ms veces.

Una buena aplicacin para probar expresiones regulares es rubular. http://rubular.com/

16) Scrapping
Scrapping es una tcnica de extraccin de datos de la web, y es muy similar a leer archivos, slo que en lugar de
abrir un archivos abriremos una pgina web.
Primero necesitamos incorporar la librera open-uri, eso lo hacemos con:

require 'open-uri'

Luego:

1
2
3

open(url) do |f|
page_string = f.read
end

Entonces si quisieramos leer el twitter de desafiolatam lo podramos hacer asi:

1
2
3
4
5
6
7
8

require 'open-uri'
url = "https://twitter.com/desafiolatam"
open(url) do |f|
page_string = f.read
end
puts page_string

Pero ahora como leemos este string gigante que consiste en todo el HTML y del cual cuesta mucho sacar
informacin?.
Hay dos opciones buenas, una es Nokogiri, la otra Mechanize esta ltima ocupa a Nokogiri.

Introduccin a Mechanize
Como primera prueba vamos a hacer un mini scrap de la wikipedia.
Paso 1) instalar la gema Mechanize desde el terminal

gem install mechanize

Paso 2) crear el parser

1
2
3
4
5
6
7
8
9
10
11

require "mechanize"
def main()
agent = Mechanize.new
agent.user_agent_alias = 'Mac Safari'
agent.get("http://es.wikipedia.org/wiki/2014") do |page|
puts page.parser.css('#content').text
end
end
main()

Cmo funciona?
Primero crea una agente, este agente es una instancia de mechanize, se suele usar como nombre agent, o
mechanize,este agente ser el que se conecte con la pgina y la descargue.
En lnea 5 se especifica un user_agent as el servidor que te entrega la pgina creer que el navegador que la est
viendo es Safari, es posible tambin ocupar user_agents de Firefox, Chrome, o incluso Internet Explorer.
Finalmente el agente se conecta con la pgina de la wikipedia y muestra el contenido que est debajo dentro de la
etiqueta con id="content", o sea que este script no muestra la informacin que est en las barras de navegacin.
Para saber donde tienes que apuntar puedes dentro de una pgina web puedes entrar a ella y ocupar el inspector
de elementos.

Navegando por los tags


En el caso anterior obtuvimos todos el texto removiendo el HTML, pero en ciertos casos queremos obtener el
HTML, por ejemplo los links

1
2
3
4

require "mechanize"
def parse(url, tag)
agent = Mechanize.new

5
6
7
8
9
10
11

agent.user_agent_alias = 'Mac Safari'


agent.get(url) do |page|
page.search(tag).each {|t| puts t.text; puts t}
end
end
parse("http://blog.desafiolatam.com", "a")

Navegando en google
1
2
3
4
5
6
7
8
9
10
11
12
13

require 'mechanize'
agent = Mechanize.new
agent.user_agent_alias = 'Mac Safari'
page = agent.get('https://www.google.com/')
form = page.forms.first
form['q'] = 'desafiolatam'
page = form.submit
page.search('a').each do |link|
puts link.text
puts link
puts "----"
end

Ms documentacin sobre mechanize http://docs.seattlerb.org/mechanize/Mechanize.html

Lyrics
Para ejercitar con un caso ms difcil vamos a crear un buscador de lyrics de canciones haciendo scrapping de la
pgina http://www.azlyrics.com/
Para eso tenemos que entrar al sitio, llenar el formulario, leer entre los resultados posibles, entrar al link del
resultado y finalmente mostrar los lyrics.
Una buena tcnica para la resolucin de problemas es dividirlos en componentes pequeas que podamos
resolver, para eso primero leeremos los lyrics de una pgina en especfico.
Para eso vamos a buscar Hello de Lionel Ritchie en la pgina, encontrar la url, y hacer un get de esa pgina con
mechanize.

1
2
3
4
5
6
7
8

require 'mechanize'
agent = Mechanize.new
agent.user_agent_alias = 'Mac Safari'
page = agent.get('http://www.azlyrics.com/lyrics/lionelrichie/hello.html'
page.search('div').each do |div|
puts div.text
end

XPATH
El problema del mtodo anterior es que vamos a obtener mucho ruido de la pgina que no nos interesa, lo
bueno es que el inspector de elementos de chrome (y el de otros navegadores) nos puede ayudar a seleccionar un
elemento especfico a travs del XPATH y como Mechanize est construido sobre Nokogiri lo soporta.

As que slo debemos copiarlo y utilizarlo en lugar de buscar div.

1
2
3

page.search('/html/body/div[3]/div/div[2]/div[6]').each do |div|
puts div.text
end

Ahora si veremos nicamente los lyrics como output.


Una pregunta importante es si en otro lyric esto funcionara, para eso lo probaremos manualmente cambiando la
url.

1
2
3
4
5
6
7
8
9

require 'mechanize'
agent = Mechanize.new
agent.user_agent_alias = 'Mac Safari'
page = agent.get('http://www.azlyrics.com/lyrics/scorpions/stilllovingyou.html'
)
page.search('/html/body/div[3]/div/div[2]/div[6]').each do |div|
puts div.text
end

Al probarlo veremos los lyrics, eso quiere decir que llegando a la pgina podemos leerlo, ahora tenemos que
lograr los pasos previos, o sea utilizar el formulario para buscar, y seleccionar la cancin respectiva.

El formulario de bsqueda
La parte del formulario es similar a la que hicimos en google, en la pgina principal tenemos que tomar el
formulario y pasarle un parmetro, con el inspector podemos ver que el nombre del input al igual que en google
es q

.
Luego de hacer la bsqueda llegaremos a otra pgina distinta que tambin tendremos que analizar.

1
2
3
4
5
6
7
8
9

require 'mechanize'
agent = Mechanize.new
agent.user_agent_alias = 'Mac Safari'
page = agent.get('http://www.azlyrics.com/') # Cargamos la pagina inicia
form = page.forms.first
form['q'] = 'hello lionel richie'
page = form.submit # Enviamos el formulario y recibimos la pgina resultante
puts results_page.body

Logramos llevar bien hasta la pgina de resultados de la bsqueda, el ltimo paso es llevarlo a la pgina de los
lyrics siguiendo el link, para este caso vamos a escoger siempre el primer resultado.

1
2
3
4
5
6
7
8
9

require 'mechanize'
agent = Mechanize.new
agent.user_agent_alias = 'Mac Safari'
page = agent.get('http://www.azlyrics.com/') # Cargamos la pagina inicia
form = page.forms.first
form['q'] = 'hello lionel richie'
page = form.submit # Enviamos el formulario y recibimos la pgina resultante
puts results_page.body

Analizando la pgina de resultados

Vamos a ver que existen muchos links, algunos apuntan a resultados, pero otros son como la navegacin,
paginacin, publicidad, etc apuntan fuera y no nos sirven, ahora por suerte existe un patrn, todos los
resultados dentro de la pgina apuntan dentro de http://www.azlyrics.com/lyrics/

1
2
3

results_page.links_with(:href => /http:\/\/www.azlyrics.com\/lyrics/


puts links
end

Juntando las partes


Ahora nosotros estamos construyendo una solucin sencilla, as que tomaremos slo el primer resultado,
entraremos a esa pgina y mostraremos los lyrics.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

require 'mechanize'
agent = Mechanize.new
puts "Qu lyrics deseas buscar?"
query = gets.chomp()
agent.user_agent_alias = 'Mac Safari'
page = agent.get('http://www.azlyrics.com/') # Cargamos la pagina inicia
form = page.forms.first
form['q'] = query

results_page = form.submit # Enviamos el formulario y recibimos la pgina result

lyrics_link = results_page.links_with(:href => /http:\/\/www.azlyrics.com\/lyric


lyrics_page = lyrics_link.click()
lyrics_page.search('/html/body/div[3]/div/div[2]/div[6]').each do |div
puts div.text
end

Una ltima mejora, en caso de no encontrar resultados lo notificaremos, y guardaremos la lgica en una funcin
para futura reutilizacin.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

def scrap_lyrics(query)
require 'mechanize'
agent = Mechanize.new
agent.user_agent_alias = 'Mac Safari'
page = agent.get('http://www.azlyrics.com/') # Cargamos la pagina inicia
form = page.forms.first
form['q'] = query

results_page = form.submit # Enviamos el formulario y recibimos la pgina resu


lyrics_link = results_page.links_with(:href => /http:\/\/www.azlyrics.com\/lyr
return "No hay resultados" if lyrics_link.nil?
lyrics_page = lyrics_link.click()
lyrics_page.search('/html/body/div[3]/div/div[2]/div[6]').each do
puts div.text
end
end

Y ahora lo podemos utilizar simplemente con:

scrap_lyrics("lionel richie hello")

Un gran problema del que debemos estar conscientes con el scrapping es que si en algn momento la pgina
cambia nosotros tendremos que cambiar nuestro algoritmo.

Desafo
Generar un archivo que contenga el listado de las 100 mejores pelculas de Netflix que aparecen en este post.
http://www.pastemagazine.com/articles/2015/10/the-100-best-movies-streaming-on-netflix-october-2.html

Preguntas
Para qu sirve el user agent?
Que devuelve form.submit?

Cmo podemos pasarle parmetros (inputs) al formulario?


Qu es el XPATH?

17) Objetos avanzado


Self y main
self es un objeto que apunta al objeto actual, o sea devuelve el objeto dentro del que estemos, si lo hacemos
directamente entraremos al nivel superior llamado main.

1
2

self
# main

Main y top-level
Es en main donde es donde comnmente definen las variables y mtodos que no le pertenecen a ningn objeto,
aqu al igual que en cualquier otro objeto podemos guardar variables locales, de instancia y de clase.

1
2
3
4
5

@x = 5
instance_variables
# => [:@prompt, :@x]
self.instance_variables
# => [:@prompt, :@x]

Hay que recordar que el intrprete funciona distinto a un script, si usamos el mismo cdigo dentro de un archivo
.rb necesitas ocupar puts o inspect

1
2
3
4

@x =
@y =
puts
# =>

5
7
instance_variables.inspect()
[:@x, :@y]

y adems no veremos el prompt.


Cuando definimos una funcin esta no es ms que un mtodo del top-level main, ahora por definicin self
apunta al objeto actual, as que si devolvemos self dentro de una funcin obtendremos como resultado main

1
2
3
4
5
6

def prueba
self
end
puts prueba()
# => main

Pero si devolvemos self dentro de un objeto que sucedera?

1
2
3
4
5
6
7
8
9
10
11
12

class PruebaObjeto
def initialize
end
def returnself
self
end
end
o = PruebaObjeto.new()
# => #<PruebaObjeto:0x007fd8c107e7b0>
o.returnself

De este cdigo podemos aprender dos cosas, uno es que podemos devolver una instancia del objeto desde
cualquier mtodo y otra es que initialize siempre devuelve self, para dejarlo completamente claro vamos a
devolver otra cosa y ver que pasa.

1
2
3
4
5
6
7
8
9

class PruebaInitialize
def initialize
return 2
end
end
a = PruebaInitialize.new()
puts a
#<PruebaInitialize:0x007fd8c2045ea0>

Si initialize devolviera el valor pedido, en este caso 2, a debera tener el valor 2, sin embargo observamos que
devuelve self.

Llamados implcitos vs explcitos


Para entender esta seccin tenemos que entender un concepto que viene del lenguaje smalltalk que dice que los
objetos se comunican por mensajes, ahora la parte importante y a veces contraintuitiva es quien es el receptor (en
ingls el trmino es receiver) del mensaje.
Cuando nosotros hacemos objeto1.mtodo el receptor del mensaje es objeto1, ahora dentro del objeto nosotros
nos podemos referir a el implcitamente o explcitamente.
Ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

class Foo
def initialize()
bar # forma implcita
self.bar # forma explcita
end
def bar()
puts "yeea"
end
end
Foo.new()
# yeaa
# yeaa
# => #<Foo:0x007fb1e286cba8>

O sea podemos llamar a los mtodos tanto de forma implcita o explcita y obtendremos los mismos resultados,
pero veremos ahora que esta diferencia tiene implicancias.

Mtodos pblicos privados y protegidos.


En ruby todas las variables son privadas y los mtodos son pblicos, sin embargo los mtodos pueden ser
privados o protegidos en caso de que se necesite.
Advertencia: los protected y los private no son como otros lenguajes.

Los mtodos privados

1
2
3
4
5
6
7
8
9
10
11
12
13

class Persona
attr_reader :x, :y
def initialize(x, y)
@x = x
@y = y
no_tocar
end
private
def no_tocar
puts "heeey !!!"
end
end

Si intentamos ocupar este mtodo desde dentro del constructor (o cualquier otro mtodo de instancia) veremos
que la clase existe, pero si lo intentamos ocupar directamente desde dentro de un instancia veremos que no
existe.

1
2
3
4
5

a = Persona.new(2,3)
# heeey !!!
a.no_tocar
# NoMethodError: private method `no_tocar' called for
<Persona:0x007fb8b4870860 @x=2, @y=3>

Existe otra restriccin, un mtodo privado tampoco puede ser llamado de forma explcita dentro de un objeto.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

class Persona
attr_reader :x, :y
def initialize(x, y)
@x = x
@y = y
no_tocar # llamado implcito funciona
self.no_tocar # pero llamado explcito no funciona
end
private
def no_tocar
puts "heeey !!!"
end
end

16
17

heeey !!!
NoMethodError: private method `no_tocar' called for #<Persona:0x007fb6a113d968 @

En el ejemplo anterior vimos que cuando se llama al mtodo implcitamente funciona, pero cuando se hace
explcitamente falla.

Mtodos protegidos
Los mtodos protegidos son parecidos a los privados en el sentido que una instancia no puede llamarlos
directamente, pero a diferencia de los privados estos si se pueden llamar de forma explcita.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

class Persona
attr_reader :x, :y
def initialize(x, y)
@x = x
@y = y
self.no_tocar ## llamado de forma explcita
end
protected
def no_tocar
puts "heeey !!!"
end
end
Persona.new(1,1)
# heeey !!!

Herencia
La herencia es una relacin entre clases, donde una clase hereda el comportamiento de otra, y sirve
especialmente para no tener que reescribir cdigo.
En un captulo anterior hicimos un ejercicio con personas y zombies, ahora vamos a refactorizarlo utilizando
herencia

1
2

class Persona
attr_reader :x, :y

3
4
5
6
7
8
9
10
11
12
13
14

def initialize(x, y)
@x = x
@y = y
end
end
class Zombie < Persona
end
z = Zombie.new(2,3)
z.class
# => Zombie

La herencia nos permite tratar al zombie como si fuera una persona, y de esta forma entregarle todo el
comportamiento que tiene la clase padre, aunque en este caso el comportamiento corresponde exclusivamente a
la clase Persona.

1
2

Zombie.new(2,3).class
# => Persona

Clase, Superclase, y tipo


Ya vimos en captulos anteriores que Zombie.class es class, o sea la clase de la clase es clase, no hay duda de eso,
pero ahora veremos que existe tambin la superclass y esta nos permite saber de donde est heredando una clase.

1
2
3
4

Zombie.class
# => class
Zombie.superclass
# => Persona

Es posible conocer a todos los ancestros ocupando el mtodo de clase .ancestors

1
2

Zombie.ancestors
# => [Zombie, Persona, Object, Kernel, BasicObject]

Ruby ocupa mucho la herencia internamente, de hecho todos los objetos heredan del objeto objeto

1
2

Persona.new.superclass
# => object

Tambin es posible preguntar si un objeto pertenece a alguna clase, o hereda de algunas clase, por ejemplo:

1
2
3
4
5
6
7

z = Zombie.new(1,1)
z.is_a?(Zombie)
# => true
z.is_a?(Persona)
# => true
z.is_a?(Fixnum)
# => false

Herencia de estados, mtodos y super


Es posible para una clase hija tener estados que la clase padre no posea y tambin es posible reescribir
completamente (o parcialmente) un mtodo de la clase padre.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

class Persona
attr_reader :x, :y
def initialize(x, y)
@x = x
@y = y
end
end
class Zombie < Persona
attr_reader :days_since_zombie
def initialize(x,y)
super(x,y)
@days_since_zombie = 0
end
end

Por ejemplo en este caso un Zombie tiene su propio mtodo initialize, por lo que cuando se cree Zombie.new se
llamar a este, pero dentro se llama al mtodo initialize del padre, el mtodo super llama al mtodo padre que se
llama igual. y luego asigna la variable @days_since_zombies = 0

La idea de la herencia es evitar reescribir cdigo, o sea haber hecho esto sera errneo:

1
2
3
4
5
6

class Zombie < Persona


attr_reader :days_since_zombie
def initialize(x,y)
super(x,y)
end
end

Ya que Zombie ya incluye el mtodo initialize de Persona, reescribirlo para llamarlo sera intil.

Herencia de los mtodos privados y protegidos


Los mtodos privados y protegidos tambin son heredados, pero las restricciones siguen en pie, o sea debe ser
ocupado desde dentro de la clase, no puede ser ocupado desde fuera, en el caso de los privados deben ser
ocupados de forma implcita, y los protegidos pueden ser ocupados de forma implcita o explcita.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

class Persona
attr_reader :x, :y
def initialize(x, y)
@x = x
@y = y
end
private
def no_tocar
puts "heeey !!!"
end
end
class Zombie < Persona
attr_reader :days_since_zombie
def initialize(x,y)
super(x,y)
no_tocar # se llama al mtodo e imprime heeey
@days_since_zombie = 0
end
end
z = Zombie.new(2,4)

Preguntas
Qu keywords generan la diferencia entre un receptor implcito y uno explcito
Cul es la diferencia entre private y protected?
Qu devuelve self fuera de cualquier objeto?
Qu devuelve self a nivel de una clase?
Qu devuelve self dentro de un mtodo de instancia de una clase?
Qu devuelve self dentro de un mtodo de clase?
Qu devuelve siempre el constructor independiente del return?
Para qu sirven los mtodos protegidos?
Qu sucede si una clase hereda de otra pero redefine un mtodo, cual manda?
Qu hace la instruccin super?

18) Mdulos y Mixins


Los mdulos son muy parecidos a las clases en ruby, en el sentido de que se declaran, tienen mtodos, y los
puedes ocupar para definir constantes, pero a diferencia de las clases los mdulos no pueden ser instanciados.
Se les llama mdulos porque sirven para modularizar el cdigo (doooh !!!), o sea dividir el cdigo en partes
pequeas reutilizables, adems sirven para guardar cosas bajo un espacio de nombre, y esto es til porque hay
que recordar que las variables globales son nuestros enemigos.

Mdulos vs clases
Por ejemplo supongamos que estamos construyendo un software matemtico donde hay diversas frmulas, si
bien podramos guardar las frmulas en una clase e implementar los mtodos como mtodos de clases, tambin
lo podramos hacer dentro de un mdulo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

class Formula
@@PI = 3.1415
def self.PI
@@PI
end
def self.diameter(r)
2*r
end
def self.perimeter(distance)
diameter(distance) * PI
end
end

Podemos hacer lo mismo de forma ms sencilla con mdulos

1
2
3
4
5
6

module Formula
PI = 3.1415
def diameter(r)
2*r
end

7
8
9
10
11

def perimeter(distance)
diameter(distance) * PI
end
end

Pero ahora para utilizar la constante PI fuera del mdulo deberemos hacer:

Formula::PI

Mientras que si estuvisemos ocupando una clase sera:

Formula.PI

En la utilizacin son tcnicamente iguales, pero la definicin es ms limpia en el caso de los mdulos, adems
tienen distintos usos, y eso es lo que veremos a continuacin.

Mdulo en archivo externo


Los mdulos pueden estar definidos en el mismo archivo donde estamos trabajando o en un o externo. Si
nuestro mdulo est definido en otro archivo externo podemos cargarlo con require o require_relative.

1
2

require_relative 'formula'
Formula::PI

Debemos tener cuidado de especificar la ruta correcta al archivo, la extensin .rb no es necesaria, pero no afecta
si se pone.

Includes
Hasta ahora los mdulos como los hemos estudiado no presentan mayor utilidad, pero es porque todava nos
faltan estudiar los mixins.
Tenemos un mdulo Formula, ahora vamos a construir la clase Crculo en el archivo circulo.rb en la misma
carpeta.

1
2
3
4
5
6
7

require_relative 'formula.rb'
class Circulo
include Formula
end
puts Circulo::PI

Y aqu podemos ver que los mdulos funcionan similar a la herencia pero con la ventaja de que es posible incluir
diversos simultneamente, todo los mtodos del mdulo sern incluidos como mtodos de instancia de la clase
que los incluya.
veamos el siguiente ejemplo:

1
2

c = Circulo.new
puts c.diameter(5)

o sea nuestro objeto ahora tiene un mtodo de instancia que fue importado del mdulo Formula a travs de
includes.
Cabe destacar que Ruby no tiene multiherencia pero a travs de los includes podemos implementar muchas
funcionalidades de otras clases, un ejemplo clsico de esto son los arrays de ruby que implementan el mdulo
enum.

Extends
Adems de los includes existen los extends, son muy similares pero incluyen los mtodos del mdulo como
mtodos de clase.

1
2
3
4
5

require_relative 'formula.rb'
class Circulo
extend Formula
end

en este caso el uso sera sobre la clase.

1
2
3
4
5
6
7

require_relative 'formula.rb'
class Circulo
extend Formula
end
Circulo.diameter(5)

Herencia + includes
A modo de repaso vamos a crear un pequeo ejemplo donde ocupemos simultneamente herencia e includes,
para eso vamos a ocupar el mdulo de formulas, crearemos una clase punto y una clase crculo que hereda de
punto, este punto sera el punto central del crculo.

1
2
3
4
5
6
7

module Formula
PI = 3.1415

1
2
3
4
5
6
7

class Punto
attr_accessor :x, :y
def initialize(x,y)
@x = x
@y = y
end
end

1
2
3
4
5
6
7
8

class Circulo < Punto


include Formula
def initialize(x,y, r)
super(x,y)
@r = r
end

def diameter(r)
return 2*r
end
end

9
10
11
12
13

1
2

def diameter()
super(@r)
end
end

c = Circulo.new(2,3,4)
puts c.diameter

Lo interesante que hicimos fue reescribir el mtodo diameter, y llamamos a super utilizando nuestro radio, en
este caso ese mtodo no corresponde al punto, si no que corresponde a la frmula.

Clases dentro de los mdulos


Tambin es comn que hayan clases dentro de mdulos.

1
2
3
4
5

module Foo
class Bar
@@a = 10
end
end

y podemos utilizar estas clases de la misma forma:

1
2
3
4
5
6
7
8
9
10
11

module Foo
class Bar
@@a = 10
def self.get_a
return @@a
end
end
end
puts Foo::Bar.get_a

Mdulos dentro de los mdulos


Al igual como se pueden insertar clases dentro de los mdulos, tambin es perfectamente posible ingresar
mdulos dentro de los mdulos, como por ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13

module Formula
module Matematicas
PI = 3.1415
def diameter(r)
return 2*r
end
def perimeter(distance)
return diameter(distance) * PI
end
end
end

Usarlo sera tan simple como

puts Formula::Matematicas::PI

Desafio
Crear un modulo que tenga mtodos que permitan leer un archivo csv, y guardar el archivo csv, dado un arreglo
de objetos. Si el tamao del arreglo no cumple con el layout de nuestro archivo, lanzar una excepcin.

Preguntas
Cules son las diferencia entre un mdulo y una clase?
Cul las diferencias entre herencia e incluir un mdulo?
Cul es la diferencia entre require_relative e include?
Cul es la diferencia entre include y extends?
Cul es la diferencia entre Formula::PI y Formula.PI?

Si tenemos la clase Persona, y la constante NUMERO_DE_PERSONAS_EN_LA_FIESTA_X, ese


constante sera mejor definirla dentro de una clase, o dentro del mdulo?

19) Testing
Testing es una tcnica que consiste en crear pruebas para verificar que nuestro cdigo funciona o mejor dicho
que no falla
La clave para entender testing es que no se trata de validacin, se trata de invalidacin, o sea en hacer pequeos
scripts que intenten probar que nuestro cdigo va a fallar.
Un buen test detecta fallas, uno malo no aporta informacin.
Ruby al igual que muchos otros lenguajes trae herramientas incorporadas para crear tests automatizados que
corran bajo una sola lnea de comando, y de esta forma podemos revisar cualquier set de cambios corriendo esta
lnea.
Realizar los test es sencillo, lo difcil es saber que es lo que hay que testear, esto lo vamos a ir aprendiendo con la
prctica

Mi primer test
Supongamos que queremos calcular la hipotenusa de un tringulo, esta es una frmula muy sencilla, la
hipotenusa al cuadrado es igual a la suma de los catetos al cuadrado:

h^2 = c1^2 + c2^2

Entonces si queremos un mtodo que calcula la hipotenusa sera algo como:

1
2
3

def hipotenusa(c1, c2)


h = Math.sqrt(c1*c1 + c2*c2)
end

Este cdigo lo guardaremos en un archivo llamado hipotenusa.rb


Ahora nosotros perfectamente podemos abrir una consola en la misma carpeta con irb y cargar el archivo

1
2
3

require hipotenusa
hipotenusa(3,4)
end

y obtendremos como resultado 5


Para hacer testing automatizado con ruby vamos a ocupar una gema, aqu no tenemos un gemfile como tal como
en los proyectos de ruby, pero eso no quiere decir que no tengamos un gemset
Corremos en el bash

gem install test-unit

Estructura de un test

1
2
3
4
5
6
7
8
9
10

require 'test/unit'
require_relative '../hipotenusa'
class TestHipotenusa < Test::Unit::TestCase #Hereda de la clase test
def test_resultado_pitagorico # Cada mtodo es un test
# cada mtodo debe incluir un assert
assert_equal 5, hipotenusa(3,4), "Suma Positivos Funciona"
end
end

Un test case contiene varios tests, cada uno de estos vienen con un assert, que es la prueba, pero cada test tiene
un slo assert, (por ahora) es por eso que a este tipo de testing se le llama Unit testing estamos probando el
cdigo a travs de unidades mnimas funcionales.
Hay diversos tipos de assert para distintos Test, por ejemplo nos podra interesar slo que el resultado fuera
mayor que, menor que, distinto que, o si un arreglo contiene un elemento.
Normalmente todos los tests se guardan dentro de una carpeta llamada tests, es por eso mismo que el
require_relative hace un '../hipotenusa' , es porque est cargando el archivo hipotenusa de la carpeta
padre.
Ahora si el archivo de test est dentro de la carpeta de la test, y nosotros estamos ubicado en la carpeta del
proyecto entonces podemos correr los tests con:

ruby test/test_hipotenusa.rb

y podemos ver los resultados:

1
2
3
4
5

Finished in 0.000526 seconds.


-------------------------------------------------------------------1 tests, 1 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions
100% passed
--------------------------------------------------------------------

Hagamos ahora que nuestro test falle, para eso vamos a comentar la lnea de cdigo que devuelve la hipotenusa

1
2
3

def hipotenusa(c1, c2)


# h = Math.sqrt(c1*c1 + c2*c2)
end

Y ahora al correr los test veremos:

1
2
3
4
5
6
7
8
9
10

Failure: test_result(TestHipotenusa)
test/test_hipotenusa.rb:6:in `test_result'
3:
4: class TestHipotenusa < Test::Unit::TestCase
5:
def test_result
=> 6:
assert_equal 5, hipotenusa(3,4)
7:
end
8: end
<5> expected but was
<nil>

Los test pueden tener 4 resultados posibles


success
failure
error
skipped
Los primeros dos ya los revisamos, success es si nuestra prueba es pasada, failure es si falla, error es parecido
pero resulta cuando hay un error sintctico en el test y finalmente skipped es si el test tiene nombre pero no est
implementado.

Qu testear?
La prctica hace al maestro, pero lo que no hay que perder de vista es que hay que tratar de que nuestro cdigo
falle en lugar de comprobar que funcione, hay algunos tips para eso:
1. Revisar las condiciones, o sea si hay un if o while probar ambas condiciones
2. Revisar los bordes, por ejemplo si hay un <= o un <, == o > probar como entrada con un nmero menor al
borde, el borde, y luego un nmero posterior.
3. Probar parmetros invlidos, por ejemplo en la hipotenusa si le pasamos nmeros negativos como
parmetros al elevarlos al cuadrado nos dar una respuesta positiva y real, pero, existe un tringulo de lados
negativos?, que matemticamente se pueda resolver no necesariamente quiere decir que tenga sentido.
4. Debe ser un nmero?, que pasara si le pasamos un objeto o un string?
5. Debe funcionar con nmeros muy grandes?, probar con esos nmeros
6. Es una palabra?, qu pasa si la palabra es muy larga?, qu pasa si la palabra no existe o si el string viene
vaco?, qu pasa si el string es un nmero?,
7. Es un arreglo?, revisar si no altera el orden, (quizs no importa, quizs si, revisar si al agregar un nmero
este se incluye. Revisar que los valores que no deban ser cambiados no sean cambiados.

TDD
TDD es una filosofa de testing que consiste en realizar los tests antes del cdigo. Cmo funciona?, construimos
un par de casos de prueba, por ejemplo no tenemos el cdigo de la hipotenusa, pero sabemos que 3 y 4 da como
resultado 5, entonces tenemos un caso de uso que funciona, adems buscamos un caso de uso que no funciona,
y luego escribimos el cdigo y vamos corriendo los tests hasta que obtener xito.
TDD es una filosofa que se puede trabajar en conjunto con Unit Testing

BDD
BDD esa una filosofa con la que no vamos a trabajar en este curso, pero permite testear requerimientos (en
lugar de cdigo), entonces BDD se encarga de que el cdigo realizado resuelva el problema que dice resolver, en
cambio TDD slo se encarga de que el cdigo funcione. BDD es ms completo pero agrega una carga de trabajo
extra que no siempre se transforma en valor, en cambio TDD nos da la seguridad de que el cdigo realizado
funcione bien y que los nuevos cambios hechos a nuestro proyecto no rompan funcionalidades existentes.

Ejercicios prcticos
Ejercicio Suma
Ya que revisamos la parte terica vamos a resolver diversos casos prcticos para ejercitar.
Partamos con uno bsico, una suma, pero hagamos el test primero:
/test/test_suma.rb

1
2
3
4
5
6
7
8
9
10
11
12
13

require 'test/unit'
require_relative '../suma'
class TestSuma < Test::Unit::TestCase
def test_suma
assert_equal 5, suma(3,2)
end

#Hereda de la clase test

def test_suma_negativos
assert_equal -5, suma(-3,-2)
end
end

si corremos los test sin implementar el mtodo obtendremos un resultado:

El error se debe a que todava no hemos implementado el mtodo suma, entonces claramente un error que dice
undefined method suma for #<TestSuma:0x007f8c6212b038>
Ahora creamos en el archivo suma.rb en la carpeta padre.

1
2
3

def suma (a,b)


a + b
end

y al correr los tests obtendremos:

1
2
3
4
5

Finished in 0.000647 seconds.


-------------------------------------------------------------------2 tests, 2 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions
100% passed
--------------------------------------------------------------------

Ahora implementa un test para revisar si la suma de cero con cero da como resultado cero.

Ejercicio Arreglo
Implementa una funcin para contar los elementos de un arreglo (no puedes ocupar count), crea tests para
cuando el arreglo tenga cero elementos, y para cuando tenga 5 elementos.

contar(a)

Ejercicio Arreglo 2
Implementa una funcin para contar los elementos que sean iguales a x, donde x es una parmetro de la funcin,
ejemplo:

1
2
3

contar(a, 2) # contara
# todas las apariciones del elemento
# 2 dentro del arreglo a

Ejercicio Arreglo 3
Combina lo aprendido en el ejercicio arreglo, y arreglo 2 para que sea una nica funcin que se pueda ocupar de
ambas formas, o sea con un slo parmetro, o con dos.

1
2

z.no_tocar
# => NoMethodError: private method `no_tocar' called for #<Zombie:0x007fb8b482ad

Anda mungkin juga menyukai