|
|||
Class, que contiene
todo lo relacionado con el objeto mas una lista de los m?dos y una
referencia a la superclase (que es otra clase en s?isma). Todas las
llamadas de m?dos en Ruby nombran a un receptor (que de manera
predeterminada es self, el objeto en curso).
Ruby encuentra el m?do de invocarlo buscando en la lista de m?dos
en la clase del receptor. De no hallarlo all?se fija en la
superclase, y luego en la superclase de la superclase, etc. Si el
m?do no puede ser hallado en la clase del receptor o en sus
ancestros, Ruby invoca el m?do method_missing en el
receptor original.
Y eso es todo, explicaci??erminada. Vamos al siguiente cap?lo.
''Espera'' chillas t?gast?i dinero en ?e cap?lo. Que hay del
resto? Las clases singleton, los m?dos de clases y todo eso. Como
funcionan?''
Excelente pregunta.
lucille, la clase del objeto,
Guitar, y la superclase de la clase,
Object. N??e como la referencia de clase del
objeto (llamada klass por razones hist??as que realmente
dej??capar Andy) apunta al objeto de la clase, y c??el
super
puntero desde esa clase hace referencia a la clase padre.
| Figure not available... |
Guitar.strings(), sigue el mismo proceso
anterior: va hacia el receptor, la clase
Guitar, sigue referencia de klass a la
clase Guitar$'$, y encuentra el m?do.
Finalmente, n??e que un ``S'' se ha deslizado en las flags en la
claseGuitar$'$. Las clases creadas
autom?camente se marcan internamente como clases singleton.
+stas son tratadas de manera ligeramente diferente dentro de Ruby. La
diferencia m?obvia es que son efectivamente invisibles: nunca
aparecer?en la lista de objetos devueltos por m?dos como
Module#ancestors
o
ObjectSpace::each_object
.
String. Asociamos una clase an??a con alguno
de ellos, sobrecargando uno de los m?dos en la clase base del objeto
y agregando un m?do nuevo.
a = "hello"
|
||
b = a.dup
|
||
|
||
class <<a
|
||
def to_s
|
||
"The value is '#{self}'"
|
||
end
|
||
def twoTimes
|
||
self + self
|
||
end
|
||
end
|
||
|
||
a.to_s
|
+ |
"The value is 'hello'"
|
a.twoTimes
|
+ |
"hellohello"
|
b.to_s
|
+ |
"hello"
|
class <<
obj''
la cual b?camente dice ``construye una clase nueva s??para el
objeto obj.'' Tambi?podr?os haberlo escrito as?
a = "hello"
|
||
b = a.dup
|
||
def a.to_s
|
||
"The value is '#{self}'"
|
||
end
|
||
def a.twoTimes
|
||
self + self
|
||
end
|
||
|
||
a.to_s
|
+ |
"The value is 'hello'"
|
a.twoTimes
|
+ |
"hellohello"
|
b.to_s
|
+ |
"hello"
|
a''. Esto nos da una se??clara acerca de la
implementaci??n Ruby: se crea e inserta una clase singleton como
clase directa de a. La clase original de a,
String, se vuelve la superclase de esta
singleton. Las im?nes anterior y posterior se muestran en la
figura 19.3 en pag.242.
Ruby desarrolla un ligera optimizaci??on las clases singleton. Si la
referencia a klass de un objeto ya apunta a una clase
singleton, se crea una nueva. Esto significa que el primero de las
dos definiciones de m?dos en el ejemplo previo crear?na clase
singleton, pero el segundo simplemente le agregar?n m?do a
aquella.
| Figure not available... |
module SillyModule
|
||
def hello
|
||
"Hello."
|
||
end
|
||
end
|
||
class SillyClass
|
||
include SillyModule
|
||
end
|
||
s = SillyClass.new
|
||
s.hello
|
+ |
"Hello."
|
module SillyModule
|
||
def hello
|
||
"Hi, there!"
|
||
end
|
||
end
|
||
s.hello
|
+ |
"Hi, there!"
|
| Figure not available... |
class <<obj
'', puedes mezclar
un m??o dentro un objeto utilizando
Object#extend
. Por ejemplo:
module Humor
|
||
def tickle
|
||
"hee, hee!"
|
||
end
|
||
end
|
||
|
||
a = "Grouchy"
|
||
a.extend Humor
|
||
a.tickle
|
+ |
"hee, hee!"
|
extend. Si lo usas dentro de
una definici??e clase, los m?dos del m??o se vuelven m?dos de
clase.
module Humor
|
||
def tickle
|
||
"hee, hee!"
|
||
end
|
||
end
|
||
|
||
class Grouchy
|
||
include Humor
|
||
extend Humor
|
||
end
|
||
|
||
Grouchy.tickle
|
+ |
"hee, hee!"
|
a = Grouchy.new
|
||
a.tickle
|
+ |
"hee, hee!"
|
extend equivale a
self.extend, entonces los m?dos se a??n a
self, el cual en una definici??e clases es la clase en s?
misma.
class MediaPlayer include Tracing if $DEBUGGING if ::EXPORT_VERSION def decrypt(stream) raise "Decryption not available" end else def decrypt(stream) # ... end end end |
self debe
referenciar algo. Veamos un ejemplo:
class Test
puts "Type of self = #{self.type}"
puts "Name of self = #{self.name}"
end
|
Type of self = Class Name of self = Test |
class Test
def Test.sayHello
puts "Hello from #{name}"
end
sayHello
end
|
Hello from Test |
Test.sayHello, y lo llamamos
en el cuerpo de la definici??e la clase. Dentro de
sayHello, llamamos a name, un m?do de
instancia de la clase Module. Dado que
Module es un ancestro de
Class, sus m?dos de instancia se pueden
llamar sin un receptor expl?to dentro de la definici??e clase.
De hecho, muchas de las directivas que utilizas al definir una clase o
un m??o, cuestiones como alias_method,
attr, y
public, son simples m?dos de la clase
Module. Esto abre posibilidades muy
interesantes; se puede extender la funcionalidad de las definiciones
de clases y m??os escribiendo c??o Ruby. Veamos algunos ejemplos.
Como primer ejemplo, veamos c??agregar una facilidad de
documentaci???ca a los m??os y clases. Esto nos permitir? asociar una cadena con m??os y clases que hemos escrito, cadena
que ser?ccesible al momento de ejecuci??el programa. Elegiremos
una sintaxis simple.
class Example doc "This is a sample documentation string" # .. rest of class end |
doc a cualquier m??o o clase,
entonces, debemos crear un m?do de instancia de la clase
Module.
class Module
@@docs = Hash.new(nil)
def doc(str)
@@docs[self.name] = str
end
def Module::doc(aClass)
# If we're passed a class or module, convert to string
# ('<=' for classes checks for same class or subtype)
aClass = aClass.name if aClass.type <= Module
@@docs[aClass] || "No documentation for #{aClass}"
end
end
class Example
doc "This is a sample documentation string"
# .. rest of class
end
module Another
doc <<-edoc
And this is a documentation string
in a module
edoc
# rest of module
end
puts Module::doc(Example)
puts Module::doc("Another")
|
This is a sample documentation string And this is a documentation string in a module |
date de Tadayoshi Funaba (descripto al comienzo de
pag. 439). Digamos que tenemos una clase que representa alguna
cantidad (en ?e caso, una fecha). La clase debe incorporar muchos
atributos que presenten los datos implicados en diferentes
maneras: como d? Julianos, como cadena, como [a??mes, d?,
etc. Cada valor representa la misma fecha y puede significar la
necesidad de realizar c?ulos complejos. Entonces, queremos
calcular cada atributo s??una vez: al accesarlo por primera vez.
La forma manual ser?agregar una prueba a cada accesor:
class ExampleDate def initialize(dayNumber) @dayNumber = dayNumber end def asDayNumber @dayNumber end def asString unless @string # complex calculation @string = result end @string end def asYMD unless @ymd # another calculation @ymd = [ y, m, d ] end @ymd end # ... end |
once de Eiffel.
Quisi?mos poder hacer algo como:
class ExampleDate def asDayNumber @dayNumber end def asString # complex calculation end def asYMD # another calculation [ y, m, d ] end once :asString, :asYMD end |
once como directiva escribi?olo como un m?do
de clase de ExampleDate, pero c??se ver??o
internamente? El truco es hacerlo reescribir los m?dos cuyos nombres
se le env?. Para cada m?do, crea un alias del c??o original,
luego crea un nuevo m?do con id?ico nombre. +ste nuevo m?do hace dos
cosas. Primero, invoca al m?do original (usando los alias) y guarda
el valor resultante en una variable de instancia. Segundo, se redefine a
s?ismo, as?en las pr??as llamadas simplemente devolver?l valor
de la variable de instancia directamente. El c??o de Tadayoshi
Funaba, ligeramente modificado.
def ExampleDate.once(*ids)
for id in ids
module_eval <<-"end_eval"
alias_method :__#{id.to_i}__, #{id.inspect}
def #{id.id2name}(*args, &block)
def self.#{id.id2name}(*args, &block)
@__#{id.to_i}__
end
@__#{id.to_i}__ = __#{id.to_i}__(*args, &block)
end
end_eval
end
end
|
module_eval para ejecutar un bloque de
c??o en el contexto del m??o que llama (o, en ?e caso, la clase que
llama). El m?do original es renombrado a __nnn__, siendo
nnn la representaci??ntera del s?olo id del nombre del
m?do. El c??o usa el mismo nombre para la variable de instancia cacheada.
La mayor parte del c??o es un m?do que din?camente se redefine a
s?ismo. N??e que esta redefinici??tiliza el hecho que el m?do
puede contener definiciones de m?dos singleton anidadas, un truco
astuto.
Comprende ?e c??o, y estar?en camino de la verdadera maestr?en
Ruby.
Sin embargo, podemos llegar m?lejos. Mira en el m??o
date, y ver?el m?do once escrito de manera
ligeramente diferente.
class Date class << self def once(*ids) # ... end end # ... end |
class << self''. Esto define una clase
basada en el objeto self, y self parece
ser la clase objeto para Date. El resultado?
Cada m?do dentro de la definici??nterna de clase es
autom?camente un m?do de clase de Date.
La caracter?ica once es generalmente aplicable y
deber?funcionar para cualquier clase. Si tomaste once y
la hiciste un m?do de instancia privada de la clase
Module, deber?estar disponible para usarse en
cualquier clase Ruby.
Class. Cuando dices
algo como String.new("gumby"), est?enviando un mensaje
new al objeto que es la clase
String. Pero c??sabe Ruby hacer ?o? Despu?
de todo, el receptor del mensaje deber?ser una referencia de objeto,
lo cual implica que debe haber una constante llamada "String" en
alg?gar conteniendo una referencia al objeto
String.[ Ser?na constante, no
variable, porque "String" comienza con min?as.]
Y de hecho, esto es lo que ocurre exactamente. Todas las clases
incluidas, junto con las clases que defines, tienen una constate
global correspondiente con el mismo nombre de la clase. Esto es al
mismo tiempo, sutil y directo. La parte sutil proviene del hecho que
existen 2 cosas llamadas, por ejemplo, String
en el sistema. Hay una constant que hace referencia a un
objeto de clase String, y est?l objeto
propiamente dicho.
EL hecho de que los nombres de clase sean s??constantes, significa
que debes tratar las clases como cualquier objeto Ruby: puedes
copiarlos, pasarlos a m?dos y utilizarlos en expresiones.
def factory(klass, *args)
|
||
klass.new(*args)
|
||
end
|
||
|
||
factory(String, "Hello")
|
+ |
"Hello"
|
factory(Dir, ".")
|
+ |
#<Dir:0x401b51bc>
|
|
||
flag = true
|
||
(flag ? Array : Hash)[1, 2, 3, 4]
|
+ |
[1, 2, 3, 4]
|
flag = false
|
||
(flag ? Array : Hash)[1, 2, 3, 4]
|
+ |
{1=>2, 3=>4}
|
puts "Hello, World" |
"Hello, World" genera un
String Ruby, o sea que hay un objeto. Tambi?
sabemos que el m?do simple de puts es efectivamente
lo mismo que self.puts. Pero qu?s ``self''?
self.type
|
+ |
Object
|
Object, podemos usar todos los
m?dos de Object (incluyendo aquellos mixtos
desde Kernel) en formularios de
funci??Esto explica porqu?odemos llamar m?dos
Kernel tal como puts a alto
nivel (y de hecho a trav?de Ruby): ?os m?dos son parte de
cada objeto.
class Base def aMethod puts "Got here" end private :aMethod end class Derived1 < Base public :aMethod end class Derived2 < Base end |
aMethod en
instancias de clase Derived1, pero no a trav?
de instancias de Base o
Derived2.
Entonces, c??soluciona Ruby el hecho de tener un m?do con dos
visibilidades diferentes. Poni?olo, simple, te enga??
Si una subclase cambia la visibilidad de un m?do en un padre. Ruby
efectivamente inserta un m?do proxy oculto en la subclase que invoca
al m?do original, usando super. Entonces ajusta la
visiblidad de ese proxy a lo que sea que hayas requerido. Esto
significa que el c??o:
class Derived1 < Base public :aMethod end |
class Derived1 < Base def aMethod(*args) super end public :aMethod end |
super puede acceder al m?do del padre,
independientemente de su visibilidad, entonces la reescritura permite
a la subclase sobrecargar las reglas de visibilidad de su padre. Algo
temible no?
Object#freeze
.
Un objeto congelado no puede ser modificado: no puedes modificar sus
variables de instancia (directa o indirectamente), no puedes asociarle
m?dos singleton, y, si es una clase o m??o, no puedes agregar,
borrar o modificar sus m?dos. Una vez congelado, un objeto permanece
as?no hay un
Object#thaw
.
(N.del T: Thaw: descongelar).
Puedes verificar si un objeto est?ongelado mediante
Object#frozen?
.
Qu?ucede al copiar un objeto congelado? Eso depende en el m?do que
uses. Si llamas a un m?do de objeto clone, el estado
de todo el objeto (incluyendo si est? no congelado) se copia el
nuevo objeto. Por otro lado dup t?camente copia s??
los contenidos del objeto, la nueva copia no heredar?l estado de
congelamiento.
str1 = "hello"
|
||
str1.freeze
|
+ |
"hello"
|
str1.frozen?
|
+ |
true
|
str2 = str1.clone
|
||
str2.frozen?
|
+ |
true
|
str3 = str1.dup
|
||
str3.frozen?
|
+ |
false
|