---
title: 'Nesne Yönelimi ve Metaprogramlama'
source: 'https://academia.sh/tr/kurslar/ruby-nesne-yonelimi'
language: tr
updated: '2026-10-21T05:03:41+00:00'
license: 'CC BY-SA 4.0'
lessons: 9
outcomes:
    - 'Sınıflar, kalıtım ve super ile durum ve davranışı düzenler'
    - 'Modül ve karışımlarla davranış paylaşır'
    - 'Yöntem arama zincirini açıklar ve bir yöntemin zincirde nerede tanımlandığını ölçer'
    - 'Bir yöntemi programcının mı, bir atanın mı, üreten bir kodun mu yazdığını ya da hiç tanımı olmadığını ayırt eder'
    - 'Metaprogramlama araçlarını ölçülü ve okunabilir kullanır'
---

# Nesne Yönelimi ve Metaprogramlama

Ruby'de hesap.bakiye_metni, nokta.x, ayar.renk ve vekil.sort aynı biçimde çağrılır, ama dördünün gövdesi dört ayrı yoldan gelir: programcı alıcının sınıfında bir def yazmıştır, tanım zincirdeki bir atadadır, tanımı çalışma zamanında bir kod üretmiştir ya da ortada tanım yoktur ve ileti method_missing ile yakalanır. Kurs her düzeneği tek bir soruyla ölçer — bu yöntemi kim yazdı? Sınıflar, kalıtım, modüller, görünürlük, yöntem arama zinciri, yansıma, dinamik yöntem tanımı, vekil nesneler ve alan özel diller aynı çerçeveden geçirilir.

## Sınıflar ve Modüller

Örnek değişkenlerinin nesnede, yöntemlerin sınıfta durması, kalıtım ve super ile üst sınıfa erişim, modüllerle çoklu kalıtımsız davranış paylaşımı, genel, korumalı ve özel yöntemler ve ata zincirinin çözümlenmesi.

### Sınıf Tanımı ve Örnek Değişkenler

Source: https://academia.sh/tr/kurslar/ruby-nesne-yonelimi/sinif-tanimi-ve-ornek-degiskenler

Bir yöntemi kimin yazdığını dört yazanla sınıflandırma; örnek değişkenlerinin nesnede, yöntemlerin sınıfta durması; initialize'ın özel oluşu; attr_reader ile yazılmış yöntemin def ile yazılmışa göre farkı.

Önceki kurs bir iletinin karşılığını nerede bulduğunu ölçtü: alıcının sınıfında, karıştırılmış bir modülde, verilen blokta ya da sözdiziminde (M13/K01). Kapanışında bir soru açık kaldı. `hesap.bakiye_metni`, `nokta.x`, `ayar.renk` ve `vekil.sort` aynı biçimde çağrılır, ama dördünün gövdesi dört ayrı yoldan gelir. Yöntemin belirli bir sahipte durduğu biliniyor; o tanımı kimin yazdığı bilinmiyor.

Bu kurs o soruyu ölçer. İlk ders bir sınıfın en küçük hâlini alır: örnek değişkenleri nerede durur, yöntemler nerede durur, ve bir yöntemin yazanı nasıl sınıflandırılır?

#### Dört Yazan ve Ölçüm Çekirdeği

Kursun her dersi aynı soruyu sorar: bu yöntemi kim yazdı? Yanıt dört kategoriden biridir. **Programcı**, gövdeyi alıcının kendi sınıfında `def` ile yazan kişidir. **Ata**, tanımı zincirde bir üst sınıfta ya da bir modülde bırakandır. **Kod**, tanımı çalışma zamanında bir çağrıyla üreten düzenektir: `attr_reader`, `define_method`, `Struct`. **Kimse**, zincirde hiç tanım bulunmayan ve iletinin `method_missing` ile yakalandığı durumdur. Çıktıda bu dört ad ASCII yazılır: `programci`, `ata`, `kod`, `kimse`.

**SM1.** Bu kursta "yazan", bir yöntemin tanımının nereden geldiğini söyleyen kategoridir. Sınıflandırmayı etiket değil kural yapar: alıcının tekil sınıfında tanım yoksa kimse; tanımın sahibi alıcının sınıfı ya da tekil sınıfı değilse ata; tanımın `iseq` değeri `nil` ya da etiketi `block` ile başlıyorsa kod; hiçbiri değilse programcı. Kural tanımın biçimini okur, yazanın niyetini okumaz. Bu cümle kursun birkaç sınırlayıcı ölçümünün ortak yüzüdür.

Kural `yazan.rb` dosyasındadır. Kurs boyunca yeniden yazılmaz, her derste aynı biçimde yüklenir. Dosyadaki sıra önemlidir. Önce tanımın var olup olmadığı sorulur, çünkü tanımı olmayan bir yöntemin sahibi sorulamaz. Sonra sahip sorulur, çünkü sahibi alıcının sınıfı olmayan bir tanım zaten başka bir yerden gelmiştir. En son tanımın nasıl üretildiğine bakılır: yalnız alıcının kendi sınıfında duran tanımlar için gövdenin bir `def` mi yoksa bir çağrının ürünü mü olduğunu ayırmak gerekir.

```ruby
# yazan.rb — bir yontemi kimin yazdigini siniflandiran yardimci

# Siniflandirma sirayla sorar: once alicinin zincirinde bir tanim var mi,
# sonra tanimi alicinin kendi sinifi mi tasiyor, sonra tanim nasil uretildi.
def yazan(alici, ad)
  zincir = alici.singleton_class
  unless zincir.method_defined?(ad) || zincir.private_method_defined?(ad)
    return ["kimse", "tanim yok"]
  end

  tanim = zincir.instance_method(ad)
  sahip = tanim.owner
  yer = sahip == zincir ? "tekil sinif" : sahip.to_s
  return ["ata", yer] unless sahip == alici.class || sahip == zincir

  iz = RubyVM::InstructionSequence.of(tanim)
  return ["kod", "#{yer}, ruby govdesi yok"] if iz.nil?
  return ["kod", "#{yer}, blok govdesi"] if iz.label.start_with?("block")

  ["programci", "#{yer}, def govdesi"]
end
```

`yazan(alici, ad)` iki değerli bir dizi döndürür: yazanın adı ve kanıt. Kanıt, tanımın sahibini ve gövdenin nasıl yazıldığını söyler. Dokuz gözlemi bir arada koşan ikinci dosya `yazanlar.rb`'dir. Her gözlem aynı biçimde çağrılan bir yöntemdir; ayrım yalnız çağrının arkasındadır.

```ruby
# yazanlar.rb — dokuz gozlem: bu yontemi kim yazdi
require_relative "yazan"

module Raporlanir
  def rapor = "rapor: #{bakiye_metni}"
end

class Hesap
  include Raporlanir

  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
  def net = @bakiye - komisyon

  private

  def komisyon = 2
end

class Vadeli < Hesap
end

module Kilit
  def ac = "kilit acildi, #{super}"
end

class Kasa
  prepend Kilit
  def ac = "kasa acildi"
end

Nokta = Struct.new(:x, :y)

class Ayar
  %i[renk boyut].each do |ad|
    define_method(ad) { "#{ad} varsayilan" }
  end
end

class Vekil
  def initialize(hedef)
    @hedef = hedef
  end

  def method_missing(ad, *argumanlar, &blok)
    return super unless @hedef.respond_to?(ad)

    @hedef.public_send(ad, *argumanlar, &blok)
  end

  def respond_to_missing?(ad, ozel = false) = @hedef.respond_to?(ad, ozel) || super
end

class Model
  def self.alan(ad, varsayilan)
    define_method(ad) { instance_variable_get("@#{ad}") || varsayilan }
  end
end

class Urun < Model
  alan :ad, "adsiz"
  alan :fiyat, 0
end

hesap = Hesap.new("ece", 100)
gozlemler = {
  "durum okuma"   => [hesap, :bakiye_metni, -> { hesap.bakiye_metni }],
  "ust sinif"     => [Vadeli.new("can", 50), :bakiye_metni, -> { Vadeli.new("can", 50).bakiye_metni }],
  "karisim"       => [hesap, :rapor, -> { hesap.rapor }],
  "ozel yardimci" => [hesap, :komisyon, -> { hesap.net }],
  "one eklenen"   => [Kasa.new, :ac, -> { Kasa.new.ac }],
  "uye okuyucu"   => [Nokta.new(3, 4), :x, -> { Nokta.new(3, 4).x }],
  "uretilen"      => [Ayar.new, :renk, -> { Ayar.new.renk }],
  "vekil"         => [Vekil.new([3, 1, 2]), :sort, -> { Vekil.new([3, 1, 2]).sort }],
  "bildirim"      => [Urun.new, :fiyat, -> { Urun.new.fiyat }],
}

adet = { "programci" => 0, "ata" => 0, "kod" => 0, "kimse" => 0 }

printf("%-15s%-11s%-27s%s\n", "gozlem", "yazan", "kanit", "sonuc")
gozlemler.each do |ad, (alici, ileti, cagri)|
  tur, kanit = yazan(alici, ileti)
  adet[tur] += 1
  printf("%-15s%-11s%-27s%s\n", ad, tur, kanit, cagri.call.inspect)
end

puts
adet.each { |tur, n| printf("%-11s%d\n", tur, n) }
puts "toplam gozlem: #{adet.values.sum}"
```

```
gozlem         yazan      kanit                      sonuc
durum okuma    programci  Hesap, def govdesi         "ece 100"
ust sinif      ata        Hesap                      "can 50"
karisim        ata        Raporlanir                 "rapor: ece 100"
ozel yardimci  programci  Hesap, def govdesi         98
one eklenen    ata        Kilit                      "kilit acildi, kasa acildi"
uye okuyucu    kod        Nokta, ruby govdesi yok    3
uretilen       kod        Ayar, blok govdesi         "renk varsayilan"
vekil          kimse      tanim yok                  [1, 2, 3]
bildirim       kod        Urun, blok govdesi         0

programci  2
ata        3
kod        3
kimse      1
toplam gozlem: 9
```

Dokuz gözlemin 2'sini programcı, 3'ünü bir ata yazdı; 3'ünü bir kod üretti; 1'inde tanım yoktu, iletiyi `method_missing` yakaladı. Bu sayılar kurs boyunca değişmez. Programcı `durum okuma` ve `ozel yardimci` satırlarını yazdı: ikisinin gövdesi `Hesap` içinde bir `def` ile durur. Ata `ust sinif`, `karisim` ve `one eklenen` satırlarını yazdı; üçünün tanımı alıcıdan uzakta, `Hesap`'ta, `Raporlanir`'de ve `Kilit`'te durur. `uye okuyucu`, `uretilen` ve `bildirim` satırlarında tanımı bir çağrı üretti. `vekil` satırında tanım yok ve sonuç yine de döndü.

Bu dersin payı ilk programcı satırıdır: `durum okuma`. Sonraki dersler ata, kod ve kimse yazanlarını sırayla açar; bu ders ise programcının yazdığı en sade yapıyı, bir sınıfı ve onun örnek değişkenlerini ölçer.

#### Durum Nesnede, Davranış Sınıfta

`Hesap` sınıfının `initialize` yöntemi iki değer alır ve ikisini örnek değişkenine atar. Örnek değişkeni `@` ile başlayan, nesneye bağlı bir değişkendir (M13/K01). Aşağıdaki dosya sınıfın küçük bir kesitini yeniden tanımlar, çünkü `yazanlar.rb` yüklendiğinde dokuz satırlık tabloyu basar; bu kesit onu tekrarlamadan çalışır.

```ruby
# durum.rb — ornek degiskenleri nesnede durur
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end
end

hesap = Hesap.new("ece", 100)
p hesap.instance_variables
p hesap.instance_variable_defined?(:@bakiye)
p Hesap.instance_methods(false)
begin
  hesap.bakiye
rescue NoMethodError => e
  puts e.class
end
```

```
[:@sahip, :@bakiye]
true
[]
NoMethodError
```

`instance_variables` iki değişkeni atama sırasıyla listeledi: `@sahip` ve `@bakiye`. Değişkenler `Hesap` sınıfında değil, `hesap` nesnesinde durur. `Hesap.instance_methods(false)` ise boş bir dizi verdi: sınıfın genel bir yöntemi yok. `initialize` özeldir ve `false` argümanı yalnız sınıfın kendi tanımlarını sorar. Bu yüzden `hesap.bakiye` çağrısı `NoMethodError` verdi: değişken nesnenin içindedir, ama onu okuyan bir yöntem yazılmadığı için dışarıdan görünmez.

**SM2.** Bu derste "örnek değişkeni", bir nesneye bağlı olan ve `@` ile başlayan değişkendir. Değişkenin bir nesnede bulunması, o değişkeni okuyan bir yöntemin bulunduğu anlamına gelmez. Durum nesnede, davranış sınıfta durur; iki taraf `@bakiye` gibi bir adla birbirine bağlanır.

Bir örnek değişkeni, atanmadıkça nesnede hiç oluşmaz. Aşağıdaki dosya bunu ölçer: okunan değişken henüz atanmamıştır.

```ruby
# atanmamis.rb — atanmamis ornek degiskeni nil okunur ve listede yoktur
class Hesap2
  def oku = @yok
  def yaz(deger) = @yok = deger
end

nesne = Hesap2.new
p nesne.oku
p nesne.instance_variables
nesne.yaz(5)
p nesne.oku
p nesne.instance_variables
```

```
nil
[]
5
[:@yok]
```

Atanmamış değişken `nil` olarak okundu, ama `instance_variables` listesine girmedi. Atama yapıldıktan sonra değişken hem okundu hem listelendi. Okumak nesnede bir şey oluşturmaz; yalnız atama oluşturur. Bu davranış `Hesap2` gibi bir sınıfın nesneleri arasında bir fark da doğurur: yalnız `yaz` çağrılmış bir nesne `@yok` değişkenini taşır, çağrılmamış bir nesne taşımaz. İki nesne aynı sınıftan gelse de örnek değişkenlerinin listesi aynı olmak zorunda değildir; liste, sınıfın değil, nesnenin geçmişini yansıtır. Hata vermeyen bir okuma bu yüzden yazım hatasını gizleyebilir: `@bakyie` yazılırsa Ruby sessizce `nil` verir.

#### İki Nesne, Tek Sınıf

Durumun nesnede, davranışın sınıfta durması iki nesneyle ölçülür. Aşağıdaki dosya aynı sınıftan iki hesap üretir ve iki şeyi karşılaştırır: yöntemin sahibini ve örnek değişkenlerinin adlarını.

```ruby
# iki.rb — iki nesne ayni yontemi paylasir, degerleri paylasmaz
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
end

a = Hesap.new("ece", 100)
b = Hesap.new("can", 50)
puts a.bakiye_metni
puts b.bakiye_metni
p a.method(:bakiye_metni).owner == b.method(:bakiye_metni).owner
p a.instance_variables == b.instance_variables
p Hesap.instance_of?(Class)
p a.class
```

```
ece 100
can 50
true
true
true
Hesap
```

Aynı yöntem iki nesnede iki ayrı sonuç verdi: `ece 100` ve `can 50`. İki nesnenin yönteminin sahibi aynıdır, ikisinin örnek değişkenlerinin adları da aynıdır; ayrılan yalnız değerlerdir. Yöntem gövdesi bir kez, sınıfta yazılır ve her çağrıda çağrıyı alan nesnenin değişkenlerini okur. `Hesap.instance_of?(Class)` `true` verdi: `class Hesap` satırı `Hesap` adında bir sabit kurar ve sabitin gösterdiği şey `Class` türünde bir nesnedir. `a.class` bu nesneyi geri döndürür. Sınıf, yöntemlerin durduğu yer olduğu kadar, nesneleri üreten bir nesnedir de.

Bu ayrımın pratik bir sonucu vardır. Yöntem sayısı nesne sayısıyla çoğalmaz: bin hesap yaratıldığında `bakiye_metni` yine tek bir yerde, tek bir kez durur. Nesne başına çoğalan şey yalnız örnek değişkenlerinin değerleridir. Bir yöntemin yazanını sormak bu yüzden hep sınıfa ya da zincire yönelir; nesnenin kendisi yalnız durumu taşır ve yazanı soran ölçüm onun değişkenlerine bakmaz.

#### initialize Özeldir ve Argüman Sayısını Denetler

`Hesap.new("ece", 100)` çağrısı yeni bir nesne üretir ve `initialize` yöntemini bu nesnede çağırır. `initialize`'ı çağıran `new`'dir, dışarıdaki kod değil. Sınıf bunu özel yöntem olarak saklar; kod `hesap.initialize(...)` yazarak onu çağıramaz. Aşağıdaki dosya çekirdekteki `Hesap`'ın tam tanımını kullanır, çünkü özel yöntem `komisyon` da burada ölçülür.

```ruby
# ozel.rb — initialize ozel bir yontemdir
require_relative "yazan"

class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
  def net = @bakiye - komisyon

  private

  def komisyon = 2
end

hesap = Hesap.new("ece", 100)
p hesap.respond_to?(:initialize)
p Hesap.private_instance_methods(false).sort
p yazan(hesap, :initialize)
begin
  Hesap.new("a")
rescue ArgumentError => e
  puts e.class
end
```

```
false
[:initialize, :komisyon]
["programci", "Hesap, def govdesi"]
ArgumentError
```

`respond_to?(:initialize)` `false` verdi: özel yöntem dışarıdan sorulduğunda görünmez. `private_instance_methods(false)` sınıfın iki özel yöntemini listeledi: `initialize` ve `komisyon`. `initialize` için `yazan` programcıyı buldu, çünkü gövdeyi sınıfta bir `def` yazdı. `Hesap.new("a")` tek bir argümanla çağrıldığında `ArgumentError` verdi: `initialize` iki argüman bekler ve Ruby sayıyı çağrı anında denetler.

**SM3.** Bu derste "argüman sayısı", bir yöntemin kabul ettiği argümanların sayısıdır; `Hesap.new` bu sayıyı `initialize` tanımından alır. `initialize(sahip, bakiye)` iki argüman ister; `Hesap.new("a")` bu yüzden `ArgumentError` verdi ve hiçbir nesne döndürmedi. Ruby sayıyı gövde koşmadan önce denetler, `@sahip` ataması da yapılmadı. Argüman sayısı böylece nesnenin ilk durumunu da sınırlar: iki örnek değişkenini kuran yapıcı, iki değer verilmeden çalışmaz. `initialize` içinde atanmayan bir değişken, sonradan başka bir yöntem tarafından atanmadıkça o nesnede hiç var olmaz; sınıfın yapıcısı bu yüzden nesnenin ilk durumunu kurduğu yerdir ve değişken adlarını bir arada görmek için en güvenilir noktadır.

Sınıfı yeniden açmak, aynı sınıf adıyla bir `class` bloğu daha yazmaktır. Mevcut tanım silinmez, yenisi eklenir (M12/K03). Bir sınıfın tanımı bu yüzden tek bir yerde olmak zorunda değildir; aşağıdaki ölçüm sınıfı yeniden açar.

#### Sınırlayıcı Ölçüm

Aynı sınıfta, aynı sahipte iki yazan bir arada bulunabilir. `Hesap` yeniden açılır ve `attr_reader :sahip` eklenir. `attr_reader` bir okuyucu yöntem üretir: `sahip` yöntemi `@sahip`'i döndürür. Önceki kurslarda (M08/K01) derleyicinin bir sınıfa ne eklediği ölçülmüştü. Burada soru farklıdır: sınıfta duran iki yöntemden birini bir `def` yazdı, ötekini bir çağrı üretti.

```ruby
# yeniden.rb — ayni sinifta iki yazan: def ve attr_reader
require_relative "yazan"

class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
  def net = @bakiye - 2
end

class Hesap
  attr_reader :sahip
end

hesap = Hesap.new("ece", 100)
p yazan(hesap, :sahip)
p yazan(hesap, :bakiye_metni)
p Hesap.instance_methods(false).sort
[:sahip, :bakiye_metni].each do |ad|
  tanim = Hesap.instance_method(ad)
  puts "#{ad} #{tanim.owner} #{tanim.source_location.nil?} #{RubyVM::InstructionSequence.of(tanim).nil?}"
end
```

```
["kod", "Hesap, ruby govdesi yok"]
["programci", "Hesap, def govdesi"]
[:bakiye_metni, :net, :sahip]
sahip Hesap false true
bakiye_metni Hesap false false
```

`yazan(hesap, :sahip)` kodu, `yazan(hesap, :bakiye_metni)` programcıyı buldu. İkisinin sahibi de `Hesap`'tır ve ikisi de `instance_methods(false)` listesinde görünür: `[:bakiye_metni, :net, :sahip]`. Sahibi bilmek yazanı söylemez. Son döngü ayrımın nereden geldiğini gösterir. `source_location` iki yöntem için de `nil` değildir: `attr_reader` çağrısının satırı kaynak yeri olarak kayıtlıdır. Ayrımı `iseq` yapar. `def` ile yazılmış `bakiye_metni` için `iseq` vardır, `attr_reader` ile üretilmiş `sahip` için `nil`'dir. Kural bu yüzden kaynak yerine değil `iseq`'e bakar.

Bu ölçüm, sahibin yazanı belirlemediğini gösterir: iki yöntem aynı sahipte durdu ve iki ayrı yazanla çıktı. Aynı ölçüm sınıfın bir kimlik kartı olmadığını da gösterir. `Hesap` tek bir yazan taşımaz; her yöntemin yazanı ayrı ölçülür. Sınıf düzeyinde "bu sınıfı programcı yazdı" diyen bir kategori yoktur.

#### Özet

- Kursun ölçüsü, bir yöntemi kimin yazdığıdır: yazan programcı, ata, kod ya da kimse olabilir ve kararı `yazan.rb` içindeki kural verir.
- Dokuz gözlemin 2'sini programcı, 3'ünü ata yazdı; 3'ünü kod üretti; 1'inde tanım yoktu ve `method_missing` yakaladı.
- Örnek değişkenleri nesnede, yöntemler sınıfta durur; okuyucu yoksa `hesap.bakiye` `NoMethodError` verir, atanmamış değişken `nil` okunur ve `instance_variables`'a girmez.
- `initialize` özel bir yöntemdir ve argüman sayısını denetler; `Hesap.new("a")` `ArgumentError` verir.
- Bu dersin yazanı programcıdır: `durum okuma` satırında `bakiye_metni`'ni `Hesap`'ın içinde bir `def` yazdı.
- Aynı sınıfta `def` ile yazılmış yöntem programcı, `attr_reader` ile üretilmiş yöntem kod çıkar; ikisinin sahibi aynıdır ve ayrımı `iseq` yapar.

#### Sonraki Adım

`Hesap` içinde `bakiye_metni` yazılı, ama `Vadeli` adlı boş bir alt sınıf da aynı yöntemi çağırıp yanıt alabiliyor. Alt sınıfta tek satır tanım yok. Yöntemi alt sınıfa kim getiriyor ve aynı yöntemi alt sınıfta yeniden yazmak neyi değiştiriyor? Sonraki ders, Kalıtım ve `super`, bunu ölçer.

### Kalıtım ve `super`

