R: Ruby asocia dinámicamente todos los mensajes a los método. Busca primero los métodos singleton del receptor, luego los métodos definidos en su propia clase y por último los definidos en sus superclases (incluído cualquier módulo que se haya incorporado). Se puede ver el orden de búsqueda mostrando por pantalla Class-name.ancestors, que contiene las clases y módulos ancestros de Class-name.
Si después de buscar en las alternativas no se encuentra ningún método que corresponda con el mensaje, Ruby intenta ejecutar un método llamado method_missing, repitiendo el mismo procedimiento de búsqueda descrito. Esto hace que sea posible tratar los mensajes com métodos desconocidos y se utiliza con frecuencia pra proporcionar interfaces dinámicos a clases.
module Indexed def [] (n) to_a[n] end end class String include Indexed end String.ancestors # -> [String, Indexed, Enumerable, Comparable, Object, Kernel] "abcde".gsub!(/./,"\\&\n")[1] # -> 10 |
Este programa no devuelve ""b\n"" como cabría esperar, sino 10. Cuando se busca el método [], se encuentra en la clase String, que se encuentra antes de Indexed. Se debería redefinir directamente [] en la clase String.
R: +, - y similares no son operadores, son llamadas a métodos. Por lo tanto se pueden sobrecargar con nuevas definiciones.
class MyString < String
def -(other) # Nuevo método
self[0...other.size] # devuelve el propio objeto truncado al
# tamaño de "other"
end
end
|
Sin embargo las siguientes son estructuras de control incluídas, no métodos y por lo tanto no se pueden sobrecargar.
=, .., ..., !, not, ||, &&, and, or, :: |
Para sobrecargar o definir operadores unarios, se pueden utiliza los nombres de método siguientes +@ y -@.
= define un método para asignar un atributo a un objeto.
class Test def attribute=(var) @attribute = val end end t = Test.new t.attribute = 1 |
Si se definen operadores como + y -, Ruby maneja automaticamente las formas +=, -= y similares.
R: Ruby no tiene los operadores de autoincremento y autodecremento. Se pueden utilizar += 1 y -= 1.
R: Si y no, Ruby tiene métodos que se comportan como funciones de lenguajes como C o Perl.
def writeln(str) print (str,"\n") end writeln("hola mundo!") |
Hola mundo! |
Sin embargo se tratan de llamadas a procedimientos en las que se omite el receptor, Ruby asume que es self. Por lo tanto, writeln imita a una función pero realmente se trata de un método que pertenece a la clase Object y se envía como mensaje al receptor oculto self. Ruby es un lenguaje orientado a objetos puro.
Por supuesto se pueden utilizar estos métodos como si fuesen funciones.
R: Todas las clases de Ruby derivan de la clase Object. La definición de la clase incluye (mix-in) los métodos definidos en el móduflo Kernel. Por lo tanto estos métodos están disponibles para todos los objetos del sistema.
Incluso si se escribe un programa Ruby muy sencillo, sin clases, se está trabajando realmente dentro de la clase Object.
R: Las variables de instancia de un objeto (aquella precedidas de @) no se pueden acceder fuera del objeto. Esto favorece una buena encapsulación. Sin embargo Ruby facilita la definición de accesores a estas variables de instancia de tal forma que los usuarios de la clase puedan utilizar estas variables como atributos. Basta con utilizar de Module.attr uno de los siguientes métodos attr_reader, attr_writer o attr_accesor.
class Persona attr :nombre #sólo lectura attr_accesor :usa_sombrero #lectura/escritura def initialize(nombre) @nombre = nombre end end p = Persona.new("David") p.name #-> "David" p.usa_sombrero #-> nil p.usa_sombrero = true p.usa_sombrero #-> true |
También es posible definir funciones de acceso propias (quizás para realizar validaciones o manejar atributos derivados). Un accesor de lectura es realmente un método que no tiene parámetros y un accesor de asignación es un método que termina en = y tiene uno. Aunque no puede haber espacio entre el nombre del método y el = en su definición, se pueden insertar en la llamada al método, dando una apariencia semejante a la de cualquier otra asignación. También es posible utilizar autoasignaciones como += y -= siempre que se hayan definido los correspondientes métodos + y -.
R: La palabra clave private, que controla la visibilidad, hace que un método sólo se pueda ejecutar en formato función , es decir que sólo pueda tener self como receptor. A un método privado sólo le puede utilizar la clase que lo ha definido o cualquiera de sus descendientes.
class Test def func return 99 end def test(other) p func p other.func end end t1 = Test.new t2 = Test.new t1.test(t2) #Ahora hacemos privada 'func' class Test private :func end t1.test(t2) |
99 99 99 prog.rb:7:in 'test': private method 'func' called for #<Test:0x4018d400> (NameError) from prog.rb:21 |
Los métodos protegidos sólo se pueden utilizar en su propia clase o descendientes pero, utilizando tanto el formato de función o de método. Por ejemplo,
def <=>(other) age <=> other.age end |
Se ejecutará si age es un método protegido pero no si es privado.
Estas funciones permiten controlar el acceso a la parte interna de la clase.
R: Se puede modificar la visibilidad de un método utilizando private, protected y public. Cuando se utilizan sin parámetros en la definición de una clase, afectan a todos los métodos que les sigan. Utilizada con parámetros, modifican la visibilidad de los métodos listados.
class Foo def test print "hola\n" end private :test end foo = Foo.new foo.test |
prog.rb:9: private method 'test' called for #<Foo:0x4018d7e8> (NameError) |
class Foo def Foo.test print "hola\n" end private_class_method :test end Foo.test |
prog.rb:8 private method 'test' called for Foo:Class (NameError) |
La visibilidad por defecto definida para un método de una clase es pública. La excepción es el método de inicialización de instancias initialize.
Los métodos definidos en el nivel superior son por defecto, ta,bién públicos.xs
R: Si, puede, pero no se puede hacer a la ligera. Si Ruby ve un nombre que comienza con mayúsculas seguido de un espacio, probablemente (y dependiendo del contexto) suponga que se trata de una constante, no un nombre de método. Por lo tanto si se utilizan nombres de métodos que comienzan con letra en mayúsculas hay que recordar que hay que poner la lista de parámetros entre paréntesis, y poner el paréntesis de apertura inmediatamente después del nombre del método sin que haya entre medias espacios en blanco. (Esta última sugerencia es una buenea idea aplicarla siempre).
R: La llamada a super sin parámetros hace que todos los argumentos del método desde que se le invoca se le pasen al método del mismo nombre en la superclase. Si el número de argumentos del método original no concuerdan con el del método del nivel superior, se lanza una excepción ArgumentError. Para evitar esto, basta con llamar a super con el número adecuado de argumentos.
R: super llama al método del mismo nombre que se encuentra un nivel por encima. Si se ha sobrecargado un método en un ancestro más distante, hay que utilizar alias para darle un nuevo nombre antes de enmascararlo con una nueva definición. Luego se le puede llamar utilizando su alias.
R: Dentro de la definición del método, usando super. También es posible darle un nuevo nombre utilizando alias. Por último, es posible llamar al método original como un método singleton del Kernel.
R: Es un método que altera el estado de un objeto. String, Array y Hash u otras clases poseen este tipo de métodos. A menudo existen dos versiones del método, una con el nombre normal y otra con el mismo nombre seguido de una admiración (!). La versión normal realiza una copia del receptor, la modifica y devuelve la copia. La versión con la admiración modifica el receptor.
Hay que estar al tanto de la existencia de un número considerable de métodos destructivos que no tiene la admiración, como los operadores de asignación (name=), las asignaciones a arrays []=) y métodos tales como array.collect.
R: Hay que recordar que en la mayoría de los casos las asignaciones sólo copian referencias a objetos y que el paso de parámetros es equivalente a una asignación. Esto supone que al final se tienen multiples referencias a un mismo objeto. Si una de estas variables se usa para llamar a un método destructivo, todas las referencias al objeto se verán afectadas.
def foo(str) str.sub!(/foo/,"baz") end obj = "foo" foo(obj) # -> "baz" obj # -> "baz" |
R: Si y no.
def m1 return 1, 2, 3 end def m2 return [1, 2, 3] end m1 # -> [1, 2, 3] m2 # -> [1, 2, 3] |
def foo return 20, 4, 7 end a, b, c = foo a # -> 20 b # -> 4 c # -> 7 |