Skip to content

Module

Class’a benzeyen ama Class gibi instantiate edilemeyen şeydir modül. Modül denen şeye Class eklenebilir (include edilir) Modülden gelen methodlar artık ilgili Class’ın methodu haline gelir.

Yani düşünün ki bir Class var, bu Class’ın farklı 2-3 Class’tan özellik almasını istiyorsunuz. Bunu başarmak için ilgili Class’a o 2-3 Class’ı Modül olarak ekliyorsunuz!

Module’ler sayesinde Namespace ve Mix-in fonksiyonalitesi de gelmiş olur.

Tahmin edebileceğiniz gibi module kelimesiyle başlarlar ve aynı Class’larda olduğu gibi büyük harfle başlayan module adı ya da Namespace tanımlaması yapılır:

module RandomNumbers
  def generate
    rand(10)
  end
end

class DiceGame
  include RandomNumbers
end

class RaceGame
  include RandomNumbers
end

g = DiceGame.new
g.generate # => 3

x = RaceGame.new
x.generate # => 7

RandomNumbers adında bir Module yaptık, iki farklı Class’ımız var, DiceGame ve RaceGame diye, include ile bu Module’ü 2 farklı Class’a ekledik. Şimdi her iki Class’ın da generate adında method’u oldu...

Namespacing

Module içinde Module tanımlayabilirsiniz. Bu sayede belirlediğiniz Module tanımı altında başka alt Module’ler ve method’lar ekleyebilir, bu sayede tüm fonksiyonaliteyi ortak bir isim altından yürütebilirsiniz:

module Framework
  module HttpFunctions
    def self.fetch_url
      "This is url fetcher"
    end
  end
end

Framework::HttpFunctions.fetch_url # => "This is url fetcher"

Alt Module’e ulaşmak için :: kullandık. Aynı kodu şu şekilde de tanımlayabilirdik:

module Framework
end

module Framework::HttpFunctions
  def self.fetch_url
    "This is url fetcher"
  end
end

Framework::HttpFunctions.fetch_url # => "This is url fetcher"

Bu sayede, başka bir kütüphaneden gelen Module’e ek Module’ler takma şansınız olur. Örneğin Sinatra için ek bir özellik yapıyorsunuz. Bu durumda;

module Sinatra::MyFeature
end

Şeklinde kullanabilirsiniz.

Scope (Kapsama Alanı)

Dikkat ettiyseniz Module’ü kullanırken Class gibi instanciate etmedik. Keza örnekte self.fetch_url diye method tanımlaması yaptık. Aslında burada Singleton gibi kullandık. Örnekte fetch_url methodu için scope olarak HttpFunctions vermiş olduk. Yani fetch_url sadece Framework::HttpFunctions.fetch_url şeklinde erişilebilir oldu.

Constants (Sabitler)

Module içinde sabit değer tanımlaması da yapmak mümkündür.

module A
  SABIT = 5
end

A::SABIT # => 5

Eğer nested (iç içe) yani Module içinde Module yaparsak, sabitlere aşağıdaki gibi erişebiliriz:

module A
  SABIT = 5
  module B
    def self.sabit_degeri_ver
      SABIT
    end
  end
end

A::SABIT              # => 5
A::B.sabit_degeri_ver # => 5

Peki, dışarıda tanımlanmış bir sabit varsa?

SABIT = 5 # en dıştaki global
module A
  SABIT = 10 # içerideki
  module B
    def self.sabit_degeri_ver
      "#{::SABIT}, #{SABIT}"
    end
  end
end

A::B.sabit_degeri_ver # => "5, 10"

En dıştakini ::SABIT ile aldık.

Visibility, Access Level (Erişim):

Aynı Class’lardaki gibi public, private ve protected olayı Module’ler için de geçerlidir.

module A
  def sadece_iceriden
    "Bu private method"
  end

  def bu_sayede_private_erisim_olur
    sadece_iceriden
  end

  private :sadece_iceriden
end

class Deneme
  include A
end

c = Deneme.new
c.sadece_iceriden # => NoMethodError: private method ‘sadece_iceriden’ called for #<Deneme:0x007f8f7c9188c8>
c.bu_sayede_private_erisim_olur # => "Bu private method"

Extend ve Include Durumları

Ruby’de bir Class sadece tek bir Class’tan türeyebildiği için module ve include çözümlerinden bahsetmiştik:

module Person
  attr_accessor :name
  def say_hi
    "Hello #{@name}"
  end
end

Person # => Person

class User
  include Person
  def initialize(name)
    @name = name
  end
end

User # => User

u = User.new("Uğur") # => #<User:0x007fcd42976de8 @name="Uğur">
u.say_hi # => "Hello Uğur"

u.name = "vigo"
u.say_hi # => "Hello vigo"

Person modülünden gelen say_hi method’una;

User.new("Ezel").say_hi # => "Hello Ezel"

erişebiliyorsunuz ama ;

User.say_hi # => undefined method `say_hi’ for User:Class (NoMethodError)

yaptığımızda olmayan bir method çağrımı yapmış oluruz. Eğer include yerine extend kullansaydık;

module Person
  attr_accessor :name

  def say_hi
    @name ||= "Undefined name"
    "Hello #{@name}"
  end
end

class User
  extend Person

  def initialize(name)
    @name = name
  end
end
user = User.new("Yeşim") # => #<User:0x007f87c39702a0 @name="Yeşim">
user.name # => undefined method `name’ for #<User:0x007f87c39702a0 @name="Yeşim"> (NoMethodError)

Çünki Persona ait özellikleri eklemek (include) yerine extend (genişletme) ettik ve;

User.say_hi # => "Hello Undefined name"
User.instance_methods - Object.instance_methods # => [] # boş array

say_hi artık bir singleton haline geldi yani Instance Method’u olmak yerine Class Method’u oldu. Zaten ilgili sınıfın varolan method’larına baktığımızda boş Array döndüğünü görürüz.

Özetle, include ile sanki başka bir sınıftan türer gibi tüm özellikleri instance method olarak alırken, extend kullandığımızda direk Class kopyası gibi davranıyor.

Modülden gelen method’ları Class içinde instance method gibi kullanmak yerine singleton method olarak kullanacağınız zaman extend kullanabilirsiniz!