Ruby'de module iki iş görür: isimleri gruplayan bir ad alanı (namespace) ve birden fazla sınıfa ortak metot kazandıran bir mixin. Modülden nesne üretilmez. Bir modülü sınıfa include ile eklerseniz metotları o sınıfın nesnelerine instance metodu olarak gelir; extend ile eklerseniz metotlar tek bir nesneye (ya da sınıf nesnesine) eklenir; prepend ile eklerseniz modül sınıfın önüne geçer ve sınıfın kendi metodunu sarabilir.
Bu yazıda üç ekleme biçiminin ancestors listesini nasıl değiştirdiğini, included kancasını, module_function ile hem modül üzerinden hem mixin olarak kullanılan yardımcıları ve ad alanlarındaki sabit arama kuralını Ruby 4.0.9'da çalıştırılmış örneklerle gösteriyoruz. Sınıfların temeli için Ruby class ve nesneler yazısını okuyabilirsiniz.
Modül bir ad alanı olarak

Farklı kütüphaneler aynı ada sahip sınıflar tanımlayabilir. Her birini bir modülün içine koymak bu çakışmayı önler: Muhasebe::Fatura ile Kargo::Fatura birbirinden bağımsız iki sınıftır. İç içe tanımlanan bir sınıf, dıştaki modülün sabitlerini doğrudan görebilir.
module Muhasebe
KDV = 20
class Fatura
def oran = KDV
end
module Rapor
p Module.nesting # çıktı: [Muhasebe::Rapor, Muhasebe]
end
end
module Kargo
class Fatura
end
end
p Muhasebe::Fatura.new.oran # çıktı: 20
p Muhasebe::Fatura == Kargo::Fatura # çıktı: false
p Muhasebe::KDV # çıktı: 20
p Muhasebe.constants.sort # çıktı: [:Fatura, :KDV, :Rapor]module A::B yazımındaki tuzak
Dış modül zaten tanımlıysa module Muhasebe::Ozet biçiminde tek satırla iç modül açabilirsiniz; girinti azalır. Ancak belgeler sabit aramanın bu durumda farklı çalıştığını belirtiyor: Module.nesting listesinde yalnızca Muhasebe::Ozet yer alır, Muhasebe yer almaz. Bu yüzden dış modüldeki sabit bulunamaz:
module Muhasebe::Ozet
p Module.nesting # çıktı: [Muhasebe::Ozet]
p KDV
# NameError: uninitialized constant Muhasebe::Ozet::KDV
endÇözüm ya iç içe module Muhasebe; module Ozet yazımını kullanmak ya da sabite tam yoluyla, Muhasebe::KDV diye erişmektir. En üst seviyedeki bir sabite ::KDV biçiminde ulaşılır.
include: modülü mixin olarak eklemek
include, modüldeki instance metotlarını sınıfın nesnelerine kazandırır. Modülün metodu, içinde bulunduğu nesnenin diğer metotlarını çağırabilir; aşağıda selam metodu her sınıfın kendi ad metodunu kullanıyor. Ruby'nin kendi Comparable ve Enumerable modülleri de bu şekilde çalışır.
module Selamlayan
def selam
"Merhaba, ben #{ad}"
end
end
class Kisi
include Selamlayan
attr_reader :ad
def initialize(ad)
@ad = ad
end
end
class Robot
include Selamlayan
def ad = "R2"
end
p Kisi.new("Deniz").selam # çıktı: "Merhaba, ben Deniz"
p Robot.new.selam # çıktı: "Merhaba, ben R2"
p Kisi.ancestors # çıktı: [Kisi, Selamlayan, Object, Kernel, BasicObject]
p Kisi.include?(Selamlayan) # çıktı: true
p Kisi.new("Deniz").is_a?(Selamlayan) # çıktı: true
Selamlayan.new
# NoMethodError: undefined method 'new' for module Selamlayan
class Kisi
include Robot
end
# TypeError: wrong argument type Class (expected Module)ancestors çıktısında modül, sınıfın hemen arkasına yerleşmiş. Ruby bir metodu ararken önce Kisi'ye, bulamazsa Selamlayan'a, sonra Object'e bakar. Bu yüzden sınıfta aynı adlı bir metot varsa sınıfınki kazanır. Bir sınıfın başka bir sınıfa mixin olarak eklenemediğini de son hata gösteriyor.
Birden fazla include ve sıra
Ayrı satırlarda include A ve include B yazarsanız son eklenen modül sınıfa en yakın yere girer ve önce o aranır. Belgelere göre include A, B biçiminde tek çağrıda verilen modüller ise ters sırayla işlenir; sonuçta A öne geçer. Aynı modülü ikinci kez eklemek listeyi değiştirmez.
module A; def kim = "A"; end
module B; def kim = "B"; end
class X
include A
include B
end
class Y
include A, B
end
p X.ancestors.first(3) # çıktı: [X, B, A]
p X.new.kim # çıktı: "B"
p Y.ancestors.first(3) # çıktı: [Y, A, B]
p Y.new.kim # çıktı: "A"extend: metotları tek bir nesneye eklemek
extend, modülün instance metotlarını çağrıldığı nesnenin singleton sınıfına ekler. Bir sınıfın içinde extend Modul yazdığınızda nesne sınıfın kendisi olduğu için metotlar sınıf metodu olur. Sıradan bir nesneye uyguladığınızda ise yalnızca o nesne yeni yeteneği kazanır.
module Fabrika
def ornek_olustur(*args)
new(*args)
end
end
class Nokta
extend Fabrika
def initialize(x, y)
@x, @y = x, y
end
end
p Nokta.ornek_olustur(1, 2).instance_variables # çıktı: [:@x, :@y]
p Nokta.singleton_class.include?(Fabrika) # çıktı: true
p Nokta.ancestors.include?(Fabrika) # çıktı: false
module Konusan
def konus = "Bu nesne konuşabiliyor"
end
a = Object.new
b = Object.new
a.extend(Konusan)
p a.konus # çıktı: "Bu nesne konuşabiliyor"
p b.respond_to?(:konus) # çıktı: falseprepend: sınıfın önüne geçip metodu sarmak

