Ruby

Visita este artí­culo en http://www.estadobeta.com/2007/08/06/ruby/

Por Ismael en Desarrollo, Ruby & Rails, artículos, tips

Para entender el éxito de Rails hay que entender la joya con que está construido, Ruby.
Ruby, como cualquier lenguaje Orientado a Objetos, tiene tipos nativos. Uno de ellos es Array.

Code (ruby)
  1.  
  2. a = [] #lo mismo que a = Array.new
  3. a << 1
  4. a << 2
  5. a << 3 << 4
  6. a #=> [1,2,3,4]
  7.  

Nada nuevo aquí. Creamos un array y usamos el operador << para agregar elementos.

Programa para una interfaz, no una implementación.

Lo de arriba es una de las máximas de la programación Orientada a Objetos, y Ruby está diseñado desde la base para honrarla. Imaginemos la interfaz de un objeto para almacenar productos (luego vemos su implementación). Nuestro catálogo de productos contiene objetos Producto, con nombre, marca y precio.

Code (ruby)
  1.  
  2. catalogo = Catalogo.new
  3. catalogo << Producto.new(‘Ipod’, ‘Apple’, 250000)
  4. catalogo << Producto.new(‘N95′, ‘Nokia’, 300000)
  5. catalogo.total #=> 550000
  6.  

¿Cómo? Catalogo claramente no es un Array*. ¿Cómo es posible que soporte el operador <<?

Lo que si es nuevo para quienes no conocen Ruby, es que <<, y otros operadores, en en realidad un método (función) de las instancias de clase Array. No sólo eso: podemos definir nuestro propio << para nuestras clases. Veamos la implementación de Catalogo

Code (ruby)
  1.  
  2. class Catalogo
  3.   # en Ruby, "initialize" es el constructor
  4.   def initialize
  5.     @productos = [] #Array de proudctos
  6.   end
  7.   # definimos el operador
  8.   def <<( producto )
  9.     @productos < < producto
  10.   end
  11.   # calcula el precio de todos los productos
  12.   def total
  13.     @productos.inject{|a,b| a.precio + b.precio }
  14.   end
  15. end
  16.  

Catalogo tiene un atributo “@productos” (la @ indica que es un atributo de instancia. En otros lenguajes sería this.productos o self.productos), que es una instancia de Array. Luego, Catalogo define su propio operador <<, que recibe un nuevo producto y a su vez lo agrega al array interno, “@productos”.

Por supuesto, hasta ahora nuestro catálogo no nos sirve mucho más que un simple Array; el método total calcula el precio total de todos los productos en el catálogo. El ejemplo muestra cómo Ruby permite que nuestras clases definan, si queremos, la misma interfaz que las clases nativas (u otras clases). En Ruby, no es importante de qué clase es un objeto, sino cómo se comportan. Esto es conocido como “duck-typing” y en nuestro caso Catalogo se comporta como un Array.

Pero sigamos. En Ruby, usamos métodos iteradores para visitar los elementos de un Array.

Code (ruby)
  1.  
  2. a = [1,2,3,4,5,6]
  3. a.each {|elem| puts elem} #=> "1", "2", "3", "4", "5", "6"
  4. a.inject{|a,b| a+b} #=> 21
  5. a.delete_if{|elem| elem >= 2 && elem < = 4} #=> [1, 5, 6]
  6.  

Lo ideal sería disponer de todos estos métodos en Catalogo, para poder tratarlo como un verdadero array y recorrer y manipular los productos. ¿Tenemos que implementar cada uno de estos métodos en Catalogo? No.

Resulta que esos métodos, soportados por la clase Array y otras, en realidad son definidos por el módulo Enumerable. Los módulos, en Ruby, son contenedores de funcionalidad que puedes incluír en tus clases. Los métodos definidos en un módulo pasarán a formar parte de las clases que los incluyan. Esto es una alternativa de Ruby a la herencia múltiple de otros lenguajes.

Pues bien, para que una clase implemente los métodos del módulo Enumerable, sólo tenemos que incluirlo en la clase y definir el método each, en el que se basan todos los demás.

Code (ruby)
  1.  
  2. class Catalogo
  3.  
  4.   include Enumerable
  5.  
  6.   def initialize
  7.     @productos = []
  8.   end
  9.  
  10.   def <<(producto)
  11.     @productos << producto
  12.   end
  13.   def each
  14.     @productos {|p| yield p}
  15.   end
  16.   …
  17. end
  18.  

Catalogo#each permite que funcionen todos los métodos de Enumerable sobre los elementos del Array “@productos”. ¿Qué es eso de “yield”? Es algo que tiene que ver con los bloques en Ruby (lo que va entre los corchetes), pero eso lo podemos ver en otro artículo. La ventaja es que podemos iterar las instancias de Catalogo como si fueran arrays.

Code (ruby)
  1. catalogo = Catalogo.new
  2. catalogo << ipod
  3. catalogo << nokiaN95
  4.  
  5. catalogo.each {|p| puts p.marca} #=> "Apple", "Nokia"
  6.  
  7. catalogo.size #=> 2. "size" tambien viene gratis con Enumerable
  8.  
  9. catalogo.delete_if {|p| p.modelo = ‘Ipod’}