Source: https://academia.sh/tr/kurslar/ruby-nesne-yonelimi/kalitim-ve-super

Boş bir alt sınıfın yöntemi üst sınıftan alması; superclass ve ancestors ile ata zinciri; yeniden tanımlamada super; initialize içinde super; çıplak super ile super(...) farkı.

Önceki ders `Hesap` sınıfında `bakiye_metni` yazılı olduğunu, ama `Vadeli` adlı boş bir alt sınıfın da aynı yöntemi çağırıp yanıt alabildiğini söyledi. Alt sınıfta tek satır tanım yok. Yöntemi oraya kim getirdi ve aynı yöntemi alt sınıfta yeniden yazmak neyi değiştirir?

Bu dersin yazanı ata yazanıdır: tanım alıcıdan uzakta, zincirde bir üst sınıfta durur. Ders önce boş bir alt sınıfı ölçer, sonra `super` ile yeniden tanımlamanın tanımı nasıl el değiştirdiğine bakar.

#### Boş Bir Alt Sınıf Yöntemi Nereden Alır

`class Vadeli < Hesap` satırı `Hesap`'ı `Vadeli`'nin üst sınıfı yapar. Üst sınıfın yöntemleri alt sınıfın nesnelerine açıktır. Bu ilişkiye kalıtım denir (M08/K02). Aşağıdaki dosya `Vadeli` için çekirdekteki `ust sinif` gözlemini yeniden kurar. `yazan.rb` dosyası ilk derste verilen kuraldır ve aynen yüklenir.

```ruby
# yazan.rb — bir yontemi kimin yazdigini siniflandiran yardimci

# Siniflandirma sirayla sorar: once alicinin zincirinde bir tanim var mi,
# sonra tanimi alicinin kendi sinifi mi tasiyor, sonra tanim nasil uretildi.
def yazan(alici, ad)
  zincir = alici.singleton_class
  unless zincir.method_defined?(ad) || zincir.private_method_defined?(ad)
    return ["kimse", "tanim yok"]
  end

  tanim = zincir.instance_method(ad)
  sahip = tanim.owner
  yer = sahip == zincir ? "tekil sinif" : sahip.to_s
  return ["ata", yer] unless sahip == alici.class || sahip == zincir

  iz = RubyVM::InstructionSequence.of(tanim)
  return ["kod", "#{yer}, ruby govdesi yok"] if iz.nil?
  return ["kod", "#{yer}, blok govdesi"] if iz.label.start_with?("block")

  ["programci", "#{yer}, def govdesi"]
end
```

`yazan.rb` bu derste yeni bir şey eklemez; kaynak olarak durur. Ölçüm onu çağıran dosyadadır.

```ruby
# ust_sinif.rb — bos alt sinif yontemi atadan alir
require_relative "yazan"

class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
end

class Vadeli < Hesap
end

vadeli = Vadeli.new("can", 50)
p vadeli.bakiye_metni
p yazan(vadeli, :bakiye_metni)
p Vadeli.instance_methods(false)
```

```
"can 50"
["ata", "Hesap"]
[]
```

`vadeli.bakiye_metni` `can 50` verdi. `yazan` ata yazanını, kanıt olarak `Hesap`'ı buldu: tanımın sahibi alıcının sınıfı olan `Vadeli` değil, `Hesap`'tır. `Vadeli.instance_methods(false)` boş bir dizi verdi, yani `Vadeli` kendi başına hiçbir yöntem taşımıyor. `Vadeli.new("can", 50)` çağrısı da çalıştı, çünkü `initialize` de aynı yoldan `Hesap`'tan geldi. Alt sınıf boş olsa da nesne eksiksiz bir `Hesap` davranışı taşır.

**SM4.** Bu derste "üst sınıf", `class Vadeli < Hesap` satırında `<` işaretinin sağında yazılan sınıftır; "alt sınıf" solda yazılandır. "Ata", bir sınıfın zincirinde kendisinden sonra gelen her sınıf ya da modüldür. Kural, yöntemin sahibi alıcının sınıfı değilse yazanı ata sayar.

#### Ata Zinciri

Üst sınıf tek bir sınıfı gösterir. Ama `Hesap`'ın da bir üst sınıfı vardır ve zincir oradan devam eder. Aşağıdaki dosya zinciri iki yoldan sorar: `superclass` bir adım, `ancestors` bütün sırayı verir.

```ruby
# zincir.rb — superclass ve ancestors
module Raporlanir
  def rapor = "rapor: #{bakiye_metni}"
end

class Hesap
  include Raporlanir
end

class Vadeli < Hesap
end

p Vadeli.superclass
p Hesap.superclass
p Vadeli.ancestors.take(5)
p Integer.ancestors.take(4)
```

```
Hesap
Object
[Vadeli, Hesap, Raporlanir, Object, Kernel]
[Integer, Numeric, Comparable, Object]
```

`Vadeli.superclass` `Hesap`, `Hesap.superclass` `Object` verdi. `ancestors` listesi sınıfları ve modülleri bir arada taşır: alıcının sınıfı en başta, sonra `Hesap`, sonra `Hesap`'ın karıştırdığı `Raporlanir` modülü, sonra `Object` ve `Kernel`. Modüllerin zincirdeki yeri bir sonraki dersin konusudur; burada yalnız `Raporlanir`'in `Hesap` ile `Object` arasında durduğuna dikkat etmek yeter. Çekirdek sınıflar aynı biçimde zincire sahiptir: `Integer` önce `Numeric`'e, sonra `Comparable` modülüne, sonra `Object`'e bağlanır. Yani `1.between?(0, 2)` çağrısındaki `between?` de `Integer`'ın değil, zincirdeki bir atanın yazdığı bir yöntemdir.

#### Yeniden Tanımlamada super

Alt sınıf üst sınıftaki bir yöntemi aynı adla yeniden yazabilir. Bu işleme yeniden tanımlama denir. Yeniden tanımlama üst sınıfın tanımını silmez; alıcının zincirinde alt sınıf önce geldiği için ileti önce ona ulaşır. `super` ise bu tanımdan, zincirde bir sonraki aynı adlı tanıma iner. Aşağıdaki dosya `Vadeli2` sınıfında `bakiye_metni`'ni yeniden yazar ve gövdenin yarısını `super`'e bırakır.

```ruby
# yeniden_tanim.rb — super zincirde bir sonraki tanima iner
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
end

class Vadeli2 < Hesap
  def bakiye_metni = "vadeli #{super}"
end

v2 = Vadeli2.new("can", 50)
puts v2.bakiye_metni
p v2.method(:bakiye_metni).owner
p v2.method(:bakiye_metni).super_method.owner
p v2.method(:bakiye_metni).super_method.super_method
```

```
vadeli can 50
Vadeli2
Hesap
nil
```

`vadeli can 50` çıktısı iki parçanın birleşimidir: `vadeli ` ön ekini `Vadeli2` yazdı, `can 50` kısmını `super` çağrısı üzerinden `Hesap` yazdı. `method(:bakiye_metni).owner` `Vadeli2` verdi, `super_method.owner` `Hesap`. `super_method` zincirde bir sonraki tanımı yöntem nesnesi olarak verir. `Hesap`'tan sonra `bakiye_metni` tanımlayan bir ata kalmadığı için `super_method.super_method` `nil` döndü. Yani `super` çağrısı zincirde aşağı yürümeye devam eden bir atlamadır, ama yalnız aynı adlı bir tanım olduğu sürece.

#### Sahibi ve Kopyayı Ayırmak

`Vadeli.instance_methods(false)` boş çıktı, ama `Vadeli` yine de `bakiye_metni`'ne yanıt veriyor. İki soru ayrı tutulmalı: yöntem bu sınıfa mı yazılı, yoksa bu sınıfın nesneleri tarafından mı çağrılabiliyor? `instance_methods(false)` yalnız birincisini sorar. `method_defined?` ikincisini, `instance_method(...).owner` ise tanımın gerçekte nerede durduğunu söyler. Aşağıdaki dosya üç soruyu aynı sınıfa, ardından yeniden tanımlayan `Vadeli2`'ye yöneltir.

```ruby
# sahip_sorusu.rb — yazili olmak ile cagrilabilir olmak ayri sorulardir
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
end

class Vadeli < Hesap
end

class Vadeli2 < Hesap
  def bakiye_metni = "vadeli #{super}"
end

p Vadeli.method_defined?(:bakiye_metni)
p Vadeli.instance_methods(false)
p Vadeli.instance_method(:bakiye_metni).owner
p Vadeli2.instance_methods(false)
p Vadeli2.instance_method(:bakiye_metni).owner
p Hesap.instance_method(:bakiye_metni).owner
```

```
true
[]
Hesap
[:bakiye_metni]
Vadeli2
Hesap
```

`Vadeli.method_defined?(:bakiye_metni)` doğru verdi, `Vadeli.instance_methods(false)` ise boş kaldı; ikisi çelişmez. İlki zincirin tamamına bakar, ikincisi yalnız sınıfın kendi gövdesine. `instance_method(...).owner` `Vadeli` için `Hesap` verdi: `Vadeli`'nin nesneleri çağırdığında çalışan tanım `Hesap`'a yazılmıştır, `Vadeli`'de bir kopyası yoktur. `Vadeli2` için tablo değişir: `instance_methods(false)` artık `bakiye_metni`'ni taşır ve `owner` `Vadeli2`'dir. `Hesap`'ın tanımı yerinde durur ve hâlâ `Hesap` sahipli çıkar. Yeniden tanımlama bir üzerine yazma değil, zincirin önüne ikinci bir tanım koymaktır.

#### initialize İçinde super

`initialize` da bir yöntemdir ve alt sınıfta yeniden tanımlanabilir. Alt sınıf kendi ek değişkenini kurarken üst sınıfın değişkenlerini kurmasını `super` ile ister. Aşağıdaki dosya iki alt sınıf tanımlar ve ikisinin `initialize` yöntemi aynı imzayı taşır, ama `super` farklı yazılır.

```ruby
# initialize_super.rb — super(...) ile cıplak super
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
end

class Faizli < Hesap
  def initialize(sahip, bakiye, oran)
    super(sahip, bakiye)
    @oran = oran
  end
end

class Hatali < Hesap
  def initialize(sahip, bakiye, oran)
    super
    @oran = oran
  end
end

p Faizli.new("ece", 100, 5).instance_variables
begin
  Hatali.new("ece", 100, 5)
rescue ArgumentError => e
  puts e.class
end
```

```
[:@sahip, :@bakiye, :@oran]
ArgumentError
```

`Faizli` iki argümanı `super(sahip, bakiye)` ile üst sınıfa iletti ve `instance_variables` üç değişkeni sırasıyla listeledi: üst sınıfın iki değişkeni, ardından alt sınıfın `@oran`'ı. `Hatali` sınıfında çıplak `super` yazıldı ve `ArgumentError` doğdu. Çıplak `super`, çağıran yöntemin aldığı argümanların hepsini aynen iletir; `Hatali#initialize` üç argüman aldığı için `Hesap#initialize`'a üç argüman gitti, oysa o ikisini bekler.

**SM5.** Bu derste `super(...)` parantezli çağrıyı, "çıplak `super`" ise parantezsiz ve argümansız yazılan çağrıyı anlatır. Parantezli çağrı üst sınıfa yalnız yazılan argümanları iletir; çıplak çağrı yöntemin kendi argümanlarını olduğu gibi iletir. Alt sınıfın imzası üst sınıfınkinden farklıysa çıplak `super` yanlış argümanlar iletebilir; bu durumda parantezli çağrı yazılır.

Parantezli ve parantezsiz biçimin farkı bir üçüncü yazımla tamamlanır: boş parantezli `super()`. Çıplak `super` yöntemin kendi argümanlarını iletir, `super()` ise hiç argüman iletmez. Fark ancak üst sınıftaki yöntem varsayılan değerli bir parametre taşıdığında görünür olur. Aşağıdaki dosya üç alt sınıfta üç biçimi yan yana koyar.

```ruby
# super_bicimleri.rb — cıplak super, super() ve super(arguman)
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def etiket(onek = "hesap") = "#{onek}: #{@sahip}"
end

class Ciplak < Hesap
  def etiket(onek = "ciplak") = super
end

class Bos < Hesap
  def etiket(onek = "bos") = super()
end

class Acik < Hesap
  def etiket(onek = "acik") = super("sabit")
end

puts Ciplak.new("can", 1).etiket
puts Bos.new("can", 1).etiket
puts Acik.new("can", 1).etiket
puts Ciplak.new("can", 1).etiket("elle")
```

```
ciplak: can
hesap: can
sabit: can
elle: can
```

Çıplak `super` alt sınıfın `onek` parametresini üst sınıfa taşıdı; üst sınıftaki varsayılan `hesap` hiç kullanılmadı. `super()` hiçbir şey iletmedi, bu yüzden üst sınıf kendi varsayılanına döndü. `super("sabit")` yazılan değeri iletti ve alt sınıfın parametresini görmezden geldi. Dördüncü satır çıplak `super`'in ilettiğinin parametrenin o anki değeri olduğunu gösterir: çağrıya `elle` verilince üst sınıfa `elle` gitti. Çıplak `super`'i yararlı kılan bu, tehlikeli kılan da aynı özelliğidir; alt sınıfın imzası üst sınıfınkinden ayrıldığı anda iletilen şey de ayrılır.

Bir `super` çağrısı zincirde aynı adlı tanım bulamazsa ne olur? Aşağıdaki ölçüm bunu sorar.

```ruby
# yetim_super.rb — atada tanimi olmayan super
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end
end

class Yetim < Hesap
  def olmayan = super
end

begin
  Yetim.new("a", 1).olmayan
rescue NoMethodError => e
  puts e.class
end
```

```
NoMethodError
```

`super` zincirde `olmayan` adlı bir tanım bulamadı ve `NoMethodError` verdi. Yani `super` bir sihir değil, bir arama sorgusudur: zincirde bir sonraki aynı adlı tanımı arar, bulamazsa her iletinin başına gelen hata ona da gelir.

Zincir iki basamakla sınırlı değildir. Her alt sınıf kendi `super` çağrısıyla bir sonrakine iner ve çağrılar sırayla geri döner. Aşağıdaki dosya üç sınıflı bir zincir kurar ve `super_method` ile zinciri baştan sona yürüyüp sahiplerini toplar.

```ruby
# uc_basamak.rb — super_method zinciri sonuna kadar yurunur
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
end

class Vadeli2 < Hesap
  def bakiye_metni = "vadeli #{super}"
end

class Altin < Vadeli2
  def bakiye_metni = "altin #{super}"
end

a = Altin.new("can", 50)
puts a.bakiye_metni

sahipler = []
yontem = a.method(:bakiye_metni)
while yontem
  sahipler << yontem.owner
  yontem = yontem.super_method
end
p sahipler
```

```
altin vadeli can 50
[Altin, Vadeli2, Hesap]
```

Metin, en içteki tanımdan dışa doğru birleşti: `Hesap` `can 50` kısmını, `Vadeli2` `vadeli ` ekini, `Altin` `altin ` ekini yazdı. Alıcıya en yakın tanım ilk çalışan, ama gövdesinin ortasında bir sonrakini bekleyen tanımdır. `super_method` döngüsü sahipleri aynı sırayla verdi ve `nil` döndüğünde durdu. Böylece `super` çağrısının görünmez bir mekanizma olmadığı, `ancestors` sırasının üzerinden okunan bir yol olduğu doğrulanır.

#### Zincir Çağrı Anında Aranır

`Vadeli` sınıfı tanımlandığında `Hesap`'ın tanımlarını kendine kopyalamadı; çağrı geldiğinde zincire bakar. Bunun gözlenebilir bir sonucu var: `Vadeli` kurulduktan sonra `Hesap`'a eklenen bir yöntem, `Vadeli` nesnesinde de hemen çağrılabilir. Aşağıdaki dosya aynı nesneye aynı soruyu, `Hesap` yeniden açılmadan önce ve sonra yöneltir.

```ruby
# gec_eklenen.rb — atanin sonradan kazandigi yontem alt sinifta gorunur
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end
end

class Vadeli < Hesap
end

v = Vadeli.new("can", 50)
p v.respond_to?(:faiz)

class Hesap
  def faiz = @bakiye / 10
end

p v.respond_to?(:faiz)
p v.faiz
p Vadeli.instance_methods(false)
```

```
false
true
5
[]
```

İlk `respond_to?` yanlış verdi, `Hesap` yeniden açıldıktan sonra doğru verdi ve `v.faiz` `5` döndü. `v` bu arada hiç yeniden kurulmadı. `Vadeli.instance_methods(false)` hâlâ boş: `faiz` alt sınıfa yazılmadı, yalnız zincirdeki bir atanın tanımı oldu. Aranan tanım çağrı anında zincirin o anki hâline göre bulunur.

**SM13.** Bu derste "zincir", alıcının sınıfından başlayıp `BasicObject`'e kadar giden arama sırasıdır. Zincirin üyeleri sınıfa kopyalanmaz; arama her çağrıda sıfırdan yapılır. Bir atanın sonradan kazandığı yöntem bu yüzden alt sınıfın mevcut nesnelerinde de görünür.

#### Sınırlayıcı Ölçüm

`Vadeli` hiçbir şey yazmadı ve yazanı ata çıktı. `Vadeli2` gövdenin yarısını atadan aldı, öbür yarısını kendisi yazdı. Kural bu ikinciyi nasıl sınıflandırır?

```ruby
# yarim_ata.rb — super cagiran yeniden tanim programci sayilir
require_relative "yazan"

class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
end

class Vadeli < Hesap
end

class Vadeli2 < Hesap
  def bakiye_metni = "vadeli #{super}"
end

p yazan(Vadeli.new("can", 50), :bakiye_metni)
p yazan(Vadeli2.new("can", 50), :bakiye_metni)
```

```
["ata", "Hesap"]
["programci", "Vadeli2, def govdesi"]
```

`Vadeli` için ata, `Vadeli2` için programcı çıktı. `Vadeli2`'nin gövdesi yarısını atadan alsa da kural bunu ölçmez. Kural zincirde ilk bulunan tanımı sayar ve o tanımın sahibini alıcının sınıfıyla karşılaştırır; zincirin geri kalanına bakmaz.

**SM6.** Bu derste "ilk bulunan tanım", alıcıdan başlayan arama sırasında karşılaşılan ilk tanımdır. `instance_method(ad)` onu verir. `super` çağrısının zincirin devamında başka bir tanıma indiğini kural görmez, çünkü ilk tanımın gövdesine bakmaz.

Sonuç iki yönlüdür. Alt sınıfa tek bir `def` yazmak, yazanı atadan programcıya çevirir; gövdenin ne kadarının üst sınıfa devredildiği değişmez. Öte yandan yazanı programcı olan bir yöntemin gövdesi tamamen o programcıya ait olmak zorunda değildir. Yazan kategorisi, gövdenin kaynağını değil, zincirdeki ilk tanımın yerini söyler.

#### Özet

- Boş bir alt sınıf yöntemlerini üst sınıftan alır: `Vadeli.instance_methods(false)` boştur ve `Vadeli.new("can", 50).bakiye_metni` atadan gelir.
- `superclass` bir adımı, `ancestors` bütün zinciri verir; `Vadeli.ancestors.take(5)` alt sınıf, üst sınıf, karıştırılmış modül, `Object` ve `Kernel` sırasını gösterir.
- Yeniden tanımlama üst sınıfın tanımını silmez; `super` zincirde bir sonraki aynı adlı tanıma iner ve `super_method.owner` o tanımın sahibini verir.
- `initialize` içinde `super(sahip, bakiye)` yalnız yazılan argümanları iletir; aynı imzada çıplak `super` argümanları aynen ilettiği için `ArgumentError` verir.
- Bu dersin yazanı atadır: `ust sinif` satırında `bakiye_metni`'ni `Hesap` yazdı, `Vadeli` hiçbir şey yazmadı.
- Atada tanımı olmayan `super` `NoMethodError` verir; `super` çağıran yeniden tanımlamada yazan programcı çıkar, çünkü kural ilk bulunan tanımı sayar.

#### Sonraki Adım

`Hesap` sınıfında `rapor` diye bir yöntem hiç yazılmadı, ama `hesap.rapor` çalıştı ve `rapor: ece 100` verdi. Yöntem ne bir üst sınıftan geliyor, ne de sınıfın kendi gövdesinden. Zincirde `Hesap` ile `Object` arasında duran `Raporlanir` o yöntemi nasıl taşıyor? Sonraki ders, Modüller ve Karışımlar, bunu ölçer.

### Modüller ve Karışımlar

Source: https://academia.sh/tr/kurslar/ruby-nesne-yonelimi/moduller-ve-karisimlar

Modülün sınıftan farkı; include ile modülü ata zincirine karıştırmak; Comparable ile <=>; aynı adlı iki modülde sıra; tek nesneye extend.

Önceki ders `Hesap` sınıfında `rapor` yazılmadığı hâlde `hesap.rapor` çağrısının `rapor: ece 100` verdiğini bıraktı. Zincirde `Hesap` ile `Object` arasında duran `Raporlanir` yöntemi taşıyor, ama bir modülün bir sınıfa yöntem vermesi tam olarak ne demek? Modül sınıf değilse zincire nasıl girer?

Bu dersin yazanı yine ata yazanıdır, ama atanın türü değişir: tanım bir üst sınıfta değil, karıştırılmış bir modülde durur. Ders önce modülü sınıftan ayırır, sonra karışımın zincirdeki yerini ve sırasını ölçer.

#### Karıştırılmış Modül Yazan Olur

Aşağıdaki dosya `Raporlanir` modülünü `Hesap` sınıfına `include` ile karıştırır. `yazan.rb` önceki derslerdeki kuraldır ve burada da aynen yüklenir.

```ruby
# yazan.rb — bir yontemi kimin yazdigini siniflandiran yardimci

# Siniflandirma sirayla sorar: once alicinin zincirinde bir tanim var mi,
# sonra tanimi alicinin kendi sinifi mi tasiyor, sonra tanim nasil uretildi.
def yazan(alici, ad)
  zincir = alici.singleton_class
  unless zincir.method_defined?(ad) || zincir.private_method_defined?(ad)
    return ["kimse", "tanim yok"]
  end

  tanim = zincir.instance_method(ad)
  sahip = tanim.owner
  yer = sahip == zincir ? "tekil sinif" : sahip.to_s
  return ["ata", yer] unless sahip == alici.class || sahip == zincir

  iz = RubyVM::InstructionSequence.of(tanim)
  return ["kod", "#{yer}, ruby govdesi yok"] if iz.nil?
  return ["kod", "#{yer}, blok govdesi"] if iz.label.start_with?("block")

  ["programci", "#{yer}, def govdesi"]
end
```

```ruby
# karisim.rb — include ile karistirilan modulun yazani
require_relative "yazan"

module Raporlanir
  def rapor = "rapor: #{@sahip} #{@bakiye}"
end

class Hesap
  include Raporlanir

  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end
end

hesap = Hesap.new("ece", 100)
puts hesap.rapor
p yazan(hesap, :rapor)
p Hesap.include?(Raporlanir)
p Object.include?(Raporlanir)
p Hesap.ancestors
```

