Modül dahil etmenin neden Ruby'de olduğu gibi çalıştığını anlamak için gereken metaprogramlama kavramlarını açıklayan tam hikaye.
Bir modül dahil edildiğinde ne olur?
Bir modülün bir sınıfa dahil edilmesi, modülü sınıfın atalarına ekler . ancestors
Yöntemini çağırarak herhangi bir sınıfın veya modülün atalarına bakabilirsiniz :
module M
def foo; "foo"; end
end
class C
include M
def bar; "bar"; end
end
C.ancestors
Bir örneğinde bir yöntem çağırdığınızda C
, Ruby sağlanan ada sahip bir örnek yöntemi bulmak için bu üst öğe listesinin her öğesine bakacaktır . Biz dahil beri M
içine C
, M
şimdi bir atası C
dediğimiz zaman bu kadar, foo
bir örneği C
, Yakut bu yöntemi bulacaksınız M
:
C.new.foo
Not o içerme sınıfına herhangi örneği veya sınıf yöntemleri kopyalamaz - bu sadece o da dahil modülünde örnek yöntemler aramaya gerektiğini sınıfa bir "not" ekler.
Modülümüzdeki "sınıf" yöntemleri ne olacak?
Dahil etme, yalnızca örnek yöntemlerinin gönderilme şeklini değiştirdiğinden, bir modülün bir sınıfa dahil edilmesi, yalnızca örnek yöntemlerinin o sınıfta kullanılabilir olmasını sağlar. Modüldeki "sınıf" yöntemleri ve diğer bildirimler otomatik olarak sınıfa kopyalanmaz:
module M
def instance_method
"foo"
end
def self.class_method
"bar"
end
end
class C
include M
end
M.class_method
C.new.instance_method
C.class_method
Ruby sınıf yöntemlerini nasıl uygular?
Ruby'de, sınıflar ve modüller düz nesnelerdir - bunlar sınıfın örnekleridir Class
ve Module
. Bu, dinamik olarak yeni sınıflar oluşturabileceğiniz, bunları değişkenlere atayabileceğiniz vb. Anlamına gelir:
klass = Class.new do
def foo
"foo"
end
end
klass.new.foo
Ayrıca Ruby'de, nesneler üzerinde tekil yöntemler tanımlama olanağına sahipsiniz . Bu yöntemler , nesnenin özel, gizli tekli sınıfına yeni örnek yöntemler olarak eklenir :
obj = Object.new
def obj.foo
"foo"
end
obj.singleton_class.instance_methods(false)
Ancak sınıflar ve modüller aynı zamanda düz nesneler değil mi? Aslında öyleler! Bu, tekil yöntemlere de sahip olabilecekleri anlamına mı geliyor? Evet öyle! Ve sınıf yöntemleri böyle doğar:
class Abc
end
def Abc.foo
"foo"
end
Abc.singleton_class.instance_methods(false)
Veya, bir sınıf yöntemini tanımlamanın daha yaygın yolu, self
oluşturulan sınıf nesnesine atıfta bulunan sınıf tanımlama bloğu içinde kullanmaktır :
class Abc
def self.foo
"foo"
end
end
Abc.singleton_class.instance_methods(false)
Sınıf yöntemlerini bir modüle nasıl dahil ederim?
Az önce belirlediğimiz gibi, sınıf yöntemleri gerçekten sadece sınıf nesnesinin singleton sınıfındaki örnek yöntemleridir. Bu , bir grup sınıf yöntemi eklemek için singleton sınıfına bir modül dahil edebileceğimiz anlamına mı geliyor ? Evet öyle!
module M
def new_instance_method; "hi"; end
module ClassMethods
def new_class_method; "hello"; end
end
end
class HostKlass
include M
self.singleton_class.include M::ClassMethods
end
HostKlass.new_class_method
Bu self.singleton_class.include M::ClassMethods
satır çok hoş görünmüyor, bu yüzden Ruby de eklendi Object#extend
, ki bu aynı şeyi yapıyor - yani nesnenin singleton sınıfına bir modül içeriyor:
class HostKlass
include M
extend M::ClassMethods
end
HostKlass.singleton_class.included_modules
extend
Çağrıyı modüle taşıma
Bu önceki örnek, iki nedenden ötürü iyi yapılandırılmış kod değildir:
- Şimdi aramak zorunda hem
include
ve extend
de HostClass
bizim modülü düzgün dahil olsun tanımı. Çok sayıda benzer modül eklemeniz gerekirse, bu çok külfetli olabilir.
HostClass
doğrudan referanslar M::ClassMethods
, modülün bilmesi veya dikkat etmesi gereken bir uygulama detayıdır .M
HostClass
Öyleyse şuna ne dersiniz: include
İlk satırda aradığımızda, modülün dahil edildiğini bir şekilde bildiririz ve ayrıca extend
kendisini arayabilmesi için ona sınıf nesnemizi veririz . Bu şekilde, eğer isterse sınıf yöntemlerini eklemek modülün görevidir.
Özel self.included
yöntem tam olarak bunun içindir. Ruby, modül başka bir sınıfa (veya modüle) dahil edildiğinde otomatik olarak bu yöntemi çağırır ve ana bilgisayar sınıfı nesnesini ilk argüman olarak iletir:
module M
def new_instance_method; "hi"; end
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def new_class_method; "hello"; end
end
end
class HostKlass
include M
def self.existing_class_method; "cool"; end
end
HostKlass.singleton_class.included_modules
Elbette, sınıf yöntemleri eklemek yapabileceğimiz tek şey değil self.included
. Sınıf nesnesine sahibiz, böylece onun üzerinde herhangi bir başka (sınıf) yöntemi çağırabiliriz:
def self.included(base)
base.existing_class_method
end