From Fedora Project Wiki

< FWN‎ | Beats

No edit summary
No edit summary
Line 5: Line 5:
LATAM Fedora is a regular column of Spanish language contributions around open source software.  It is our first expansion into incorporating foreign language content into FWN.   
LATAM Fedora is a regular column of Spanish language contributions around open source software.  It is our first expansion into incorporating foreign language content into FWN.   


This week's contribution is from [[User:gomix|Guillermo Gómez]], a second installment primer on Ruby.  Enjoy!
This week's contribution is from [[User:gomix|Guillermo Gómez]], a third installment on Ruby.  Enjoy!


===Ruby Capítulo 2 : Una clase algo más completa===
===Ruby Capítulo 3 : Métodos===


Siguiendo nuestra secuencia al estilo tutorial, en esta edición vamos a completar un poco más nuestras clases, introduciresmo los conceptos de métodos y variables de clase.
Como ya hemos venido desarrollando en nuestra previas entregas, los métodos sabemos se definen por medio de la palabra clave def. Los nombres de métodos deben comenzar con una letra minúscula. Los métodos que funcionan como consultas suelen ser llamados con el sufijo "?", tal como instance_of? Los métodos peligrosos o que modifican el objeto mismo suelen también llamarse con un el sufijo "!". Por ejemplo String tiene el método chop y chop!. El primero devuelve un string modificado, el segundo modifica el objeto mismo.


Una variable de clase es una variable compartida de la clase misma. Sólo existe una copia de una variable de clase en particular para una clase dada. Los nombres de las variables de clase comienzan con @@ tal como en @@reproducciones.
El cuerpo de un método contiene expresiones Ruby normales, excepto que no se puede definir un método de instancia, clase o modulo dentro de un método, si lo intenta obtendrá un error como el siguiente:


<code>
<code>
  1 class Cancion
  1 SyntaxError: compile error
  2   @@reproducciones = 0
  2 (irb):2: class definition in method body
  3  
  3    from (irb):7
4  def Cancion.reproducciones
5     @@reproducciones
6  end
7  
8  def reproducir
9    @@reproducciones =+ 1
10  end
11 end
</code>
</code>


A diferencia de las variables de instancia y variables globales, las variables de clase deben se inicializadas antes de poder ser utilizadas, usualmente se hace con una declaración simple en el cuerpo de la definición de la clase. En el código anterior igualmente se ha introducido el concepto de método de clase. Un método de clase es un método que no necesita que se instancie la clase para ser invocado, es decir, "son métodos propios de la clase y no de sus objetos".
El valor devuelto por los métodos es el valor de la última expresión ejecutada, o el resultado explícito de una expresión return.


Su utilidad es obvia en el ejemplo, se quiere saber cuántas reproducciones se han hecho en total en nuestro reproductor de música. Esta información no es específica a una canción en particular, es decir, no es sujeto de las instancias sino de la clase misma. La diferencia al momento de declarar el método de clase es que debe anteponer el nombre de la clase al método, tiene dos opciones, o usar explícitamente Cancion, o self. El uso de *self" da mayor flexibilidad ya que permite cambiar el nombre de la clase sin mayores traumas.
<code>
1 >> def resultado
2 >> "Uno"
3 >> end
4 >> p resultado
5 "Uno"
6
7 >> def resultado
8 >> "Uno"
9 >> return true
10 >> end
11 => nil
12 >> p resultado
13 true
</code>
 
====Parámetros====
 
Ya que sabe definir un método con su nombre, ahora seguramente se encontrará con la necesidad de invocar el método pasando parámetros.


<code>
<code>
  1 class Cancion
  1 def miMetodo(arg1, arg2, arg3)
  2  @@reproducciones = 0
  2  # su codigo
  3  
  3 end
  4   def self.reproducciones
  4  
  5     @@reproducciones
  5 def miOtroMetodo
  6  end
  6  # sin argumentos
  7  
  7   # su codigo
  8   def reproducir
  8 end
9    @@reproducciones =+ 1
10  end
11 end
</code>
</code>


====Constantes vs variables====
Ruby le permite definir valores por omisión a los argumentos de lo métodos que son obviados durante la invocación.