```
rapor: ece 100
["ata", "Raporlanir"]
true
false
[Hesap, Raporlanir, Object, Kernel, BasicObject]
```

`yazan` ata yazanını ve kanıt olarak `Raporlanir`'i verdi: tanım `Hesap`'ın gövdesinde değil, modülde yazılı. `Hesap.include?(Raporlanir)` doğru, `Object.include?(Raporlanir)` yanlış çıktı; karışım yalnız `include` çağrısını alan sınıfı etkiler. `Hesap.ancestors` listesinde `Raporlanir`, `Hesap` ile `Object` arasında durur. Bir sınıfa modül karıştırmak, modülü o sınıfın hemen üstüne yerleştirmektir.

**SM7.** Bu derste "karıştırmak", `include` çağrısıyla bir modülü bir sınıfın ata zincirine, sınıfın hemen üstüne yerleştirmektir. Bunu yapan kod satırı `include Raporlanir`, sonuç ise zincirdeki yeni yerdir. Karışımın yazanı, kuralın diliyle ata çıkar, çünkü tanım alıcının sınıfında değil zincirin üst kısmındadır.

Karışımın zincire bir halka eklediği, yöntemleri sınıfa kopyalamadığı iki ek soruyla görülür. `include` bir ifadedir ve bir değer döndürür; `instance_methods(false)` sınıfın kendi gövdesini sorar. Aşağıdaki dosya ikisini ölçer.

```ruby
# kopya_yok.rb — include zincire halka ekler, yontemi kopyalamaz
module Raporlanir
  def rapor = "rapor"
end

class Hesap
  p(include(Raporlanir))
end

p Hesap.instance_methods(false)
p Raporlanir.instance_methods
p Hesap.instance_method(:rapor).owner
p Hesap.included_modules.take(2)
```

```
Hesap
[]
[:rapor]
Raporlanir
[Raporlanir, Kernel]
```

`include` çağrısı alıcıyı, yani `Hesap` sınıfını döndürdü. `Hesap.instance_methods(false)` boş çıktı: `rapor` sınıfın gövdesine taşınmadı, modülde kaldı. `Raporlanir.instance_methods` yöntemi modülün kendi listesinde gösterdi ve `instance_method(:rapor).owner` sahibin `Raporlanir` olduğunu doğruladı. `included_modules` zincirdeki modülleri `Raporlanir`, `Kernel` sırasıyla verdi. Yani önceki dersteki boş alt sınıfla aynı durum geçerlidir: çağrılabilir olmak ile sınıfa yazılı olmak ayrı şeylerdir, ve modül yazılı olanın yerini değiştirmez, zincirin uzunluğunu değiştirir.

#### Modül Sınıf Değildir

Karışım modülü sınıfa benzetir ama ikisi aynı şey değildir. Bir modülden nesne üretilemez; modül yalnız başkalarının zincirine girmek için vardır. Aşağıdaki dosya bunu iki soruyla ölçer.

```ruby
# modul_sinif_degil.rb — modulun ornegi olmaz
module Raporlanir
  def rapor = "rapor"
end

p Raporlanir.instance_of?(Class)
p Raporlanir.instance_of?(Module)
begin
  Raporlanir.new
rescue NoMethodError => e
  puts e.class
end
```

```
false
true
NoMethodError
```

`Raporlanir` bir `Module` örneğidir, `Class` örneği değildir ve `new` yöntemi onda tanımlı değildir. Bu fark karışımın neden gerekli olduğunu da açıklar: kalıtım yalnız bir üst sınıf kabul eder, oysa bir sınıfa istenen sayıda modül karıştırılabilir. Birbirinden bağımsız davranışlar, örneğin raporlama ile karşılaştırma, tek bir üst sınıf zincirine sığdırılmaya çalışılmaz; ayrı modüllerde yazılır ve ihtiyaç duyan sınıfa karıştırılır.

#### Çekirdekten Bir Karışım: Comparable

Karışım yalnız kendi yazdığımız modüllerle sınırlı değildir. Çekirdekteki `Comparable` modülü, sınıfa yalnız `<=>` yazıldığında karşılaştırma yöntemlerinin geri kalanını verir. Aşağıdaki dosya küçük bir `Puan` sınıfı üzerinde bunu ölçer.

```ruby
# karsilastir.rb — Comparable ile <=>
require_relative "yazan"

class Puan
  include Comparable

  attr_reader :deger

  def initialize(deger)
    @deger = deger
  end

  def <=>(diger) = deger <=> diger.deger
end

a = Puan.new(3)
b = Puan.new(5)
c = Puan.new(9)
p c.between?(a, c)
p b > a
p [c, a, b].min.deger
p yazan(a, :between?)
p yazan(a, :<=>)
```

```
true
true
3
["ata", "Comparable"]
["programci", "Puan, def govdesi"]
```

`Puan` içinde yalnız `<=>` yazılı, ama `between?` ve `>` çağrıları ile `<=>` üzerinden çalışan `min` çağrısı sonuç verdi. `between?` için yazan ata ve kanıt `Comparable`; `<=>` için programcı, çünkü onu `Puan` kendi gövdesinde yazdı. Aynı sınıfta iki yöntem, iki ayrı yazan: karışım sınıfın kendi tanımını silmez, yanına ekler.

`Comparable` yalnız `between?` değil, `<=>` üzerinden kurulan bütün karşılaştırmaları verir. Aşağıdaki dosya `Puan` sınıfında hiç yazılmayan iki davranışı daha ölçer: eşitlik ve aralığa sıkıştırma.

```ruby
# karsilastir_ek.rb — Comparable uc yontemi daha saglar
require_relative "yazan"

class Puan
  include Comparable

  attr_reader :deger

  def initialize(deger)
    @deger = deger
  end

  def <=>(diger) = deger <=> diger.deger
end

a = Puan.new(3)
b = Puan.new(5)
p a == Puan.new(3)
p a.equal?(Puan.new(3))
p Puan.new(9).clamp(a, b).deger
p yazan(a, :==)
p Puan.ancestors.take(3)
```

```
true
false
5
["ata", "Comparable"]
[Puan, Comparable, Object]
```

`a == Puan.new(3)` doğru verdi, çünkü `Comparable#==` iki nesneyi `<=>` sonucu sıfır mu diye sınar. Oysa `equal?` iki ayrı nesne olduklarını gösterip yanlış verdi: eşitlik değere göre, kimlik nesneye göre sorulur. `clamp` dokuz değerini beşe indirdi. `yazan` `==` için `Comparable`'ı gösterdi; `BasicObject` de bir `==` tanımlar, ama zincirde `Comparable` önce geldiği için o gölgelenir. Bu, aşağıda ölçülen sıra kuralının çekirdek örneğidir.

#### Aynı Ad İki Modülde

Bir sınıfa iki modül karıştırılırsa ve ikisi de aynı adı tanımlıyorsa hangisi kazanır? Zincirdeki sıra bunu belirler, ve karıştırma sırası zincirin sırasını belirler: sonradan karıştırılan, öncekinin önüne geçer.

```ruby
# iki_modul.rb — aynı ad, iki modul
module A1
  def ad = "A1"
end

module B1
  def ad = "B1"
end

class C1
  include A1
  include B1
end

p C1.ancestors.take(3)
puts C1.new.ad
p C1.ancestors.count(B1)
```

```
[C1, B1, A1]
B1
1
```

`include A1` önce, `include B1` sonra yazıldı ve zincir `[C1, B1, A1]` çıktı: son karıştırılan, alıcıya en yakın durur. `ad` çağrısı bu yüzden `B1` verdi. `ancestors.count(B1)` bir verdi: modül zincirde tek halka olarak durur. Aynı modülü ikinci kez karıştırmak bu tabloyu değiştirir mi, aşağıdaki ölçüm sorar.

```ruby
# tekrar_include.rb — ikinci include zinciri degistirmez
module A1
  def ad = "A1"
end

module B1
  def ad = "B1"
end

class C1
  include A1
  include B1
  include A1
end

p C1.ancestors.take(3)
puts C1.new.ad
p C1.ancestors.count(A1)
```

```
[C1, B1, A1]
B1
1
```

Sondaki ikinci `include A1` zincirde bir şey kıpırdatmadı. `A1` zaten zincirde olduğu için Ruby onu yeniden öne almadı, bu yüzden `B1` kazanmaya devam etti ve `ancestors.count(A1)` bir verdi. Karışım zincirde bir modülü en çok bir kez barındırır; sırayı değiştirmenin yolu yeniden `include` etmek değildir.

**SM8.** Bu derste "zincirdeki sıra", `ancestors` dizisinin soldan sağa okunuşudur ve yöntem araması bu sırayla yürür. İlk eşleşen tanım kazanır. Sonradan `include` edilen modül sınıfın hemen üstünde yer aldığı için, öncekini gölgeler.

#### Modüllerde super

Modül içindeki bir yöntem de `super` çağırabilir ve `super` o anki zincirde bir sonraki halkaya iner. Bu, modülün sınıftan önce mi sonra mı geldiğine göre iki ayrı sonuç verir. Aşağıdaki dosya iki modülü bir üst sınıfla ve alt sınıfla birleştirir.

```ruby
# modul_super.rb — super zincirde modullerin icinden gecer
module A2
  def ad = "A2 " + super
end

module B2
  def ad = "B2 " + super
end

class Temel
  def ad = "temel"
end

class C2 < Temel
  include A2
  include B2
end

class D2 < Temel
  include A2
  def ad = "D2 " + super
end

puts C2.new.ad
p C2.ancestors.take(4)
puts D2.new.ad
```

```
B2 A2 temel
[C2, B2, A2, Temel]
D2 A2 temel
```

`C2` için çağrı `B2`'ye girdi, `super` ile `A2`'ye, oradan `Temel`'e indi ve metin zincir sırasıyla birleşti. `ancestors` aynı yolu `[C2, B2, A2, Temel]` olarak gösterdi: `super` zincirin üzerinde yürümektir. `D2`'de sınıf kendi `ad`'ını yazdı; modül sınıfın altında kaldığı için önce `D2` çalıştı, `super` ile `A2`'ye ve ondan `Temel`'e inildi. Önceki dersin `super_method` ölçümüyle birleştirildiğinde sonuç şudur: yeniden tanımlayan alt sınıf da, karışan modül de aynı zincirdeki halkalardır ve `super` ikisini ayırt etmez.

**SM14.** Bu derste "zincirde super", `super` çağrısının `ancestors` sırasında bir sonraki halkaya inmesidir. Halkanın sınıf mı modül mü olduğu çağrıyı değiştirmez; yalnız sıra belirleyicidir.

Modüller de birbirine karıştırılabilir. Bir modülün zinciri, içine karıştırdığı modülleri taşır ve o modülü `include` eden sınıf hepsini birden devralır. Aşağıdaki dosya iki katmanlı bir karışımı ölçer.

```ruby
# ic_ice.rb — modul icine karisan modul sinifa birlikte gelir
module Ust
  def ust = "ust"
end

module Alt
  include Ust

  def alt = "alt"
end

class Kutu
  include Alt
end

p Alt.ancestors
p Kutu.ancestors.take(3)
p Kutu.new.ust
p Kutu.include?(Ust)
```

```
[Alt, Ust]
[Kutu, Alt, Ust]
"ust"
true
```

`Alt.ancestors` modülün kendi zincirini gösterdi ve `Ust` onda `Alt`'ın hemen arkasında durdu. `Kutu` yalnız `Alt`'ı karıştırdı, ama zincirine `Ust` de girdi; `Kutu.new.ust` bu yüzden çalıştı ve `Kutu.include?(Ust)` doğru verdi. Karışımın bir halkayı değil, modülün o anki zincirinin tamamını getirdiği buradan görülür. Bir sınıfın zincirini okurken modüllerin kendi karışımlarını da hesaba katmak gerekir.

#### Sınırlayıcı Ölçüm

İki sınır var. Birincisi, sınıf kendi `rapor`'unu yazarsa karışımın tanımı ne olur? İkincisi, modül sınıfın tamamına değil, yalnız tek bir nesneye karıştırılırsa yazan ve zincir nasıl görünür?

```ruby
# sinirlayici.rb — sinif golgeler, extend tek nesneye karisir
require_relative "yazan"

module Raporlanir
  def rapor = "rapor: #{@sahip} #{@bakiye}"
end

class Hesap2
  include Raporlanir

  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def rapor = "hesap2 raporu"
end

class Hesap3
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end
end

p yazan(Hesap2.new("ece", 100), :rapor)

tek = Hesap3.new("can", 50)
tek.extend(Raporlanir)
puts tek.rapor
p yazan(tek, :rapor)
p Hesap3.new("a", 1).respond_to?(:rapor)
```

```
["programci", "Hesap2, def govdesi"]
rapor: can 50
["ata", "Raporlanir"]
false
```

`Hesap2` kendi `rapor`'unu yazınca kural programcı verdi: sınıfın kendi tanımı, karıştırdığı modülden önce gelir ve onu gölgeler, `Raporlanir#rapor` zincirde durmaya devam eder ve yalnız `super` çağrısıyla ulaşılabilir. `tek.extend(Raporlanir)` ise modülü yalnız `tek` nesnesine karıştırdı; o nesne `rapor: can 50` yanıtı verdi, ata yazanı `Raporlanir` çıktı, ama aynı sınıftan yeni üretilen bir nesne `respond_to?(:rapor)` sorusuna yanlış dedi. Bu nesnenin zinciri sınıfından farklı olmuştur; o zincir nereye yerleşir sorusu ilerleyen derslerde tekil sınıf adıyla ele alınacak.

`extend`'in nesneye ne yaptığını `include`'la aynı sorularla ölçmek mümkün: nesne modülün örneği sayılır mı, sınıfın diğer nesneleri sayılır mı ve nesnenin kendine özgü zincirinde modül nerede durur?

```ruby
# extend_olcum.rb — extend yalniz o nesnenin zincirine halka ekler
module Raporlanir
  def rapor = "rapor"
end

class Hesap3
end

tek = Hesap3.new
oteki = Hesap3.new
tek.extend(Raporlanir)

p tek.is_a?(Raporlanir)
p oteki.is_a?(Raporlanir)
p tek.singleton_class.include?(Raporlanir)
p tek.singleton_class.ancestors.drop(1).take(2)
p Hesap3.include?(Raporlanir)
p oteki.singleton_class.ancestors.drop(1).take(2)
```

```
true
false
true
[Raporlanir, Hesap3]
false
[Hesap3, Object]
```

`tek` modülün örneği gibi davrandı, `oteki` davranmadı ve `Hesap3` sınıfı modülü hiç görmedi. Nesnenin kendi zincirinin başındaki halkadan sonra `Raporlanir`, ardından `Hesap3` geldi; `oteki` için aynı yerde doğrudan `Hesap3` ve `Object` var. Yani `extend` modülü sınıfın zincirine değil, o tek nesneye özgü zincire ekler. Bu zinciri tutan yapının adı tekil sınıftır ve sonraki derslerde ayrıca incelenecek.

#### Karışım ile Kalıtım Arasında Seçim

Modül ile üst sınıf arasındaki seçim, ilişkinin türüne bağlıdır. `Vadeli` bir `Hesap`'tır: aynı türün özelleşmiş bir hâli. `Hesap` ise raporlanabilir bir şeydir: türün bir yeteneği. Yetenekler farklı türlerde tekrar eder, örneğin hesap da fatura da raporlanabilir, ama aynı üst sınıfı paylaşmak zorunda değildir. Yeteneği modülde yazmak, onu iki ayrı kalıtım ağacına tek yerde yazılmış olarak vermenin yoludur.

Bir kısıt da var: Ruby'de bir sınıfın tek üst sınıfı olur ama istenen kadar karışımı olabilir. Zincir bu yüzden her zaman doğrusal kalır. Modüller zincire sıralı halkalar olarak girer ve ölçümde görüldüğü gibi bu sıra tahmin edilebilir: son karıştırılan başa gelir. Birden çok üst sınıf olsaydı zincirin hangi yoldan yürüyeceği ayrı bir kural gerektirirdi; tek zincir bunu gereksiz kılar.

Karışımın bir bedeli de yazanı dağıtmasıdır. Bir sınıfın yönteminin hangi modülden geldiğini görmek için sınıfın gövdesi yetmez, `ancestors` ve `yazan` gibi ölçümlere ihtiyaç duyulur. Bu ders boyunca kullanılan `yazan.rb`'nin işi tam olarak budur: tanımın yerini, sınıfın gövdesine bakmadan, çalışan nesneye sorarak bulmak.

#### Özet

- Modül bir `Module` örneğidir, `Class` örneği değildir; `Raporlanir.new` `NoMethodError` verir. `include` modülü sınıfın hemen üstüne, `Hesap` ile `Object` arasına karıştırır; `Hesap.include?(Raporlanir)` doğru, `Object.include?(Raporlanir)` yanlıştır.
- Karışımın yazanı atadır: `hesap.rapor` için kanıt `Raporlanir` çıktı; `Hesap.instance_methods(false)` ise boştur, yöntem sınıfa kopyalanmaz.
- `Comparable` karışımında yalnız `<=>` yazılır; `between?` ve `==` ata yazanıdır, `<=>` programcı yazanıdır.
- Aynı ad iki modülde varsa sonradan karıştırılan kazanır (`[C1, B1, A1]`); aynı modül zincirde bir kez durur ve ikinci `include` sırayı değiştirmez.
- Modül içinde `super` zincirde bir sonraki halkaya iner; modül içine karışan modül de sınıfa birlikte gelir (`Kutu.include?(Ust)`).
- Sınıf kendi tanımını yazarsa karışımı gölgeler ve yazan programcı olur; `extend` modülü yalnız tek nesneye karıştırır.

#### Sonraki Adım

`Hesap` sınıfının `komisyon` adlı bir yöntemi var ve `hesap.komisyon` dışarıdan çağrılınca `NoMethodError` veriyor; yine de `net` yöntemi aynı nesnenin içinden onu çağırabiliyor. Yazan hâlâ programcı, ama kim çağırabiliyor ve neden bazı çağrılar reddediliyor? Sonraki ders, Görünürlük, bu soruyu ölçer.

### Görünürlük

Source: https://academia.sh/tr/kurslar/ruby-nesne-yonelimi/gorunurluk

Genel, korumalı ve özel yöntemler; dışarıdan çağrının reddi, send ile public_send farkı, self alıcısı, korumalı yöntemin aynı sınıftaki başka nesnede çalışması ve alt sınıfta görünürlüğü değiştirmek.

Önceki ders sonunda `hesap.komisyon` çağrısının dışarıdan reddedildiğini, oysa `net` yönteminin aynı nesnenin içinden onu çağırabildiğini bıraktı. Yöntem aynı, yazan aynı, ama bir çağrı kabul edilirken öbürü reddediliyor. Fark yöntemin kendisinde mi, çağrıyı yapanda mı?

Bu dersin yazanı programcıdır: ölçülen yöntemlerin çoğunu sınıflar kendi gövdesinde `def` ile yazar; yazılmayan erişimciler ise sınırlayıcı ölçümde ayrıca ele alınır. Ders, yazanın değiştirmediği ikinci bir özelliği ölçer: yöntemin görünürlüğünü. M11/K03'te yöntemlerin kime açık olduğunu paketler üzerinden gördük; burada aynı soru tek bir sınıfın içinde sorulur.

#### Özel Yöntem Dışarıdan Çağrılmaz

Ruby'de üç görünürlük vardır: genel, korumalı ve özel. Genel yöntem herkese açıktır. Özel yöntem ise yalnız nesnenin kendi içinden, açık bir alıcı yazılmadan çağrılabilir. `private` anahtar sözcüğü, kendinden sonra tanımlanan yöntemleri özel yapar.

```ruby
# yazan.rb — bir yontemi kimin yazdigini siniflandiran yardimci

# Siniflandirma sirayla sorar: once alicinin zincirinde bir tanim var mi,
# sonra tanimi alicinin kendi sinifi mi tasiyor, sonra tanim nasil uretildi.
def yazan(alici, ad)
  zincir = alici.singleton_class
  unless zincir.method_defined?(ad) || zincir.private_method_defined?(ad)
    return ["kimse", "tanim yok"]
  end

  tanim = zincir.instance_method(ad)
  sahip = tanim.owner
  yer = sahip == zincir ? "tekil sinif" : sahip.to_s
  return ["ata", yer] unless sahip == alici.class || sahip == zincir

  iz = RubyVM::InstructionSequence.of(tanim)
  return ["kod", "#{yer}, ruby govdesi yok"] if iz.nil?
  return ["kod", "#{yer}, blok govdesi"] if iz.label.start_with?("block")

  ["programci", "#{yer}, def govdesi"]
end
```

```ruby
# ozel.rb — ozel yontem disaridan reddedilir
require_relative "yazan"

class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def net = @bakiye - komisyon

  private

  def komisyon = 2
end

hesap = Hesap.new("ece", 100)
p hesap.net
p yazan(hesap, :komisyon)
begin
  hesap.komisyon
rescue NoMethodError => e
  puts e.class
end
p hesap.send(:komisyon)
begin
  hesap.public_send(:komisyon)
rescue NoMethodError => e
  puts e.class
end
p Hesap.private_method_defined?(:komisyon)
p Hesap.public_method_defined?(:komisyon)
```

```
98
["programci", "Hesap, def govdesi"]
NoMethodError
2
NoMethodError
true
false
```

`hesap.net` 98 verdi: içeriden çağrı `komisyon`'u buldu. `yazan` programcı yazanını verdi, yani özel olmak yazanı değiştirmedi. Dışarıdan `hesap.komisyon` ise `NoMethodError` verdi. Aynı yöntemi `send` ile çağırmak çalıştı ve 2 verdi, `public_send` ise reddedildi. `send` görünürlüğü yok sayan, `public_send` ise onu uygulayan çağrıdır. Son iki satır görünürlüğü yöntem tanımı düzeyinde sorar: `komisyon` özel olarak kayıtlıdır, genel olarak değil.

**SM9.** Bu derste "görünürlük", bir yöntemin kimin tarafından çağrılabileceğini belirleyen üç değerli özelliktir: genel, korumalı, özel. Görünürlük yazanı değiştirmez; yazan tanımın nerede olduğunu söyler, görünürlük çağrının kabul edilip edilmediğini.

#### Kendi Alıcısı ve Özel Yöntem

Özel yöntemin kuralı bir alıcı kuralıdır: açık alıcıyla çağrı reddedilir. Açık alıcı `self` ise bu kural esner. Aşağıdaki dosya aynı yöntemi üç biçimde çağırır.

```ruby
# kendi_alicisi.rb — self alicisi ozel yontemi cagirabilir
class Hesap
  def initialize(bakiye)
    @bakiye = bakiye
  end

  def acik = komisyon
  def kendi = self.komisyon
  def baska(diger) = diger.komisyon

  private

  def komisyon = 2
end

a = Hesap.new(100)
b = Hesap.new(50)
p a.acik
p a.kendi
begin
  a.baska(b)
rescue NoMethodError => e
  puts e.class
end
```

```
2
2
NoMethodError
```

