Este resumen está basado en Ruby Course de Brian Schröder.
'Hola mundo'
puts 'Hola mundo'
salida:
Hola mundo
Asignaciones
# Toda asignación devuelve su valor a = 4 # 4 # Las asignaciones se pueden usar en cadena a = b = 4 # 4 a + b # 8 # y usados en un test fichero = File.open('../tutor.txt') # #<File:../tutor.txt> cont_linea = 0 # 0 cont_linea += 1 while (line = fichero.gets)# nil # Atajos a += 2 # 6 a = a+2 # 8 # Asignación en paralelo a, b = b, a # [4, 8] # Dividiendo arrays array = [1, 2] # [1, 2] a, b = *array # [1, 2]
irb y ri
irb: es el intérprete interactivo de ruby, donde se pueden probar al vuelo porciones de código.
ri: herramienta para acceder a la documentación. Por ejemplo, para documentarnos sobre el método tr de la clase String, ponemos en la línea de comandos:
ri String#tr
Sintaxis
variables, constantes, métodos,..
Variables / Métodos: estudiante, i, epsilon, piso_grande
Las variables y los métodos se parecen. Esto es lógico, puesto que una variable se puede sustituír por un método.
Las variables y los métodos deben ser escritos en minúsculas, que empiecen por una letra, y que contengan cualquier carácter alfanumérico o el guión bajo. Por ej: piso_grande. Sin embargo, las clases deben empezar por mayúscula. Además, a diferencia de los métodos/variables, no se usa el guión bajo para nombres compuestos. Por ej: PisoGrande.
Las clases deben empezar por mayúscula. Además, a diferencia de los métodos/variables, no se usa el guión bajo para nombres compuestos.
Constantes: PDF_KEY, R2D2, PERSONA_VIAJE
Las constantes sólo se pueden definir una vez.
Las constantes deben ser escritas en letras mayúsculas.
Variables de objeto: @nombre, @maximo, @hora_comer
Las variables de objeto (o variables de instancia) sólo pueden ser leídas/escritas por el propio objeto.
Variables de clase: objeto, llave_maestra
Las variables de clase no pertenecen a los objetos, si no a la clase. Existen únicamente en la clase, y son compartidas por todos los objetos.
Variables globlales: $global, $1, $contador
El uso de las variables globales ha sido declarado un crimen capital por la escuela del buen diseño.
Símbolos: :nombre, :edad, :Clase
Los símbolos son identificadores únicos que los encontraremos en varios sitios.
funciones, strings interpolados, números
# Las funciones están definidas por la palabra clave "def" # Los argumentos pueden tener valores por defecto def multi_foo(count = 3) 'foo'*count end # Los paréntesis pueden omitirse, si la situación no es ambigua multi_foo(3) # "foo foo foo" puts "hola mundo" " hola mundo # Para interpolar valores dentro de los strings: # i. Usar comillas dobles # ii. Usar #{...} 'Simple #{multi_foo(2)}' # Simple #{multi_foo(2)} "Interpolado #{multi_foo}" # Interpolado foo foo foo # Números 10 # 10 0.5 # 0.5 2e-4 # 0.0002 0xFFFF # 65535 010 # 8
Definición de funciones
Las funciones se definen por la palabra clave def
def hola(programador) puts "Hola #{programador}" end hola('Pepe') # Hola Pepe
Clases y objetos
Todo es un objeto…
…acostúmbrate a usar la notación del .método
(5.6).round # 6 (5.6).class # Float (5.6).round.class # Fixnum 'un string'.length # 9 'un string'.class # String 'pepe dice'.gsub('p','t') # 'tete dice' 'abc'.gsub('b','xxx').length # 5 ['algunas', 'cosas', 'array'].length # 3 ['algunas', 'cosas', 'array'].reverse # ['array', 'cosas', 'array'] # Al ser un objeto, el método suma es: 1.+(2) # 3 # pero para estos casos hay un azúcar sintáctico: 1 + 2 # 3
Definición de clases
class Persona def initialize(nombre) @nombre = nombre end def saludar "Hola, mi nombre es #{@nombre}." end end pepe = Person.new('Pepe') puts pepe.saludar # Hola, mi nombre es Pepe
Herencia de clases
class Matz < Persona def initialize super('Yukihiro Matsumoto') end end puts Matz.new.saludar # Hola, mi nombre es Yukihiro Matusmoto
Accesores
Los accesores son funciones que permiten el acceso a los atributos de un objeto.
class Perro def initialize(nombre, raza) @nombre = nombre @raza = raza end # para leer las propiedades de un objeto def nombre @nombre end def raza @raza end end perro1 = Perro.new('Trampas', 'palleiro') perro1.nombre # Trampas perro1.raza # palleiro
Esto es equivalente a:
class Perro def initialize(nombre, raza) @nombre = nombre @raza = raza end attr_reader :nombre, :raza end perro1 = Perro.new('Trampas', 'palleiro') perro1.nombre # Trampas perro1.raza # palleiro
Esta es la tabla con el tipo de accesores, y su función:
accesor | función |
attr_reader | lectura |
attr_writer | escritura |
attr_accessor | lectura y escritura |
Clases: extendiendo las clases
En Ruby las clases nunca se consideran cerradas, y se pueden modificar al vuelo, añadiendo métodos, variables,…
Por ejemplo, veamos como añadir una nueva funcionalidad a la clase Integer:
class Integer def fac raise "Factorización no definida para #{self}" if self < 0 return (1..self).inject(1) {|result,i| result*i} end end puts(0..5).map{|i| i.fac}.join(', ') # 1, 1, 3, 6, 24, 120
Excepciones
La captura se hace mediante rescue previo uso de begin:
begin # Código que puede dar problemas rescue ClaseExcepcion1 => exception1 # Se ejecuta si se lanza una ClaseExcepcion1 rescue ClaseExcepcion2 => exception2 # Se ejecuta si se lanza una ClaseExcepcion2 rescue # Captura cualquier excepción ensure # Código que siempre se ejecuta end
Para lanzar una exepción, usaremos raise
Módulos
Definición
Los módulos son similares a las clases en que contienen una colección de métodos, constantes y otros módulos y definiciones. Pero a diferencia de las clases, no se pueden crear clases derivadas de los módulos.
# trig.rb module Trig PI = 3.1416 # métodos def Trig.sin(x) # ... end def Trig.cos(x) # ... end end require 'trig' Trig.sin(Trig::PI/4) # "::" -> PI/4 de la clas Trig
Herencia múltiple
Podemos añadir funcionalidades de distintos módulos:
module D1 ... end module D2 ... end class ClaseCualquiera include D1 include D2 end
Arrays
# Array literal ['Un', 'array', 'con', 5, 'entradas'].join(' ') # Un array con 5 entradas # Nuevo array a = Array.new # [] a << 'algunas' << 'cosas' # algunas cosas a[2] # cosas a[0] = 3 # 3 a # 3 cosas # Se pueden usar valores por defecto... Array.new(4, 0) # [0, 0, 0, 0] # ...pero ten cuidado a = Array.new(2, 'Silke') # ['Silke', 'Silke'] a[0] << 'Amberg' # Silke Amberg a # ['Silke Amberg', 'Silke Amberg']
Arrays: los arrays pueden ser usados como pilas o colas
print 'Array como pila: ' pila = Array.new() pila.push('a') pila.push('b') pila.push('c') print pila.pop until pila.empty? # Array como pila: cba print "\n" print 'Array como cola: ' cola = Array.new() cola.push('a').push('b').push('c') print cola.shift until cola.empty? # Array como cola: abc
Hashes (diccionarios)
# Hash literal h0 = {'uno' => 1, 'dos' => 2} h0['uno'] # 1 # Rellenando un hash h1 = Hash.new #{} h1['joya'] = 'ruby' h1['fruta'] = 'banana' h1 # {"joya"=>"ruby", "fruta"=> "banana"} # A menudo, los símbolos se usan como llaves h2 = {:junio => 'perl', :julio => 'ruby'} h2[:julio] # ruby
Bloques e iteradores
Bloques: definición
Un bloque es una pieza de código, similar a un función anónima. Una función puede usar un bloque como argumento.
# Un iterador sencillo, llamando al bloque por cada elemento del array ['soy', 'un', 'platano'].each do |elemento| print elemento, ' ' end #soy un platano # Otro iterador muy usado. El bloque modifica el contexto done fue creado fac = 1 1.upto(5) do |i| fac *= i end fac # 120 # El resultado del bloque puede ser usado por quién lo usa... [1,2,3,4,5].map{ |num| num*num} # [1,4,9,16,25] # ...y puede usarse más de un argumento (0..100).inject(0){ |resultado, num| result + num} #5050
Bloques: sintaxis
# Los bloques son encerrados por do || ... end [1,2,3,4,5].each do |e| puts e end # o por paréntesis {|| ... } [1,2,3,4,5].map{ |e| e*e} #[1,4,9,15,25]
Por convención:
- usa do || … end donde las modificaciones sean importantes
- y {|| … } donde el valor de retorno sea importante
Iteradores
def f(cont, &bloque) valor = 1 1.upto(cont) do |i| valor = valor * i block.call(i, valor) end end f(5).do |i, f_i| puts "f(#{i}) = #{f_i}" end
f(1) = 1
f(2) = 2
f(3) = 6
f(4) = 24
f(5) = 120
Bloques: salvando los bloques
class Repetidor def initialize(&bloque) @bloque = bloque @cont = 0 end def repetir @cont += 1 @block.call(@cont) end end repetidor = Repetidor.new do |cont| puts " Me has llamado #{cont} veces" end 3.times do repetidor.repetir end
Me has llamado 1 veces
Me has llamado 2 veces
Me has llamado 3 veces
Estructuras de control
case
def saludar(*nombres) # * indica número indefinido de argumentos case nombres.length when 0 "Que triste, nadie ha leido mi tutorial" when 1 "Hola #{nombres}. Por lo menos una persona quiere saber Ruby" when 2..5 "Hola #{nombres.join(', ')}. Gracias por venir" when 6..12 "#{nombres.length} lectores. Bienvenidos a Ruby!" else "Wow #{nombres.length} lectores. Cuanta gente!" end end puts saludar('Alejandro', 'Luis', 'Pedro', 'Antonio', 'Guido', 'Matz, 'Satish') # 7 lectores. Bienvendios a Ruby!
condicionales
Ruby tiene todas las estructuras de control estándar. Y además, se pueden anexar a una expresión.
# Estructura de control habitual... if (1+1 == 2) "Me gusta la escuela" else "Menuda sorpresa!" end #...que podemos anexar a la derecha "Me gusta la escuela." if (1+1 == 2) # "Me gusta la escuela" "Menuda sorpresa!" unless (1+1 == 2) # nil (1+1 == 2) ? 'Correcto':'Incorrecto' # Correcto prob_lluvia = rand(100) # 64 (por ejemplo) case prob_lluvia when 0...10 then "Probabilidad más baja" when 10...50 then "Baja probabilidad" when 50...90 then "Alta probabilidad" when 90...100 then "Probabilidad más alta" end # Alta probabilidad
verdadero y falso
Únicamente nil y false son falsos; todo lo demás, es verdadero.
def es_true(valor) puts valor ? true : false end # nil es_true(false) # false es_true(nil) # false es_true(true) # true es_true(1) # true es_true(0) # true es_true([0,1,2]) # true es_true('a'..'z') # true es_true('') # true es_true(:un_simbolo) # true
bucles
Ruby tiene donde elegir en las construcciones de bucles, ¡pero no hay que olvidarse de los bloques!
i = 1 # 1 while (i < 10) i *=2 end # nil puts i # 16 i *= 2 while (i < 100) # nil puts i # 128 begin i *= 2 end while (i < 100) # nil puts i # 256 i *=2 until (i >= 1000) # nil puts i # 1024 loop do break i if (i >= 4000) i *= 2 end # 4096 puts i # 4096 4.times do i *= 2 end # 4 puts i # 65536 r = [] # [] for i in 0..7 next if i % 2 == 0 r << i end # 0..7 puts r # [1, 3, 5, 7] # Los bloques pueden simplificar muchas cosas (0..7).select{ |i| % 2 != 0} # [1, 3, 5, 7]
Expresiones regulares
En las expresiones regulares:
- Se ponen entre / /
- Cualquier caracter concuerda con si mismo, excepto \/^$|.+*?()[]\{\} que tienen distintas funciones dentro de las expresiones. Para poder buscarlos, hay que usar la secuencia de escape \. P. ej: \\, \/,…
- ^ busca el principio de una línea, $ busca el final de una línea
- . busca cualquier carácter
- Si a,b son expresiones regulares, entonces:
- ab es también una expresión regular
- a* es una expresión regular que busca el caparazón de a
- a+ es equivalente a aa*
- a|b busca a ò b
- Las expresiones pueden ser agrupadas por paréntesis. P. ej:
/(a|b)c/ # busca {'ac', 'bc} /a|bc/ # busca {'a', 'bc'}
- [caracteres] busca un rango de caracteres.
/[a-zA-Z0-9]/ # busca los caracteres alfanuméricos
- [^caracteres] busca los caracteres que NO pertenecen al rango.
- Atajos para los rangos:
atajo | rango |
\w | [0-9A-Za-z] |
\W | [^0-9A-Za-z] |
\s | [\t\n\r\f] |
\S | [^ \t\n\r\f] |
Unidades de Testeo
El testeo de unidades es un método para testear el código en pequeños trozos.
Por qué
- Significa que nunca tendrás el problema de crear un error mientras solucionas otro.
- Significa que no tendrás que ejecutar tu programa y jugar con él (lo que es lento) para arreglar los errores. El testeo de unidades es mucho más rápido que el "testeo manual".
- Conociendo cómo usar las unidades de test, abre el mundo al driven development.
Requisitos
- Cargar la biblioteca 'test/unit'
- Hacer que la clase a testear sea una subclase de Test::Unit::TestCase
- Escribir los métodos con el prefijo test_
- Afirmar (assert) las cosas que decidas que sean ciertas.
- Ejecutar los tests y corregir los errores hasta que desaparezcan.
require 'test/unit' class MiPrimerTest < Test::Unit::TestCase def test_de_verdad assert true end end