From Fedora Project Wiki

< FWN‎ | Beats
Revision as of 20:18, 23 June 2011 by Pcalarco (talk | contribs)

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, an introduction to bash-completion. Enjoy!

bash-completion

Si usted es administrador de sistemas o desarrollador, y si no ha montado este paquete, tal vez llore de felicidad después de unos minutos de su uso. No haré más comentarios, sólo ejemplos.

  1. yum install bash-completion

service

$ service ht<tab>

alias

$ unalias <tab><tab> egrep fgrep grep l. ll ls mc vi which

yum

$ yum install bas<tab><tab> bashdb.noarch basket-libs.i686 bastet.x86_64 bash-doc.x86_64 basket-libs.x86_64 BasiliskII.x86_64 basket.x86_64

yumdb

$ yumdb rename<tab><tab> rename rename-force

pkcon (PackageKit)

$ pkcon re<tab><tab> refresh repo-disable repo-list resolve remove repo-enable repo-set-data

svn

$ svn c<tab><tab> cat checkout cl co copy changelist ci cleanup commit cp

git

$ git cl<tab><tab> clean clone

modprobe

$ modprobe co<tab><tab> cobra compal-laptop coretemp coda configfs

man

$ man ls<tab><tab> ls lscgroup lseek64 lsof lstat lsame lscpu lset lsort lstat64 lsattr lsdiff lsetfilecon lspci lsusb lsblk lsearch lshal lspcmcia lsb_release lseek lsmod lssubsys

ssh

$ ssh 10.10.10.<tab><tab> 10.10.10.223 10.10.10.226 10.10.10.50 10.10.10.225 10.10.10.233 10.10.10.80

Gomix"

   * Métodos públicos: pueden ser invocados por cualquiera, no se aplica ningún control de acceso. Los métodos son públicos por omisión excepto initiliaze que siempre es privado.
   * Métodos protegidos: sólo pueden ser invocados por objetos de la misma clase o subclases. El acceso se mantiene a la familia.
   * Métodos privados: no pueden ser invocados con un receptor explícito, el receptor siempre es self. Esto significa que los métodos privados sólo pueden ser llamados en el contexto del objeto actual, no se puede invocar los métodos privados de otro objeto.

Una diferencia importante con otros lenguajes orientados a objeto es que el control de acceso es determinado dinámicamente en la medida que el programa se ejecuta. Obtendrá una violación de acceso sólo cuando intente ejecutar el método restringido.

Especificación del control de acceso

Se especifica el nivel de acceso de los métodos dentro de una clase o módulo utilizando una o más de las tres funciones public, protected y private. Puede usar cada función en dos formas diferentes.

Si se usan sin argumentos, las tres funciones definen el control de acceso de los métodos subsiguientes.

1 class MiClase
2     def metodo1    # 'public' por omisión
3       #
4     end
5 
6   protected        # los subsiguientes métodos serán 'protected'
7 
8     def metodo2    
9       #
10     end
11 
12   private          # los subsiguientes métodos serán 'private'
13 
14     def metodo3
15       #
16     end      
17 
18   public           # los subsiguientes métodos serán 'public'
19 
20     def metodo4
21       #
22     end
23 end

De forma alternativa puede simplemente listar los métodos en dichas funciones.

1 class MiClase
2   def metodo1
3     #
4   end
5   # ... y el resto de las definiciones de métodos
6 
7   public    :metodo1, :metodo4
8   protected :metodo2
9   private   :metodo3
10 end

Ejemplo private

El ejemplo abajo muestra un esqueleto para proteger un método peligroso de alteración del estado del objeto por medio del uso de otro método intermediario que se supone asegura el acceso, esto ayuda a mantener el código separado pero simple, uno asegura por ejemplo las credenciales, el otro ajusta el estado del objeto.