Alıcısız çağrı ve `self.komisyon` çalıştı, başka bir `Hesap` nesnesi üzerinden yapılan çağrı reddedildi. Özel yöntem bir sınıfa değil, bir nesneye özeldir: aynı sınıftan olsa bile başka bir nesne onun özel yöntemine ulaşamaz.

#### Korumalı Yöntem

İki hesabı karşılaştıran bir yöntem, karşı tarafın bakiyesine ihtiyaç duyar ama bakiyeyi herkese açmak istemeyebilir. Korumalı yöntem tam olarak bu iş içindir: dışarıdan çağrılamaz, ama aynı sınıftan başka bir nesne çağırabilir.

```ruby
# korumali.rb — korumali yontem ayni siniftan nesneye acik
class Kumbara
  def initialize(tutar)
    @tutar = tutar
  end

  def fazla_mi?(diger) = tutar > diger.tutar
  def gizli_fazla_mi?(diger) = gizli > diger.gizli

  protected

  def tutar = @tutar

  private

  def gizli = @tutar
end

a = Kumbara.new(10)
b = Kumbara.new(7)
p a.fazla_mi?(b)
begin
  a.tutar
rescue NoMethodError => e
  puts e.class
end
begin
  a.gizli_fazla_mi?(b)
rescue NoMethodError => e
  puts e.class
end
p Kumbara.protected_method_defined?(:tutar)
```

```
true
NoMethodError
NoMethodError
true
```

`fazla_mi?` içinde `diger.tutar` çağrısı çalıştı: korumalı yöntem, çağıran nesne aynı sınıftan olduğu sürece başka bir nesnede kabul edilir. Dışarıdan `a.tutar` reddedildi. Benzer yapıdaki `gizli_fazla_mi?` ise `diger.gizli` satırında `NoMethodError` verdi, çünkü özel yöntem açık alıcıyı hiç kabul etmez. Korumalı yöntemin sınırı sınıftır, özel yöntemin sınırı nesne.

Korumalı yöntemin "aynı sınıftan" ölçütü tam olarak neyi kapsar? Aşağıdaki dosya çağıranı üst sınıf, alt sınıf ve ilgisiz bir sınıf olarak değiştirir.

```ruby
# korumali_soy.rb — korumali yontemin siniri sahibin soyudur
class Kumbara
  def initialize(tutar)
    @tutar = tutar
  end

  def fazla_mi?(diger) = tutar > diger.tutar

  protected

  def tutar = @tutar
end

class Altin < Kumbara
end

class Yabanci
  def sor(kumbara) = kumbara.tutar
end

p Kumbara.new(10).fazla_mi?(Altin.new(4))
p Altin.new(4).fazla_mi?(Kumbara.new(10))
begin
  Yabanci.new.sor(Kumbara.new(1))
rescue NoMethodError => e
  puts e.class
end
```

```
true
false
NoMethodError
```

Üst sınıf nesnesi alt sınıf nesnesinin korumalı yöntemini, alt sınıf nesnesi de üst sınıf nesnesininkini çağırabildi (sonuçlar `true` ve `false`: 10 > 4 ve 4 > 10): ölçüt çağıranın, yöntemin sahibi olan sınıfın soyundan olmasıdır. `Yabanci` bu soydan değil, bu yüzden aynı çağrı ona kapalı kaldı. Korumalı yöntemin sınırı bu yüzden dar bir sınıf adı eşleşmesi değil, kalıtım ağacıdır.

**SM10.** Bu derste "genel" herkese açık, "korumalı" aynı sınıftan nesnelere açık, "özel" yalnız alıcısız ya da `self` alıcılı çağrıya açık yöntemdir. Dışarıdan açık alıcıyla yapılan çağrı genel dışında her durumda reddedilir.

#### private Bir Değer Döndürür

`private` yalnız bir bildirim değildir; bir yöntem tanımının önüne konunca onu da özel yapar. `def` ifadesi yöntemin adını bir sembol olarak döndürür ve `private` o sembolü alır.

```ruby
# private_def.rb — private def tek yontemi ozel yapar
class Erisim
  private def yalniz_bu = 1

  def acik = 2
end

erisim = Erisim.new
p erisim.acik
p Erisim.private_instance_methods(false)
p Erisim.public_instance_methods(false)
begin
  erisim.yalniz_bu
rescue NoMethodError => e
  puts e.class
end
```

```
2
[:yalniz_bu]
[:acik]
NoMethodError
```

`private def yalniz_bu = 1` yalnız o yöntemi özel yaptı; sonraki `acik` genel kaldı. Böylece görünürlük, bir satır boyunca değil, bir yöntem boyunca da yazılabilir.

`private`'ın aldığı değeri doğrudan görmek için `def` ifadesinin dönüşünü ve `private` çağrısının kendi dönüşünü bastırmak yeter. Aşağıdaki dosya ikisini ölçer.

```ruby
# private_deger.rb — def sembol dondurur, private aldigini dondurur
class Erisim
  p(def ilk = 1)
  p(private def ikinci = 2)
  p(private(:ilk))
end
```

```
:ilk
:ikinci
:ilk
```

`def` ifadesi yöntemin adını sembol olarak döndürdü, `private def` ise aldığı sembolü aynen geri verdi. `private(:ilk)` de aldığı adı döndürdü. Bu yüzden `private def yalniz_bu = 1` yazımı çalışır: iç ifade bir ad üretir, dış çağrı o adı alıp görünürlüğünü değiştirir. Aynı sözleşme, görünürlüğü sonradan değiştirmenin de bir `private :ad` çağrısı olduğunu gösterir; tanımın yeri değişmez, yalnız kaydı değişir.

#### Soru Soran Yöntemler ve Kalıtım

Görünürlük iki yerde daha karşımıza çıkar. Birincisi `respond_to?` sorusudur: bir nesnenin yanıt verip vermediği, çağrının dışarıdan kabul edilip edilmediğine göre değişir. İkincisi kalıtımdır: özel yöntem alt sınıfa geçer mi? Aşağıdaki dosya ikisini ölçer.

```ruby
# soru_ve_kalitim.rb — respond_to? ve alt siniftan cagri
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  private

  def komisyon = 2
end

class Vadeli < Hesap
  def net_vadeli = @bakiye - komisyon * 2
  def disardan(diger) = diger.komisyon
end

v = Vadeli.new("can", 50)
p v.respond_to?(:komisyon)
p v.respond_to?(:komisyon, true)
p v.net_vadeli
begin
  v.disardan(Vadeli.new("a", 1))
rescue NoMethodError => e
  puts e.class
end
p Vadeli.private_method_defined?(:komisyon)
```

```
false
true
46
NoMethodError
true
```

`respond_to?(:komisyon)` yanlış verdi, `respond_to?(:komisyon, true)` ise doğru: ikinci argüman özel yöntemleri de soruya katar. `Vadeli` kendi gövdesinde `komisyon`'u alıcısız çağırabildi ve `net_vadeli` `46` verdi; özel yöntem alt sınıfa geçer, çünkü kural çağrının biçimine bakar, tanımın sahibine değil. Ama `diger.komisyon` gibi açık alıcılı çağrı, alt sınıfın içinde bile olsa reddedildi. `Vadeli.private_method_defined?(:komisyon)` doğru: görünürlük de zincir boyunca taşınır.

#### Özel Yazıcı ve Özel Sınıf Yöntemi

Alıcı kuralının `self` ile esnemesi en çok yazıcı yöntemlerde işe yarar: `deger = 5` yazımı yerel değişken tanımlar, yöntem çağırmaz; yazıcıyı çağırmak için `self.deger = 5` gerekir. Aşağıdaki dosya özel bir yazıcıyı bu yoldan çağırır. İkinci bölüm sınıf yöntemlerinin görünürlüğünü ölçer.

```ruby
# yazici_ve_sinif.rb — self yazicisi ve private_class_method
class Sayac
  def initialize
    @deger = 0
  end

  def artir
    self.deger = deger + 1
    deger
  end

  private

  attr_accessor :deger
end

class Form
  def self.kur = new
  private_class_method :new
end

s = Sayac.new
p s.artir
p s.artir
begin
  s.deger = 9
rescue NoMethodError => e
  puts e.class
end
p Form.kur.class
begin
  Form.new
rescue NoMethodError => e
  puts e.class
end
```

```
1
2
NoMethodError
Form
NoMethodError
```

`artir` özel `deger=` yazıcısını `self.deger = ...` biçiminde çağırdı ve sayaç `1`, sonra `2` oldu; dışarıdan `s.deger = 9` ise reddedildi. Sınıf yöntemleri için aynı kural `private_class_method` ile uygulanır: `Form.new` reddedildi, ama `Form.kur` sınıfın kendi içinden `new`'ü alıcısız çağırdığı için nesneyi üretti. Böylece nesne üretiminin tek kapısı genel bir sınıf yöntemi olur.

#### Sınırlayıcı Ölçüm

Görünürlük yazanı değiştirmedi. Peki `attr_reader` gibi bir üretici özel bir erişimci doğurduğunda yazan kim olur? Ve bir alt sınıf üst sınıfın özel yöntemini genel yaparsa, yöntem kimin olur?

```ruby
# sinirlayici.rb — private attr_reader ve alt sinifta public
require_relative "yazan"

class Kumbara
  def initialize(tutar)
    @tutar = tutar
  end

  private attr_reader :tutar
end

class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  private

  def komisyon = 2
end

class Hesap3 < Hesap
  public :komisyon
end

p yazan(Kumbara.new(3), :tutar)

h = Hesap3.new("ece", 100)
p h.komisyon
p h.method(:komisyon).owner
p yazan(h, :komisyon)
p Hesap.new("a", 1).respond_to?(:komisyon)
```

```
["kod", "Kumbara, ruby govdesi yok"]
2
Hesap3
["programci", "Hesap3, def govdesi"]
false
```

`private attr_reader :tutar` ile üretilen erişimci özel olsa da `yazan` kod yazanını verdi: tanım `def` ile değil `attr_reader` çağrısıyla üretildi ve Ruby'de gövdesi yok. Ruby 4.0.7'de `Hesap3`'teki `public :komisyon` ise iki sonucu değiştirdi: `h.komisyon` artık 2 verdi ve yöntemin sahibi `Hesap3` çıktı, `Hesap` değil. Bu sonuç, görünürlük çağrısının ne yaptığına bakılınca anlaşılır: üst sınıfın tanımı olduğu gibi kalır, `public :komisyon` ise alt sınıfa aynı adlı bir kayıt koyar. `yazan` zincirde ilk bulunan kaydı sayar, o kayıt `Hesap3`'te durur, bu yüzden programcı ve `Hesap3, def govdesi` çıktı. Üst sınıfın kendi nesnesi `respond_to?` sorusuna yanlış demeye devam etti: görünürlüğü değiştirmek yalnız alt sınıfı etkiler. Bu açıklamanın doğruluğunu aşağıdaki ölçüm sınar.

```ruby
# sinirlayici_neden.rb — public :ad alt sinifa kayit koyar
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  private

  def komisyon = 2
end

class Hesap3 < Hesap
  public :komisyon
end

h = Hesap3.new("ece", 100)
p Hesap3.instance_methods(false)
p Hesap.instance_method(:komisyon).owner
p Hesap3.instance_method(:komisyon).owner
p h.method(:komisyon).super_method.nil?
p Hesap.private_instance_methods(false)
p Hesap.public_method_defined?(:komisyon)
```

```
[:komisyon]
Hesap
Hesap3
true
[:komisyon, :initialize]
false
```

`Hesap3.instance_methods(false)` `komisyon`'u listeledi: alt sınıfa gerçekten bir kayıt eklendi, oysa gövdesinde tek satır tanım yok. `Hesap`'ın tanımı `Hesap` sahipli kaldı, `Hesap.private_instance_methods(false)` onu hâlâ özel olarak sıraladı (listedeki `initialize` her sınıfın özel yöntemidir) ve `Hesap.public_method_defined?` yanlış verdi. Alt sınıftaki kaydın `super_method`'u ise `nil` çıktı: Ruby 4.0.7 bu kaydı ayrı bir gövde olarak değil, üst sınıfın tanımına bakan bir görünürlük kaydı olarak tutar ve zincirde ikinci bir halka saymaz. Görünürlük, tanımın bir özelliği değil, tanımı zincirde bulan kaydın özelliğidir.

**SM15.** Bu derste "kayıt", bir sınıfın yöntem tablosundaki ad ve görünürlük çiftidir; genel ve korumalı kayıtlar `instance_methods(false)`, özel kayıtlar `private_instance_methods(false)` ile okunur. Görünürlük çağrısı kaydı alt sınıfa koyar; tanımın gövdesini kopyalamaz.

#### Hangi Görünürlük Ne Zaman

Üç görünürlüğün seçimi, bir yöntemin kime hizmet ettiğine bağlıdır. Sınıfın dışına verdiği sözleşme genel yöntemlerdir: onları çağıran kodlar var olabilir ve sınıf onları değiştirirse o kodlar kırılır. `Hesap` örneğinde `net` böyle bir sözleşmedir. `komisyon` ise `net`'in nasıl hesaplandığını anlatan bir ayrıntıdır; özel yapıldığı için sınıf onu istediği gibi değiştirebilir, çünkü dışarıdan çağıran kod yoktur.

Korumalı yöntem bu ikisinin arasındaki dar bir boşluğu doldurur: sınıfın nesneleri birbirleriyle konuşurken kullandığı, ama dışarıya açılmayan sözleşme. `Kumbara` örneğinde `tutar` böyledir. Korumalı yöntemin gerektiği yer, karşılaştırma gibi iki nesneyi bir arada ele alan yöntemlerdir: `fazla_mi?` öbür nesnenin `tutar`'ını ancak korumalı olduğu için okudu. Yöntemi yalnız nesnenin kendisi çağırıyorsa özel yapıp `self` alıcısıyla çağırmak yeter.

Bu derste görülen bir başka sonuç, görünürlüğün bir güvenlik önlemi olmadığıdır. `send` özel yöntemi çağırabildi; yani görünürlük bir sözleşmeyi belgeler ve yanlışlıkla çağrıyı engeller, kasıtlı bir çağrıyı durdurmaz. Bu yüzden özel bir yöntemi `send` ile çağıran kod, sınıfın izin vermediği bir şeyi bilerek yapar ve sınıf o yöntemi değiştirdiğinde ilk kırılan o olur.

#### Özet

- Özel yöntem dışarıdan açık alıcıyla çağrılamaz ve `NoMethodError` verir; nesnenin içinden alıcısız ya da `self` alıcısıyla çağrılabilir.
- `send` görünürlüğü yok sayar ve 2 verir; `public_send` uygular ve reddeder.
- Korumalı yöntem aynı sınıftan başka nesnede çağrılabilir; özel yöntem başka bir nesnede çağrılamaz.
- `private def x = 1` yalnız o yöntemi özel yapar; görünürlük `private_instance_methods(false)` ile sorulur.
- Bu dersin yazanı programcıdır: `komisyon` için `yazan` programcı, `private attr_reader :tutar` için kod verdi.
- Alt sınıfta `public :komisyon` görünürlüğü yalnız o sınıfta değiştirir; alt sınıfa bir kayıt koyar, `Hesap`'ın özel tanımı yerinde kalır ve sahip `Hesap3` çıkar.

#### Sonraki Adım

`Kasa` sınıfına bir `Kilit` modülü karıştırılmış ve her ikisi de `ac` tanımlıyor. `Kasa.new.ac` çağrısında hangi tanım önce çalışıyor, ve `super` zinciri bu sırayı nasıl izliyor? Sonraki ders, Yöntem Arama Zinciri, bu soruyu ölçer.

### Yöntem Arama Zinciri

Source: https://academia.sh/tr/kurslar/ruby-nesne-yonelimi/yontem-arama-zinciri

Bir ileti alıcıya ulaşınca yöntemin ata zincirinde nasıl arandığı; önüne eklenen modül, super_method, zincirin sonu, sınıf yöntemlerinin zinciri ve tek nesneye eklenen tekil yöntem.

Önceki ders sonunda `Kasa` sınıfında ve karıştırılmış `Kilit` modülünde aynı adlı `ac` yönteminin olduğunu, ve `Kasa.new.ac` çağrısında hangisinin çalıştığını sormayı bıraktı. Önceki üç ders bu sorunun parçalarını ayrı ayrı yanıtladı: sınıf kendi tanımını yazar, üst sınıf devralır, modül karıştırılır. Hepsi aynı arama sırasının içinde.

Bu dersin yazanı ata yazanıdır, ama bu kez atanın bir üst sınıf mı yoksa bir modül mü olduğu önemli değildir. M13/K01'de yazanı tanımın yazarı üzerinden ölçmüştük; burada yazar sorusu aynı kalır, arama sırasının kendisi ölçülür. Tanımın üst sınıfta mı modülde mi durduğu ayrılmaz: ikisi tek bir kategoridir, ata.

#### Önüne Eklenen Modül

`include` modülü sınıfın üstüne yerleştirir, yani sınıf kazanır. `prepend` ise modülü sınıfın önüne yerleştirir; alıcıya ulaşan ileti önce modüle gider. Aşağıdaki dosya bir `Kilit` modülünü `Kasa`'ya `prepend` ile ekler.

```ruby
# yazan.rb — bir yontemi kimin yazdigini siniflandiran yardimci

# Siniflandirma sirayla sorar: once alicinin zincirinde bir tanim var mi,
# sonra tanimi alicinin kendi sinifi mi tasiyor, sonra tanim nasil uretildi.
def yazan(alici, ad)
  zincir = alici.singleton_class
  unless zincir.method_defined?(ad) || zincir.private_method_defined?(ad)
    return ["kimse", "tanim yok"]
  end

  tanim = zincir.instance_method(ad)
  sahip = tanim.owner
  yer = sahip == zincir ? "tekil sinif" : sahip.to_s
  return ["ata", yer] unless sahip == alici.class || sahip == zincir

  iz = RubyVM::InstructionSequence.of(tanim)
  return ["kod", "#{yer}, ruby govdesi yok"] if iz.nil?
  return ["kod", "#{yer}, blok govdesi"] if iz.label.start_with?("block")

  ["programci", "#{yer}, def govdesi"]
end
```

```ruby
# one_eklenen.rb — prepend ile modul sinifin onune gecer
require_relative "yazan"

module Kilit
  def ac = "kilit acildi, #{super}"
end

class Kasa
  prepend Kilit

  def ac = "kasa acildi"
end

kasa = Kasa.new
puts kasa.ac
p yazan(kasa, :ac)
p Kasa.ancestors.take(3)
p kasa.method(:ac).owner
p kasa.method(:ac).super_method.owner
```

```
kilit acildi, kasa acildi
["ata", "Kilit"]
[Kilit, Kasa, Object]
Kilit
Kasa
```

`Kasa.new.ac` iki parçayı birleştirdi: önce `Kilit`'in ön eki, ardından `super` ile `Kasa`'nın kendi tanımı. `yazan` ata yazanını ve kanıt olarak `Kilit`'i verdi: zincirde ilk bulunan tanım `Kilit`'te, çünkü `Kasa.ancestors` listesinde `Kilit` `Kasa`'nın önünde durur. `method(:ac).owner` `Kilit`, `super_method.owner` `Kasa` çıktı. Arama soldan sağa yürür, ilk eşleşme çalışır ve `super` o noktadan zincirde bir adım ilerler.

**SM11.** Bu derste "yöntem arama", bir ileti alıcıya ulaştığında Ruby'nin alıcının ata zincirini `ancestors` sırasıyla soldan sağa gezip adı eşleşen ilk tanımı bulmasıdır. "Önüne eklemek", `prepend` ile bir modülü sınıfın önüne yerleştirmektir; `include` ise modülü sınıfın arkasına yerleştirir.

#### include ile prepend Yan Yana

İki anahtar sözcüğün farkı, aynı modülle aynı sınıfı iki kez kurup zincirleri karşılaştırınca görünür. Aşağıdaki dosya `Kilit`'i bir sınıfa `include`, öbürüne `prepend` ile ekler; ardından iki modülü art arda önüne ekleyerek sırayı da sınar.

```ruby
# include_prepend.rb — iki anahtar sozcugun zincirdeki yeri
module Kilit
  def ac = "kilit acildi"
end

module Alarm
  def ac = "alarm, #{super}"
end

class KasaI
  include Kilit

  def ac = "kasa acildi"
end

class KasaP
  prepend Kilit

  def ac = "kasa acildi"
end

class KasaIki
  prepend Kilit
  prepend Alarm

  def ac = "kasa acildi"
end

p KasaI.ancestors.take(3)
puts KasaI.new.ac
p KasaP.ancestors.take(3)
puts KasaP.new.ac
p KasaIki.ancestors.take(4)
puts KasaIki.new.ac
```

```
[KasaI, Kilit, Object]
kasa acildi
[Kilit, KasaP, Object]
kilit acildi
[Alarm, Kilit, KasaIki, Object]
alarm, kilit acildi
```

`include` ile `KasaI` zincirinde sınıf önce geldi ve `Kilit` arkasına düştü; `ac` çağrısı sınıfın kendi tanımında durdu, modülün tanımına hiç ulaşmadı. `prepend` ile `KasaP` zincirinde `Kilit` sınıfın önüne geçti, ama `Kilit#ac` `super` çağırmadığı için bu kez sınıfın tanımı çalışmadı. Yani önüne eklenen modül, `super` yazmazsa sınıfı tamamen gölgeler. `KasaIki` zincirinde ikinci `prepend` ilkinin önüne girdi: sonradan eklenen alıcıya en yakın durur, tıpkı `include` sırasında olduğu gibi. Üç sınıfta da kural aynıdır; yalnız halkanın sınıfın önüne mi arkasına mı gireceği değişir.

Zinciri sonuna kadar yürümek için `super_method` bir döngüde de kullanılabilir. Aşağıdaki dosya tekil yöntemi, önüne eklenen modülü ve sınıfı üst üste koyar ve her halkanın sahibini sırayla toplar. Tekil sınıfın adı bellek adresi taşıdığı için sahip, çıktıya adıyla değil `:tekil` etiketiyle yazılır.

```ruby
# zinciri_yuru.rb — super_method ile halkalari saymak
module Kilit
  def ac = "kilit acildi, #{super}"
end

class Kasa
  prepend Kilit

  def ac = "kasa acildi"
end

k = Kasa.new
def k.ac = "tekil, #{super}"

sahipler = []
yontem = k.method(:ac)
while yontem
  sahip = yontem.owner
  sahipler << (sahip == k.singleton_class ? :tekil : sahip)
  yontem = yontem.super_method
end

p sahipler
puts k.ac
p Kasa.new.method(:ac).super_method.super_method
```

```
[:tekil, Kilit, Kasa]
tekil, kilit acildi, kasa acildi
nil
```

Döngü üç halka buldu: tekil sınıf, `Kilit` ve `Kasa`; bu sıra `ancestors` okuması ile birebir örtüşür. `puts k.ac` her halkanın kendi parçasını aynı sırayla birleştirdi. Tekil yöntemi olmayan bir `Kasa` nesnesinde zincir iki halkadır ve `super_method.super_method` `nil` verir: `Kasa`'dan sonra zincirde `ac` tanımlayan başka halka yoktur.

#### Zincirin Sonu