Las variables y constantes en Ruby mantienen referencias a objetos. Las variables no poseen un tipo intrínseco, a cambio el tipo es únicamente definido por los mensajes a los que responde el objeto referenciado.
<code>
1 def trampa(arg1="boba", arg2="tnt", arg3="30s")
2  "#{arg1}, #{arg2}, #{arg3}"
3 end
 
1 ?> trampa
2 => "boba, tnt, 30s"
3 >> trampa("lisa")
4 => "lisa, tnt, 30s"
5 >> trampa("lisa","h2o")
6 => "lisa, h2o, 30s"
7 >> trampa("lisa","h2o", "45h")
8 => "lisa, h2o, 45h"
</code>


Una constante en Ruby también es una referencia a un objeto. Las constantes son creadas cuando son asignadas por primera vez, a diferencia de otros lenguajes Ruby le permite alterar el valor de las constantes si bien ello provocará un mensaje de alerta. En general usted no debe alterar una constante en tiempo de ejecución y debería evitarlo a toda costa.
Si desea pasar una cantidad variable de argumentos, o desea capturar múltiples argumentos en un único parámetro, el colocar un asterisco (*) antes del nombre del último argumento hace justo eso.


<code>
<code>
  1 >> MI_CONSTANTE = 1
1 def varargs(arg1, *resto)
  2 => 1
2  "Recibi #{arg1} y #{resto.join(', ')}"
  3 >> MI_CONSTANTE = 2
3 end
  4 (irb):23: warning: already initialized constant MI_CONSTANTE
 
  5 => 2
  1 >> varargs("uno")
  2 => "Recibi uno + "
  3 >> varargs("uno","dos")
4 => "Recibi uno + dos"
5 >> varargs("uno","dos","tres")
6 => "Recibi uno + dos, tres"
  7 >> varargs("uno","dos","tres", "cuatro")
  8 => "Recibi uno + dos, tres, cuatro"
</code>
</code>


Note como las constantes pueden declararse dentro de la definición de clase lo que nos lleva a la definición de constante de clase y a la noción de espacios de nombres por la forma en que nos referimos a dichas constantes.
El resto de argumentos se apilan en un Array y se pone a disposición del método bajo el nombre especificado.
 
====Métodos con y sin bloques asociados====
 
Una técnica poderosa con Ruby es la posibilidad de asociar bloques de código a los métodos, veamos un ejemplo simple para ilustrar la técnica.


<code>
<code>
  1 class Cancion
  1 >> def con_y_sin_bloque(p1)
  2  VERSION="1.0"
  2 >>   if block_given?
  3 end
3 >>    yield(p1)
4 >>  else
5 ?>    p1
6 >>  end
7 >> end
8 => nil
9 >> con_y_sin_bloque(10) { |p1| (1..5).each { |m| p m + p1 } }
10 11
11 12
12 13
13 14
14 15
15 => 1..5
16 >> con_y_sin_bloque(10)
  17 => 10
</code>
</code>
Para determinar la presencia del bloque hemos usado el método block_given? para condicionar el procesamiento en la ejecución del método. Luego yield permite ejecutar dicho bloque de código ruby con el argumento que deseemos, en este caso p1.
Si en la definición de método, el último parámetro es precedido por un ampersand "&", cualquier bloque asociado a la llamada es convertido a un objeto Proc y dicho objeto es asignado a dicho parámetro, veamos un ejemplo para visualizar su uso.


<code>
<code>
  1 >> Cancion::VERSION
1 class CalculadoraDeVentas
  2 => "1.0"  
2  def initialize(nombre, &bloque)
  3 >> VERSION
3    @nombre, @bloque = nombre, bloque
  4 => "1.8.7"  
4  end
5
6  def precio_de_venta(costo)
7    @bloque.call(costo)
8  end
9 end
 
  1 >> cpd = CalculadoraDeVentas.new("Distribuidor1") { |costo| p "Precio de venta #{(1.5*costo).to_s} BsF." }
  2 => #<CalculadoraDeVentas:0xb735ccc8 @nombre="Distribuidor1", @bloque=#<Proc:0xb735cd18@(irb):75>
  3 >> cpm = CalculadoraDeVentas.new("Minorista1") { |costo| p "Precio de venta #{(1.8*costo).to_s} BsF." }
  4 => #<CalculadoraDeVentas:0xb7347008 @nombre="Minorista1", @bloque=#<Proc:0xb7347058@(irb):77>
