Aprendiendo Ruby parte 3 – Clases, objetos y demás
Publicado el 14 de febrero de 2011Siguiendo con la serie sobre Ruby, con este post cubro parte del capítulo 3 del libro Pickaxe. Respecto a la orientación a objetos del lenguaje, todo lo que manipulamos en Ruby es un objeto. Voy a copiar y pegar el código que hice siguiendo el ejemplo del libro. Creo que está bastante simple, y se puede ir entendiendo y viendo cómo maneja las cosas Ruby con solo mirar el código.
El libro explica muchos conceptos básicos de orientación a objetos que voy a omitir, suponiendo que los lectores ya tienen conocimiento del tema. Voy a hacer un resumen de referencia, con algunos detalles, que muestre lo específico del lenguaje. Así que este post es algo así como algo para tener de referencia, pero no voy a detallar demasiado los conceptos.
Veamos la estructura de un objeto básico, con algunas de sus características:
# -*- coding: utf-8 -*- class Libro attr_accessor :isbn, :price #Crea ambos (getter y setter) #Constructor - llamado con Libro.new def initialize(isbn, precio) #Variables de instancia: @variable @isbn = isbn @precio = precio end #Método que envía a un objeto cuando quiere mostrarse como String #Similar a toString() de Java def to_s "Libro - ISBN: #{@isbn} - Precio: #{@precio}" end end |
Acá tenemos una clase “Libro”, con un constructor que recibe dos parámetros y establece esos valores en las variables de instancia, y un método to_s para mostrar el objeto. Por ahora las variables de instancia son privadas y no tienen forma de accederse desde afuera, o cambiar su valor. Para esto Ruby podemos hacer lo siguiente:
def isbn @isbn end def isbn=(new_isbn) @isbn = new_isbn end |
De esta manera creamos métodos para acceder a las variables, al estilo “getter y setter” de Java:
Como los getter y setter son tan comunes, Ruby provee las siguientes atajos:
attr_reader :isbn, :price #Crea el "get" del atributo attr_writer :isbn, :price #Crea el "set" del atributo attr_accessor :isbn, :price #Crea ambos (getter y setter) |
Bien, a continuación pego el código que sigue el ejemplo del libro. Tenemos tres archivos:
libro.rb – la clase “libro”
# -*- coding: utf-8 -*- class Libro attr_accessor :isbn, :price #Crea ambos (getter y setter) #Constructor - llamado con Libro.new def initialize(isbn, precio) #Variables de instancia: @variable @isbn = isbn @precio = precio end #Método que envía a un objeto cuando quiere mostrarse como String #Similar a toString() de Java def to_s "Libro - ISBN: #{@isbn} - Precio: #{@precio}" end end |
csv_reader.rb – El lector de CSV:
require 'csv' #Dependencia externa require_relative 'videogame' #Dependencia relativa a este archivo class CsvReader def initialize @libros = [] end def leer_datos_csv(nombre_archivo_csv) CSV.foreach(nombre_archivo_csv, headers: true) do |row| @libros << Libro.new(row['isbn'], row['precio']) end end def mostrar_todos @libros.each do |libro| puts libro end end end |
Y por último el app.rb con el código que usa todo esto:
#!/usr/bin/ruby require_relative 'csv_reader' reader = CsvReader.new ARGV.each do |nombre_archivo| STDERR.puts "Procesando #{nombre_archivo}" reader.leer_datos_csv(nombre_archivo) end reader.mostrar_todos |
Como ven, se usa ARGV, el array de parámetros recibidos por el script. En mi caso creé un archivo datos.csv con esta info:
“978-1-93435-608-1″,”49.95”
“978-0136006633″,”120.74”
Y lo pasé como parámetro al script de arriba:
[fernando@hoth app]$ ./app.rb datos.csv Procesando datos.csv Libro - ISBN: 978-1-93435-608-1 - Precio: 49.95 Libro - ISBN: 978-0136006633 - Precio: 120.74 |
Bien, visto esto, pasamos a control de acceso a una clase. En Ruby hay tres niveles de protección: public
, protected
y private
. La diferencia de Ruby con otros lenguajes es que los métodos protected pueden ser llamados de cualquier instancia de las clases que lo definen y sus subclases. Y el acceso de control es determinado dinámicamente, por lo que los errores saltan cuando se ejecuta el código.
Cuando definimos un método sin especificar el acceso, el valor por defecto es public. Y tenemos dos formas de especificar el acceso de los métodos:
class Clase def metodo #por defecto es public end protected #A partir de aca, los métodos son protected def metodo0 #protected end def metodo1 #protected end private #A partir de este punto, los métodos son privados def metodo2 #privado end #otra forma de definir el acceso: public :metodo0 #protected :metodo #private :metodo end |
Bien, la última parte del capítulo 3 del libro habla sobre variables. En Ruby las variables son referenicas a objetos. Por lo tanto sucede lo siguiente:
[fernando@hoth app]$ irb irb(main):001:0> persona = "Fernando" => "Fernando" irb(main):002:0> persona2 = persona => "Fernando" irb(main):003:0> puts persona.object_id 18425820 => nil irb(main):004:0>puts persona2.object_id 18425820 => nil irb(main):005:0> persona2[8] = "s" => "s" irb(main):006:0> puts persona Fernandos => nil |
Esto pasa también en Java, y en Ruby lo que podemos hacer para almacenar una copia del primer objeto en la segunda variable (para crear un objeto nuevo, y por ende referenicar a un objeto distinto) es usar dup
. Otro método interesante que podemos usar en Ruby es freeze
. Al hacer objeto.freeze
, Ruby lanzará un RuntimeError si otra parte de nuestro código intenta modificar objeto
.
Para este capítulo, como dije en los posts anteriores, no entré mucho en detalle. Fue todo más que nada una pasada de cosas que tiene Ruby. Es que a esta altura, ya empecé a hacer pruebas, y tengo ganas de programar.Ya estuve mirando algo más de Ruby On Rails, y leí un poco de Sinatra.
En principio ya estuve haciendo una prueba básica que subí en GitHub: RSS Script. Entre otras tantas ideas, tenía una idea para una aplicación en Ruby que necesitaba leer RSS, así que ahí está el resultado. Fue bastante rápido y simple, no es nada del otro mundo el código, pero a lo mejor queda como algo terminado algun día.
También estoy por juntarme con más gente para desarrollar una aplicación simple, pero más completa. Vamos a ver qué sale, ya estaré contando por acá.
7 comentarios en este post
Feed de comentarios-
Tweets that mention Aprendiendo Ruby parte 3 - Clases, objetos y demás | Picando Código -- Topsy.com |
14 febrero. 2011 - 16:11
[…] This post was mentioned on Twitter by Pedro Mejias, Fernando. Fernando said: Post: Aprendiendo Ruby parte 3 — Clases, objetos y demás http://ur1.ca/38ki0 […]
Alejandro Segovia 14 febrero. 2011 - 11:01
Hola Fernando.
Excelente Post, gracias por compartir.
De repente fijate en “csv_reader.rb”, que me parece que el editor de WordPress te cambió el operador << por dos lt de HTML.
Saludos,
Alejandro.-
Fernando 14 febrero. 2011 - 18:43
¡Arrrgh! Gracias.
¡Estoy podrido de que el editor de WordPress me transforme los “<" a "<"! Tendré que instalar un plugin para esto... Saludos!
williams 14 febrero. 2011 - 18:24
Hola te cuento que me vota un error al querer usar require_relative:
./app.rb:3: undefined method `require_relative’ for main:Object (NoMethodError)
la version de ruby q tengo instalado es:
ruby 1.8.7 (2010-01-10 patchlevel 249) [i586-linux]
es necesario instalar alguna version o libreria especifica de ruby??
saludos williams
Fernando 14 febrero. 2011 - 18:50
En ningún momento especifiqué qué versión de Ruby estaba usando, pero debí hacerlo. Más que el libro que estoy usando es específico de una versión.
Estoy usando Ruby 1.9.2, y por lo que leí,
require_relative
es nuevo en Ruby 1.9. Te recomiendo actualizar a esta nueva versión.¿Estás siguiendo los posts? ¿Cómo vas con eso?
¡Saludos!
MaG 9 junio. 2011 - 11:57
# -*- coding: utf-8 -*-
Corregidme si me equivoco, Pero creo que ese comentario magico es de python y no de ruby.
Fernando 9 junio. 2011 - 20:08
¡Puede ser!
Emacs lo agrega cuando guardo el archivo con extensión .rb. Por lo que leí, es algo del ruby-mode de Emacs. Pero puede que sea algo de Python…