Zincir sonsuz değildir. `Object`'ten türeyen her sınıfın zinciri `Object`, `Kernel` ve `BasicObject` ile biter. Aşağıdaki dosya `Hesap` sınıfının zincirini sonuna kadar okur ve son duraktaki özel bir yöntemi sorar.

```ruby
# zincirin_sonu.rb — zincir BasicObject ile biter
class Hesap
end

p Hesap.ancestors
p Hesap.ancestors.last
p Hesap.ancestors.size
p BasicObject.private_method_defined?(:method_missing)
p Kernel.private_method_defined?(:puts)
```

```
[Hesap, Object, Kernel, BasicObject]
BasicObject
4
true
true
```

Boş bir `Hesap` sınıfının zinciri `[Hesap, Object, Kernel, BasicObject]` dizisidir ve `ancestors.size` dördü verdi; karıştırılmış bir modül olsaydı beş olurdu. `BasicObject` zincirin son durağıdır. `method_missing` orada özel olarak tanımlıdır: zincirde ad bulunamazsa son çare olarak bu yöntem çağrılır ve varsayılan davranışı `NoMethodError` yükseltmektir. `puts` ise `Kernel`'de özel olarak durur; bu yüzden her nesnenin içinden alıcısız çağrılabilir, ama `"x".puts` yazılamaz.

Zincirin sonuna ulaşıp ad bulunamazsa ne olacağı `BasicObject`'in özel `method_missing` tanımına bağlıdır. Bir sınıf bu yöntemi kendi gövdesinde yeniden tanımlarsa, eşleşmeyen ileti ona düşer. Aşağıdaki dosya iki sınıfın aynı eşleşmeyen çağrıya verdiği yanıtı karşılaştırır.

```ruby
# son_care.rb — zincirde bulunamayan ad method_missing'e duser
class Hesap
end

class Hesap2
  def method_missing(ad, *) = "yok: #{ad}"
  def respond_to_missing?(ad, ozel = false) = false
end

begin
  Hesap.new.baska
rescue NoMethodError => e
  puts e.class
end

puts Hesap2.new.baska
p Hesap2.new.respond_to?(:baska)
p Hesap2.instance_method(:method_missing).owner
p Hesap.instance_method(:method_missing).owner
```

```
NoMethodError
yok: baska
false
Hesap2
BasicObject
```

`Hesap` için zincir boyunca ad bulunamadı, iletiyi `BasicObject#method_missing` aldı ve `NoMethodError` yükseltti. `Hesap2` kendi `method_missing`'ini yazdığı için ileti zincirin sonuna inmeden, sınıfın kendi tanımına düştü. `respond_to?` yanlış verdi; bir nesnenin böyle yanıt vermesi, onun yöntem tanımlamış olduğu anlamına gelmez. `owner` karşılaştırması `Hesap2` için sınıfın kendisini, `Hesap` için `BasicObject`'i gösterdi. Yani zincirin sonu bir varsayılan davranıştır ve sınıf onu zincirin herhangi bir halkasında değiştirebilir.

**SM16.** Bu derste "zincirin sonu", alıcının zincirinde adın bulunamadığı durumda iletiyi alan `method_missing` çağrısıdır. Varsayılan tanımı `BasicObject`'tedir ve `NoMethodError` yükseltir; zincirde daha yakın bir halka onu yeniden tanımlayabilir.

#### Sınıf Yöntemlerinin Zinciri

`Hesap.new` çağrısındaki `new` bir yöntemdir ve alıcısı `Hesap` sınıfının kendisidir. Sınıfın da bir zinciri vardır. Bu zincir, sınıfa özel tekil sınıf üzerinden kurulur.

```ruby
# sinif_zinciri.rb — sinifin kendi zinciri
class Hesap
end

p Hesap.method(:new).owner
p Hesap.singleton_class
p Hesap.singleton_class.ancestors.take(4)
```

```
Class
#<Class:Hesap>
[#<Class:Hesap>, #<Class:Object>, #<Class:BasicObject>, Class]
```

`Hesap.new` için `new` yönteminin sahibi `Class` çıktı: sınıf yöntemleri de aynı arama kuralıyla bulunur, yalnız alıcı bir sınıftır ve zincir onun tekil sınıfından başlar. Sınıfın tekil sınıfı `#<Class:Hesap>` adıyla yazdırılır. Zincir bu tekil sınıftan `Object`'in tekil sınıfına, `BasicObject`'in tekil sınıfına ve en sonunda `Class`'a doğru yürür. Yani üst sınıfta tanımlı bir sınıf yöntemi, alt sınıftan çağrılınca da bulunur.

Zincirin tekil sınıflardan sonra neden `Class`'a bağlandığı ve alt sınıfın üst sınıfın sınıf yöntemini nasıl bulduğu iki ayrı soru. Aşağıdaki dosya ikisini ölçer; karşılaştırmalar çıktıdaki adla değil, eşitlik sorusuyla yapılır, böylece sonuç adın yazılışına değil nesnelerin kendisine dayanır.

```ruby
# sinif_zinciri_ek.rb — sinif yontemi alt siniftan bulunur
class Hesap
  def self.varsayilan = new("-", 0)

  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end
end

class Vadeli < Hesap
end

p Vadeli.varsayilan.class
p Vadeli.method(:varsayilan).owner == Hesap.singleton_class
p Vadeli.singleton_class.superclass == Hesap.singleton_class
p Vadeli.singleton_methods
p Vadeli.singleton_methods(false)
p BasicObject.singleton_class.superclass
p Hesap.singleton_class.ancestors.drop(3)
```

```
Vadeli
true
true
[:varsayilan]
[]
Class
[Class, Module, Object, Kernel, BasicObject]
```

`Vadeli.varsayilan` bir `Vadeli` nesnesi üretti: yöntem `Hesap`'ın tekil sınıfında yazılı olduğu hâlde alıcı `Vadeli` olduğu için `new` ona uygulandı. `owner` karşılaştırması tanımın `Hesap`'ın tekil sınıfında durduğunu, `superclass` karşılaştırması `Vadeli`'nin tekil sınıfının `Hesap`'ın tekil sınıfına bağlandığını gösterdi. `singleton_methods` üst sınıftan gelen sınıf yöntemini de saydı, `singleton_methods(false)` ise boş kaldı. Zincirin tekil sınıflardan sonra `Class`'a geçmesinin nedeni de buradan okunur: `BasicObject`'in tekil sınıfının üst sınıfı `Class`'tır, o noktadan sonra zincir `Module`, `Object`, `Kernel` ve `BasicObject` ile sıradan sınıf zincirine karışır.

#### Sınırlayıcı Ölçüm

Şimdiye dek zincirler sınıflardan ve modüllerden kuruluydu. Bir nesnenin zinciri o nesneye özgü bir halka da taşıyabilir: tekil sınıf. Aşağıdaki dosya tek bir `Kasa` nesnesine bir tekil yöntem ekler ve bir modül `extend` eder.

```ruby
# tekil.rb — tek nesneye ozgu halka
require_relative "yazan"

module Ek
  def ac = "ek, #{super}"
end

module Kilit
  def ac = "kilit acildi, #{super}"
end

class Kasa
  prepend Kilit

  def ac = "kasa acildi"
end

k = Kasa.new
def k.ac = "tekil, #{super}"

puts k.ac
puts Kasa.new.ac
p yazan(k, :ac)
p k.singleton_methods

j = Kasa.new
j.extend(Ek)
p j.singleton_class.ancestors.drop(1).take(4)
puts j.ac
```

```
tekil, kilit acildi, kasa acildi
kilit acildi, kasa acildi
["programci", "tekil sinif, def govdesi"]
[:ac]
[Ek, Kilit, Kasa, Object]
ek, kilit acildi, kasa acildi
```

`def k.ac` tek bir nesneye bir tekil yöntem ekledi. `k.ac` önce kendi ön ekini, sonra zincirin geri kalanını verdi; yeni üretilen bir `Kasa` ise etkilenmedi. Yazan programcı çıktı, kanıt olarak `tekil sinif, def govdesi`: kuralın dilinde tanımın sahibi nesnenin tekil sınıfıdır. `k.singleton_methods` yalnız `[:ac]` verdi. `j.extend(Ek)` ise `Ek` modülünü `j` nesnesinin tekil sınıfına karıştırdı ve `j.singleton_class.ancestors.drop(1)` listesi `Ek`'le başlayıp ardından `Kilit`, `Kasa`, `Object` ile devam etti. Zincirin başındaki tekil sınıf adı çıktıya yazılmadı, çünkü o ad bellek adresi taşır.

**SM12.** Bu derste "tekil sınıf", tek bir nesneye ya da bir sınıfa özgü, ata zincirinin en başına eklenen gizli sınıftır. "Tekil yöntem", o sınıfta tanımlanan yöntemdir. Başka nesneler bu zinciri paylaşmaz.

#### Yöntemi Kimin Verdiğini Sormak

Zincir okunurken sık yapılan bir ayrım, `method` ile `instance_method` arasındadır. İlki nesneye, ikincisi sınıfa sorar; bu yüzden tekil yöntemi yalnız ilki görür. Aşağıdaki dosya ikisini ve `Kernel`'den gelen bir yöntemi yan yana koyar.

```ruby
# kim_verdi.rb — method ile instance_method farki
module Kilit
  def ac = "kilit acildi"
end

class Kasa
  prepend Kilit

  def ac = "kasa acildi"
end

k = Kasa.new
def k.ac = "tekil acildi"

p k.method(:ac).owner == k.singleton_class
p Kasa.instance_method(:ac).owner
p Kasa.new.method(:ac).owner
p k.method(:puts).owner
p k.method(:class).owner
p Kasa.instance_method(:ac).super_method.owner
```

```
true
Kilit
Kilit
Kernel
Kernel
Kasa
```

`method` nesneye sorduğu için ilk satırda tekil sınıfın sahip çıktığı görüldü. `instance_method` yalnız sınıfın zincirine baktı; tekil yöntem o zincirde olmadığından cevap `Kilit` oldu, yani önüne eklenen modül sınıfın önünde durduğu için sahip olarak o göründü. `Kasa.new` ile tekil yöntemi olmayan bir nesnede de `method` aynı cevabı verdi. `puts` ve `class` için sahip `Kernel` çıktı: bu iki yöntemi yazan ne programcıdır ne sınıf, `Object` zincirine karışan `Kernel` modülüdür. Son satır, `Kilit#ac`'ın bir altındaki halkayı sorar ve `Kasa#ac`'ı bulur; tek halkalık bir önüne ekleme bile `super_method` ile iki halkalık bir zincir olarak okunabilir.

Bu soruların hepsi aynı alışkanlığa çıkar: bir yöntemin hangi tanımla çalışacağı, tanımın metne göre nerede yazıldığıyla değil, alıcının zincirinde hangi halkanın önce geldiğiyle belirlenir. Sahip sorusu, o halkanın adını verir.

#### Aramayı Okumanın Üç Kuralı

Yukarıdaki ölçümlerden arama için üç kural çıkar. Birincisi, sıra alıcının zincirinden gelir: yöntemin hangi sınıfta yazıldığına değil, alıcının hangi zincire sahip olduğuna bakılır. İkincisi, zincirdeki ilk eşleşmeyi alır, öbür tanımlar yalnız `super` ile yeniden çağrılırsa çalışır. Üçüncüsü, `super` zincirin başına dönmez, bulunduğu halkadan bir sağa ilerler. `Kilit#ac` içindeki `super` bu yüzden `Kilit`'e değil `Kasa`'ya iner.

Bu kurallar `include`, `prepend` ve tekil yöntemin tek bir mekanizmanın üç görünümü olduğunu da söyler. Üçü de zincire bir halka ekler; fark halkanın nereye gireceğidir. `include` halkayı sınıfın arkasına koyar, `prepend` önüne, tekil sınıf ise nesnenin kendi zincirinin en başına. Hangi halkanın kazandığını anlamak için tahmin yürütmek gerekmez: `ancestors` dizisini soldan sağa okumak yeter. Tekil sınıf listede görünmez, ama `singleton_class.ancestors` ile sorulursa zincirin başında durur.

Zincirin okunabilir olması, hata ayıklamada da işe yarar. Bir yöntem beklenmeyen bir sonuç verdiğinde ilk soru hangi tanımın çalıştığıdır. Bunun yanıtı `method(:ad).owner` satırıdır. İkinci soru o tanımın arkasında başka bir tanım olup olmadığıdır; yanıt `super_method` ile bir halka daha sorularak alınır ve `nil` dönene kadar tekrarlanır. `super_method` zincirdeki sıradaki tanımı verir; tanımın `super` çağırıp çağırmadığını yöntemin gövdesi gösterir. Bu iki yöntemle zincir tahmin edilmez, okunur.

#### Özet

- Yöntem arama, alıcının `ancestors` zincirinde soldan sağa yürür ve adı eşleşen ilk tanımı çalıştırır.
- `prepend` modülü sınıfın önüne koyar; `Kasa.new.ac` önce `Kilit`'i, `super` ile sonra `Kasa`'yı çalıştırır.
- `method(:ac).owner` ilk tanımın sahibini, `super_method.owner` zincirde bir sonrakini verir.
- `Object`'ten türeyen bir sınıfın zinciri `BasicObject` ile biter; `method_missing` orada, `puts` ise `Kernel`'de özel olarak durur.
- Sınıf yöntemleri sınıfın tekil sınıf zincirinden gelir: `Hesap.new` için `new` yönteminin sahibi `Class`'tır.
- Bu dersin yazanı atadır; tek nesneye eklenen tekil yöntem ise zincirin başına girer ve yazanı programcı yapar.

#### Sonraki Adım

`Nokta` adlı bir `Struct` sınıfında `x` yöntemi hiç `def` ile yazılmadı, yine de `nokta.x` çalışıyor ve `yazan` onun yazanını kod olarak veriyor. Yöntemleri bir nesneden çalışma zamanında sormak ve `x`'i kimin yazdığını nesneye sordurmak mümkün mü? Sonraki ders, Yansıma, nesneye bu soruları sormayı ölçer.

## Metaprogramlama

Nesneleri çalışma zamanında sorgulamak, kod üreten kodla yöntem tanımlamak, eksik yöntemleri yakalayan vekil nesneler ve riskleri ve bildirimsel arayüzlerle alan özel diller kurmak.

### Yansıma

Source: https://academia.sh/tr/kurslar/ruby-nesne-yonelimi/yansima

Bir nesneye kendi yöntemlerini, durumunu ve türünü çalışma zamanında sormanın ve bir yöntemi kimin yazdığını bu sorularla sınıflandırmanın ölçümü.

Önceki ders, aynı iletinin aynı sınıfın iki örneğinde iki farklı yazana ulaşabildiğini gösterdi: tek bir nesnenin tekil sınıfı, yöntem arama zincirinin en önünde durur. O dersin son cümlesi bir soru bıraktı: `Nokta`'nın `x` yöntemini kimin yazdığını, dosyayı açıp bakmadan, nesnenin kendisine çalışma zamanında sorabilir miyiz? Bu ders o soruya cevap verir. **Yansıma (reflection)**, bir programın kendi nesnelerini, sınıflarını ve yöntemlerini çalışma sırasında incelemesidir. Yöntemi çağırmak yerine yöntem hakkında soru sorulur.

Kursun ikinci yarısının konusu **metaprogramlama (metaprogramming)**, yani program yazan ya da programın yapısını çalışırken değiştiren programdır. Yansıma onun salt okunur yarısıdır: yalnız sorar, hiçbir şeyi değiştirmez. Sonraki iki ders yazmaya geçer. Eksen aynı kalır: **bu yöntemi kim yazdı?** Dört olası yazan vardır: programcı, ata, kod ve kimse. Programcı yöntemi `def` ile kendisi yazmıştır. Ata, yöntemi üst sınıf ya da modül yazmıştır. Kod, yöntemi bir kod parçası üretmiştir; programcının elle yazdığı bir gövde yoktur. Kimse, o ada hiçbir tanım yoktur.

#### Sınıflandıran Yardımcı

Aşağıdaki dosya kursun ortak yardımcısıdır ve bu ders onu olduğu gibi verir. Üç soru sorar: alıcının zincirinde o ada bir tanım var mı, tanımı alıcının kendi sınıfı mı taşıyor, tanım bir `def` gövdesine mi dayanıyor. Her soru bir yansıma çağrısıdır; sırayla bakacağız.

```ruby
# yazan.rb — bir yontemi kimin yazdigini siniflandiran yardimci

# Siniflandirma sirayla sorar: once alicinin zincirinde bir tanim var mi,
# sonra tanimi alicinin kendi sinifi mi tasiyor, sonra tanim nasil uretildi.
def yazan(alici, ad)
  zincir = alici.singleton_class
  unless zincir.method_defined?(ad) || zincir.private_method_defined?(ad)
    return ["kimse", "tanim yok"]
  end

  tanim = zincir.instance_method(ad)
  sahip = tanim.owner
  yer = sahip == zincir ? "tekil sinif" : sahip.to_s
  return ["ata", yer] unless sahip == alici.class || sahip == zincir

  iz = RubyVM::InstructionSequence.of(tanim)
  return ["kod", "#{yer}, ruby govdesi yok"] if iz.nil?
  return ["kod", "#{yer}, blok govdesi"] if iz.label.start_with?("block")

  ["programci", "#{yer}, def govdesi"]
end
```

Bu blok kaynaktır, çıktısı yoktur; yalnız diske yazılır ve diğer dosyalar onu `require_relative` ile çeker. Yardımcı yalnız biçimi okur: tanımın nerede durduğuna ve gövdesinin hangi türden olduğuna bakar. Yazarın niyetini okumaz. **MP1.** Bu sınıflandırma yalnız Ruby ile yazılmış kullanıcı sınıflarında anlamlıdır; çekirdek sınıflar için sonuç bu dersin sonunda ölçülür.

#### Ölçülen Sınıflar

Ders boyunca ölçülen nesneler aşağıdaki dosyadadır. Kursun ortak tanımlarından bu dersin gözlemine gereken kesit alınmıştır: bir hesap sınıfı, onun alt sınıfı, bir modül ve bir `Struct`.

```ruby
# yazanlar.rb — bu dersin gozlemi icin gereken tanimlar
require_relative "yazan"

module Raporlanir
  def rapor = "rapor: #{bakiye_metni}"
end

class Hesap
  include Raporlanir

  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
  def net = @bakiye - komisyon

  private

  def komisyon = 2
end

class Vadeli < Hesap
end

Nokta = Struct.new(:x, :y)
```

`Struct`, verilen adlarla bir sınıf üreten çekirdek sınıftır; her ad bir **üye (member)** olur ve o üye için okuyucu ile yazıcı yöntemler hazır gelir. `Nokta` böyle üretilmiş bir sınıftır. Bu blok da kaynaktır.

#### Üye Okuyucusunu Kim Yazdı

Kursun bu dersteki gözlemi `Nokta` nesnesinin `x` yöntemidir. Aşağıdaki dosya yardımcıyı çağırır ve yöntemin çalışan değerini yanına basar.

```ruby
# uye_okuyucu.rb — Nokta#x yontemini kim yazdi
require_relative "yazanlar"

n = Nokta.new(3, 4)
p yazan(n, :x)
puts n.x
```
```
["kod", "Nokta, ruby govdesi yok"]
3
```

Yazan **kod**: `x` yöntemini ne programcı `def` ile yazdı ne bir üst sınıf; `Struct.new` çağrısı onu sınıf üretilirken yarattı. Yardımcı bunu iki şeyden çıkardı. Yöntemin sahibi `Nokta`'nın kendisi çıktı, yani ata değil. Ruby gövdesi de yoktu, çünkü `RubyVM::InstructionSequence.of` bu yöntem için `nil` döndürdü. Hangi sorunun hangi cevabı verdiği sıradaki bölümlerde tek tek ölçülür. **MP2.** Tek bir `Struct` sınıfı Ruby 4.0.7'de ölçüldü; `Struct`'ın başka bir sürümde üyeleri farklı yazması bu ölçümden çıkarılamaz.

#### Sınıfa ve Nesneye Sormak

Yansımanın ilk grubu sınıfın ve nesnenin yapısını sorar: hangi üyeler var, üst sınıf kim, nesnenin kendi durumunda ne var.

```ruby
# sorgu.rb — Nokta nesnesine yansima ile sormak
require_relative "yazanlar"

n = Nokta.new(3, 4)
p Nokta.members
p n.to_a
p Nokta.superclass
p yazan(n, :to_a)
p yazan(n, :x=)
p n.instance_variables
```
```
[:x, :y]
[3, 4]
Struct
["ata", "Struct"]
["kod", "Nokta, ruby govdesi yok"]
[]
```

`members` üye adlarını sıralı döndürdü, `to_a` değerleri aynı sırayla döndürdü. `superclass` `Nokta`'nın `Struct`'tan türediğini söyledi. İki `yazan` çağrısı iki farklı sonuç verdi: `to_a` yöntemini `Struct` yazmıştır, çünkü sahip `Nokta` değil; yazan **ata**. `x=` yöntemini ise `Nokta` taşıyor ve gövdesi yok; yazan **kod**. Aynı nesnede iki yöntem iki farklı yazana ulaştı. Son satır şaşırtıcıdır: `instance_variables` boş çıktı. `Struct` değerlerini örnek değişkenlerinde değil, kendi iç düzeninde tutar; yansıma bu yüzden `@x` bulamaz. **MP3.** Boş liste yalnız `Struct` için ölçüldü; sıradan bir sınıfın örnek değişkenleri bir sonraki blokta görülür.

#### Durumu ve Özel Yöntemi Sormak

Sıradan bir sınıfın nesnesinde durum örnek değişkenlerinde durur. Yansıma onları adıyla listeler, değerini adıyla okur. Özel (private) yöntemler ise `respond_to?` ikinci argüman `true` ile sorulursa görünür.

```ruby
# durum.rb — nesnenin durumunu ve ozel yontemini sormak
require_relative "yazanlar"

h = Hesap.new("ece", 100)
p h.instance_variables
p h.instance_variable_get(:@bakiye)
p h.respond_to?(:komisyon)
p h.respond_to?(:komisyon, true)
p h.respond_to?(:net)
```
```
[:@sahip, :@bakiye]
100
false
true
true
```

`instance_variables` iki örnek değişkenini oluşturulma sırasıyla verdi. `instance_variable_get(:@bakiye)` değeri okudu; bu çağrı, dışarıdan okuyucu yöntem tanımlanmamış olsa da çalışır, çünkü örnek değişkenlere yansıma yoluyla doğrudan erişir. `respond_to?(:komisyon)` `false` verdi, çünkü `komisyon` özeldir ve varsayılan soru yalnız herkese açık yöntemleri kapsar. İkinci argüman `true` verilince aynı soru `true` döndü. `net` herkese açıktır ve `true` döndü. Yani varsayılan `respond_to?` sorusu yöntemin var olup olmadığını değil, çağıran için görünür olup olmadığını sorar; ikinci argüman `true` verilince özel yöntemler de kapsama girer. **MP4.** Özel yöntem sorusu yalnız `respond_to?` ile ölçüldü; `private_methods` gibi diğer sorgular bu derste kullanılmadı.

#### Yöntemin Kendisine Sormak