5
6 >> cpd.precio_de_venta(100)
7 "Precio de venta 150.0 BsF."
8 => nil
9 >> cpm.precio_de_venta(100)
10 "Precio de venta 180.0 BsF."  
11 => nil
</code>
</code>


Note que VERSION no referencia ni colisiona con Cancion::VERSION.
En el ejemplo hemos introducido el uso del método call de Proc para invocar la ejecución correspondiente y con los argumentos necesarios. Tendremos más que decir acerca de Proc en futuras ediciones.


====El poder de herencia====
Por supuesto que esta funcionalidad puede implementarse de otras maneras, muchas otras maneras, el ejemplo lo que demuestra sutilmente es que también es posible reusar bloques de código por medio del uso de los bloques asociados a las llamadas de los métodos, es decir, puede por ejemplo compartir un bloque de código con más de una definición de método en su aplicación de hecho reduciendo y reusando el código por medio de funcionalidad común no representaba en clases o módulos. Esto igualmente puede hacerse en tiempo de ejecución, es decir, código que genera código y que lo pasa otro código para que lo ejecute. Si está confundido es porque está comenzando a experimentar el fuerte componente de dinamismo que tiene Ruby, no se asuste y abrace el poder que de esto se deriva.


Tal vez el santo grial de la programación es el principio DRY, "Don Repeat Yourself". Una forma de evitar repetirse, es escribir código reusable, cada vez que estamos escribiendo nuevo código y tenemos esa sensación de deja-vu de que ya esto lo hicimos una vez en el pasado, es porque no estamos reusando nuestro código de forma inteligente. En POO la herencia es una excelente forma de mantener el código reusable sin mayores repeticiones.
====Invocando los métodos====


La herencia le permite crear clases que sean refinamientos o especializaciones de otra clase. Por ejemplo, y siguiendo el ejemplo de Cancion en nuestro reproductor de música ahora a alguien se le ocurrió que sería interesante que se incluyera el soporte para canciones de tipo Karaoke. La única diferencia con las otras canciones es que las canciones Karaoke tienen asociadas la letra de la canción en conjunto con infomación de sincronización para reproducir las letras del Karaoke en nuestro flamante reproductor ahora con una gran pantalla de video.
Usted puede invocar o "llamar" un método especificando un "receptor" (objeto), el nombre del método, y opcionalmente algunos parámetros y un bloque asociado. Para el caso de los métodos de clase y módulo, el receptor sería el nombre de clase o módulo.


<code>
<code>
1 class Karaoke < Cancion
1   File.size("archivo_grande.mpeg")
def initialize(nombre, artista, duracion, letra)
Math.sin(Math::PI/3)
3     super(nombre, artista, duracion)
4    @letra = letra
5  end
6 end
</code>
</code>


El "< Cancion" en la definición de la clase le indica a Ruby que Karaoke es un subclase de Cancion, o en dirección opuesta, Cancion es la superclase de Karaoke. De esta forma la clase hija hereda todos los métodos y declaraciones de variables de su padre. Al invocar un método sobre un objeto o clase, Ruby defiere su ubicación hasta el momento de ejecución, en dicho momento Ruby primero mira en la clase para determinar si existe el método invocado, si no es así, intenta en el padre, y así atravesando todos los ancestros de la clase hasta ubicar el método invocado. Si se acaban los ancestros donde buscar, Ruby ejecuta una acción especial que puede ser interceptada si se desea Object#method_missing, si no, produce un error.
Si se omite el receptor, se usa self, el objeto actual:


Cada clase especializada maneja sus detalles, para el resto se utiliza super, es decir, el método del padre o de su ancestro. De esta forma, por ejemplo, en nuestra definición de Karaoke#initialize dejamos que la inicialización de todo lo común a Cancion ocurra igual que antes por medio de la llamada a super con los respectivos parámetros necesarios, luego inicializamos los datos específicos a nuetras clase Karaoke.
<code>
1 >> self.class
2 => Object
3 >> self.hash
4 => -608576338
5 >> hash
6 => -608576338
7 >> self.object_id
8 => -608576338
9 >> object_id
10 => -608576338
</code>
 
