[Ror-es] #include en ruby
Xavier Noria
fxn at hashref.com
Mon Sep 25 21:39:13 GMT 2006
On Sep 25, 2006, at 8:03 PM, Esteban wrote:
> Magnífica solución: funcional, sencilla, elegante, ruby-idiomática
Estupendo, me alegro de que te guste :-).
> Claro también me gustaría entender por qué funciona, un poco de
> teoría de Ruby ya sabes, así que te disparo unas preguntas si no te
> molesta
>
> 1. self.abstract_class, le asigna un valor a una variable de clase
> (o un setter de clase), es este un atributo de ActiveRecord o de
> Object?
abstract_class es un atributo de la clase ActiveRecord::Base, esta
definido en el archivo activerecord/lib/active_record/base.rb:
# Set this to true if this is an abstract class (see
#abstract_class?).
attr_accessor :abstract_class
Dada un clase persistente, klass, AR necesita determinar de donde
leer su metadata, para ello usa el metodo de clase base_clase, y como
se ve ignora las clases abstractas:
# Returns the base AR subclass that this class descends from. If A
# extends AR::Base, A.base_class will return A. If B descends from A
# through some arbitrarily deep hierarchy, B.base_class will
return A.
def base_class
class_of_active_record_descendant(self)
end
# Returns the class descending directly from ActiveRecord in the
inheritance hierarchy.
def class_of_active_record_descendant(klass)
if klass.superclass == Base || klass.superclass.abstract_class?
klass
elsif klass.superclass.nil?
raise ActiveRecordError, "#{name} doesn't belong in a
hierarchy descending from ActiveRecord"
else
class_of_active_record_descendant(klass.superclass)
end
end
> 2. A has_* y validates_* les llamas métodos estáticos, vengo de C++
> y Java, ahí eso equivale a un método de clase, que significa en Ruby
Eso mismo, son metodos de clase. Cuando en Ruby defines un metodo
"foo" de este modo
class Foo
def self.foo
# ...
end
end
estas definiendo un metodo de clase y puedes invocarlo de este modo:
Foo.foo
Hay idiomas alternativos para definir metodos de clase pero ese es
para mi el mas evidente. Hay todo un tema aqui muy chulo para
estudiar para saber exactamente como va esto, ahi intervienen los
conceptos de metodo singleton, que las clases son instancias de
Class, que todo esta basado en envio de mensajes, etc. Eso esta muy
bien explicado en el Pickaxe y en Ruby for Rails.
Por cierto, una de las diferencias con los metodos estaticos de Java
es que no puedes invocar metodos de clase sobre instancias de dicha
clase:
irb(main):001:0> class Foo; def self.foo; true; end; end
=> nil
irb(main):002:0> Foo.foo
=> true
irb(main):003:0> f = Foo.new
=> #<Foo:0x35a618>
irb(main):004:0> f.foo
NoMethodError: undefined method `foo' for #<Foo:0x35a618>
from (irb):4
from :0
OK, pues sucede que ActiveRecord::Base define montones de metodos de
clase, por ejemplo has_many es uno.
Cuando el codigo que define una clase se evalua, "self" es la clase
misma, y cuando se esta en la definicion de un metodo, "self" es el
objeto al cual se esta enviando el mensaje. Si el metodo es de clase
"self" dentro del metodo sigue siendo la clase, siempre es el
receptor del mensaje.
Un dato importante es que el codigo que define una clase se ejecuta
*al interpretar la definicion de la clase*:
irb(main):005:0> class Foo; puts "foo"; end
foo
=> nil
Ves que se imprime "foo"? En Java se podria medio emular eso con
bloques static { ... }.
Por ello, cuando escribes
class User < ActiveRecord::Base
has_many :posts
end
se ejecuta el metodo de clase has_many, con el simbolo :posts como
argumento, al procesarse la definicion de la clase User. Como no se
explicita el receptor este es self por definicion, y en ese punto
self es User. Lo interesante de esto es que como justamente has_many
se evalua al interpretar la definicion de User tenemos ahi la
oportunidad de realizar metaprogramacion. Para cuando vayamos a usar
la clase todos los metodos de coleccion existen como si los hubieras
picado de verdad.
Todo eso junto explica como funciona la solucion que envie:
class Abstract < ActiveRecord::Base
self.abstract_class = true
def self.setup_common_associations
has_many :foos
has_one :bar
end
end
class X < Abstract
setup_common_associations
end
Hemos definido un metodo de clase en Abstract. Como en cualquier
metodo su cuerpo se ejecuta solo al ser invocado.
Al interpretarse la definicion de X hay una llamada a
setup_common_associations en el top-level, por tanto dicha llamada se
realiza. La ejecucion del metodo tiene as su vez una llamada has_many
(:foos), cuyo receptor es self, que en ese momento es X. Por lo
tanto, la metaprogramacion se realiza sobre X, del mismo modo que se
haria si hubieras escrito ese has_many normal en el top-level de X.
Lo verdaderamente importante es que conseguimos que X sea el receptor
de ese codigo.
Que tal explicado asi?
-- fxn
More information about the Ror-es
mailing list