1 class Banco
2   def initialize
3     @tasa="10%" 
4   end
5 
6   def tasa
7     @tasa
8   end
9 
10   def interfase_segura(nueva_tasa)  # Se supone que es "segura", llama al método privado
11     # Código de seguridad
12     self.tasa=nueva_tasa
13   end
14 
15 private
16   def tasa=(nueva_tasa)
17     @tasa=nueva_tasa
18   end
19 end

1 >> banco = Banco.new
2 => #<Banco:0xb747e318 @tasa="10%">
3 >> banco.tasa
4 => "10%" 
5 >> banco.interfase_segura("20%")
6 => "20%" 
7 >> banco.tasa
8 => "20%" 
9 >> banco.tasa="30%" 
10 NoMethodError: private method `tasa=' called for #<Banco:0xb747e318 @tasa="20%">
11     from (irb):63
12 

Ejemplo protected

Por definición este control de acceso limita el acceso a la familia, objetos de la misma clase, y objetos de clases derivadas de la clase que define el método protegido. Abajo un ejemplo simple para comparar manzanas con manzanas, no con peras.

1 class Manzana
2   def initialize(peso)
3     @peso = peso
4   end
5 
6   def <=>(otra_manzana)
7     self.peso <=> otra_manzana.peso
8   end
9 
10   protected
11 
12   def peso
13     @peso
14   end
15 end
16 
17 class Pera
18   def initialize(peso)
19     @peso = peso
20   end
21 
22   def <=>(otra_pera)
23     self.peso <=> otra_pera.peso
24   end
25 
26   protected 
27 
28   def peso
29     @peso
30   end
31 end

1 >> m1 = Manzana.new(100)
2 => #<Manzana:0xb74838e0 @peso=100>
3 >> m2 = Manzana.new(200)
4 => #<Manzana:0xb747dda0 @peso=200>
5 >> p1 = Pera.new(50)
6 => #<Pera:0xb74783c8 @peso=50>
7 >> m1 <=> m2
8 => -1
9 >> m1 <=> p1
10 NoMethodError: protected method peso' called for #<Pera:0xb74783c8 @peso=50>
11     from ./manzanas.rb:7:in <=>'
12     from (irb):11
13 

Al intentar hacer la comparación entre manzana y pera, se invoca pera.peso en el contexto de las manzanas, y ya que el método está protegido, entonces da el error.

Ahora bien, esta idea de uso para protected en realidad está en franco desuso por muchos ya que va en contra de la flexibilidad y dinamismo natural de Ruby y del concepto de Duck Typing que explicaremos a continuación.

Duck Typing

En Ruby no declaramos tipos de variables o tipos para los métodos, todo es simplemente alguna encarnación, un objeto de una clase, y las clases no son tipos. Si deseamos programar con la filosofía Duck Typing lo único que necesita recordar es que el "tipo" de objeto está determinado por lo que puede hacer, no por su clase.

En la práctica esto significa que hay pocas pruebas de valores de objeto. Por ejemplo digamos que estamos programando un método para agregar información de dos objetos de cierta clase y obtener un resultado String. Los programadores con conocmientos de C# o Java estarían tentados a hacer algo como lo siguiente:

1 def anexar(obj1,obj2)
2   # probar que se nos han dado lo parámetros correctos
3   unless obj1.kind_of?(String)
4     fail TypeError.new("Se espera String")
5   end
6   unless obj2.kind_of?(String)
7     fail TypeError.new("Se espera String")
8   end
9   obj1 << obj2
10 end

1 >> anexar("Hola", " Mundo")
2 => "Hola Mundo" 
3 >> anexar("Hola", 1)
4 TypeError: Se espera String
5     from ./dt.rb:7:in anexar'
6 

Si abraza la filosofía Duck Typing de Ruby podrá escribir este método de una forma mucho más simple.

1 def anexar(obj1,obj2)
2   obj1 << obj2
3 end

Usted no necesita verificar el tipo de los argumentos en tanto que se soporte el método << en obj1 simplemente todo funcionará bien. Si no es así, se lanzará una excepción de todas maneras, el mismo resultado de que si usted hubiera implementado la verificación. Pero de pronto su método es mucho más flexible, puede pasarle otros objetos no necesariamente String, tal vez un Fixnum.

1 >> anexar("Hola", 1)
2 => "Hola\001" 

¿Qué pasa si obj1 no soporta el método << entonces?

1 >> anexar(1.2, " dos ")
2 NoMethodError: undefined method <<' for 1.2:Float
3     from (irb):6:in `anexar'
4     from (irb):13
5 