Note que he evitado intencionalmente llamar a class omitiendo el receptor, y ello es porque provoca un error, class es ambiguo, es una palabra clave (keyword) y Ruby cree que usted está tratando de definir una clase, no intenta self.class. En estos casos, use self.class, en general es mejor explicitar el receptor, es más legible y potencialmente evitará estas ambigüedades difíciles de depurar.
 
También observe con cuidado que no es igual Object.hash a self.hash ya que self es una instancia de Object en el ejemplo y Object.hash es un método de clase, referencian a hash diferentes, uno a la instancia (self), y el otro a la clase Object.


====Espacios de nombres====
<code>
1 >> self.class
2 => Object
3 >> self.object_id
4 => -608576338
5 >> Object.object_id
6 => -608573798
</code>


En la medida que escriba programas Ruby cada vez más grandes, usted naturalmente se encontrará con código reusable, librerías de rutinas relacionadas entre sí por ejemplo. Usted deseará separar dicho código en distintos archivos separados de tal forma que el contenido pueda ser reusado entre diferentes programas Ruby. Frecuentemente este código será organizado en clases.
Los paréntesis en la llamada al método para listar los argumentos son opcionales, sin embargo para evitar ambigüedades, le recomendamos fuertemente que siempre use los paréntesis, tal vez sólo en los casos más simples puede evitarse.


Otras veces ello simplemente no es conveniente o no aplica, son un grupo de métodos que desea reusar, usted puede simplemente colocarlos en un archivo y cargar dicho archivo en su programa con load o require. Esto funciona pero tiene un problema, digamos que se crea una librería para cocinar con los métodos calentar , mezclar, amasar en un archivo cocina.rb y otra librería para atletas con los métodos calentar, correr, descansar en el archivo atleta.rb. Si carga ambos archivos uno después del otro, simplemente el método calentar colisiona y sólo el más recientemente cargado tendrá la definicón actual del método, el otro simplemente será olvidado, en resumen la colisión en los nombres es el problema.
====Expandiendo arreglos en las llamadas====


La respuesta es el mecanismo de modulos. Los modulos definen un espacio de nombres en donde sus métodos y constantes pueden aplicar sin tener que preocuparse en colisionar con otros métodos y constantes:
De forma similar a la invocación con asterisco, en reversa, al invocar un método es posible "expandir" un arreglo para que cada elemento sea tomado como elemento individual.


<code>
<code>
  1 module Cocina
1  def cinco(a,b,c,d,e)
  2  ESCUELA="Latinoamericana"
2    "Argumentos #{a} #{b} #{c} #{d} #{e}"
  3   def Cocina.calentar(x)
3   end
  4    # Se espera que x sea un valor númerico para representar temperatura en ºC
 
  5    # ..
1 >> cinco("un", *["dos", "tres" , "cuatro", "cinco"])
  6   end
2 => "Argumentos un dos tres cuatro cinco"
  7 ...
  3 >> cinco(*["un", "dos", "tres" , "cuatro", "cinco"])
  8 end
  4 => "Argumentos un dos tres cuatro cinco"  
  9
  5 >> cinco("un", *["dos", "tres" , "cuatro", "cinco"])
  10 module Atleta
  6 => "Argumentos un dos tres cuatro cinco"
  11  ESCUELA="Simón Bolívar"  
  7 >> cinco("un", *["dos", "tres"] , "cuatro", "cinco")     # Debe ser el último argumento
  12  def Atleta.calentar(t)
  8 SyntaxError: compile error
  13    # Se espera que t sea un valor numérico que represente tiempo en segundos
  14     # ..
  15  end
16 end
</code>
</code>
==== Argumentos opcionales====
Usando el mismo ejemplo de arriba, intente pasar menos de cinco argumentos:


<code>
<code>
  1 >> Cocina::ESCUELA
  1 >> cinco
  2 => "Latinoamericana"
  2 ArgumentError: wrong number of arguments (0 for 5)