prepend ile eklenen modül ancestors listesinde sınıfın önüne yerleşir. Böylece aynı adlı metotta önce modülünki çalışır; modül super çağırarak sınıfın asıl metoduna geçer. Kayıt tutma, ölçüm ya da önbellek gibi bir metodun öncesine ve sonrasına iş eklemek için uygun bir yoldur. Aynı modülü include ile eklediğinizde sınıfın metodu kazanır ve modüldeki sarmalayıcı hiç çalışmaz:
module Kayitci
def kaydet
puts "önce: kayıt başlıyor"
sonuc = super
puts "sonra: kayıt bitti"
sonuc
end
end
class Belge
prepend Kayitci
def kaydet
puts "belge diske yazıldı"
:tamam
end
end
class Not
include Kayitci
def kaydet
puts "not kaydedildi"
:tamam
end
end
p Belge.new.kaydet
# çıktı: önce: kayıt başlıyor
# çıktı: belge diske yazıldı
# çıktı: sonra: kayıt bitti
# çıktı: :tamam
p Belge.ancestors.first(3) # çıktı: [Kayitci, Belge, Object]
p Not.new.kaydet
# çıktı: not kaydedildi
# çıktı: :tamam
p Not.ancestors.first(3) # çıktı: [Not, Kayitci, Object]| Yöntem | Metotlar nereye gider | ancestors içindeki yeri | Tipik kullanım |
|---|---|---|---|
| include M | Sınıfın nesnelerine (instance metodu) | Sınıfın arkasında | Ortak davranış paylaşmak |
| extend M | Tek nesneye ya da sınıf nesnesine | Sınıfın listesinde görünmez | Sınıf metodu eklemek, tek nesneyi genişletmek |
| prepend M | Sınıfın nesnelerine, sınıftan önce | Sınıfın önünde | Var olan metodu sarmak |
| module_function | Modülün kendisine ve private kopya olarak | Değiştirmez | Yardımcı fonksiyonlar |
included kancası ile sınıf metotları eklemek
Bir modül eklendiğinde Ruby modül üzerindeki included metodunu, eklendiği sınıfı argüman olarak vererek çağırır. extend için extended, prepend için prepended karşılıkları vardır. Yaygın bir kalıp, included içinde eklenen sınıfa ikinci bir modülü extend ederek tek bir include ile hem instance hem sınıf metotları kazandırmaktır:
module Izlenebilir
def self.included(sinif)
puts "#{self}, #{sinif} içine eklendi"
sinif.extend(SinifMetotlari)
end
module SinifMetotlari
def izlenen_alanlar = @alanlar ||= []
def izle(*adlar) = izlenen_alanlar.concat(adlar)
end
def degisenler
self.class.izlenen_alanlar
end
end
class Makale
include Izlenebilir # çıktı: Izlenebilir, Makale içine eklendi
izle :baslik, :govde
end
p Makale.izlenen_alanlar # çıktı: [:baslik, :govde]
p Makale.new.degisenler # çıktı: [:baslik, :govde]module_function: hem Modul.metot hem mixin
Bazı modüller nesne durumu tutmayan yardımcı fonksiyonlar toplar. module_function, ardından gelen metotları modülün kendisi üzerinden çağrılabilir yapar ve modülü include eden sınıflara da bu metotların private bir kopyasını verir. Belgeler bunların kopya olduğunu vurgular: modüldeki instance metodunu sonradan değiştirirseniz modül fonksiyonu eski haliyle kalır.
module Para
module_function
def kurus_to_tl(kurus)
format("%.2f TL", kurus / 100.0)
end
end
p Para.kurus_to_tl(12_950) # çıktı: "129.50 TL"
class Kasa
include Para
def goster(k)
kurus_to_tl(k)
end
end
p Kasa.new.goster(500) # çıktı: "5.00 TL"
Kasa.new.kurus_to_tl(500)
# NoMethodError: private method 'kurus_to_tl' called for an instance of KasaSık yapılan hatalar