Yansıma bir yöntemi nesne olarak da yakalar. `instance_method` sınıftan, `method` ise nesneden bir yöntem nesnesi verir. Yöntem nesnesi argüman sayısını, parametrelerini, tanımlandığı yeri sorgulatır ve çağrılabilir.

```ruby
# yontem.rb — yontemin kendisine sormak
require_relative "yazanlar"

h = Hesap.new("ece", 100)
p Hesap.instance_method(:net).arity
p Hesap.instance_method(:initialize).parameters
p h.method(:net).call
p Hesap.instance_method(:net).source_location.nil?
p Nokta.instance_method(:x).source_location.nil?
p Object.const_get(:Hesap)
```
```
0
[[:req, :sahip], [:req, :bakiye]]
98
false
true
Hesap
```

**Argüman sayısı (`arity`)** `net` için `0` çıktı: yöntem argüman almıyor. `parameters`, `initialize`'ın iki zorunlu parametresini adlarıyla listeledi; `:req`, zorunlu demektir. `h.method(:net).call` yöntemi yakalayıp çağırdı ve `98` verdi: `100` eksi özel `komisyon`'un `2`'si. `source_location` yöntemin dosya ve satırını verir; yol ortama bağlı olduğundan burada yalnız boş olup olmadığı basıldı. `Hesap#net` için boş değil, çünkü `def` ile yazılmış; `Nokta#x` için boş, çünkü `Struct` onu yazmış ve ortada bir kaynak satırı yok. Bu, `yazan`'ın `nil` kontrolünün gözlemlenebilir karşılığıdır. Son satır, sabiti adından bulur: `const_get(:Hesap)` sınıfın kendisini döndürdü. **MP5.** `source_location` yalnız iki yöntem için ölçüldü; `nil` olmaması yazanın programcı olduğunu tek başına göstermez.

#### Sahibi Sormak

`yazan` yardımcısının kalbi tek bir soru: yöntem nesnesinin sahibi kim. Sahip, yöntemin tanımının durduğu sınıf ya da modüldür. Sahibi bulmak için yardımcı alıcının sınıf zincirine bakar; aynı zinciri elle de listeleyebiliriz.

```ruby
# sahip.rb — yontemin sahibi ve alicinin zinciri
require_relative "yazanlar"

h = Hesap.new("ece", 100)
v = Vadeli.new("a", 1)
p Vadeli.ancestors.first(3)
p h.singleton_class.ancestors.drop(1).first(3)
p h.method(:rapor).owner
p Vadeli.instance_method(:net).owner
p yazan(v, :net)
p yazan(h, :rapor)
```
```
[Vadeli, Hesap, Raporlanir]
[Hesap, Raporlanir, Object]
Raporlanir
Hesap
["ata", "Hesap"]
["ata", "Raporlanir"]
```

`ancestors` sınıfın **ata zincirini (ancestors)** soldan sağa verir: önce sınıfın kendisi, sonra `include` edilen modüller ve üst sınıflar, yöntem arama zincirinin okunma sırasıyla. `Vadeli` için ilk üçü `Vadeli`, `Hesap` ve `Raporlanir` oldu: modül, `include` edildiği sınıfın hemen arkasında durur. Nesnenin kendi tekil sınıfının zinciri tekil sınıfın kendisiyle başlar; o ad her koşumda değiştiğinden basılmaz, ilk eleman atılır ve kalan zincir `Hesap`, `Raporlanir`, `Object` olarak görünür. `rapor` yönteminin sahibi `Raporlanir` çıktı, `Vadeli.instance_method(:net)` ise `Vadeli`'nin değil `Hesap`'ın tanımına ulaştı. Bu yüzden `yazan(v, :net)` ve `yazan(h, :rapor)` ikisi de yazanı **ata** olarak sınıflandırdı: sahip, alıcının sınıfı ya da tekil sınıfı değil. **MP6.** Zincir yalnız ilk üç elemanla basıldı; `Object`'ten sonraki kuyruk ölçülmedi.

#### Tek Nesnenin Yöntemi

Önceki dersin gözlemine dönelim: aynı sınıfın iki örneğinde aynı ileti iki yazana ulaşabilir. Yansıma bunu da sorar. `singleton_methods`, nesnenin yalnız kendi tekil sınıfında tanımlı yöntemlerini listeler. Aşağıdaki ölçüm iki `Hesap` nesnesi kurar ve yalnız birine kendi `net` yöntemini yazar.

```ruby
# tekil.rb — iki ornekte ayni ileti
require_relative "yazanlar"

a = Hesap.new("ece", 100)
b = Hesap.new("can", 50)
def b.net = 0

p a.singleton_methods
p b.singleton_methods
p a.net
p b.net
p yazan(a, :net)
p yazan(b, :net)
```
```
[]
[:net]
98
0
["programci", "Hesap, def govdesi"]
["programci", "tekil sinif, def govdesi"]
```

`a` için liste boş, `b` için `net` var. Aynı sınıfın iki örneği aynı iletiye iki farklı değerle cevap verdi: `a` için `98`, `b` için `0`. İki `yazan` çağrısı farkı adlandırır. `a.net` yöntemini `Hesap` sınıfındaki `def` yazmıştır; `b.net` yöntemini ise yine bir `def` yazmıştır, ama bu kez tanım `b`'nin tekil sınıfında durur ve yardımcı bunu sahip adı yerine **tekil sınıf** diye anar. Yazan her iki durumda **programcı**dır; fark tanımın yerindedir. Yansıma bu yüzden iki ayrı soruyu ayırır: yöntem kimin tarafından üretildi ve hangi katmanda duruyor. **MP7.** Yalnız `net` yöntemi iki örnekte ölçüldü; tekil yöntemin sınıf yöntemiyle ilişkisi bu derste kurulmadı.

#### Tür ve Yöntem Listesi

Son grup, nesnenin hangi türden olduğunu ve bir sınıfın kendi yöntemlerinin listesini sorar. Liste bir kez de sınıf yeniden açıldıktan sonra alınır: yeniden açılan sınıfa `attr_reader` eklenir ve liste değişir.

```ruby
# tur.rb — nesnenin turunu ve sinifin yontemlerini sormak
require_relative "yazanlar"

h = Hesap.new("ece", 100)
v = Vadeli.new("a", 1)
p h.is_a?(Raporlanir)
p v.is_a?(Hesap)
p v.instance_of?(Hesap)

class Hesap
  attr_reader :sahip
end

p Hesap.public_instance_methods(false).sort
p yazan(h, :sahip)
```
```
true
true
false
[:bakiye_metni, :net, :sahip]
["kod", "Hesap, ruby govdesi yok"]
```

`is_a?` zincirdeki her sınıf ve modül için `true` döndürür: `h` `Raporlanir`'a, `v` de `Hesap`'a bağlıdır. `instance_of?` ise yalnız tam sınıfı kabul eder; `Vadeli` örneği için `Hesap` sorusu `false` çıktı. `public_instance_methods(false)` yalnız `Hesap`'ın kendi herkese açık yöntemlerini listeler; `false`, atalardan gelenleri dışarıda bırakır. Bu yüzden `Raporlanir`'dan gelen `rapor` listede yok, `initialize` de yok, çünkü `initialize` özeldir. `attr_reader :sahip` listeye `sahip`'i ekledi. Ve `yazan` onun yazanını **kod** olarak sınıflandırdı: `attr_reader` bir `def` gövdesi bırakmaz, yöntemi kendisi üretir. **MP8.** Liste, yeniden açma anından sonra alındı; sınıfın başka yerde yeniden açılmadığı varsayılır.

#### Sınırlayıcı Ölçüm

Şimdiye kadar yansıma, programcının yazdığı sınıflarda iş gördü. Yardımcı çekirdek sınıflara çevrildiğinde ne olur?

```ruby
# sinir.rb — kuralin sinirlari
require_relative "yazanlar"

p yazan("a", :upcase)
p yazan(Hesap, :new)
[5, :a].each do |deger|
  begin
    deger.singleton_class
  rescue => e
    puts "#{deger.inspect} -> #{e.class}"
  end
end
p nil.singleton_class
```
```
["kod", "String, ruby govdesi yok"]
["kod", "Class, ruby govdesi yok"]
5 -> TypeError
:a -> TypeError
NilClass
```

`String#upcase` ve `Class#new` için yardımcı **kod** dedi, ama bu cevap yanıltır. Bu yöntemleri Ruby'nin kendi çekirdeği derlenmiş kodla yazdı; ortada bir `def` gövdesi olmadığından kural onları, `Struct` üyeleriyle aynı gerekçeyle koda atadı. Aralarındaki fark yardımcıya görünmez: biri çalışma zamanında üretilmiştir, diğeri çekirdekte baştan yazılmıştır. Kural biçimi okur, niyeti okumaz. İkinci sınır daha sertti: tamsayının ve `Symbol` değerinin tekil sınıfı yok, ikisi de `TypeError` verdi. `nil` ise `NilClass` döndürdü, yani `nil` için yardımcı çalışır. Bu üç sonuçtan çıkan sınır şudur: yansıma, tanımın nerede durduğunu doğru söyler, yazarın kim olduğunu her zaman söylemez. **MP9.** Çekirdek sınıflardaki iki sonuç yalnız bu iki yöntem için ölçüldü; başka çekirdek yöntemleri farklı çıkabilir.

#### Özet

- Yansıma, nesneye yöntemini, durumunu ve türünü çalışma zamanında sormaktır; `members`, `instance_variables`, `respond_to?`, `is_a?` ve `public_instance_methods(false)` böyle sorulardır.
- `Nokta#x` yöntemini kod yazdı: `Struct.new` onu sınıf üretilirken yarattı, ortada `def` gövdesi yok.
- `Nokta#to_a` yöntemini ata yazdı: sahip `Struct`.
- Yöntem nesnesi `arity`, `parameters` ve `source_location` ile sorgulanır ve `call` ile çağrılır.
- `attr_reader` ile eklenen okuyucu da kod tarafından yazılmış sayılır.
- Yardımcı yalnız Ruby ile yazılmış kullanıcı sınıflarında anlamlı sonuç verir; ölçülen iki çekirdek yöntem (`String#upcase`, `Class#new`) koda gitti.

#### Sonraki Adım

Bu derste kod, bir sınıfın yöntemini sınıf tanımlanırken üretti ve biz üretimi yalnız sonuçtan okuduk. Sıradaki ders üretimin kendisini yapar: `define_method`, ad çalışma zamanında belirlenen bir yöntemi bir blokla tanımlar. Aynı soru, bu kez yazan olarak kodun nasıl ortaya çıktığını sorar.

### Dinamik Yöntem Tanımı

Source: https://academia.sh/tr/kurslar/ruby-nesne-yonelimi/dinamik-yontem-tanimi

define_method ile çalışma zamanında yöntem üretmenin, bu yöntemlerin parametre denetiminin ve üretimin yazanı nasıl sınıflandırdığının ölçümü.

Önceki ders yansımayı verdi: bir nesneye yöntemlerini, durumunu ve türünü sormak. `Nokta#x` yöntemini `Struct.new` yazmıştı ve `yazan` yardımcısı ona **kod** demişti. Orada üretim bir kara kutuydu; yalnız sonuca baktık. Bu ders üretimin kendisini yapar. **Dinamik yöntem tanımı (dynamic method definition)**, bir yöntemin `def` yazılarak değil, program çalışırken ad ve gövde verilerek tanımlanmasıdır. Ruby'de bunun aracı `define_method`'dur. M11/K01, C'de bir kararın hangi aşamada verildiğini ölçmüştü; burada tanımın ne zaman oluştuğu sorulmuyor, soru yine kimin yazdığıdır.

Eksen aynı: **bu yöntemi kim yazdı?** Bu dersin yazanı **kod**. Programcı bir kez döngü yazar, döngü iki yöntemi yazar. Programcının elinden çıkan yalnız `define_method` çağrısıdır, tek tek yöntem gövdeleri değil. Dört yazanı hatırlayalım: programcı `def` ile yazar, ata bir üst sınıf ya da modüldür, kod bir üretim çağrısıdır, kimse ise o ada tanım olmamasıdır. Dinamik tanım, kodun yollarından biridir. Neden gerektiği de açıktır: aynı kalıpta onlarca yöntem gerekiyorsa, onları tek tek yazmak yerine kalıbı bir kez yazıp adları bir listeden almak hem kısadır hem de listeye bir ad eklendiğinde yöntemin kendiliğinden doğmasını sağlar. Bu derste o kısalığın bedelini ölçeceğiz: kaynakta aranan bir yöntem adı, kaynakta hiç geçmeyebilir. Aşağıdaki yardımcı önceki dersle aynıdır ve olduğu gibi verilir.

```ruby
# yazan.rb — bir yontemi kimin yazdigini siniflandiran yardimci

# Siniflandirma sirayla sorar: once alicinin zincirinde bir tanim var mi,
# sonra tanimi alicinin kendi sinifi mi tasiyor, sonra tanim nasil uretildi.
def yazan(alici, ad)
  zincir = alici.singleton_class
  unless zincir.method_defined?(ad) || zincir.private_method_defined?(ad)
    return ["kimse", "tanim yok"]
  end

  tanim = zincir.instance_method(ad)
  sahip = tanim.owner
  yer = sahip == zincir ? "tekil sinif" : sahip.to_s
  return ["ata", yer] unless sahip == alici.class || sahip == zincir

  iz = RubyVM::InstructionSequence.of(tanim)
  return ["kod", "#{yer}, ruby govdesi yok"] if iz.nil?
  return ["kod", "#{yer}, blok govdesi"] if iz.label.start_with?("block")

  ["programci", "#{yer}, def govdesi"]
end
```

Bu blok kaynaktır; dosya diske yazılır ve `require_relative` ile çekilir.

#### Ölçülen Sınıflar

Bu dersin gözlemi iki sınıfa dayanır. `Hesap`, `def` ile yazılmış bir yöntemi karşılaştırma için sağlar. `Ayar` ise yöntemlerini bir döngüden alır. Ortak tanımların kesiti şudur.

```ruby
# yazanlar.rb — bu dersin gozlemi icin gereken tanimlar
require_relative "yazan"

module Raporlanir
  def rapor = "rapor: #{bakiye_metni}"
end

class Hesap
  include Raporlanir

  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
  def net = @bakiye - komisyon

  private

  def komisyon = 2
end

class Ayar
  %i[renk boyut].each do |ad|
    define_method(ad) { "#{ad} varsayilan" }
  end
end
```

Bu blok de kaynaktır. `Ayar` tanımında `%i[renk boyut]` iki `Symbol` değerinden oluşan bir dizi kurar; `each` her ad için `define_method` çağırır ve bloğu o adın yöntemi yapar. `Ayar` sınıfında `renk` ve `boyut` adında bir `def` yoktur.

#### Üretilen Yöntemi Kim Yazdı

Bu dersin gözlemi `Ayar` nesnesinin `renk` yöntemidir. Aşağıdaki dosya yardımcıyı çağırır ve yöntemin ürettiği değeri yanına basar.

```ruby
# uretilen.rb — Ayar#renk yontemini kim yazdi
require_relative "yazanlar"

a = Ayar.new
p yazan(a, :renk)
puts a.renk
```
```
["kod", "Ayar, blok govdesi"]
renk varsayilan
```

Yazan **kod**. Yöntemin sahibi `Ayar`'ın kendisi çıktı, yani ata değil; ama gövde bir `def` değil, `define_method`'a verilen bir blok. Yardımcı bu ayrımı iz etiketinin `block` ile başlamasından okudu. Önceki dersteki `Nokta#x` da koddu, ama onun gövdesi hiç yoktu: orada cevap `ruby govdesi yok` idi, burada `blok govdesi`. Aynı kategoriye iki ayrı biçim düşer. **MP10.** Yalnız `renk` yöntemi ölçüldü; `boyut` aynı döngüden geldiği için aynı sonucu verecektir, ama bu bir varsayımdır.

#### Üretim İzi

Yöntem nesnesinin gövdesinin bir **iz etiketi (label)** vardır; Ruby bunu derlenmiş gövdeye yazar. Yardımcı etikete bakıp `block` ile başlayıp başlamadığını sorar. Etiketi doğrudan basarak iki yöntemi karşılaştırırız: döngüyle üretilen `renk` ve `def` ile yazılan `net`.

```ruby
# iz.rb — iki yontemin gövde etiketi
require_relative "yazanlar"

p Ayar.instance_methods(false)
iz = RubyVM::InstructionSequence
puts iz.of(Ayar.instance_method(:renk)).label
puts iz.of(Hesap.instance_method(:net)).label
```
```
[:boyut, :renk]
block (2 levels) in <class:Ayar>
net
```

`instance_methods(false)` yalnız `Ayar`'ın kendi herkese açık yöntemlerini listeledi. Etiketler farkı gösterir: `renk` için `block (2 levels) in <class:Ayar>`, `net` için `net`. İki seviye, tanımın iki blok içinde olduğunu söyler: biri `each` bloğu, öteki `define_method` bloğu. `net`'in etiketi ise yöntemin kendi adıdır. Yardımcının `start_with?("block")` sorusu bu iki etiketi ayırır. **MP11.** Etiket biçimi Ruby 4.0.7'de ölçüldü; başka bir sürümde metin değişebilir, ama `block` ile başlama kuralı bu ölçümden çıkarılan tek şeydir.

#### Çalışırken Yöntem Eklemek

`define_method` bir ad ve bir blok alır, yöntemi o ada bağlar ve adı `Symbol` olarak döndürür. Yöntem aynı satırdan sonra çağrılabilir. Sınıf zaten yüklenmişken de çalışır: sınıfa yeni bir yöntem eklemek için `class` anahtar sözcüğüyle yeniden açmak gerekmez.

```ruby
# tanim.rb — define_method ile yontem eklemek ve kaldirmak
require_relative "yazanlar"

donus = Ayar.define_method(:tema) { "koyu" }
p donus
p Ayar.new.tema
p yazan(Ayar.new, :tema)
Ayar.remove_method(:tema)
begin
  Ayar.new.tema
rescue NoMethodError => e
  puts e.class
end
```
```
:tema
"koyu"
["kod", "Ayar, blok govdesi"]
NoMethodError
```

Dönüş `:tema` oldu; yeni yöntem hemen cevap verdi ve `yazan` onu **kod** olarak sınıflandırdı, tıpkı döngüyle üretilenler gibi. Üretimin zamanı değişti, tanımın biçimi değişmedi: gövde yine bir blok. `remove_method` yöntemi `Ayar`'ın kendi tablosundan sildi ve ardından aynı ileti `NoMethodError` verdi. Silinen yöntem, aynı adda bir ata tanımı olsaydı, o ata tanımı yeniden görünür olurdu; `remove_method` yalnız sınıfın kendi tanımını siler. **MP12.** `remove_method` yalnız bu sınıfta ve ata tanımı olmayan bir adla ölçüldü; ata tanımı olan durum burada gösterilmedi.

#### Adı Çalışırken Belirlemek

`def` ile yazılan yöntemin adı kaynakta sabittir. `define_method` ise adı bir dizgi ya da `Symbol` olarak alır; ad bir değişkenden ya da hesaplamadan gelebilir. Aşağıdaki sınıf iki alan adı için birer okuyucu ve yazıcı üretir; ürettiği yöntem adlarını kaynakta hiçbir yerde düz yazı olarak taşımaz.

```ruby
# form.rb — adi hesaplanan yontemler
require_relative "yazanlar"

class Form
  %w[ad soyad].each do |alan|
    define_method("#{alan}_yaz") { |deger| instance_variable_set("@#{alan}", deger) }
    define_method("#{alan}_oku") { instance_variable_get("@#{alan}") }
  end
end

f = Form.new
f.ad_yaz("ece")
p f.ad_oku
p f.soyad_oku
p f.instance_variables
p Form.instance_methods(false).sort
p yazan(f, :ad_yaz)
```
```
"ece"
nil
[:@ad]
[:ad_oku, :ad_yaz, :soyad_oku, :soyad_yaz]
["kod", "Form, blok govdesi"]
```

Dört yöntem tek bir döngüden çıktı: `ad_oku`, `ad_yaz`, `soyad_oku` ve `soyad_yaz`. Yazılmış alan okunduğunda `"ece"` geldi; yazılmamış alan `nil` verdi, çünkü o örnek değişkeni hiç atanmadı. `instance_variables` yalnız `@ad`'ı listeledi: ikinci alanın örnek değişkeni, ona ilk değer yazılana kadar var değildir. Yazan yine **kod** çıktı. Burada programcı yalnız bir kalıp yazdı; kalıbın dört örneğini kod üretti. **MP13.** Bu sınıf iki alanla ölçüldü; ad dizgisinin yöntem adı olarak geçersiz karakterler içermesi durumu sınanmadı.

#### Parametre Denetimi

Blok normalde gevşek davranır: eksik argüman `nil` olur. `define_method`'a verilen blok ise yöntem gibi davranır ve argüman sayısını denetler. Aşağıdaki ölçüm bunu iki yoldan sınar: bloğu doğrudan vermek ve bir `proc` nesnesini `&` ile vermek.

```ruby
# parametre.rb — define_method bloklari ve argüman denetimi
class Ayar
  define_method(:carp) { |n| n * 2 }
  define_method(:pcarp, &proc { |n| n * 2 })
end

a = Ayar.new
p Ayar.instance_method(:carp).arity
p a.carp(4)
p proc { |n| n }.call
[:carp, :pcarp].each do |ad|
  begin
    a.public_send(ad)
  rescue ArgumentError => e
    puts "#{ad} -> #{e.class}"
  end
end
```
```
1
8
nil
carp -> ArgumentError
pcarp -> ArgumentError
```

`carp` için argüman sayısı `1` çıktı: üretilen yöntem, bloğun parametresini zorunlu bir parametre olarak taşıyor. `carp(4)` `8` verdi. Üçüncü satır karşıtı gösterir: argümansız çağrılan sıradan bir `proc` hata vermedi, `nil` döndürdü. Aynı biçimdeki blok `define_method` içinde ise `ArgumentError` verdi; `proc` nesnesini `&` ile geçirmek bunu değiştirmedi, `pcarp` da hata verdi. Yani üretim anında blok yöntem kuralına geçer. **MP14.** Yalnız tek zorunlu parametreli bloklar ölçüldü; varsayılan değerli ve değişken argümanlı bloklar bu derste sınanmadı.

#### Bloğun Gördüğü Değişkenler

`define_method` ile `def` arasındaki en kullanışlı fark, bloğun çevresindeki yerel değişkenleri görmesidir. **Kapanış (closure)**, bir bloğun tanımlandığı yerdeki yerel değişkenleri sonradan çağrıldığında da taşımasıdır. `def` yeni bir kapsam açar ve çevresindeki yerel değişkenleri görmez; blok ise görür.

```ruby
# sayac.rb — define_method blogu cevresindeki degiskeni gorur
class Sayac
  adet = 0
  define_method(:say) { adet += 1 }
  def bak = defined?(adet).inspect
end

a = Sayac.new
b = Sayac.new
p [a.say, a.say, b.say]
puts a.bak
```
```
[1, 2, 3]
nil
```