3 >> Atleta::ESCUELA
4 => "Simon Bolivar"
5 >> include Atleta
6 => Object
7 >> Atleta.calentar(1000)
8 calentando 1000 segundos
9 => nil
10 >> include Cocina
11 => Object
12 >> Cocina.calentar(10)
13 => 10
</code>
</code>


Ahora entonces tiene mucho menos probabilidades de provocar colisiones, note que debe cargar el código, y luego include para usar las funcionalidades del módulo.
Los cinco argumentos son obligatorios, una forma de convertirlos en opcionales es asignarles un valor por omisión nil.
Herencia múltiples vs Mixins¶
<code>
1  def cinco(a=nil,b=nil,c=nil,d=nil,e=nil)
2    "Argumentos #{a} #{b} #{c} #{d} #{e}"
3  end


Algunos lenguajes orientados a objetos, tales como C++, soportan herencia múltiple en donde una clase puede tener más de una superclase heredando funcionalidad de cada una de ellas. Si bien potente, esta técnica puede ser peligrosa ya que la jerarquía puede tener ambigüedades. Otros lenguajes como java y C# soportan sólo herencia simple. Si bien más limpia y simple, la herencia simple también tiene sus desventajas, en la realidad los objetos heredan atributos de múltiples fuentes en el mundo real.
1 >> cinco
 
2 => "Argumentos    "
Ruby ofrece una alternativa interesante y poderosa que le ofrece la simplicidad de la herencia simple y el poder la herencia múltiple. Una clase Ruby tiene una única clase padre, en consecuencia Ruby es un lenguaje con herencia simple, sin embargo, las clases Ruby pueden incluir la funcionalidad de cualquier cantidad de mixins (un mixin es una especie de definición parcial de clase). Nuevamente dejemos que Ruby hable, creemos nuestro archivo con la definición de módulo y un par de clases tontas que lo usen como mixin:
3 >> cinco("uno")
4 => "Argumentos uno    "
</code>


=====mixin_module.rb=====
Sin embargo note que no puede especificar el tercer argumento sin especificar el segundo, debe respetar la secuencialidad en la definición de los argumentos. Veamos la siguiente versión revisada para visualizar los argumentos pasados al método.


<code>
<code>
  1 module Debug
1  def cinco(a=nil,b=nil,c=nil,d=nil,e=nil)
  2   def quien_soy?
2     "Argumentos 1:#{a} 2:#{b} 3:#{c} 4:#{d} 5:#{e}"  
  3     "#{self.class.name} (\##{self.objet_id}): #{self.to_s}"
  3   end
  4  end
  5 end
  6
  7 class Cocina
  8  include Debug
  9  def estilo
10    puts "Italiana"  
  11  end
12 end
13
14 class Nevera
15  include Debug
16  def capacidad
17    puts "150 pies cúbicos"
18   end
19 end
</code>
</code>


Si ahora cargamos dichas definiciones de módulo y clases en irb, podemos evidenciar y experimentar con el uso de los mixins (note que le voy a pasar la forma de hacerlo):
Use nil para saltar al próximo argumento en la lista cuanda haga la invocación del método.


<code>
<code>
  1 $ irb
  1 >> cinco
2 >> load 'mixin_module.rb'
  2 => "Argumentos 1: 2: 3: 4: 5:"
  3 => true
  3 >> cinco("A",nil,nil,"D")
4 >> nevera = Nevera.new
  4 => "Argumentos 1:A 2: 3: 4:D 5:"  
5 => #<Nevera:0xb7587b38>
  6 >> cocina = Cocina.new
  7 => #<Cocina:0xb7585c5c>
8 >> nevera.quien_soy?
9 => "Nevera (#-609469028): #<Nevera:0xb7587b38>"
10 >> cocina.quien_soy?
11 => "Cocina (#-609472978): #<Cocina:0xb7585c5c>"  
</code>
</code>


Bien, es todo por hoy, nos recontraremos en un tercer capítulo dedicado a métodos en una próxima edición de esta serie dedicada a aprender Ruby.
==== Simulando argumentos por nombres: hash ====
 