Obtenemos una excepción NoMethodError para el método << para la clase Float en este ejemplo. Una forma de prevenir posible es usar el método respond_to?:

1 def anexar(obj1,obj2)
2   # probar que se nos han dado lo par ámetros correctos
3   unless obj1.respond_to?(:<<)
4     fail TypeError.new("'obj1' necesita la capacidad <<")
5   end
6 
7   obj1 << obj2
8 end

Pero nuevamente esto es más código que mantener y hay que evaluar si realmente quiere tomar ese trabajo ya que igualmente podría manejar las excepciones más arriba.

Si ahora pasa un Array como obj2 obtendrá un error de tipo ya que Array no es un String pero si podemos invocar el método to_s para obtener una representación razonable. En última instancia lo que queremos es que nuestro método si nos devuelva un String.

1 >> arreglo = [0,1,2]
2 => [0, 1, 2]
3 >> anexar("Hola", arreglo.to_s)
4 => "Hola012" 

Esto nos lleva a una nueva versión del método anexar, usando to_s para "convertir" los objetos en String, ahora ya tampoco necesitamos la verificación respond_to? ya que String responde a << . Tal vez ahora lo que necesita es validar es que tengan una representación en String, es decir, que respondan a to_s.

1 def anexar(obj1, obj2)
2   # probar que se nos han dado lo parámetros correctos
3   unless obj1.respond_to?(:to_s)
4     fail TypeError.new("Se espera que responda a to_s")
5   end
6   unless obj2.respond_to?(:to_s)
7     fail TypeError.new("Se espera que responda a to_s")
8   end
9 
10   obj1.to_s << obj2.to_s
11 end

El código de prueba puede llevarle nuevamente a situaciones indeseables. ¿Soporta obj1 << pero no to_s? Es mejor lidiar con las excepciones que ponerse a probar los tipos y/o los métodos a los que responde. Con esta nueva versión, podemos por ejemplo pasarle dos arreglos, mejor dicho, cualquier par de objetos que tenga una representación String (to_s) y concatenarlos y obtener una salida en String.

1 >> arreglo = [0,1,2]
2 => [0, 1, 2]
3 >> anexar(arreglo, arreglo)
4 => "012012" 

Ahora veamos nuestro método en acción con varios objetos involucrados:

1 >> anexar("Hola", " Mundo")                           ; dos String
2 => "Hola Mundo" 
3 >> anexar(1, " Mundo")                                ; Fixnum + String
4 => "1 Mundo" 
5 >> anexar(1, 1)                                       ; dos Fixnum
6 => "11" 
7 >> anexar(1, 1.1)                                     ; Fixnum + Float
8 => "11.1"    
9 >> anexar(1, [1,2," :) "])                            ; Fixnum + Array
10 => "112 :) " 
11 >> anexar({1=> "mundo", 2 => "hola"}, [1,2," :) "])   ; Hash + Array
12 => "1mundo2hola12 :) " 

De lo único que debe preocuparse en su clase particular es que soporte to_s para tomar ventaja de nuestro método, si no fallará con una excepción. Veamos:

1 >> class Miclase
2 >> end
3 => nil
4 >> miobj = Miclase.new
5 => #<Miclase:0x7fb2a1b2c770>
6 >> anexar("Uh?", miobj)
7 => "Uh?#<Miclase:0x7fb2a1b2c770>"           ; to_s ya existe!
8 >> anexar(miobj, "¡Ah!")
9 => "#<Miclase:0x7fb2a1b2c770>\302\241Ah!"   ; to_s ya existe...

Por supuesto puede sobrescribir el método to_s heredado de Object.