2. Ruby
Creado en 1995 por Yukihiro Matsumoto (Matz)
Lenguaje Orientado a Objetos puro
Interpretado (desde 1.9 se compila a MV)
Tipos dinámicos
Matz, Fuente: Wikipedia
"Ruby is designed for programmer productivity and fun"
Compromiso
+Simplicidad -seguridad
+Productividad -rendimiento
5. TDD - BDD
TDD (Test Driven Development)
Desarrollo basado en pruebas
BDD (Behaviour Driven Development)
Desarrollo basado en comportamiento
Especificaciones mediante ejemplos
Varios frameworks:
Cucumber, RSpec, etc.
6. RSpec
Crear los siguientes métodos
par(n) compruebe si un número es par
fact(n) devuelve el factorial de un nº
https://gist.github.com/labra/6fa18dc87cb72ed1f82d
7. Conceptos básicos
Ruby = Orientado a Objetos puro
Todo lo que se manipula son objetos
Herencia universal (... < Object < BasicObject)
...y mensajes entre objetos (aunque no lo parezca)
Ruby tiende a camuflar algunas cosas para ser más intuitivo
pepe.saluda("Hola") pepe.send(:saluda,"Hola")
pepe.edad pepe.send(:edad)
pepe.edad = 2 pepe.send(:edad=,2)
8. No hay tipos primitivos
Todo son objetos
23, "Hola", [1,2,3] son objetos
class: clase a la que pertenecen
superclass: superclase
"Hola".class # => String
"Hola".class.superclass # => Object
2.class # => FixNum
2.class.superclass # => Integer
[1,2,3].class # => Array
[1,2,3].class.superclass # => Object
9. Tipos
Ruby = tipado dinámico
Las variables no declaran el tipo
Las variables de instancia = nil hasta que se asignen
Tipos dinámicos: chequeo en tiempo de ejecución
x = "Hola" # => "Hola"
x.class # => String
x = 2 # => 2
x.class # => FixNum
x = 2 + "Pepe" # => TypeError: String can't be
# coerced into Fixnum
10. Sintaxis básica
Ruby = lenguaje orientado a una línea (scripting)
En general, expresiones terminan al final de la línea
; puede usarse para separar varias sentencias
No es necesario al final de las sentencias
Bloques de varias líneas
indica que se necesita contenido en siguientes líneas
Algunas expresiones indicant que hace falta contenido
puts 2 # => 2
puts 2 + 3 # => 5
puts 2 +
3 # => 5 (no necesita )
puts 2 + 3
+ 4 # => 9 (necesita )
11. Sintaxis básica
Todo consiste en enviar mensajes a objetos
objeto.send(mensaje,parámetros...)
Muchas simplificaciones para mayor claridad
pepe.crece pepe.crece() pepe.send(:crece)
f 2 f(2) self.send(:f,2)
2 + 3 2.+(3) 2.send(:+,3)
a.should be >= 7 a.should(be() >= 7) a.should(be.send(:>=,7))
12. Comentarios
Comentarios
Hasta fin de línea: # hasta fin de línea
Varias líneas (no es muy popular en Ruby)
=begin
varias líneas
=end
rdoc genera documentación
13. Identificadores
Clases y constantes = empiezan por mayúscula
String ActiveRecord DEBUG MAX_ROWS
Variables y métodos = empiezan por minúscula
Variables globales: empiezan por $
$nombre
Variables de instancia: por @
@nombre
Variables de clase (estáticos): por @@
@@cuentaPersonas
Símbolos (cadenas immutables): por :
:action
14. Convenciones
Variables y métodos: snake_case
Clases: CamelCase
Métodos booleanos suelen terminar en ?
empty?
Métodos peligrosos: terminan en !
reverse!, sort!
La definición de "peligrosos" es relativa
Suelen ser métodos que modifican objeto actual
pero no siempre es así...es una convención
Más información: http://goo.gl/VlPKhe
15. Objetos
Mediante constructor new se puede crear un objeto
class: devuelve la clase a la que pertenece
superclass: la superclase de una clase
methods: devuelve los métodos que tiene
def: permite definer métodos
self se refiere al objeto actual
objeto = Object.new # => #<Object:0x28f0458>
objeto.class #=> Object
objeto.class.superclass #=> BasicObject
objeto.methods #=> [:nil?, :class,...]
def objeto.saluda(nombre)
puts "Hola #{nombre}"
end # => nil
objeto.saluda("pepe") # => Hola pepe
16. Ejemplo creación objeto (sin clase)
Variables de instancia empiezan por @
juan = Object.new
juan.instance_variable_set(:@nombre,"Juan Manuel")
juan.instance_variable_set(:@edad,34)
def juan.crece
@edad = @edad + 1
end
def juan.getEdad
@edad
end
def juan.masViejo?(otro)
self.getEdad > otro.getEdad
end
17. Clases
Clase = plantilla para generar objetos
Variables de instancia, empiezan por @
class Persona
def initialize(nombre, edad)
@nombre = nombre
@edad = Integer(edad)
end
def to_s
"Persona: #{@nombre}, edad: #{@edad}"
end
def envejece
@edad = @edad + 1
end
end
juan = Persona.new("Juan", 30)
juan.envejece
puts juan #=> Persona: Juan, edad: 31
18. Herencia simple
Mediante <
class Usuario < Persona
def initialize(nombre, edad, email)
super(nombre,edad)
@email = email
end
def to_s
"Usuario: #{@nombre}, edad: #{@edad}, email: #{@email}"
end
def login(email)
email == @email
end
end
luis = Usuario.new("Luis", 24,"luis@mail.com")
luis.envejece
puts luis.login("luigi@mail.com") #=> false
19. Ejercicio: Figuras
Crear una clase Figura con 2 atributos (x,y)
Método que permita mover la figura
Crear una clase Rect para representar Rectángulos
Atributos a (ancho) y b (altura)
Crear una clase Circulo para representar Círculos
Atributo r (radio)
Crear método area para calcular el area
Crear método areas que calcula el area de una lista de
figuras
https://gist.github.com/labra/731f261deb9cc9c112a1
22. Condicional
Varias posibilidades
if...then...else...end en una línea
if al final
if !today.monday?
puts "Juerga"
end
if !today.monday? then puts "Juerga" end
puts "Juerga" if !today.monday? unless today.monday?
puts "Juerga"
end
puts "Juerga" unless today.monday?
unless = if not
24. while
while...end
while linea = gets
puts linea.upcase
end
En una sola línea
x = 2
while x < 1000
x = x * x
end
x = 2
x = x * x while x < 1000
=
25. Ejercicio
Calcular los factores primos de un número
Ejemplo:
factores 1 = [1]
factores 2 = [1,2]
factores 3 = [1,3]
factores 4 = [1,2,2]
factores 5 = [1,5]
factores 6 = [1,2,3]
...
https://gist.github.com/labra/4a8b03b7dd5680eb09e1
26. Métodos
Los métodos se definen mediante def
Paso de parámetros por referencia
Puede haber parámetros por defecto
También se permite número variable de parámetros
Devuelve valor última sentencia
No suele necesitarse return
def tlfno(num, prefijo = "34")
prefijo + "-" + num
end
puts tlfno("985103000")
puts tlfno("234567781","01")
34-985103000
01-985103000
27. Bloques de código
Fragmentos de código entre {...} ó do...end
Se asocian a un método
Pueden tener parámetros. Sintaxis: |x|
yield: invoca el bloque asociado
Puede pasar argumentos
def saluda(nombre)
puts "Hola " + nombre
yield 1
yield 2
end
saluda("Pepe") {|n| puts "Recibo #{n}" }
Hola Pepe
Recibo un 1
Recibo un 2
Hola Juan
Recibo un 1
Recibo un 2
saluda("Juan") do |n|
puts "Recibo un #{n}"
end
28. Iteradores
Iterador = método que devuelve sucesivos
elementos a partir de una colección
Ejemplo each
[ 'juan', 'pepe', 'luis' ].each {|nombre| puts nombre }
3.times { puts "*" }
3.upto(6) {|i| puts i }
('a'..'d').each {|char| puts char }
29. Excepciones
raise: lanza una excepción
rescue: captura una excepción
def divide(x,y)
x = x / y
rescue ZeroDivisionError
"No se puede dividir por 0"
end
def divide(x,y)
if y == 0
raise "No puedo dividir por 0"
end
x / y
end
NOTA: Existen catch/throw pero tienen otro propósito
31. Números
FixNum: enteros (longitud fija)
BigNum: enteros (sin longitud determinada)
Float
También hay números racionales y complejos
num = 10001
4.times do
puts "#{num.class}: #{num}"
num *= num
end Fixnum: 10001
Fixnum: 100020001
Bignum: 10004000600040001
Bignum: 100080028005600700056002800080001
32. Números
Las operaciones convierten al tipo más general
1 + 2 # => 3
1 + 2.0 # => 3.0
1.0 + 2 # => 3.0
1.0 + Complex(1,2) # => (2.0+2i)
1 + Rational(2,3) # => (5/3)
1.0 + Rational(2,3) # => 1.6666666666666665
NOTA: La division entre enteros devuelve un entero por defecto
1.0 / 2 # => 0.5
1 / 2.0 # => 0.5
1 / 2 # => 0
33. Números
Bucles mediante números
3.times { print "X " } # => X X X
1.upto(5) {|i| print i, " " } # => 1 2 3 4 5
99.downto(95) {|i| print i, " " } # => 99 98 97 96 95
50.step(80, 5) {|i| print i, " " } # => 50 55 60 65 70 75 80
34. String
Varias formas de declarar cadenas:
Mediante "", '', %q, %Q
Interpolación #{ }
Delimitadores %
Cadenas HERE
pepe = "Jose Luis"
puts "hola #{pepe}"
puts 'hola #{pepe}'
cadena1 = %(
cadena de texto
con salto)
cadena2 = <<HERE
cadena de texto
literal
HERE
39. Ejercicio con Arrays
Modelar cursos con alumnos
Una clase curso compuesta por:
Nombre del curso
Lista de alumnos
Una clase alumno compuesta por
id del alumno
nota del alumno
Definir métodos de curso:
getNota(id)
ponNota(id,nota)
media
Curso Alumno
1..n1
https://gist.github.com/labra/7773beaf06a66fcd5424
40. Hashes
Arrays asociativos
Clave => Valor
No están ordenados
Simplificación sintáctica
Cuando es el último argumento de un método
link_to('Edit',{:controller=>'students', :action=>'edit'})
link_to 'Edit', :controller => 'students', :action => 'edit'
Más información: http://ruby-doc.org/core-2.1.3/Hash.html
43. nil
Valor equivalente a falso
Las variables no inicializadas toman valor nil
x = Array.new
if x[1] then
puts "hay valor"
else
puts "No hay valor"
end
44. Expresiones regulares
Valores entre / y / son expresiones regulares
=~ intenta encajar
$1, $2,... toman el valor de los grupos encajados
email = "pepe@gmail.com"
if email =~ /^(.*)@(.*).com$/i
puts "encaja " + $1 + ' y ' + $2
else
puts "no encaja"
end
45. Simplificaciones
attr_accessor, attr_reader, attr_writer
class Persona
attr_reader :nombre
attr_accessor :edad
def initialize(nombre,edad)
@nombre = nombre
@edad = edad
end
end
pepe = Persona.new("Jose",23)
puts pepe.nombre # => Jose
puts pepe.edad # => 23
pepe.edad = 34
puts pepe.edad # => 34
pepe.nombre = "Juan" # undefined method `nombre='
46. traits mediante módulos
module Saludador
def saluda(msg)
puts "Hola: " + msg
end
end
class Persona
include Saludador
def initialize(nombre)
@nombre = nombre
end
end
pepe = Persona.new("Jose")
pepe.saluda("¿Qué tal?")
47. Variables estáticas (de clase)
Mediante @@
class Persona
@@cuentaPersonas = 0
def initialize(nombre)
@nombre = nombre
@@cuentaPersonas += 1
puts "#Personas: " + @@cuentaPersonas.to_s
end
end
pepe = Persona.new("Jose")
luis = Persona.new("Kiko")
mario = Persona.new("Mario")
48. Métodos estáticos (de clase)
Se indican mediante self.método
class Persona
def initialize(nombre)
@nombre = nombre
end
def self.doctor(nombre)
Persona.new("Doctor " + nombre)
end
def to_s
@nombre
end
end
pepe = Persona.doctor("Jose Luis")
puts pepe # => Doctor Jose Luis
49. Programación funcional en Ruby
Objetos Proc pueden invocarse mediante call
lambda = simplificación
suma3 = Proc.new { |x| x + 3 }
puts suma3.call(2) # => 5
suma3 = lambda { |x| x + 3 }
≡
aplica2 = lambda { |f,x| f.call(f.call(x)) }
puts aplica2(suma3,2) # => 8
suma3 = ->(x){ x + 3 }
50. Modularización
Módulos permiten separar espacio de nombres
M::a = elemento a de módulo M
module Universidad
class Profesor
def initialize(nombre)
@nombre = nombre
end
def to_s
@nombre
end
end
end
module Ayuntamiento
pepe = Universidad::Profesor.new("Jose")
puts pepe
end
module Ciudad
include Universidad
juan = Profesor.new("Juan")
puts juan
end
51. Gestión de dependencias
Inclusión de módulos
require
require_relative
Gestión de dependencias mediante gem
gem install
gem list
Gestión de tareas
rake
52. Organización y distribución
Estructura habitual de directorios
saluda/
bin/
saluda
lib/
saluda/
options.rb
main.rb
test/
saluda_spec.rb
saluda.gemspec
Ejemplo: https://github.com/cursosLabra/saludaRuby
Creación de gema: gem build saluda.gemspec
53. Metaprogramación
Programas que tratan otros programas como datos
Pueden acceder a partes de otros programas
Conocer aspectos del Sistema
Métodos tiene una clase, objetos existentes, etc.
Crear/modificar métodos o atributos en ejecución
"Métodos mágicos" que se invocan al vuelo
Asociar callbacks (hooks) a determinadas acciones
Cuando se crea una instancia, una subclase, etc.
...
54. Reflectividad
ObjectSpace información sobre objetos existentes
respond_to? chequea si admite mensaje
kind_of?, is_a? si pertenece a una clase (superclase)
instance_of? si es una instancia exacta de una clase
...y muchos más:
class, superclass, instance_methods,
private_instance_methods, class_variables, constants,
instance_variables,
55. Ejemplo de Metaprogramación
Añadir un atributo que recuerda los valores
class Class
def atributo_con_memoria(nombre)
nombre = nombre.to_s
attr_reader nombre
attr_reader nombre+"_valores"
class_eval %(
def #{nombre}=(val)
if @#{nombre+"_valores"}== nil
@#{nombre+"_valores"} = [nil, val]
else
@#{nombre+"_valores"} << val
end
@#{nombre}=val
end
)
end
end
class Persona
atributo_con_memoria :nombre
atributo_con_memoria :edad
end
juan = Persona.new
juan.nombre = "Juan"
juan.edad = 3
juan.nombre = "Juan Manuel"
print juan.nombre_valores
print juan.edad_valores
[nil, "Juan", "Juan Manuel"]
[nil, 3 ]
56. Ejemplo de metaprogramación
method_missing se ejecuta cuando no se encuentra un método
class Numeric
@@monedas = { 'euro' => 1, 'dolar' => 1.27 }
def method_missing(nombre_metodo)
moneda_singular = en_singular(nombre_metodo)
if @@monedas.has_key?(moneda_singular)
self * @@monedas[moneda_singular]
else
super
end
end
def en(moneda)
nombre = en_singular(moneda)
if @@monedas.has_key?(nombre)
self / @@monedas[nombre]
else
super
end
end
def en_singular(cadena)
cadena.to_s.gsub(/s$|es$/,'')
end
end
puts 5.dolares.en("euros") #=> 6.35
puts 20.euros.en("dolares") #=> 15.74