Si bien Ruby no tiene un verdadero soporte para "keyword arguments", es decir, pasar argumentos con su nombre y valor, nada nos impide simular dicho comportamiento usando un hash. Ruby incluso tiene un pequeño atajo que nos evita la sintáxis Hash {} para evitarnos confundir con un bloque asociado. El hash debe estar especificado de último en la lista de argumentos.
 
<code>
1 def hash_params(params)
2  p "ID: #{params[:id]}"
3  p "Clave: #{params[:clave]}"
4 end


Este artículo se mantiene en línea en gomix.fedora-ve.org para posibles mejoras y erratas.
code class="ruby">
>> hash_params(:id => "XXX" , :clave => "Z A Z Z")
"ID: XXX"
"Clave: Z A Z Z"
=> nil
>> hash_params(:clave => "1 2 3 4", :id => "007")
"ID: 007"
"Clave: 1 2 3 4"
=> nil
>> hash_params({:clave => "1 2 3 4", :id => "007"})  # Sintaxis estricta con las {}
"ID: 007"
"Clave: 1 2 3 4"


Guillermo Gómez
Loading...
Fudcon-panama-2011_728x90_leaderboard_rotated
Powered by Redmine Poder_fedora Poder_ruby Haproxy Powered_by_apache
</code>

Revision as of 14:52, 16 April 2011

LATAM Fedora!

LATAM Fedora is a regular column of Spanish language contributions around open source software. It is our first expansion into incorporating foreign language content into FWN.

This week's contribution is from Guillermo Gómez, a third installment on Ruby. Enjoy!

Ruby Capítulo 3 : Métodos

Como ya hemos venido desarrollando en nuestra previas entregas, los métodos sabemos se definen por medio de la palabra clave def. Los nombres de métodos deben comenzar con una letra minúscula. Los métodos que funcionan como consultas suelen ser llamados con el sufijo "?", tal como instance_of? Los métodos peligrosos o que modifican el objeto mismo suelen también llamarse con un el sufijo "!". Por ejemplo String tiene el método chop y chop!. El primero devuelve un string modificado, el segundo modifica el objeto mismo.

El cuerpo de un método contiene expresiones Ruby normales, excepto que no se puede definir un método de instancia, clase o modulo dentro de un método, si lo intenta obtendrá un error como el siguiente:

1 SyntaxError: compile error
2 (irb):2: class definition in method body
3     from (irb):7

El valor devuelto por los métodos es el valor de la última expresión ejecutada, o el resultado explícito de una expresión return.

1 >> def resultado
2 >> "Uno" 
3 >> end
4 >> p resultado
5 "Uno" 
6 
7 >> def resultado
8 >> "Uno" 
9 >> return true
10 >> end
11 => nil
12 >> p resultado
13 true

Parámetros

Ya que sabe definir un método con su nombre, ahora seguramente se encontrará con la necesidad de invocar el método pasando parámetros.

1 def miMetodo(arg1, arg2, arg3)
2   # su codigo
3 end
4 
5 def miOtroMetodo
6   # sin argumentos
7   # su codigo
8 end

Ruby le permite definir valores por omisión a los argumentos de lo métodos que son obviados durante la invocación.

1 def trampa(arg1="boba", arg2="tnt", arg3="30s")
2   "#{arg1}, #{arg2}, #{arg3}" 
3 end
1 ?> trampa
2 => "boba, tnt, 30s" 
3 >> trampa("lisa")
4 => "lisa, tnt, 30s" 
5 >> trampa("lisa","h2o")
6 => "lisa, h2o, 30s" 
7 >> trampa("lisa","h2o", "45h")
8 => "lisa, h2o, 45h" 

Si desea pasar una cantidad variable de argumentos, o desea capturar múltiples argumentos en un único parámetro, el colocar un asterisco (*) antes del nombre del último argumento hace justo eso.

1 def varargs(arg1, *resto)
2   "Recibi #{arg1} y #{resto.join(', ')}" 
3 end
1 >> varargs("uno")
2 => "Recibi uno + " 
3 >> varargs("uno","dos")
4 => "Recibi uno + dos" 
5 >> varargs("uno","dos","tres")
6 => "Recibi uno + dos, tres" 
7 >> varargs("uno","dos","tres", "cuatro")
8 => "Recibi uno + dos, tres, cuatro" 

