Si es que os suena, posiblemente conozcáis Ruby por Ruby on Rails, un entorno web creado a mediados de la primera década del siglo que usa Ruby como lenguaje subyacente; mucha gente llega a Ruby desde la derecha (el On Rails) y se piensan que es como un lenguaje de macros que imita a Python o a Perl o a ambos... pero no es así.
El lenguaje Ruby lo creó Matz, Yukihiro Matsumoto, con la intención de que fuera fácil de aprender y se pareciera lo más posible a la forma en la que hablan las personas, no a cómo las máquinas quieren que hablemos. Y tiene mérito que no le haya salido un completo desastre, porque las últimas dos veces que dijeron eso salió el COBOL y el SQL. Por otro lado, el nombre Ruby puede ser un juego de palabras con las perlas de Perl, o quizás no. El propio autor dice que es una mezcla de Python (que es anterior, pero no mucho) y Perl con un poco de Lisp y Smalltalk espolvoreado para que no falte de nada.
¿Y qué es lo que sale de esa mezcla? Pues un lenguaje interpretado, de tipado dinámico y fuerte, orientado a objeto, reflexivo, que hasta no hace mucho no era demasiado rápido pero que últimamente está experimentando un incremento de rendimiento considerable. La versión estable actual es la 2.1.4, aunque todavía se usan de forma extensa versiones anteriores como la 1.9.3. Por supuesto que por lo que más se le sigue conociendo es por Ruby on Rails, pero aplicaciones muy populares como Amarok o Sketchup usan este lenguaje. Y tú puedes tambien usarlo, si prestas atención y ejecutas los ejemplos de este tutorial, ¿por qué no?
Si bien es cierto que los puestos de trabajo de Ruby tienen menos demanda que los de otros lenguajes como Python, Perl o Javascript, también es cierto que hay una gran demanda de programadores senior de Ruby, sobre todo Ruby on Rails. En el ránking TIOBE está en el Top 20 de forma más o menos consistente y es el 7º más popular según RedMonk (que mide proyectos nuevos en GitHub y preguntas en StackOverflow), justo por debajo del C++. Y tiene una popularidad creciente en el área de devops, es decir, del área de sistemas para apoyar al desarrollo. En todo caso, hay muchas razones, incluyendo que es un lenguaje más a conocer para añadir a una panoplia de herramientas que todo informático debe tener.
Desde Ubuntu, Guadalinex o Debian, haz
sudo apt-get install ruby irb rubygems rdoc
.
También puedes (y, de hecho, debes) instalar
el Ruby Version
Manager, que te permite trabajar con diferentes versiones de
Ruby a la vez:
curl -L https://get.rvm.io | bash -s stable --ruby
y que se instala directamente como una aplicación de usuario. Trabajar
de esta forma permite uniformizar las versiones en un equipo de
trabajo y por supuesto actualizar fácilmente el intérprete
instalado. Este programa lanza un guión del intérprete de comandos
y, en algunos casos, te podrá pedir la clave de superusuario,
aunque la instalación final se hace en tu
espacio $HOME
. Previamente habrás tenido que
instalar curl
, aunque está instalado en las distros
más habituales; si no encuentra una distribución binaria descargará
y compilará una nueva distro, por lo que también habrá que tener
las correspondientes herramientas instaladas (principalmente
compiladores; una vez más, en la mayoría de los casos ya lo
están). En esta página da
diferentes opciones para instalar RVM con/sin Rails y otra serie
de alternativas.
Para usar directamente en el intérprete donde estás RVM, un par de comandos más:
bash$ source ~/.rvm/scripts/rvm
rvm requirements
Y, por supuesto,
rvm use ruby --default
para usarlo. Casi todos los manuales hoy en día aconsejan trabajar con RVM (o un entorno similar) en vez de la versión que viene con la distribución.
También hay otra forma de manejar desde espacio de usuario
diferentes versiones de
Ruby: rbenv
. Los
pasos seguidos son similares: se instala el entorno, se descarga y
construye el intérprete, y posteriormente se selecciona el que
deseemos. En vez de curl
, rbenv
usa git
para descargarse (cuyo uso también aconsejamos
vivamente, pero que tampoco viene instalado por omisión).
Estos entornos te permiten instalar diferentes versiones de Ruby,
pero cómo elegir una debe tener en cuenta varios asuntos: hay varias implementaciones de Ruby, aunque la más
popular es la oficial, la de Matz (llamada a veces
MRI). Hay, sin embargo, otras como JRuby (dentro de la máquina
virtual Java) o
incluso Rubinius
una versión que trata de implementar la mayor parte de Ruby usando
el propio lenguaje y diseñada para que sea concurrente. En
tu ordenador puede que tengas una u otra, aunque también te puedes
instalar cualquier
otra. Aconsejamos que se use la oficial para la distribución
o bien, como se ha hecho arriba, se instale rbenv
o rvm
.
Para programar en Ruby necesitas el editor y el intérprete de Ruby propiamente
dicho. Descárgatelo e instálalo, aunque te vendrá bien también
bajarte irb
, un intérprete interactivo (un REPL:
Read-Eval-Print loop) que te permitirá
probar cosas sobre la marcha (viene incluido con los entornos virtuales). Servidor usa Emacs como editor, pero
cualquier otra cosa también servirá, incluso Notepad. Ahora, si
quieres ir un poco más allá, te puedes
descargar
un plugin para Eclipse o
el RDE, sólo
para Windows.
Ruby es un intérprete, así que no se "ejecuta" desde el menú. El
ciclo es el habitual en programas para lenguajes interpretados: se
escribe y se guarda el programa, nos vamos al directorio donde lo
hemos guardado, si estamos en Linux (o algún otro sabor de Unix como
el Mac) lo hacemos ejecutable
con chmod +x
, y lo ejecutamos. Pero todavía no podemos
ejecutar nada, porque no hemos visto ningún programa, así que vamos
con el primero.
#!/usr/bin/ruby
puts "Esto es jauja"
La primera línea es la habitual en lenguajes interpretados: le dice
al intérprete de órdenes de Linux (y al servidor Apache en Windows,
también) dónde tiene que buscar el intérprete; así que habrá que
comprobar que efectivamente se encuentra allí
escribiendo which ruby
(sí, en Linux, así que ya no lo
voy a decir más y asumid directamente que cualquier cosa que diga es
para Linux a no ser que se diga lo contrario).
Si usamos RVM (lo que aconsejamos vivamente) el programa anterior se convertiría en
#!/usr/bin/env ruby
puts "Esto es jauja"
En el que hemos cambiado la primera línea por otra que busca en el camino de ejecución un programa que corresponda a Ruby; si RVM está bien configurado, el primero que aparecerá será la instalación que se haya hecho. En todo caso, esta segunda forma es más aconsejable porque es independiente del lugar donde esté instalado Ruby, basta con que esté en el camino de ejecución
La siguiente línea no vacía ya sí está escrita en el lenguaje Ruby (aunque
usa la misma orden que C, put string
), pero tampoco es que extrañe demasiado. La cadena va
entre comillas, se usa el cambio de línea para acabar la sentencia,
y ya está.
Para ejecutarlo se guarda y se hace lo que se ha dicho antes, no
voy a repetirlo. Y el resultado será el esperado. También pasará lo
mismo si lo hacemos desde irb
, el intérprete
interactivo (que también se instala con RVM):
[jmerelo@leonard ruby-para-impacientes]$ irb
pirb(main):001:0> puts "esto es jauja"
esto es jauja
=> nil
Como Ruby usa la indentación mediante tabuladores para indicar bloques, los copia/pega de las páginas web no tienen por qué funcionar bien. Es mejor descargar los programas enlazados pulsando con el botón de la derecha y salvar como o descargarlos del directorio en GitHub
Pero Ruby es un lenguaje orientado a objetos, o más bien empotrado de objetos: todo es un objeto en Ruby. Así que lo anterior (y algo más) podríamos escribirlo de la forma siguiente.
puts "--" << "Esto es jauja".center(20) << "--"
Lo que consigue este programa es escribir una cadena centrada en
una línea de 20 caracteres y rodeada por dos guiones
(--
). <<
es el operador de
concatenación, que pega una cadena a la siguiente. Pero la
parte orientada a objetos está alrededor del
.center
es un método de la
clase String,
pero como todo es un objeto en Ruby, no hace falta que lo
declaremos explícitamente, ya es un objeto de por sí, por lo que
podemos aplicarle los métodos correspondientes, tales como
ese. Pasándole el argumento 20, centra la cadena en un espacio
equivalente a 20
caracteres:
usuario@usuario-desktop:~/code$ ./jauja-center.rb
-- Esto es jauja --
También podíamos haber creado el objeto explícitamente, pero hubiera sido mucho más clásico:
jauja = String::new( "Esto es jauja" )
puts "--" << jauja.center(20) << "--"
En la primera línea, en la que definimos una variable llamada jauja
vemos un par de cosas: como en otros lenguajes,
las variables en Ruby no tienen ningún carácter
adicional (en realidad se verán más adelante algunos caracteres, que
se usan principalmente para resolución de ámbito). Sólo el nombre
de la variable, lo que tiene sentido, porque hace que uno
tenga que escribir menos. Por otro lado, String
es una
clase, y además una clase estándar, por lo que no hay que decirle al
programa que la incluya ni nada. El método new
es un
método de clase, con lo que la sintaxis para llamarlo, a diferencia del método de un
objeto, es de cuatro puntos (dos puntos dobles). El contenido de la
variable sigue siendo un objeto, así que se usa de la misma forma que
antes.
El resto de los tipos de datos se define también de la forma más
lógica; Ruby trabaja bajo el principio de la mínima sorpresa (lo que
muchas veces provoca sorpresa viniendo de otros lenguajes), o más bien
de la máxima coherencia: una vez aprendida parte del lenguaje, el
resto es más o menos igual. Por ejemplo, las matrices:
matriz = ['esto','es',1,'matriz']
puts matriz.join << " " << matriz.join("-")
Como ocurre en
otros lenguajes
de tipificado dinámicos como el Perl cada elemento de una matriz
puede tener un tipo diferente: una matriz puede contener números enteros,
cadenas e incluso otras matrices, y desde su creación son objetos de
pleno derecho, pudiéndosele aplicar métodos como join
que
une todos los elementos de la matriz, con o sin algún carácter de por
medio. Este pequeño programa imprimirá:
usuario@usuario-desktop:~/ruby-para-impacientes$ code/matriz.rb
estoes1matriz esto-es-1-matriz
como, imagino, era de esperar.
No son los únicos tipos de matrices: las matrices asociativas son aquellas que usan una clave para acceder a cada uno de los elementos (en vez de hacerlo en secuencia), sumamente útiles para evitar la distribución de la información de una estructura de datos por múltiples matrices y su acceso fácil usando una clave
sonido_de = { :vaca => 'muuu',
:buho => 'uuu',
:caballo => 'iiiii' }
puts sonido_de.inspect
Que, aparte de introducir las llaves (para claves... ¿lo ves como se
trata de no sorprender?) pone unos dos puntitos delante de
las mismas que la verdad es que sí sorprenden. Y es porque se
trata de cadenas un poco especiales, denominadas
símbolos. Los símbolos en Ruby son como cadenas con las que
no se va hacer nada de lo que se suele hacer con las mismas:
ni partirlas, ni añadirles nada, ni quitarles nada. Unas
cadenas constantes, más o menos, que no es otra cosa lo que
necesitamos en una variable asociativa (porque las claves, una vez
establecidas, no se cambian, si acaso se añaden claves nuevas),
denominada Hash
en Ruby. Por supuesto, se puede usar una cadena normal y
corriente como clave:
precio_de = { "pipas" => 'bajo',
"coche" => 'depende',
"plan E" => 'exagerado' }
puts precio_de.to_s
que al ejecutarse, por usar to_s
para convertir a una
cadena la matriz asociativa en vez del inspect anterior no
se ve nada, pero es otra forma de hacer las cosas.
Ruby, aparte de tener tipificación dinámica, también tiene tipificación fuerte. Es decir, una vez que se ha establecido el tipo de una variable sólo se pueden hacer las operaciones que permita la clase.
irb(main):015:0> bar='hola k ase'
=> "hola k ase"
irb(main):016:0> bar+3
TypeError: can't convert Fixnum into String
from (irb):16:in `+'
from (irb):16
from /usr/bin/irb:12:in `<main>'
irb(main):017:0> bar=3
=> 3
irb(main):018:0> bar+3
=> 6
En el ejemplo anterior primero definimos bar
como
una cadena, lo que fija su tipo a String
, por lo que
no podemos usar la operación de suma y obtenemos el error que se
indica (no se puede convertir Fixnum
en String
). Sin embargo, usamos la misma variable
para asignarle un número y dinámicamente se le asigna ese tipo o
clase, por lo que ya podemos usarlo para esa operación
Tratándose de un lenguaje orientado a objetos, habrá que buscar la
clase para abrir y cerrar ficheros, que se llama en un alarde de
originalidad File
(recordemos, mínima sorpresa).
fh = File::new( ARGV[0] )
while (line = fh.gets )
nombre, apellidos = line.split(',')
puts "* Nombre #{nombre}\n\tapellidos #{apellidos}"
end
En este caso, tampoco es sorprendente la matriz que se usa para
acceder a la línea de comandos: ARGV
, igual que en C (pero en
mayúsculas) o en Perl (pero sin dólares). Ya puestos, introducimos
también una estructura de control: el bucle while
que
va leyendo línea a línea con gets
(lo contrario
que puts
, que es para escribir). El cuerpo del bucle no
usa llaves, sólo la indentación y la palabra end
para indicar el
final.
Fijaros también en una cosa curiosa: el =
de la primera línea
tiene a la izquiera y a la derecha una matriz: dos variables a las
que se le asigna lo que queda al partir (split
) la línea del
tipo nombre, apellidos
por la coma que lo
divide. Simplemente se ponen a la izquierda las variables a las que
van a ir a parar los diferentes elementos de la matriz. Y en la
línea siguiente se imprime la salida, interpolando las cadenas
usando algo para distinguirlas: #{}
. Como las variables
no tienen ningún símbolo delante, hace falta eso al menos para saber
que se trata de variables, y no de parte de la cadena. El
resultado es el esperado:
$ ruby code/fichero.rb code/nombres.txt
* Nombre Ginés
apellidos Ibn Hassan Rodríguez
* Nombre Sergei
apellidos Ben Ayoun
* Nombre Malika
apellidos Maliki
* Nombre Juan
apellidos Gómez Gómez
al menos sobre el fichero
Ginés, Ibn Hassan Rodríguez Sergei, Ben Ayoun Malika, Maliki Juan, Gómez Gómez
Las clases están organizadas jerárquicamente en espacios de
nombres. Net
, por ejemplo, agrupa diferentes funciones
relacionadas con la red: web , FTP, y todas esas cosas; dentro de
esa jerarquía, los descendientes se separan con ::
,
igual que en
Perl. Net::HTTP
serviría para leer cosas de la web. Pero no forma parte del núcleo o
core, así que tendremos que importarla explícitamente con
require
:
require 'net/http'
Net::HTTP.get_print 'osl.ugr.es', '/'
En require
cambia un poco la sintaxis: se separan las partes de la
librería con /
y se pone todo en minúsculas. require
importa el código, pero no los identificadores; por eso para usar
alguna función del módulo hay que decir todo el nombre de la misma:
nombre de la clase nombre_del_método
. get_print
es un método
de clase, y recibe como argumentos el nombre del servidor (el HTTP
va de soi) y la dirección dentro de ese servidor, en este caso el
directorio raíz. Al ejecutarlo nos dará de resultado un mogollón de
texto, todo lo que haya en la página. De camino, conviene fijarse
que aquí nos hemos ahorrado unos cuantos paréntesis, lo que,
sinceramente, me ha sorprendido.
Juntando todo lo anterior, y añadiendo alguna cosilla más de nuestra cosecha, podemos bajarnos una página web y meterla en un fichero
require 'net/http'
url = ARGV[0]
respuesta = Net::HTTP.get url, '/'
fname = url + ".html"
if ( File.writable?(fname) )
salida = File.new fname, "w"
salida.puts( respuesta )
else
puts('No puedo escribir en ' +fname)
end
Nuestra cosecha incluye una interrogación y un if
, que no
habíamos visto antes. La interrogación se pone en los métodos que
devuelven un valor lógico, verdadero o falso (valores que tienen un
tratamiento diferente en Ruby y en otros lenguajes: un valor lógico
es un valor lógico, no un 0 o una cadena nula). En este caso, si se
trata o no de un fichero sobre el que tengamos derechos de escritura
(en lo que, al parecer, es un poco peculiar este Ruby). El bloque
if
termina en end
, como antes el bucle. Además, hemos usado "w"
como segundo argumento de File.new
para abrirlo para
escritura. Como se ve, no hace falta cerrarlo. Pa qué, si ya sabe
hacerlo el ordenador.
Después de las variables uno de los conceptos importantes en Ruby
son los bloques. Un bloque es una secuencia de código que define un ámbito léxico, es decir que puede tener sus
propias variables, y en Ruby se denota por
llaves {}
o por do
- done
. Se usa, por ejemplo, para bucles tales como los siguientes.
host = ARGV[0]
partes = host.split(".")
partes.each do |p|
puts "* #{p}"
end
En este mini-programa le pasamos un nombre de servidor en internet
(del tipo subdominio.dominio.tld) y nos da cada una de sus partes, que
se guardan precisamente en una variable que se llama así. Pero el truco
está en la tercera línea: partes.each
es una función que recibe un bloque como argumento. También lo podríamos expresar de la forma siguiente:
partes = host.split(".")
partes.each { |p|
puts "* #{p}"
}
y sería exactamente lo mismo (salvo la precedencia, pero eso no nos importa ahora).
Los bloques tienen todos la misma estructura: al principio se
declara una variable, que será la variable que irá tomando los
valores que reciba de su función uno por uno. En este caso la hemos
llamado p
, pero es un nombre arbitrario, porque estamos haciendo
una declaración. Dentro ya del bloque metemos el código que
consideremos necesario, y lo finalizamos con llaves o end,
dependiendo de cómo lo hayamos comenzado
Lo que ocurre con los bloques en Ruby es que tienen entidad
propia. Son como funciones anónimas, y de hecho se pueden usar como
tales; además, como todo en Ruby, son objetos, o sea que podemos
crearlos y pasarlos por ahí como queramos.
prefijos = %w( pre post ante super macro mega)
prefijadores = Hash.new
prefijos.each { |p|
prefijadores[p] = lambda { |post| return "#{p}#{post}";}
}
puts prefijadores['macro'].call( 'objetivo' )
puts prefijadores['super'].call( 'chanchi' )
puts prefijadores['mega'].call( 'chuli' )
En este ejemplo hemos empezado definiendo una matriz de forma
abreviada: usando %w
para ahorrarnos comas y comillas, y
hemos seguido creando un Hash
(matriz asociativa) donde vamos a
guardar todas las funciones. Recorriendo el array creado y usando
lambda
creamos una función que tiene una parte fija, p
que recibe
del bucle, y una parte variable, post
, que es el argumento que
recibirá cuando se llame, tal como se hace abajo usando call
(recordad que es un objeto, y para ejecutar esa función hay que llamar
al método call
de ese objeto). La
función prefijadores['macro']
se comportará de la misma
forma que si la hubiéramos definido así
def prefijador( post )
"macro#{post}";
end
puts prefijador('micro');
la única diferencia es que en este caso no hace falta usar call
para
llamar a la función: se puede usar directamente el nombre de la
misma. De camino, vemos como se definen funciones en ruby: usando
también def
. Igual que antes, salvo que ahora damos un nombre al
bloque, lo que le da más derechos, al parecer.
Qué sería de cualquier lenguaje si tuviéramos que conformarnos con lo que nos da, y no pudiéramos instalar cosas nuevas... comenzó LaTeX con CTAN, luego siguió Perl con CPAN, y Ruby tiene su colección de gemas para poder bajártelas cómodamente. En la mayor parte de las distribuciones incluidas hoy en día, o en cualquier caso a partir de la 1.9, es parte del mismo paquete que Ruby, así que no hace falta descargárselo aparte. Si no es el caso, lo mejor es usar el entorno RVM como se ha explicado anteriormente.
Aparte de gem, hay que instalarse alguna cosa más, porque muchos
módulos en ruby necesitan herramientas de construcción
adicionales. En concreto, la versión -dev
del paquete
Ruby que tengamos instalado. Por ejemplo, en Ubuntu habría que
escribir
sudo apt-get install ruby1.8-dev
(o algo similar, dependiendo de la versión de Ubuntu que tengamos instalada). No siempre es necesario, pero si te da un error algún módulo típico,
posiblemente sea por eso; si tenemos alguno de los entornos virtuales también es posible que haya que realizar alguna instalación adicional para los módulos.
Una vez instalado todo eso, no hay más que usarlo. Empezamos por buscar algo que queramos instalar:
jmerelo@sheldon:~/public_html/tutoriales/ruby-para-impacientes$
gem search mysql
*** LOCAL GEMS ***
Joeves, no devuelve nada. Pero claro, es que busca en la colección local de gemas. Habrá que buscar en la remota:
jmerelo@sheldon:~/public_html/tutoriales/ruby-para-impacientes$ gem search --remote mysql
*** REMOTE GEMS ***
activerecord-jdbcmysql-adapter (0.9.6)
activerecord-mysql-adapter-flags (0.0.3)
dbd-mysql (0.4.4)
do_mysql (0.10.1)
...
Y así hasta un mogollón de cosas. Tendremos un listado de todas las disponibles, y todas las versiones. Vamos a instalarnos la tercera; si queremos que esté disponible para todos los usuarios tendremos que lanzar la orden con privilegios de administrador:
jmerelo@sheldon:~/ruby-para-impacientes$ sudo gem install ruby-mysql
Successfully installed ruby-mysql-2.9.2
1 gem installed
Installing ri documentation for ruby-mysql-2.9.2...
Installing RDoc documentation for ruby-mysql-2.9.2...
[...]
En algunos módulos puede que dé error, porque falte alguna
dependencia que haya que instalar desde el sistema operativo; en ese
caso, es conveniente instalar el paquete correspondiente, en vez de
hacerlo desde gem
. Si no es la ultimísima versión luego se puede
actualizar con gem update. Por ejemplo, un paquete de mysql se puede
instalar con sudo apt-get install libdbd-mysql-ruby
;
posteriormente, al hacer gem update
se actualizará
alguna de las librerías dependientes que se han instalado con el
paquete (en mi caso, sólo una denominada deprecated
)
Hay verdaderas virguerías entre los paquetes gem, pero uno de los
más útiles para los que nos dedicamos
a rascar HTML
es hpricot, un analizador
liberal de HTML que permite extraer información de las páginas
muy fácilmente. Lo instalamos con gem (sudo gem install
hpricot
) y podemos empezar a usarlo
require 'rubygems'
require 'hpricot'
require 'open-uri'
osl = Hpricot(open("http://osl.ugr.es/"))
osl.search("//h2[@class='entry-title']").each { |h2|
this_h2 = Hpricot( h2.to_s )
link = this_h2.search("a[@href]").inner_html
text = this_h2.at("a").inner_html
puts "#{link} => #{text}"
}
Hay que incluir unos cuantos módulos; a partir de ahí se trata de
usar expresiones XPath (hay que saber un poco de XML para esto, pero
es bastante lógico todo) y de usar inner_html
para
extraer el interior de los elementos que vamos analizando, en este
caso los enlaces y títulos de los titulares del blog de
la OSL. Por ejemplo, en este caso
le estamos diciendo que extraiga los elementos h2
que, además,
tengan como atributo class
entry-title
; y lo estamos
haciendo pasándole un bloque con las cosas que tiene que hacer
cada vez que se encuentre con un elemento que cumpla esa
condición.
Por cierto, no es la única forma de hacer scraping con Ruby: se puede usar también WWW::Mechanize, una versión de la librería en Perl.
Como es de esperar, hay libros enteros gratuitos sobre Ruby: Programming Ruby, por ejemplo, pero el más curioso es la guía intensa de Why's a Ruby, con cómics, vericuetos inefables, pero que finalmente termina enseñando bastante. También tienes este tutorial en 20 minutos, y los libros que te aparecen a ambos lados de este texto.
Como seguramente conoces otro lenguaje de programación, prueba Ruby desde otros lenguajes, con tutoriales en inglés y español que explican cómo trabajar con Ruby si se conoce Perl, o Java, o Python.
En español se puede mirar este tutorial de Ruby, bastante completo, o este resumen para aprender en sólo 15 minutos. También hay traducciones de todos los libros anteriores, y si quieres meterte más a fondo, tienes este blog para seguir. Esta presentación también está bien, aunque le falta detalle; es bastante buena, en general, e instructiva.
Cuando ya estés harto de Ruby, también puedes aprender un poquico de Ruby on Rails, ya puesto.