Internamente, Catalogo itera los elementos de “@productos”, pero lo importante es que, no importa cómo resolvamos la implementación de nuestro catálogo, para el mundo exterior este se verá y actuará como un sencillo Array.

Pero hay mucho más en Ruby. Rails aprovecha esta flexibilidad de manera muy inteligente y lo podemos ver, por ejemplo, en la forma en que puedes asociar objetos ActiveRecord.

Code (ruby)
  1.  
  2. juan = Persona.find(:first)
  3. andres = Persona.find(:all).last
  4.  
  5. juan.amigos << andres
  6.  

ActiveRecord redefine << para agregar un objeto a un array interno y guardar la relación automáticamente en la base de datos.

* Para que una clase herede las propiadades de Array, también podíamos usar herencia simple.

Code (ruby)
  1.  
  2. class Catalogo < Array
  3.  …
  4. end
  5.  

Pero esto probablemente complicaría la implementación de nuestro catálogo más adelante.

13 comentarios para “Ruby”

  1. Gravatarvladimir prieto Dice:

    como aprendíz de ruby, puedo decir que casi todo lo mostrado en el artículo se puede probar en :

    http://tryruby.hobix.com/

    sin necesidad de instalar ruby, ya que es un terminal de “pruebas” para ruby en linea.

    ;)

  2. GravatarIsmael Dice:

    Si. Útil herramienta.
    Una vez instalado, Ruby viene con IRB, que es una consola donde también se puede probar Ruby interactívamente. Simplemente tipeas “irb” y ENTER

  3. Gravatarmatias Dice:

    Buen articulo Isma, tambien hubieras podido usar sum en vez de inject ;)

    saludos!

  4. GravatarIsmael Dice:

    Matías, sum no existe en el módulo Enumerable, pero es muy fácil de implementar:

    
    class Catalogo
      include Enumerable
      def sum
        self.inject{|a,b| a.precio+b.precio}
      end
      #... Todo lo demás
    end
    

    O bien, defines sum en un módulo y así te sirve para incluírlo en otras clases que soporten Enumerable (como Array, Range o Hash).

    
    module Summable
      def sum
        self.inject {|a,b| a + b}
      end
    end
    
    class Array
      include Summable
    end
    
    class Range
      include Summable
    end
    
    # == algunas pruebas
    [1,2,3,4,5].sum #=> 15
    (1..5).sum #=> 15
    
  5. Gravatarcoto Dice:

    Mi estimado.
    ya no me sorprende tu gures en temas webisticos, en otras palabras eri seco!!!

    Pero permíteme recomendarte (y a la vez solicitarte) que pongas un post con RoR desde cero, quizas para ti eso sea redundante (hasta casi innecesario), pero te aseguro que tendrás muchos más lectores, si tu tutoria es básica… solo de entradita.
    suerte.
    coto

  6. GravatarIsmael Dice:

    Gracias por el comentario Coto.
    Lo que pasa es que igual hay muchos tutoriales e introducciones a RoR en la Web (basta con googlear), y me pareció innecesario. Igual he pensado en eso: empiezo desde cero, para los que no conocen RoR? Pero eso me tendría ocupado horas y horas y me impediría hacer lo que realmente quiero en EstadoBeta: llevas una bitácora de las cosas que me interesan en este momento. La única forma de escribir aquí es escribir cosas que me tienen entretenido.

    Se puede empezar por aquí, o buscar en español (por ejemplo este).

  7. GravatarEntradas en las blogosferas.12 - Carrero Bitácora de los Hermanos Carrero, David Carrero Fernández-Baillo y Jaime Carrero Fernández-Baillo. Dice:

    […] Porque Ruby es tan popular. […]

  8. GravatarRodrigo Dice:

    Sumamente offtopic: Solo quería decir que notables ofertas de trabajos en jobs.estadobeta.com!!!

  9. GravatarGallo Dice:

    Aun no me he puesto a mirarme este lenguaje que al parecer está siendo muy utilizado ultimamente pero queria puntualizar un detalle sobre la frase:

    “Lo que si es nuevo para quienes no conocen Ruby, es que <<, y otros operadores, en en realidad un método (función) de las instancias de clase Array”

    Bueno la sobrecarga de operadores (definir que hace un operador para los objetos de ese tipo de clase) es algo que existe por ejemplo en C++ desde hace ya mucho (desde que se inventó el lenguaje XD), no es nada nuevo, pero si es verdad que lenguajes mas nuevos como PHP no lo implementan (no de momento que yo sepa).

    Personalmente creo que lenguajes como Ruby o PHP (y trabajo mucho con PHP) que hacen tan “comodo” trabajar con los recursos del lenguaje, hacen que cualquiera que lea un tutorial se autodenomine programador porque parece muy facil programar con estos, cuando en realidad salen unas chapuzas increhibles. NO a los tutorialeros…

  10. Gravatarruby on rails tutorial Dice:

    ruby on rails tutorial…

    As usual there is so much talk about Web 2.0 and not many solutions how to really make money with it….

  11. Gravatarlore Dice:

    calcu cientificaaaaaaaaa

  12. Gravatargqlazlfemi Dice:

    I am having a hedll of a time reading your bog in IE 7.2, just figurdd I would lef oyu know.

    liock here

  13. Gravatarzpgbnwlsuf Dice:

    I can’t read your website in IE 7.5, I just figured I mightlet you know.

    frere viedosw