El resto de argumentos se apilan en un Array y se pone a disposición del método bajo el nombre especificado.

Métodos con y sin bloques asociados

Una técnica poderosa con Ruby es la posibilidad de asociar bloques de código a los métodos, veamos un ejemplo simple para ilustrar la técnica.

1 >> def con_y_sin_bloque(p1)
2 >>   if block_given?
3 >>     yield(p1)
4 >>   else
5 ?>     p1
6 >>   end
7 >> end
8 => nil
9 >> con_y_sin_bloque(10) { |p1| (1..5).each { |m| p m + p1 } }
10 11
11 12
12 13
13 14
14 15
15 => 1..5
16 >> con_y_sin_bloque(10)
17 => 10

Para determinar la presencia del bloque hemos usado el método block_given? para condicionar el procesamiento en la ejecución del método. Luego yield permite ejecutar dicho bloque de código ruby con el argumento que deseemos, en este caso p1.

Si en la definición de método, el último parámetro es precedido por un ampersand "&", cualquier bloque asociado a la llamada es convertido a un objeto Proc y dicho objeto es asignado a dicho parámetro, veamos un ejemplo para visualizar su uso.

1 class CalculadoraDeVentas
2   def initialize(nombre, &bloque)
3     @nombre, @bloque = nombre, bloque
4   end
5 
6   def precio_de_venta(costo)
7     @bloque.call(costo)
8   end
9 end
1 >> cpd = CalculadoraDeVentas.new("Distribuidor1") { |costo| p "Precio de venta #{(1.5*costo).to_s} BsF." }
2 => #<CalculadoraDeVentas:0xb735ccc8 @nombre="Distribuidor1", @bloque=#<Proc:0xb735cd18@(irb):75>
3 >> cpm = CalculadoraDeVentas.new("Minorista1") { |costo| p "Precio de venta #{(1.8*costo).to_s} BsF." }
4 => #<CalculadoraDeVentas:0xb7347008 @nombre="Minorista1", @bloque=#<Proc:0xb7347058@(irb):77>
5 
6 >> cpd.precio_de_venta(100)
7 "Precio de venta 150.0 BsF." 
8 => nil
9 >> cpm.precio_de_venta(100)
10 "Precio de venta 180.0 BsF." 
11 => nil

En el ejemplo hemos introducido el uso del método call de Proc para invocar la ejecución correspondiente y con los argumentos necesarios. Tendremos más que decir acerca de Proc en futuras ediciones.

Por supuesto que esta funcionalidad puede implementarse de otras maneras, muchas otras maneras, el ejemplo lo que demuestra sutilmente es que también es posible reusar bloques de código por medio del uso de los bloques asociados a las llamadas de los métodos, es decir, puede por ejemplo compartir un bloque de código con más de una definición de método en su aplicación de hecho reduciendo y reusando el código por medio de funcionalidad común no representaba en clases o módulos. Esto igualmente puede hacerse en tiempo de ejecución, es decir, código que genera código y que lo pasa otro código para que lo ejecute. Si está confundido es porque está comenzando a experimentar el fuerte componente de dinamismo que tiene Ruby, no se asuste y abrace el poder que de esto se deriva.

Invocando los métodos

Usted puede invocar o "llamar" un método especificando un "receptor" (objeto), el nombre del método, y opcionalmente algunos parámetros y un bloque asociado. Para el caso de los métodos de clase y módulo, el receptor sería el nombre de clase o módulo.

1 File.size("archivo_grande.mpeg") 2 Math.sin(Math::PI/3)

Si se omite el receptor, se usa self, el objeto actual:

1 >> self.class
2 => Object
3 >> self.hash
4 => -608576338
5 >> hash
6 => -608576338
7 >> self.object_id
8 => -608576338
9 >> object_id
10 => -608576338

Note que he evitado intencionalmente llamar a class omitiendo el receptor, y ello es porque provoca un error, class es ambiguo, es una palabra clave (keyword) y Ruby cree que usted está tratando de definir una clase, no intenta self.class. En estos casos, use self.class, en general es mejor explicitar el receptor, es más legible y potencialmente evitará estas ambigüedades difíciles de depurar.