Üç çağrı `[1, 2, 3]` verdi. `adet` sınıf gövdesinde bir yerel değişkendi ve `say` bloğu onu kapanış olarak taşıdı. Sayaç iki nesne arasında paylaşıldı: ikinci nesnenin ilk çağrısı `3` oldu, çünkü değişken nesnede değil, bloğun kapanışında duruyor. `def` ile yazılan `bak` ise aynı adı göremedi; `defined?(adet)` orada `nil` döndü ve `inspect` bunu `"nil"` olarak yazdırdı. Bu yüzden `define_method` ile üretilen bir yöntemin durumu örnek değişkenlerinde olmayabilir; yansıma ile `instance_variables` çağrısı bu sayacı göstermez. **MP15.** Bloğun kapanışı yalnız bu sınıf gövdesindeki tek yerel değişkenle ölçüldü; kapanışın başka yöntemler arasında paylaşılması bu örnekte gösterilmedi.

#### Kanca

Bir sınıfa yeni bir yöntem eklendiğinde Ruby sınıfın `method_added` yöntemini çağırır. **Kanca (hook)**, belirli bir olay olduğunda çalışması için tanımlanan yöntemdir. Aşağıdaki sınıf bu kancayı yazar ve eklenen adları bir listede toplar. Üç ayrı yolla yöntem eklenir: `def`, `define_method` ve `attr_reader`.

```ruby
# kanca.rb — method_added hangi yollarda tetiklenir
class Kumbara
  @eklenen = []

  class << self
    attr_reader :eklenen

    def method_added(ad)
      @eklenen << ad
    end
  end

  def a = 1
  define_method(:b) { 2 }
  attr_reader :c
end

p Kumbara.eklenen
```
```
[:a, :b, :c]
```

Üç yol da kancayı aynı biçimde tetikledi: `[:a, :b, :c]`. Kanca yöntemi hangi yolla üretildiğini bilmez, yalnız adı alır. Bu, önceki derslerin gözlemiyle birlikte okunmalıdır: kanca tetiklemesi yazanı söylemez. Yazanı söyleyen şey, tanımın gövdesinin biçimidir; kanca bunu görmez. Bu yüzden kanca yöntem eklenen yeri izlemek için iyidir, ama kim yazdı sorusuna cevap vermez. **MP16.** Kanca yalnız bu sınıfta, sınıf gövdesi çalışırken ölçüldü.

#### Erişimci de Kod Üretir

`attr_accessor`, aynı adla bir okuyucu ve bir yazıcı yöntem üretir. **Erişimci (accessor)** bu iki yöntemin birlikte adıdır; yalnız okuyucuya `attr_reader`, yalnız yazıcıya `attr_writer` denir.

```ruby
# erisim.rb — attr_accessor iki yontem uretir
require_relative "yazan"

class Erisim
  attr_accessor :renk
end

p Erisim.instance_methods(false).sort
p yazan(Erisim.new, :renk)
p yazan(Erisim.new, :renk=)
```
```
[:renk, :renk=]
["kod", "Erisim, ruby govdesi yok"]
["kod", "Erisim, ruby govdesi yok"]
```

İki yöntem çıktı: `renk` ve `renk=`. İkisinin de yazanı **kod**, ama gövde biçimi `define_method`'dan farklı: etiket yok, çünkü `attr_accessor` Ruby gövdesi bırakmaz. `Erisim` sınıfında hiçbir `def` yazılmadı. `blok govdesi` ile `ruby govdesi yok` kodun iki alt biçimidir. **MP17.** Yalnız `attr_accessor` ölçüldü; `attr_writer` tek başına ölçülmedi.

#### Sınırlayıcı Ölçüm

Şimdiye kadar kod, `define_method` ya da `attr_*` çağrısı demekti. Bir üretim biçimi daha var: sınıfa bir dizgiyi Ruby kodu olarak yorumlatmak. `class_eval` bir dizgiyi sınıfın bağlamında çalıştırır. Aşağıdaki ölçüm aynı işi iki yolla yapar: biri dizgiyle, öteki blokla.

```ruby
# sinir.rb — ayni is, iki uretim yolu
require_relative "yazanlar"

Ayar.class_eval "def dizgiden = 4"
Ayar.define_method(:blokla) { 4 }

a = Ayar.new
p [a.dizgiden, a.blokla]
p yazan(a, :dizgiden)
p yazan(a, :blokla)
```
```
[4, 4]
["programci", "Ayar, def govdesi"]
["kod", "Ayar, blok govdesi"]
```

İki yöntem de `4` verdi, ama yazanları ayrıldı. Dizgiden üretilen yöntem çalışma zamanında yaratıldığı hâlde `yazan` onu **programcı** olarak sınıflandırdı, çünkü dizgi içinde bir `def` gövdesi var. `define_method` ile üretilen ise **kod**. Kural biçimi okur, niyeti okumaz: kategori üretimin ne zaman olduğunu değil, tanımın hangi biçimde olduğunu ölçer. Bu yüzden kod üretimi her zaman koda düşmez. Tersi de doğrudur: kaynakta `define_method` görmek, yazanın kod olduğunu söylemez; bir dizginin içinde yazılmış `def` ise çalışma zamanında üretilmiş olsa bile programcıya düşer. Yansıma yöntemin sahibini ve gövdesinin biçimini görür, üretimi başlatan çağrıyı görmez. Bu yüzden üretimin izini sürmek, etikete bakmaktan çok kaynakta `define_method` ve `class_eval` çağrılarını aramaktır. **MP18.** Dizgiden üretim yalnız basit tek satırlık `def` ile ölçüldü; daha karmaşık dizgilerde etiket ve gövde biçimi farklı olabilir.

#### Özet

- `define_method` bir ad ve bir blok alır, yöntemi o ada bağlar ve adı `Symbol` olarak döndürür; yeni yöntem hemen çağrılabilir.
- Üretilen yöntemin yazanı koddur: gövdesi bir bloktur ve iz etiketi `block` ile başlar. `def` ile yazılanın etiketi yöntemin adıdır.
- `define_method` bloğu argüman sayısını yöntem gibi denetler; sıradan `proc` denetlemez.
- `method_added` kancası `def`, `define_method` ve `attr_reader` için aynı tetiklenir; kanca yazanı söylemez.
- `attr_accessor` iki yöntem üretir ve ikisinin de yazanı koddur, ama gövde biçimi farklıdır.
- Dizgiden `class_eval` ile üretilen `def` yazanı programcı çıkar: kategori tanımın biçimini ölçer, üretim anını değil.

#### Sonraki Adım

Bu derste yöntemleri biz tanımladık, çağrılmadan önce. Sıradaki ders tersini ölçer: çağrılan ada hiçbir tanım yoksa Ruby ne yapar ve bu boşluğu bir sınıf kendisi nasıl doldurabilir. O derste yazanın dördüncü kategorisi, kimse, ortaya çıkar.

### Eksik Yöntem Yakalama

Source: https://academia.sh/tr/kurslar/ruby-nesne-yonelimi/eksik-yontem-yakalama

Hiçbir tanımı olmayan bir iletiyi method_missing ile yakalayan vekil nesnenin ölçümü ve bu yakalamanın yansıma sorularında bıraktığı boşluklar.

Önceki ders, bir yöntemin `define_method` ile çalışma zamanında tanımlandığını ve yazanının **kod** olduğunu ölçtü. Orada her ad, çağrılmadan önce tanımlanmıştı. Bu ders tersini sorar: çağrılan ada hiçbir tanım yoksa Ruby ne yapar? Varsayılan cevap `NoMethodError`'dır. Ama bir sınıf bu boşluğu kendisi doldurabilir. **Eksik yöntem yakalama (method_missing)**, bir nesneye gönderilen ve yöntem arama zincirinde hiçbir tanım bulamayan iletinin, hata vermeden önce nesnenin kendi `method_missing` yöntemine teslim edilmesidir. Ruby'de bu yöntemin adı koddur ve olduğu gibi yazılır.

Eksen yine aynı: **bu yöntemi kim yazdı?** Bu dersin yazanı **kimse**. Çağrılan ada bir tanım yoktur; ileti yine de cevap alır, çünkü bir başka yöntem onu yakalar. Yazan yardımcısı için bu, dört kategorinin sonuncusudur ve aşağıda neden burada kullanıldığı görülecek. Önce yardımcı, önceki derslerle aynı biçimde verilir.

```ruby
# yazan.rb — bir yontemi kimin yazdigini siniflandiran yardimci

# Siniflandirma sirayla sorar: once alicinin zincirinde bir tanim var mi,
# sonra tanimi alicinin kendi sinifi mi tasiyor, sonra tanim nasil uretildi.
def yazan(alici, ad)
  zincir = alici.singleton_class
  unless zincir.method_defined?(ad) || zincir.private_method_defined?(ad)
    return ["kimse", "tanim yok"]
  end

  tanim = zincir.instance_method(ad)
  sahip = tanim.owner
  yer = sahip == zincir ? "tekil sinif" : sahip.to_s
  return ["ata", yer] unless sahip == alici.class || sahip == zincir

  iz = RubyVM::InstructionSequence.of(tanim)
  return ["kod", "#{yer}, ruby govdesi yok"] if iz.nil?
  return ["kod", "#{yer}, blok govdesi"] if iz.label.start_with?("block")

  ["programci", "#{yer}, def govdesi"]
end
```

Bu blok kaynaktır; dosya diske yazılır ve `require_relative` ile çekilir.

#### Ölçülen Sınıf

Bu dersin gözlemi bir **vekil nesne (proxy)** üzerindedir: başka bir nesneyi sarmalayan ve ona gelen iletileri o nesneye ileten nesne. `Vekil`, sarmaladığı nesnenin yöntemlerinin hiçbirini kendisi tanımlamaz.

```ruby
# yazanlar.rb — bu dersin gozlemi icin gereken tanim
require_relative "yazan"

class Vekil
  def initialize(hedef)
    @hedef = hedef
  end

  def method_missing(ad, *argumanlar, &blok)
    return super unless @hedef.respond_to?(ad)

    @hedef.public_send(ad, *argumanlar, &blok)
  end

  def respond_to_missing?(ad, ozel = false) = @hedef.respond_to?(ad, ozel) || super
end
```

Bu blok de kaynaktır. `method_missing` üç şey alır: iletinin adı, argümanlar ve varsa blok. İlk satır, sarmalanan nesne o iletiyi anlamıyorsa `super` ile Ruby'nin varsayılan davranışına, yani `NoMethodError`'a bırakır. Anlıyorsa iletiyi aynı argümanlarla ve aynı blokla `public_send` ile ona iletir. `respond_to_missing?` ikinci yöntemdir; ne işe yaradığı birazdan ölçülecek.

#### Tanımsız Yöntemi Kim Yazdı

Bu dersin gözlemi `Vekil` nesnesinin `sort` yöntemidir. Nesne bir diziyi sarmalar; `Vekil` sınıfında `sort` adlı bir tanım yoktur.

```ruby
# vekil.rb — Vekil#sort yontemini kim yazdi
require_relative "yazanlar"

vk = Vekil.new([3, 1, 2])
p yazan(vk, :sort)
p vk.sort
```
```
["kimse", "tanim yok"]
[1, 2, 3]
```

Yazan **kimse**: `yazan` yardımcısı alıcının zincirinde `sort` için bir tanım bulamadı ve ilk soruda durdu. Yine de `vk.sort` çalıştı ve `[1, 2, 3]` verdi. Yani çalışan bir yöntem var, ama yöntem arama zincirinde yok. Çalıştıran şey `method_missing`'dir; o, iletiyi dizinin kendi `sort` yöntemine iletir. Bu çelişki dersin konusudur: yazan kimse olabilir ve yanıt yine de gelir. **MP19.** Bu ölçüm tek bir ileti içindir; `Vekil`'in diğer iletileri aşağıda ayrıca ölçülür.

#### Yakalanan İletiler

Vekil yalnız `sort`'a değil, zincirinde tanımı olmayan ve sarmalanan dizinin genel yöntemi olan iletilere cevap verir. Aşağıdaki ölçüm üç ileti dener: biri argümansız, biri başka bir türde sonuç veren, biri blok alan.

```ruby
# yakala.rb — vekilin iletisi yakalamasi
require_relative "yazanlar"

vk = Vekil.new([3, 1, 2])
p vk.sort
p vk.size
p vk.map { |x| x * 10 }
```
```
[1, 2, 3]
3
[30, 10, 20]
```

`sort` sıralı diziyi, `size` uzunluğu verdi. `map` blok aldı: `method_missing` bloğu `&blok` ile yakaladı ve `public_send`'e geçirdi, böylece dizi her elemanı on ile çarptı. Vekil, bu üç iletinin hiçbirini tanımlamadan cevap verdi. **MP20.** Bu ölçüm yalnız üç ileti içindir; zincirde tanımı olmayan dizi yöntemleri aynı şekilde geçer, ama bu üçünden fazlası burada görülmedi; zincirde tanımı olan adlar aşağıda ayrıca ölçülür.

#### Argümanlar ve Blok

Üç ileti `method_missing` imzasının yarısını gösterdi. Argümanların ve bloğun birlikte aktarılması ayrıca ölçülmeli, çünkü `*argumanlar` ve `&blok` yazılmazsa iletinin parçaları sessizce düşer.

```ruby
# aktar.rb — argumanlarin ve blogun aktarilmasi
require_relative "yazanlar"

vk = Vekil.new([3, 1, 2])
p vk.first(2)
p vk.inject(10) { |toplam, x| toplam + x }
p vk.each_slice(2).to_a
```
```
[3, 1]
16
[[3, 1], [2]]
```

`first(2)` argümanı, `inject(10) { ... }` hem argümanı hem bloğu, `each_slice(2)` ise blok olmadan bir sayaç nesnesi döndürdü ve `to_a` onu açtı. Üçü de dizinin kendi çağrısıyla aynı sonucu verdi: vekil iletiyi bozmadan taşıdı. Taşıma `public_send(ad, *argumanlar, &blok)` satırının işi; o satırda `*argumanlar` ya da `&blok` eksik olsaydı bu çağrılarda argüman ya da blok iletiye ulaşmaz, sonuç ya da hata değişirdi; bu derste denenmedi. **MP21.** Üç çağrı ölçüldü; anahtar sözcük argümanları bu derste denenmedi.

#### Yansıma Ne Görür

Yansıma dersi, nesneye yöntemlerini sormanın yollarını verdi. Aşağıda vekile dört soru sorulur: `respond_to?`, `defined?`, `method(:sort).owner` ve sınıfın `method_defined?` sorusu.

```ruby
# sorgu.rb — vekile yansima ile sormak
require_relative "yazanlar"

vk = Vekil.new([3, 1, 2])
p vk.respond_to?(:sort)
p defined?(vk.sort)
p vk.method(:sort).owner
p Vekil.method_defined?(:sort)
p yazan(vk, :size)
```
```
true
"method"
Vekil
false
["kimse", "tanim yok"]
```

Cevaplar tutarsız görünür ve bunun gerekçesi `respond_to_missing?`'tir. Vekil yazılırken bu yöntem tanımlandığından `respond_to?` `true` verdi, `defined?` `"method"` döndürdü ve `method(:sort)` bir yöntem nesnesi üretti. Üretilen nesnenin sahibi `Vekil` çıktı, ama sınıfın tanım tablosuna sorulduğunda `Vekil.method_defined?(:sort)` `false` verdi. Yani `respond_to_missing?` yansımaya yakalamayı bildirir, ama tanım tablosuna bir kayıt eklemez. Yardımcı tanım tablosuna baktığı için `size` için de **kimse** dedi. Yansıma bir yönteme iki yoldan bakar: ileti cevaplanır mı, ve tanım var mı. Bu iki soru vekilde ayrışır. **MP22.** `respond_to_missing?` vekil tarafından tanımlandığı için bu cevaplar verildi; tanımlanmasaydı sonuç `Yansımaya Bildirmek` bölümünde ölçülür.

#### Vekil Dizi Değildir

Vekil bir diziyi sarmalar, ama dizi değildir. Dizi gibi davranan bir nesnenin dizi olduğunu varsaymak tehlikelidir; aşağıdaki ölçüm bunun sınırlarını çizer.

```ruby
# kimlik.rb — vekil ve kimlik
require_relative "yazanlar"

vk = Vekil.new([3, 1, 2])
begin
  vk.sortt
rescue NoMethodError => e
  puts e.class
end
p vk.is_a?(Array)
p vk == [3, 1, 2]
p vk.class
p yazan(vk, :class)
```
```
NoMethodError
false
false
Vekil
["ata", "Kernel"]
```

Yanlış yazılmış `sortt` iletisi `NoMethodError` verdi: `method_missing` sarmalanan dizinin o iletiyi anlamadığını gördü ve `super` ile Ruby'nin varsayılanına bıraktı. `is_a?(Array)` `false` çıktı; vekil dizi değil, diziyi tutan bir nesne. `vk == [3, 1, 2]` de `false` verdi, çünkü `==` zincirde tanımı olan bir yöntemdir ve vekil onu yakalamaz: yakalama yalnız zincirde bulunmayan adlar için çalışır. `class` iletisi de bu yüzden yakalanmadı, `Vekil` döndü; yazanı Ruby 4.0.7'de **ata**, `Kernel`. Vekilin yakaladığı iletiler, zincirde hiç tanımı olmayanlardır. **MP23.** Bu bölümde yalnız `==`, `is_a?` ve `class` ölçüldü; `to_s` ayrı bir bölümde sınanır.

#### Zincirdeki Adlar Yakalanmaz

Vekil Dizi Değildir bölümü `==` ve `class` için yakalamanın çalışmadığını gösterdi. Aynı kural `to_s` gibi her nesnede bulunan başka adlar için de geçerli olmalı. Yanında, zincirde olmayan `to_a` ile karşılaştırılır.

```ruby
# zincirdeki.rb — to_s ve to_a karsilastirmasi
require_relative "yazanlar"

vk = Vekil.new([3, 1, 2])
p vk.to_a
p vk.to_s == [3, 1, 2].to_s
p yazan(vk, :to_s)
p yazan(vk, :to_a)
```
```
[3, 1, 2]
false
["ata", "Kernel"]
["kimse", "tanim yok"]
```

`to_a` zincirde bulunmadığı için yakalandı ve diziyi verdi. `to_s` ise Ruby 4.0.7'de `Kernel`'den geldiği için yakalanmadı: vekilin metni dizinin metni değildir ve yazanı **ata**. İki ad dizi için aynı derecede olağan, ama vekil için biri **kimse**, öbürü **ata** çıktı. Bu fark vekilin sınırını çizer: sarmalanan nesnenin davranışını kopyalamak istiyorsanız, her nesnede hazır bulunan adlar ayrıca ele alınmalıdır. **MP24.** Ölçüm `to_s` ve `to_a` içindir; `inspect` ve `hash` gibi öbür hazır adlar aynı kurala uyar, ama ölçülmedi.

#### Yakalayıcının Kendi Yazanı

Şimdiye kadar yakalanan ada baktık. Yakalayan yöntemin kendisi için de aynı soru sorulabilir: `method_missing` kimin?

```ruby
# yakalayici.rb — method_missing ve respond_to_missing? yazanlari
require_relative "yazanlar"

vk = Vekil.new([3, 1, 2])
p yazan(vk, :method_missing)
p yazan(vk, :respond_to_missing?)
p yazan(Object.new, :method_missing)
p BasicObject.private_method_defined?(:method_missing)
```
```
["programci", "Vekil, def govdesi"]
["programci", "Vekil, def govdesi"]
["ata", "BasicObject"]
true
```

Yakalanan `sort` kimse çıktı; yakalayan `method_missing` ise **programcı**: `Vekil` içinde `def` ile yazılmış, sahibi alıcının kendi sınıfı. Aynı soru sıradan bir `Object.new` için sorulunca Ruby 4.0.7'de yazan **ata**, `BasicObject` çıktı, ve son satır bu tanımın özel olduğunu söyledi. Yani her nesnenin zaten bir `method_missing` yöntemi vardır ve işi `NoMethodError` fırlatmaktır; `Vekil` bu yöntemi gölgeleyip kendi sürümünü koydu. Kimse yazanı bu yüzden bir boşluk değil, bir devir teslimdir: tanımı olmayan ad, tanımı olan bir yakalayıcıya düşer ve onun yazanı bellidir. **MP25.** Ölçüm yalnız `Vekil` ve `Object` için yapıldı; zincirde birden çok `method_missing` olduğunda `super`'ın hangisine ineceği bu derste sınanmadı.

#### Özel Yöntem Aktarılmaz

Vekil iletiyi `public_send` ile taşıdığı ve önce `respond_to?` ile sorduğu için sarmalanan nesnenin özel yöntemleri vekilden çağrılamaz. Bu bir tasarım sonucudur ve ölçülebilir.

```ruby
# ozel.rb — ozel yontemin vekil uzerinden cagrilmasi
require_relative "yazanlar"

class Kutu
  def acik = "acik"

  private

  def sir = "sir"
end

vk = Vekil.new(Kutu.new)
p vk.acik
begin
  vk.sir
rescue NoMethodError => e
  puts e.class
end
p vk.respond_to?(:sir)
p vk.respond_to?(:sir, true)
```
```
"acik"
NoMethodError
false
true
```

Genel `acik` yöntemi geçti, özel `sir` `NoMethodError` verdi. Son iki satır birbirinden ayrılır. `respond_to?(:sir)` `false` verdi, çünkü `respond_to_missing?` ikinci argümanı hedefe aktarır ve varsayılan hâlde hedefin yalnız genel yöntemleri sayılır. `respond_to?(:sir, true)` ise `true` verdi: özel yöntemler de sorulunca `Kutu` nesnesi `sir`'i tanıdı. Yine de `vk.sir` çalışmadı. Yansıma ile çağrı burada ayrışır: yansıma "bu ada cevap verilebilir" diyebilir, ama çağrıyı taşıyan `public_send` özel yöntemi yine reddeder. Vekil sarmaladığı nesnenin genel yüzünü taşır, tamamını değil; bu bazen istenen sonuçtur, çünkü sarmalama özel ayrıntıları dışarıya sızdırmaz. **MP26.**

#### Sessiz Yutma

`method_missing` yazarken `super` çağrısı unutulursa her ileti yakalanır ve hataya dönüşmez. Aşağıdaki sınıf `super` çağırmaz ve her iletiye `nil` döndürür.

```ruby
# sessiz.rb — super cagirmayan method_missing
class Sessiz
  def method_missing(*) = nil
end

s = Sessiz.new
p s.yazim_hatasi
p s.respond_to?(:yazim_hatasi)
```
```
nil
false
```

`yazim_hatasi` adı diye bir yöntem hiçbir yerde yok, ama çağrı `nil` döndürdü ve hata vermedi. Hata bildirimi, adı doğru yazdığımıza dair son güvencemizdi ve burada yutuldu. Bu yüzden `method_missing` yazarken tanımadığı iletiyi `super`'a bırakmak bir kuraldır, bir tercih değil. Ayrıca `respond_to?` `false` verdi: bu sınıf iletiyi cevaplıyor, ama yansımaya bunu bildirmiyor. **MP27.** Bu sınıf her iletiyi yuttuğundan hiçbir ad hata vermez; gerçek bir sınıfta yakalama bir önek ya da sarmalanan nesne gibi bir koşula bağlanır.