- include ile sınıf metodu beklemek:
includeinstance metodu ekler.Sinif.metotbiçiminde çağırmak istiyorsanızextendkullanın. - prepend yerine include ile sarmaya çalışmak: Sınıfta aynı adlı metot varsa include edilen modülün metodu hiç çalışmaz, çünkü önce sınıfa bakılır.
- module A::B yazımında dış sabitleri beklemek: Bu yazımda dış modülün sabitleri arama zincirinde yoktur,
NameErroralırsınız. - Modülden nesne üretmeye çalışmak: Modüllerin
newmetodu yoktur; nesne gerekiyorsa sınıf tanımlayın. - Sahibi olmadığınız modülü yeniden açmak: Belgeler bunun çakışmalara yol açabileceğini söylüyor. Kapsamı sınırlı değişiklik için refinements tercih edilebilir.
Sık Sorulan Sorular
Modül ile sınıf arasındaki fark nedir?
Her sınıf aynı zamanda bir modüldür, ama sınıftan nesne üretilebilir ve sınıf bir üst sınıftan miras alır. Modülden nesne üretilmez; buna karşılık modül başka sınıflara ve modüllere karıştırılabilir. Sınıflar başka bir sınıfa karıştırılamaz.
ancestors listesini neden kontrol etmeliyim?
Bir metodun hangi tanımının çalışacağı bu listedeki sıraya bağlıdır. Beklemediğiniz bir metot çalışıyorsa Sinif.ancestors çıktısına bakmak hangi modülün öne geçtiğini hemen gösterir.
Comparable ve Enumerable nasıl çalışıyor?
İkisi de birer mixin'dir: siz <=> ya da each gibi tek bir metot yazarsınız, modül onlarca metodu bunun üzerine kurar. Uygulamalı anlatım için kendi sınıfınızda Comparable ve Enumerable yazısına bakın.
Modül metodunu nesne üzerinden çağırınca neden private hatası alıyorum?
module_function ile tanımlanan metotların mixin kopyası private olur. Sınıfın içinden alıcı olmadan çağırabilirsiniz, nesne üzerinden çağıramazsınız. Görünürlük kuralları public, private ve protected yazısında anlatılıyor.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Ruby öğrenme rehberi · Ruby class ve nesneler · public, private, protected · Ruby refinements · Comparable ve Enumerable · Enumerable map, select, reduce