También observe con cuidado que no es igual Object.hash a self.hash ya que self es una instancia de Object en el ejemplo y Object.hash es un método de clase, referencian a hash diferentes, uno a la instancia (self), y el otro a la clase Object.

1 >> self.class
2 => Object
3 >> self.object_id
4 => -608576338
5 >> Object.object_id
6 => -608573798

Los paréntesis en la llamada al método para listar los argumentos son opcionales, sin embargo para evitar ambigüedades, le recomendamos fuertemente que siempre use los paréntesis, tal vez sólo en los casos más simples puede evitarse.

Expandiendo arreglos en las llamadas

De forma similar a la invocación con asterisco, en reversa, al invocar un método es posible "expandir" un arreglo para que cada elemento sea tomado como elemento individual.

1   def cinco(a,b,c,d,e)
2     "Argumentos #{a} #{b} #{c} #{d} #{e}" 
3   end
1 >> cinco("un", *["dos", "tres" , "cuatro", "cinco"])
2 => "Argumentos un dos tres cuatro cinco" 
3 >> cinco(*["un", "dos", "tres" , "cuatro", "cinco"])
4 => "Argumentos un dos tres cuatro cinco" 
5 >> cinco("un", *["dos", "tres" , "cuatro", "cinco"])
6 => "Argumentos un dos tres cuatro cinco" 
7 >> cinco("un", *["dos", "tres"] , "cuatro", "cinco")     # Debe ser el último argumento
8 SyntaxError: compile error

Argumentos opcionales

Usando el mismo ejemplo de arriba, intente pasar menos de cinco argumentos:

1 >> cinco
2 ArgumentError: wrong number of arguments (0 for 5)

Los cinco argumentos son obligatorios, una forma de convertirlos en opcionales es asignarles un valor por omisión nil.

1   def cinco(a=nil,b=nil,c=nil,d=nil,e=nil)
2     "Argumentos #{a} #{b} #{c} #{d} #{e}" 
3   end
1 >> cinco
2 => "Argumentos     " 
3 >> cinco("uno")
4 => "Argumentos uno    " 

Sin embargo note que no puede especificar el tercer argumento sin especificar el segundo, debe respetar la secuencialidad en la definición de los argumentos. Veamos la siguiente versión revisada para visualizar los argumentos pasados al método.

1   def cinco(a=nil,b=nil,c=nil,d=nil,e=nil)
2     "Argumentos 1:#{a} 2:#{b} 3:#{c} 4:#{d} 5:#{e}" 
3   end

Use nil para saltar al próximo argumento en la lista cuanda haga la invocación del método.

1 >> cinco
2 => "Argumentos 1: 2: 3: 4: 5:" 
3 >> cinco("A",nil,nil,"D")
4 => "Argumentos 1:A 2: 3: 4:D 5:" 

Simulando argumentos por nombres: hash

Si bien Ruby no tiene un verdadero soporte para "keyword arguments", es decir, pasar argumentos con su nombre y valor, nada nos impide simular dicho comportamiento usando un hash. Ruby incluso tiene un pequeño atajo que nos evita la sintáxis Hash {} para evitarnos confundir con un bloque asociado. El hash debe estar especificado de último en la lista de argumentos.

1 def hash_params(params)
2   p "ID: #{params[:id]}" 
3   p "Clave: #{params[:clave]}" 
4 end
code class="ruby">
>> hash_params(:id => "XXX" , :clave => "Z A Z Z")
"ID: XXX" 
"Clave: Z A Z Z" 
=> nil
>> hash_params(:clave => "1 2 3 4", :id => "007")
"ID: 007" 
"Clave: 1 2 3 4" 
=> nil
>> hash_params({:clave => "1 2 3 4", :id => "007"})   # Sintaxis estricta con las {}
"ID: 007" 
"Clave: 1 2 3 4" 
Loading...
Fudcon-panama-2011_728x90_leaderboard_rotated
Powered by Redmine Poder_fedora Poder_ruby Haproxy Powered_by_apache