#### Yansımaya Bildirmek

Yakalama koşullu olduğunda yansımayı da bilgilendirmek gerekir. Aşağıdaki sınıf `bul_` öneki ile başlayan adları yakalar; önce `respond_to_missing?` olmadan, sonra onunla ölçülür.

```ruby
# bulucu.rb — respond_to_missing? olmadan ve ile
class Bulucu
  def method_missing(ad, *)
    return super unless ad.start_with?("bul_")

    ad.to_s.delete_prefix("bul_")
  end
end

b = Bulucu.new
p b.bul_ece
p b.respond_to?(:bul_ece)
begin
  b.method(:bul_ece)
rescue NameError => e
  puts e.class
end

class Bulucu
  def respond_to_missing?(ad, ozel = false) = ad.start_with?("bul_") || super
end

p b.respond_to?(:bul_ece)
p b.method(:bul_ece).call
```
```
"ece"
false
NameError
true
"ece"
```

`bul_ece` iletisi `"ece"` verdi, ama `respond_to?` `false` çıktı ve `method(:bul_ece)` `NameError` fırlattı. Yani iletiyi cevaplayan bir nesne, yansımaya göre o yöntemi tanımıyordu. Sınıf `respond_to_missing?` ile yeniden açılınca iki soru da düzeldi: `respond_to?` `true`, `method(:bul_ece).call` ise `"ece"` verdi. Kural şudur: `method_missing` yazan, `respond_to_missing?` da yazmalıdır. Aksi hâlde nesne yansımadan saklanır. **MP28.** Önek koşulu yalnız `bul_` ile ölçüldü; `respond_to_missing?` ile yakalama koşulu aynı biçimde yazıldı, ayrı yazılırsa tutarsızlık doğabilir.

#### Sınırlayıcı Ölçüm

Kimse yazanı kalıcı mı? Aşağıdaki sınıf yakaladığı her adı ilk çağrıda `define_method` ile gerçek bir yönteme çevirir. Böylece yakalama yalnız bir kez çalışır.

```ruby
# sinir.rb — kendini tanimlayan vekil
require_relative "yazanlar"

class Puan
  attr_reader :sayac

  def initialize
    @sayac = 0
  end

  def method_missing(ad, *)
    return super unless ad.start_with?("kare_")

    @sayac += 1
    n = ad.to_s.delete_prefix("kare_").to_i
    self.class.define_method(ad) { n * n }
    n * n
  end

  def respond_to_missing?(ad, ozel = false) = ad.start_with?("kare_") || super
end

pn = Puan.new
p [pn.kare_4, pn.kare_4, pn.kare_5, pn.sayac]
p yazan(pn, :kare_4)
p yazan(pn, :kare_9)
```
```
[16, 16, 25, 2]
["kod", "Puan, blok govdesi"]
["kimse", "tanim yok"]
```

`kare_4` iki kez çağrıldı ama `method_missing` yalnız bir kez çalıştı: ilk çağrıda yöntem tanımlandı, ikincisi doğrudan tanıma ulaştı. `sayac` iki oldu, çünkü `kare_4` ve `kare_5` birer kez yakalandı. Sonra `yazan` `kare_4`'ü **kod** olarak sınıflandırdı; artık yöntem bir bloktan üretilmiş gerçek bir tanımdır. Hiç çağrılmamış `kare_9` ise hâlâ **kimse**. Yani kimse yazanı kalıcı değil: aynı ad bir çağrıdan sonra koda dönüşebilir. Kategori adın değil, o anki tanım tablosunun özelliğidir. **MP29.** Tek bir sınıf ölçüldü; `define_method`'un `self.class` üzerinden çağrılması bir tasarım tercihidir ve tek örnek için tek yöntem tanımlamak da mümkündür.

#### Özet

- Zincirde tanımı olmayan bir ileti nesnenin `method_missing` yöntemine teslim edilir; `Vekil` bu yolla sarmaladığı diziye gelen iletiyi iletir.
- Yakalanan adların yazanı kimsedir: `yazan` yardımcısı tanım tablosunda ad bulamaz, ama ileti yine cevaplanır; yakalayan `method_missing` ise programcıdır ve `BasicObject`'teki özel varsayılanı gölgeler.
- `respond_to_missing?` yansımaya yakalamayı bildirir; `Vekil.method_defined?` ise yine `false` kalır.
- `super` çağrısı olmayan `method_missing` her iletiyi yutar ve yazım hatalarını gizler.
- Vekil, sarmaladığı türün yerine geçmez: `is_a?(Array)`, `==` ve `to_s` zincirdeki tanımlar olduğundan yakalanmaz; özel yöntemler de `public_send` ile aktarılmaz.
- Kimse yazanı kalıcı değildir: ilk çağrıda `define_method` ile kendini tanımlayan vekilde aynı ad koda dönüşür.

#### Sonraki Adım

Bu dersin son gözlemi, bir adın önce kimse sonra kod olabildiğini gösterdi. Kursun kapanış dersi bu geçişi bilerek kurar: `alan :fiyat, 0` gibi tek bir bildirim satırı, arkasında yöntem doğurur. Alan Özel Diller, bu bildirimin hangi yazanı gizlediğini sorar.

### Alan Özel Diller

Source: https://academia.sh/tr/kurslar/ruby-nesne-yonelimi/alan-ozel-diller

Bildirim satırının arkasında yöntemi kimin yazdığı ölçülür: define_method ile üretilen erişimci koddur, instance_eval ise self'i değiştirir ama yerel değişkenleri değiştirmez. Ders, kursun dokuz gözleminin kapanış tablosunu da verir.

Önceki ders, bir adın önce **kimse** sonra **kod** olabildiğini gösterdi. Bu ders aynı geçişi bilerek kullanır: `alan :fiyat, 0` gibi tek bir satır okunduğunda bir çağrıya benzer, ama çalıştığında sınıfa yeni bir yöntem ekler. **Alan özel dil (domain-specific language)**, belirli bir iş için küçültülmüş ve o işin sözcükleriyle konuşan bir yazım biçimidir. Ruby'de bunun bir kurulumu, sınıf gövdesinde çağrılan bildirim yöntemleridir. Satırın kısalığı, ölçülmesini gereksiz kılmaz; tersine, bu satırların arkasında yöntemi kimin yazdığı ilk bakışta belli değildir.

Eksen kursun başından beri aynı: **bu yöntemi kim yazdı?** Bu dersin yazanı **kod**. `alan :fiyat, 0` satırını programcı yazdı, ama bu satırın doğurduğu `fiyat` yöntemini yazan Ruby kodudur, yani bir `define_method` çağrısı. Aşağıdaki ölçümler bu ayrımı çizer; sonda bloklu biçimin nerede kırıldığı gösterilir. `yazan` yardımcısı önceki derslerdeki hâliyle verilir.

```ruby
# yazan.rb — bir yontemi kimin yazdigini siniflandiran yardimci

# Siniflandirma sirayla sorar: once alicinin zincirinde bir tanim var mi,
# sonra tanimi alicinin kendi sinifi mi tasiyor, sonra tanim nasil uretildi.
def yazan(alici, ad)
  zincir = alici.singleton_class
  unless zincir.method_defined?(ad) || zincir.private_method_defined?(ad)
    return ["kimse", "tanim yok"]
  end

  tanim = zincir.instance_method(ad)
  sahip = tanim.owner
  yer = sahip == zincir ? "tekil sinif" : sahip.to_s
  return ["ata", yer] unless sahip == alici.class || sahip == zincir

  iz = RubyVM::InstructionSequence.of(tanim)
  return ["kod", "#{yer}, ruby govdesi yok"] if iz.nil?
  return ["kod", "#{yer}, blok govdesi"] if iz.label.start_with?("block")

  ["programci", "#{yer}, def govdesi"]
end
```

Bu blok kaynaktır; dosya diske yazılır ve `require_relative` ile çekilir.

#### Ölçülen Sınıf

Gözlem iki sınıf üzerindedir. `Model`, bildirim yöntemini taşır; `Urun` onu kullanır.

```ruby
# model.rb — bildirim yontemi tasiyan taban sinif
require_relative "yazan"

class Model
  def self.alan(ad, varsayilan)
    define_method(ad) { instance_variable_get("@#{ad}") || varsayilan }
  end
end

class Urun < Model
  alan :ad, "adsiz"
  alan :fiyat, 0
end
```

Bu blok de kaynaktır. `alan`, adı ve bir varsayılan değeri alır; ad için bir okuyucu yöntem tanımlar. Okuyucu, örnek değişkeni doluysa onu, boşsa varsayılanı verir. `Urun` gövdesindeki iki satır çağrıdır, tanım değildir: sınıfta tek bir `def` yoktur.

#### Bildirim Satırı Neyi Yazdı

Dersin gözlemi `Urun.new.fiyat` iletisidir. Önce yöntemin ne verdiği, sonra kimin yazdığı ve sınıfta nelerin göründüğü ölçülür.

```ruby
# olcum.rb — bildirim satirinin dogurdugu yontem
require_relative "model"

u = Urun.new
p u.fiyat
p u.ad
p yazan(u, :fiyat)
p Urun.instance_methods(false).sort
```
```
0
"adsiz"
["kod", "Urun, blok govdesi"]
[:ad, :fiyat]
```

Yöntem `0` verdi, çünkü örnek değişkeni boştu ve varsayılan devreye girdi. Yazan **kod** çıktı: `yazan` tanımı `Urun`'un kendi zincir halkasında buldu ve `iseq` etiketi bir blok gövdesine işaret etti. Sınıfın kendi yöntem listesi `[:ad, :fiyat]`; iki bildirim satırı iki yöntem doğurdu. Programcının yazdığı yalnız iki satırdır, ama sınıfın arayüzü iki yöntemdir. **MP30.** Bu ölçüm tek bir bildirim yöntemi ve iki alan içindir; `alan` başka bir gövdeyle yazılsaydı yazan değişebilirdi.

#### Çağrı Ama Yazan Kod

Yöntemin gerçekten okuduğu şey örnek değişkenidir. Değişken doldurulunca varsayılan geride kalmalıdır.

```ruby
# deger.rb — varsayilan ve doldurulmus deger
require_relative "model"

u = Urun.new
u.instance_variable_set(:@fiyat, 40)
p u.fiyat
p Urun.new.fiyat
p yazan(Urun, :alan)
```
```
40
0
["ata", "#<Class:Model>"]
```

Dolu değişken varsayılanı geçti: `40`. Aynı sınıfın yeni nesnesi ise `0` verdi, çünkü varsayılan sınıfa, değer nesneye aittir. Son satır sahibi alıcıdan ayırır: `alan` yöntemini yazan **ata** çıktı ve yer olarak `Model`'in tekil sınıfı göründü. Alıcı olarak `Urun` sınıfı verildi; `alan` tanımı `Urun`'un kendi tekil sınıfında değil, `Model`'inkinde duruyor. Sahip alıcının tekil sınıfı olmadığı için kural onu atadan saydı; yani aynı bildirim satırında iki ayrı yazan çalışır. `alan` çağrısının yöntemini ata yazdı, o çağrının doğurduğu `fiyat` yöntemini kod yazdı. **MP31.** Bu ayrım `yazan` yardımcısının tekil sınıf kurallarına dayanır; başka bir yardımcı farklı sınıflandırabilir.

Bu, M08/K06'daki soruyla karıştırılmamalıdır. Orada bir davranışın kaynaktaki sebebi soruldu: çağrı mı, açıklama mı, varsayılan mı. Burada sebep sorulmaz, tanımın yazarı sorulur: `alan :fiyat, 0` bir çağrıdır, ama doğurduğu yöntemi yazan koddur.

#### Okuma Durum Bırakmaz

Üretilen yöntemin bir yan etkisi olup olmadığı da ölçülebilir. Varsayılan, yöntemin içinde hesaplanır; nesneye yazılmaz.

```ruby
# okuma.rb — okuyucunun nesnede biraktigi iz
require_relative "model"

u = Urun.new
p u.fiyat
p u.instance_variables
p u.method(:fiyat).owner
p u.method(:fiyat).arity
```
```
0
[]
Urun
0
```

`fiyat` okundu ve `0` verdi, ama nesnenin örnek değişkenleri listesi boş kaldı: varsayılan bir değer olarak döndü, bir alan olarak saklanmadı. Yöntemin sahibi `Urun`, yani bildirim yöntemin tanımını alt sınıfa koydu; `Model`'e değil. Argüman sayısı sıfır: `define_method` ile verilen blok parametre almadığı için üretilen okuyucu da almaz. Bu üç gözlem, bildirimsel arayüzün sınıfa ne yazdığını tamamlar: yeni bir yöntem, sahibi alt sınıf, durumu yok. **MP32.** Bu ölçüm yalnız okuma yönündedir; değeri yazan bir yöntem bu `alan` tanımında yok, değer yalnız `instance_variable_set` ile verildi.

#### Sınıf Yöntemi Nerede Aranır

`Urun.alan` çağrısının neden `Model`'e ulaştığı tekil sınıf zincirinden okunur.

```ruby
# zincir.rb — sinif yontemlerinin zinciri
require_relative "model"

p Urun.singleton_class.ancestors.take(3)
p Model.singleton_class.instance_methods(false)
p Urun.respond_to?(:alan)
p Urun.new.respond_to?(:alan)
```
```
[#<Class:Urun>, #<Class:Model>, #<Class:Object>]
[:alan]
true
false
```

Zincirin ilk halkası `Urun`'un kendi tekil sınıfı, ikincisi `Model`'inkidir. Sınıf yöntemleri kalıtımla gelir çünkü alt sınıfın tekil sınıfı, üst sınıfın tekil sınıfından türer. `Urun.alan` bu zincirde ikinci halkada bulundu. Bildirim yöntemini tabanda yazmak bu yüzden yeter: her alt sınıf onu kendi gövdesinde çağırabilir. `Model`'in tekil sınıfında tek yöntem var, `alan`. `Urun` sınıfı ona cevap verir (`true`), ama `Urun`'un bir nesnesi vermez (`false`): bildirim sınıfın arayüzüne aittir, nesnenin arayüzüne değil. Nesne yalnız bildirimin doğurduğu `ad` ve `fiyat` yöntemlerini görür. Bu ayrım alan özel dilin neden sınıf gövdesinde yaşadığını da söyler: bildirim yazılırken konuşulan şey nesne değil, sınıftır. **MP33.** Çıktı yalnız ilk üç halkayı gösterir; zincirin devamı bu derste ölçülmedi.

#### Bloklu Alan Özel Dil

Bildirimleri sınıf gövdesinin dışına çıkarmak için başka bir kurulum vardır: bir blok verilir ve blok bir nesnenin bağlamında çalıştırılır.

```ruby
# form.rb — instance_eval ile bloklu biçim
class Form
  attr_reader :alanlar

  def self.kur(&blok)
    f = new
    f.instance_eval(&blok)
    f
  end

  def initialize
    @alanlar = []
  end

  def alan(ad)
    @alanlar << ad
  end
end

f = Form.kur do
  alan :ad
  alan :eposta
end
p f.alanlar
```
```
[:ad, :eposta]
```

Blok içindeki `alan` çağrılarının alıcısı yazılmadı; `instance_eval` bloğu `Form` nesnesinin bağlamında çalıştırdı, yani bloğun `self`'i o nesne oldu. Sonuç `[:ad, :eposta]`. Bu biçim okunaklıdır: kullanıcı nesne adı yazmadan yalnız sözcükleri yazar. Okunaklılığın bir bedeli var ve aşağıda ölçülür. **MP34.** Burada tek bir `alan` yöntemi ve iki çağrı vardı; bloğun içinde başka yöntem çağrıları bu ölçümde yok.

#### Sınırlayıcı Ölçüm

`instance_eval` bloğun `self`'ini değiştirir. Peki bloğun dışındaki değişkenlere ne olur? Üç değişkene bakılır: dışarıda tanımlı bir örnek değişkeni, bir yerel değişken ve `self`'in kendisi. Sonra aynı kurulumun `yield` ile yazılmış biçimi denenir.

```ruby
# sinir.rb — instance_eval ve dis degiskenler
@baslik = :kayit
yerel_ad = :yerel

class Form
  attr_reader :alanlar

  def self.kur(&blok)
    f = new
    f.instance_eval(&blok)
    f
  end

  def initialize
    @alanlar = []
  end

  def alan(ad)
    @alanlar << ad
  end
end

class Form2
  attr_reader :alanlar

  def self.kur
    f = new
    yield f
    f
  end

  def initialize
    @alanlar = []
  end

  def alan(ad)
    @alanlar << ad
  end
end

p Form.kur { alan @baslik }.alanlar
p Form.kur { alan yerel_ad }.alanlar
p Form.kur { alan self.class }.alanlar
p Form2.kur { |f| f.alan @baslik }.alanlar
```
```
[nil]
[:yerel]
[Form]
[:kayit]
```

Üç sonuç var. Dışarıdaki `@baslik` blok içinde `nil` verdi: örnek değişkenleri `self`'e bağlıdır ve `self` değişti. Yerel değişken `yerel_ad` ise `[:yerel]` verdi: yerel değişkenler bloğun kapanışıyla yakalanır, `self`'e bağlı değildir. `self.class` `Form` çıktı. Son satırdaki `yield` biçimi `self`'i korudu ve `@baslik` değeri `[:kayit]` olarak okundu. `instance_eval` böylece sözcük kazandırır ama dışarıdaki örnek değişkenlerini görünmez yapar. **MP35.** Bu fark yalnız örnek değişkenleri için ölçüldü; sabitlerin ve sınıf değişkenlerinin davranışı bu derste sınanmadı.

Bildirimin bir de sessiz bedeli vardır: aynı alanı iki kez bildirmek hata vermez.

```ruby
# tekrar.rb — ayni alanin iki kez bildirilmesi
require_relative "model"

class Etiket < Model
  alan :ad, "adsiz"
  alan :ad, "yeni"
end

p Etiket.new.ad
p Etiket.instance_methods(false)
```
```
"yeni"
[:ad]
```

İkinci bildirim birincinin üzerine yazdı: `ad` artık `"yeni"` veriyor ve sınıfta hâlâ tek `ad` yöntemi var. Hiçbir uyarı, hiçbir istisna yok. Bildirimsel arayüz bu yüzden kolay yazılır ve kolay yanlış yazılır; `define_method` aynı adı sessizce yeniden tanımlar. **MP36.** Bu ölçüm iki bildirimin aynı sınıfta olduğu durumdur; ikisi farklı kalıtım düzeylerinde olsaydı alt sınıf üst sınıfın yöntemini gölgelerdi.

#### Özet

- `alan :fiyat, 0` satırı bir çağrıdır; doğurduğu `fiyat` yöntemini yazan **kod**dur, çünkü `define_method` bir blok gövdesi üretir.
- Bildirim yöntemi `alan` ise `Model`'de yazılmıştır ve sınıf yöntemi olduğundan `Urun`'un tekil sınıf zincirinde aranır; o yöntemin yazanı **ata**dır.
- `instance_eval` bloğun `self`'ini değiştirir; dış örnek değişkenleri `nil` olur, yerel değişkenler görünür kalır.
- `yield` ile aktarılan nesne `self`'i korur; sözcük kazancı için dış örnek değişkenlerinden vazgeçilir.
- Aynı alanın iki kez bildirilmesi hata vermez; sonraki bildirim sessizce önceki yöntemin yerine geçer.
- Bu dersin yazanı kod: bildirimsel arayüzün satırları programcıya, doğurdukları yöntemler koda aittir.

#### Kurs Kapanışı

Kursun her dersi aynı soruyu sordu: bir yöntemi kim yazdı? Dokuz gözlem, dokuz kez bu soruyu bir ölçümle yanıtladı. Aşağıdaki tablo hepsini yazan kategorisine göre toplar.

| Ders | Ölçülen yöntem | Yazan | Sınırlayıcı ölçüm |
| --- | --- | --- | --- |
| Sınıf Tanımı ve Örnek Değişkenler | `hesap.bakiye_metni` | programcı | `attr_reader` aynı sınıfta ikinci yazan |
| Kalıtım ve `super` | `Vadeli.new("can", 50).bakiye_metni` | ata | `super` çağıran yeniden tanım programcı |
| Modüller ve Karışımlar | `hesap.rapor` | ata | sınıfın `def`'i karışımı gölgeler |
| Görünürlük | `komisyon` (`hesap.net` içinden) | programcı | `public :komisyon` tanımın sahibini alt sınıfa taşır |
| Yöntem Arama Zinciri | `Kasa.new.ac` | ata | tek nesnede tekil yöntem başa girer |
| Yansıma | `Nokta.new(3, 4).x` | kod | çekirdek yöntemleri de kod görünür |
| Dinamik Yöntem Tanımı | `Ayar.new.renk` | kod | `class_eval` dizgisi programcı çıkar |
| Eksik Yöntem Yakalama | `Vekil.new([3, 1, 2]).sort` | kimse | ilk çağrıda kimse koda dönüşür |
| Alan Özel Diller | `Urun.new.fiyat` | kod | `instance_eval` `self`'i değiştirir |

Yazanların dağılımı: **3 ata · 3 kod · 2 programcı · 1 kimse**. Tablo kursun bir bulgusunu taşır: yazan sınıf başına değil yöntem başına ölçüldü, ve bir yöntemin yazanı kaynak metinde nasıl göründüğüyle değil zincirde nasıl bulunduğuyla belirlendi. Aynı sınıfta iki yazan, aynı satırda iki yazan göründü.

İki çıkarım yapılabilir. Birincisi, yazanı bilmek yazım biçimini bilmeyi gerektirmez: `attr_reader` ve `define_method` ayrı sözcüklerdir ama ikisi de kodu yazan yapar; `def` ve `class_eval` dizgisi ayrı sözcüklerdir ama ikisi de programcıyı. İkincisi, yansıma yazanı söylemek için yeterli değildir: `source_location`, `owner` ve `respond_to?` ayrı ayrı yanıltıcı olabilir; kural bunları birlikte okudu. Kimse kategorisi tek örneğe düştü ve o da kalıcı olmadı. Üçüncü bir çıkarım dağılımın kendisinden gelir: dokuz gözlemin yalnız ikisinde yazan programcıdır. Ölçülen sınıflarda arayüzün büyük kısmı atadan, karışımdan ve kodun ürettiği tanımlardan geldi; sınıf gövdesine bakmak bu yüzden yazanı bulmak için tek başına yetmez, zincirin ve tanım biçiminin ölçülmesi gerekir.

**MP37.** Tablo yalnız dokuz ölçümün sınıflandırmasıdır; kategorilerin sayısı ölçülen yöntemlerin seçimini yansıtır, Ruby'deki yöntemlerin genel dağılımını değil. Yazan hücreleri her dersin Özet bölümündeki ifadeden alındı.

Kurs burada kapanır. Sıradaki kurs, Ruby ile yazılmış bir web uygulamasının dört katmanını sırayla ele alır: yönlendirme, denetleyici, model ve görünüm. Orada da aynı kural geçerli kalır: tahmin yürütülmez, ölçülür.

