1 Mart 2025 Cumartesi

Ruby Temelleri 6

https://ujk-ujk.blogspot.com/2025/03/ruby-temelleri-6.html
İçindekiler +

 

Merhaba kaldığımız yerden devam edelim.



Comparable Modülü

Deyim yapısı

include Comparable


Uzay gemisi operatörü (<=>) uyarlamasını yapar. Nesneleri sıralanabilir sınıflara karşılaştırma özelliği eklemekte kullanılır. Sınıfa metodun uyarlaması yapılır.

Aynı sınıf oluşumu olan x ve y nesnelerini alalım.

  • x < y ise , x <=> y negatif bir sayı dönmelidir.
  • x == y ise , x <=> y sıfır dönmelidir.
  • x > y ise , x <=> y pozitif bir sayı dönmelidir



-- Dikdörtgenleri alan olarak karşılaştırmak

Bir örnek uygulama ile Comparable modülü kullanımını görelim.

class Dikdörtgen
  include Comparable

  def initialize(a, b)
    @a = a
    @b = b
  end

  def alan
    @a * @b
  end

  def <=>(diğer)
    alan <=> diğer.alan
  end
end

r1 = Dikdörtgen.new(1, 1)
r2 = Dikdörtgen.new(2, 2)
r3 = Dikdörtgen.new(3, 3)

r2 >= r1 # => true
r2.between? r1, r3 # => true
r3.between? r1, r2 # => false


Sadece <=> metodunu tanımlayarak karşılaştırma komutlarının hepsini kullanabiliriz. Alan hesabı karşılaştırması yapılmasını da test edelim.

r4 = Dikdörtgen.new(1,4)
r2 == r4  # => true





Gem kullanımı

Ruby Gem'ler (Ruby yakut demek, Gem de mücevher) değişik yararlı işler yapan kodların bulunduğu açık kaynak kütüphanelerdir. Bu Gem'leri Ruby kurulumunuza dahil edersek, kendi programlarımızda o kütüphanedeki kodları kullanabiliriz.


-- Gem kurulumu

Bir gem kurmak için 

gem install [gem_adı]

komutu kullanılır. Versiyon da belirtilebilir.

gem install rails -v 0.14.1       # 0.14.0 versiyonu kurar
gem install rails -v '~> 0.14.0'  # 0.14.0 vaya büyük versiyonu kurar
gem install rails -v '~> 0.14.0, < 0.14.4'


Eğer bir kısım Gem ile bağlantılı olan bir proje geliştiriyorsanız, bu gemlerin isimleri Gemfile adında bir dosyada toplanır. Bu dosya içinde her kullanacağımız gem için 

gem 'gem_adı'

satırı eklemeliyiz. Örnek olarak.

gem 'nokogiri'
gem 'rails', '5.0.0'
gem 'rack',  '>=1.0'
gem 'thin',  '~>1.1'


Bu Gemfile içindekilerin kurulması için Bundler'a ihtiyacımız var. Bundler gemleri bağlantılı gem'lerle beraber kurar. Çalıştırmak için önce bundler gem kurulmalıdır.

gem install bundler


Daha sonra Gemfile içindeki Gem'leri kurmak için.

bundle install


Komutu girilir. 



-- -- Versiyon belirtmek

Komut satırında versiyon numarası -v opsiyonu ile verilir. 

gem install gem_adı -v 3.14


Gemfile dosyası içinde versiyon belirtirken bazı seçenekler var.

  • Versiyon belirtilmemişse ( gem 'gem_adı' )  - Gemfile içindeki diğer Gem'lerle uyumlu en son versiyon yüklenir.
  • Tam versiyon verilmişse ( gem 'gem_adı', '3.14' ) - Sadece versiyon 3.14 kurulur (ve eğer Gemfile içindeki diğer Gem'ler ile uyumsuzsa çakılır).
  • İyimser minimum versiyon numarası  ( gem 'gem_adı', '>=3.14' ) - Sadece Gemfile içindekilerle uyumlu olan 3.14 ve üstü versiyon varsa yükler , bulamazsa çakılır. İstenirse > operatörü de kullanılabilir.
  • Karamsar minimum versiyon numarası ( gem 'gem_adı', '~>3.14' ) - Bu aslında gem 'gem_adı', '>=3.14', '<4' ile aynı işi yapar. 


Tavsiye olarak : rbenv ya da rvm gibi Ruby versiyon yöneticilerinden birinde çalışmak ve projelerinizi orada geliştirmek çok daha işinizi görecektir. Bu bağımlılıklar çok sıkıntı verebiliyor. 



-- Github ya da dosya sisteminden gem kurmak

Örnek olarak rack gem'i Github'dan indirip kuralım.

$ mkdir newgem
$ cd newgem
$ git clone https://github.com/rack/rack
  Cloning into 'rack'...
$ cd rack
$ gem build rack.gemspec
  Successfully built RubyGem
  Name: rack
  Version: 3.1.1
  File: rack-3.1.1.gem
$ gem install rack-3.1.1.gem
  Successfully installed rack-3.1.1  




-- Bir Gem'in mevcut olduğunu kodunuzda test etmek

Eğer kodunuzda gerekli bir Gem'in sistemde kurulu olduğunu test etmek isterseniz, örnekteki 'nokogiri' Gem kontrolüne bakınız.

begin
  found_gem = Gem::Specification.find_by_name('nokogiri')
  require 'nokogiri'
  # ....
  # <kodunuzun geri kalanı>
rescue Gem::LoadError
  # cannot load such file -- nokogiri (LoadError)
end


Bunun yanında istersek Gem kontrolü yapan bir metod yazıp kodumuzu daha düzenli yapabiliriz.

def gem_installed?(gem_name)
  found_gem = false
  begin
    found_gem = Gem::Specification.find_by_name(gem_name)
  rescue Gem::LoadError
     return false
  else
    return true
  end
end


Şimdi bir Gem'in olup olmadığını test edip bir mesaj verebiliriz.

if gem_installed?('nokogiri')
  require 'nokogiri'
else
  printf "nokogiri gem required\n"
  exit 1
end


ya da 

if gem_installed?('nokogiri')
  require 'nokogiri'
else
  require 'REXML'
end




-- Gemfile ve Bundler kullanımı

Bir Gemfile kullanmak uygulamanızdaki bağımlılıkları yönetmenin standart yoludur. Gemfile şuna benzer.

source 'https://rubygems.org'
gem 'rack'
gem 'sinatra'
gem 'uglifier'


İstediğimiz Gem'lerin versiyonunu da belirtebiliriz.

# Sadece 1.5.X kullan
gem 'rack', '~>1.5.2'
# Tek bir versiyon kullan
gem 'sinatra', '1.4.7'
# En az ya da daha büyük versiyon kullan
gem 'uglifier', '>= 1.3.0'


Ayrıca Gem'leri bir Git reposundan da çekebiliriz.

# Github'dan çekmek
gem 'sinatra', git: 'https://github.com/sinatra/sinatra.git'
# Bir sha değeri verilebilir
gem 'sinatra', git: 'https://github.com/sinatra/sinatra.git',
    sha: '30d4fb468fd1d6373f82127d845b153f17b54c51'
# Bir branch da belirtebiliriz, ancak bu genelde güvenli değildir
gem 'sinatra', git: 'https://github.com/sinatra/sinatra.git', branch: 'master'


Ayrıca Gem'leri nerede kullanıldıklarına göre de gruplayabiliriz.

group :development, :test do
  # Bu Gem sadece dev ve test ortamlarında kullanılacak , production'da değil
  gem 'byebug'
end


Ayrıca belirli Gem'lerin hangi platformda kullanılacağını da belirtebiliriz. 

platform :jruby do
  gem 'activerecord-jdbc-adapter'
  gem 'jdbc-postgres'
end

platform :ruby do
  gem 'pg'
end


Bundler Gem sistemde yüklüyse Gemfile içindeki Gem'leri kurmak için 

bundle install





Tasarım kalıpları ve deyimleri


-- Decorator Kalıp

Decoratör tasarım kalıbı aynı sınıftan üretilmiş diğer nesneleri etkilemeden bir nesnenin davranışını değiştirir. Bu bazen alt-sınıflar yapısını kullanmaktan daha faydalıdır. 

Bir örnek verelim. Bir Pizza sınıfımız ve 300 değeri dönen bir fiyat metodu olsun.

class Pizza
  def fiyat
    300
  end
end


Pizza'ya peynir ekleyince fiyatı 350 olsun. Bunu ifade için PeynirliPizza alt sınıfı oluşturabiliriz.

class PeynirliPizza < Pizza
  def fiyat
    350
  end
end


Büyük pizzanın da normale göre 100 lira fazla fiyatı olsa.

class BüyükPizza < Pizza
  def fiyat
    400
  end
end


Ayrıca fiyatı BüyükPizza'dan 15 fazla olan ÇokBüyükPizza adlı bir sınıf olabilir. Bunun peynirli olanı BüyükPeynirliPizza ve ÇokBüyükPeynirliPizza da olabilir derken şimdiden 6 alt sınıf oluştu bile.  

Olayı basitleştirmek için modüller kullanarak Pizza sınıfına davranışlar kazandırabiliriz. 

Module + extend + super decorator:->

class Pizza
  def fiyat
    300
  end
end

module PeynirliPizza
  def fiyat
    super + 50
  end
end

module LargePizza
  def fiyat
    super + 100
  end
end

pizza = Pizza.new             #=> fiyat = 300
pizza.extend(PeynirliPizza)   #=> fiyat = 350
pizza.extend(LargePizza)      #=> fiyat = 450
pizza.fiyat                   #=> fiyat = 450




-- Observer kalıp

Observer tasarım kalıbında subject (özne) adı verilen bir nesne ve ona bağlı observer (gözlemci) adı verilen nesnelere durumlardaki değişiklikleri onların metodlarını kullanarak otomatik haber vermesi esastır.

Ruby Observer tasarım kalıbı için basit bir mekanizma sağlar. Observable modülü gözlemcilere değişklikleri haber verecek kodları içerir. 

require "observer"

class Moderator
  include Observable

  def initialize(name)
    @name = name
  end

  def write
    message = "Computer says: No"
    changed
    notify_observers(message)
  end
end

class Warner
  def initialize(moderator, limit)
    @limit = limit
    moderator.add_observer(self)
  end
end

class Subscriber < Warner
  def update(message)
    puts "#{message}"
  end
end

moderator = Moderator.new("Rupert")
Subscriber.new(moderator, 1)
moderator.write
moderator.write


Burada kilit satırlar include Observablenotify_observers(message) add_observer(self) ve update(message) metodu. Bu sayede moderator nesnesi Subscriber sınıfı oluşumu üzerinden metod çağırarak mesajları yazdırıyor.



-- Singleton tasarım kalıbı

Singleton tasarım kalıbı bir sınıftan sadece bir tane nesne üretilmesi , yenisi üretilmeye kalkınca zaten mevcut üretilmiş olanın kullanılmasıdır. 

Ruby standart kütüphanesinde Singleton modülü vardır. Sınıfa bunu ekleyerek başlarız.

require 'singleton'

class Logger
  include Singleton
end


Eğer bu sınıftan normal bildiğimiz sınıflar gibi oluşum nesnesi yapmaya kalkarsak hata oluşur. 

Logger.new
# private method `new' called for Logger:Class (NoMethodError)


Yegane oluşum nesnesine erişmek için instance metodu kullanılır. 

birinci, ikinci = Logger.instance, Logger.instance
birinci == ikinci   #=> true


Genelde kullanıldığı örnek uygulama olan Logger ile örnek verelim.



-- -- Logger örneği

require 'singleton'
class Logger
  include Singleton

  def initialize
    @log = File.open("log.txt", "a")
  end

  def log(msg)
    @log.puts(msg)
  end
end


Bu singleton Logger sınıfı oluşum nesnesini kullanarak log.txt isimli dosyaya verilen mesajı yazdırmak için.

Logger.instance.log('mesaj 1')
Logger.instance.log('mesaj 2')



-- -- Singleton olmadan

Yukarıdaki örneği kendi kontrolümüzde instance sınıf metodu tanımlayarak da yapabiliriz. 

class Logger
  def initialize
    @log = File.open("log.txt", "a")
  end

  def log(msg)
    @log.puts(msg)
  end

  def self.instance
    @instance ||= new
  end
end

Logger.instance.log('mesaj 1')
Logger.instance.log('mesaj 2')

instance metodunun açık ifadesi 

def self.instance
  @instance = @instance || Logger.new
end


Bu kod da işimizi görüyor ancak mesela kodumuzda Logger.new satırını kazara kullanırsak ikinci bir nesne üretiriz. Singleton modülü kullanmak çok daha güvenli, çünkü her türlü testleri yapılmış, olası sorunlardan temizlenmiştir. 



-- Proxy nesnesi

Proxy nesnesi genelde diğer bir nesneye güvenli erişim için kullanılır. 

Diyelim bir kaynağa sadece belirli yetkileri olan kullanıcıların erişebilmesini istiyoruz. 

Proxy tanımlaması: (Sadece rezervasyonları görebilir olarak belirtilen kullanıcılar rezervasyon servisine erişebilir)

Önce Kullanıcı sınıfı tanımlaması

class Kullanıcı
  attr_reader :isim

  def initialize(rezervasyon_görebilir, isim)
    @rezervasyon_görebilir = rezervasyon_görebilir
    @isim = isim
  end

  def rezervasyon_görebilir?
    @rezervasyon_görebilir
  end
end


Rezervasyon veri modeli ve RezervasyonServisi sınıfı

class Rezervasyon
  attr_reader :t_fiyat, :tarih

  def initialize(tarih, t_fiyat)
    @tarih = tarih
    @t_fiyat = t_fiyat
  end
end

class RezervasyonServis
  def sayılı_t_fiyat_rezervasyonlar(tarihinden, tarihine, rezervasyon_sayısı)
    # Normalde bu veri bir veritabanından okunur
    rezervasyonlar = [
      Rezervasyon.new(Date.new(2014, 5, 15), 100),
      Rezervasyon.new(Date.new(2017, 5, 15), 10),          
      Rezervasyon.new(Date.new(2017, 1, 15), 50)
    ]

    filtrelenmiş_rezervasyonlar = rezervasyonlar.select do |rezervasyon|
      rezervasyon.tarih.between?(tarihinden, tarihine)
    end

    filtrelenmiş_rezervasyonlar.take(rezervasyon_sayısı)
  end
end  


Proxy sınıfımızın tanımı

class Proxy
  def initialize(kullanıcı, rezervasyon_servis)
    @kullanıcı = kullanıcı
    @rezervasyon_servis = rezervasyon_servis
  end

  def sayılı_t_fiyat_rezervasyonlar(tarihinden, tarihine, rezervasyon_sayısı)
    # sadece yetkili kullanıcı servisten veriye ulaşabilir
    if @kullanıcı.rezervasyon_görebilir?
      @rezervasyon_servis.sayılı_t_fiyat_rezervasyonlar(
        tarihinden,
        tarihine,
        rezervasyon_sayısı
      )
    else
      []
    end
  end
end


Ve kullanıcıya hizmet veren StatsServis sınıfı

class StatsServis
  def initialize(rezervasyon_servis)
    @rezervasyon_servis = rezervasyon_servis
  end

  def yıllık_top_100_rezervasyon_ortalama_t_fiyat(sene)
    rezervasyonlar = @rezervasyon_servis.sayılı_t_fiyat_rezervasyonlar(
      Date.new(sene, 1, 1),
      Date.new(sene, 12, 31),
      100
    )

    if rezervasyonlar.length > 0
      toplam = rezervasyonlar.reduce(0) do |memo, rezervasyon|
        memo + rezervasyon.t_fiyat
      end

      toplam / rezervasyonlar.length
    else
      0
    end
  end
end


Şimdi değişik kullanıcılarla deneme yapalım.

require "date"

def test(kullanıcı, sene)
  rezervasyon_servis = Proxy.new(kullanıcı, RezervasyonServis.new)
  stats_servis = StatsServis.new(rezervasyon_servis)
  ortalama_fiyat = stats_servis.yıllık_top_100_rezervasyon_ortalama_t_fiyat(sene)
  puts "#{kullanıcı.isim} şunu görür: #{ortalama_fiyat}"
end

test(Kullanıcı.new(true, "Admin Ümit"), 2017)
# Admin Ümit şunu görür: 30

test(Kullanıcı.new(false, "Misafir"),   2017)
# Misafir şunu görür: 0


StatsServis sınıfı RezervasyonServis sınıfına Proxy sınıfı üzerinden eriştiği için Proxy sınıfı yetkisiz kullanıcıların rezervasyon kayıtlarına ulaşmasını engelliyor.


Avantajlar

  • Erişim kısıtlamaları değiştiğinde RezervasyonServis kodunda değişiklik yapmamız gerekmiyor. 
  • İşimizle ilgili veriler (tarihinden, tarihine, rezervasyon_sayısı) ve kullanıcı yetkilerini aynı kapsamlarda kullanmıyoruz.
  • Kullanıcıya hizmet veren StatsServis yetkilendirme lojiğinden bağımsız kalıyor.


Uyarı

  • Proxy nesnesi , sakladığı nesne ile hemen hemen aynı davranışı sergilediği için kullanıcı servisi proxy varlığından habersiz çalışır. 




Kaynak dosyaları kodumuza eklemek

Programımız içine kendi yazdığımız kod dosyalarını ya da hazır kütüphane dosyalarını eklemek ihtiyacı sıklıkla karşılaştığımız bir uygulama.


-- Dosyaları sadece bir kez çalıştırmak için yüklemek

Kernel#require metodu kod dosyalarını sadece bir kere yükler (aynı dosyaya bir'den fazla require satırı kullanılsa bile sadece bir kez yüklenir ve içindeki kod çalıştırılır). Dosyaları bulmak için bulunulan klasörde yoksa öncelikle Ruby $LOAD_PATH global değişkeninde verilen yerlere bakılır. 

>> $LOAD_PATH
=>
["/usr/local/lib/site_ruby/3.2.0",
 "/usr/local/lib/x86_64-linux-gnu/site_ruby",
 "/usr/local/lib/site_ruby",
 "/usr/lib/ruby/vendor_ruby/3.2.0",
 "/usr/lib/x86_64-linux-gnu/ruby/vendor_ruby/3.2.0",
 "/usr/lib/ruby/vendor_ruby",
 "/usr/lib/ruby/3.2.0",
 "/usr/lib/x86_64-linux-gnu/ruby/3.2.0"]


Dosya adı eklentileri .rb, .so, .o veya .dll opsiyoneldir. Bağıl dosya yolları bulunulan klasöre göre hesaplanır. Örnek

 require 'awesome_print'


Kernel#require_relative metodu çağrıldığı kod dosyasının bulunduğu klasöre göre bağıl olarak verilen dosyayı bulur. 

# bulunulan klasördeki myproj klasörü içinde version
# isimli dosyayı arar.
require_relative 'myproj/version'



-- Kaynak dosyayı otomatik yüklemek

Kernel#autoload metodu verilen modüle (string ya da sembol olarak) ilk ulaşıldığında kod dosyasını otomatik yüklemek için (Kernel#require metodu ile) kayıt eder. 

autoload :MyModule, '/usr/local/lib/modules/my_module.rb'


Örnek.

autoload :Modülüm, './f.rb'

puts ":Modülüm daha yüklenmedi"
# modülü çağır
Modülüm
puts ":Modülüm yüklendi"


Diyelim f.rb dosyasında da şu olsun.

module Modülüm
  puts "modülün kodu çalıştı"
end


Programın çıktısı.

:Modülüm daha yüklenmedi
modülün kodu çalıştı
:Modülüm yüklendi


Kernel#autoload? metodu da otomatik yüklenmesi beklenen (ama daha yüklenmemiş) dosyanın ismini döner. 

autoload :Modülüm, './f.rb'

p autoload? :Modülüm
# modülü çağır
Modülüm
p autoload? :Modülüm

çıktısı

"./f.rb"
modülün kodu çalıştı
nil



-- Seçenekli dosya yükleme

Eğer verilen dosya bulunamazsa,  require ailesi metodlar LoadError hatası verir. Bu örnekte eğer dosyalar mevcutsa yükleme örneği veriliyor. 

module TidBits

  @@olmayanModüller = []
 
  [
        { name: 'CoreExtend', file: 'core_extend/lib/core_extend'  } \
      , { name: 'Fs'        , file: 'fs/lib/fs'                    } \
      , { name: 'Options'   , file: 'options/lib/options'          } \
      , { name: 'Susu'      , file: 'susu/lib/susu'                } \
      , { name: 'Modülüm'   , file: './f.rb'                       } \
 
  ].each do |lib|
      begin
          require_relative lib[ :file ]
      rescue LoadError
          @@olmayanModüller.push lib
      end
  end

  def self.olmayanModüller
    @@olmayanModüller
  end
end

p TidBits.olmayanModüller

çıktısı

modülün kodu çalıştı
[{:name=>"CoreExtend", :file=>"core_extend/lib/core_extend"},
{:name=>"Fs", :file=>"fs/lib/fs"},
{:name=>"Options", :file=>"options/lib/options"},
{:name=>"Susu", :file=>"susu/lib/susu"}]




-- Dosyaları tekrar tekrar yüklemek

Kernel#load metodu aynı dosyayı tekrar tekrar çalıştırmak için kullanılır. Dosya yolu bulunması require metodu ile aynı olur. load_relative diye bir metod yoktur. 

load "./f.rb"
load "./f.rb"

çıktısı

modülün kodu çalıştı
modülün kodu çalıştı

load yerine require kullanmış olsak sadece bir kez modül kodu çalışacaktı. 



-- Yükleme listesini dinamik oluşturmak

Yüklenecek dosyaların dinamik olarak oluşturulması için her hangi bir Ruby tekniği kullanılabilir. Örnek kodumuz test ile başlayan isimli dosyaları programa dahil ediyor.

Dir[ "#{ __dir__ }**/test*.rb" ].sort.each do |dosya|

    require_relative dosya

end





Range

Range (aralık) nesneleri bir başlangıç ve bir bitiş değeri arasındaki değerler topluluğudur. 


-- Range tanımlamak

Range'in kullanım amacı bir değerler aralığı ifade etmektir. 


Deyim:

(baş..son) => bu yapı "son" değerini de içerir
(baş...son) => bu yapı "son" değerini de içermez

ya da 

Range.new(baş, son, son_hariç) => son_hariç default olarak false


En önemlisi baş değeri son değerinden küçük olmalıdır yoksa hiç bir şey dönmez.


Örnekler:

(10..1).to_a            #=> []
(1...3).to_a            #=> [1, 2]
(-6..-1).to_a           #=> [-6, -5, -4, -3, -2, -1]
('a'..'e').to_a         #=> ["a", "b", "c", "d", "e"]
('a'...'e').to_a        #=> ["a", "b", "c", "d"]
Range.new(1,3).to_a     #=> [1, 2, 3]
Range.new(1,3,true).to_a#=> [1, 2]

Türkçe karakterler için bir örneğimi burada vermiştim.



-- Range elemanlarında iterasyon

İterasyon örneği

(1..5).each do |i|
  print i
end
# 12345



-- Tarihlerde Range

require 'date'

date1 = Date.parse "01/06/2016"
date2 = Date.parse "05/06/2016"

p "Period #{date1.strftime("%d/%m/%Y")} to #{date2.strftime("%d/%m/%Y")}"

(date1..date2).each do |date|
  p date.strftime("%d/%m/%Y")
end

# "Period 01/06/2016 to 05/06/2016"
# "01/06/2016"
# "02/06/2016"
# "03/06/2016"
# "04/06/2016"
# "05/06/2016"





Operatörler

Programlama dillerinde operatörler değerler arasında işlem yapan (+ - * / gibi) deyimlerdir. Ruby dilinin operatörlere bakış açısı biraz değişiktir. 


-- Operatörler birer metoddur

Birçok operatör aslında bir metoddur. x + y işleminde x nesnesinin + metodu y argümanı ile çağrılır. Bunu x.+(y) olarak da yazabiliriz. 

Her hangi bir operatörün bizim istediğimiz işlemleri yapmasını istersek sınıf tanımında o operatör metodunun üzerine yazarız. 

Örnek olsun diye bir kod.

class Foo < String
  def +(str)
    self.concat(str).concat("ilave string")
  end
end

foobar = Foo.new("test ")
puts foobar + "baz "
# test baz ilave string


Direk String sınıfı + metodu üzerine de yazabiliriz ama programın geri kalan bölümündeki string işlemlerini etkileyecektir. 



-- && yerine 'and', || yerine 'or' ne zaman kullanılır

Boolean hesaplar yaparken and veya && ile or veya || kullanılır. Genellikle bunlar birbiri yerine kullanılabilir ama her zaman değil, bunnlara karakter yöntem ve kelime yöntem diyelim. 

Karakter yöntemler yüksek önceliğe sahip olduğu için, özellikle karmaşık hesaplamalarda, olası hataları önlemek için konan parantez kullanımını azaltırlar. 

Kelime yöntemler genelde boolean işlemler yerine akış kontrol işlemlerinde kullanılırlar. Zincirleme metod çağrılarında tercih edilirler.

raise 'hata oldu' and return


gibi. Kelime yöntemler boolean işlemlerde de kullanılabilir ama düşük önceliklerinden dolayı öngörülemez sıkıntı yaşatabilirler. 

Birçok Ruby'ci karakter yöntemini boolean eşitlikler hesaplarken kullanır, örneğin  x.nil? || x.empty?. Diğer taraftan kelime yöntemler sıralı bazı metodlar çağrılacağı zaman tercih edilir, ve metodlardan birinde başarısız olunabilir. Buna örnek olarak bir email gönderme kodu verelim.

def email_gönder
  # ilki başarısızsa ikinciyi dene olursa sorun yok
  asıl_da_gönder or cc_de_gönder and return
  # iki şekilde de gönderilemedi, hatayı çöz
end



-- Operatör öncelikleri ve metodlar

Burada en yükseğinden en düşüğüne Ruby operatör öncelikleri var. Yüksek öncelikliler düşüklerden daha önce çalışırlar.

Operatörİşlemmethod?
 .Metod çağrısı - örn. foo.bar
 [] []= Braket okuma , braket set✓¹
 ! ~ +Boolean DEĞİL, tümleyen, pozitif işareti✓²
 **Üs alma
 -Negatif işareti✓²
 * / %Çarpma, bölme, modüler hesap
 + -Toplama, çıkarma
 << >>Bit seviyesinde kaydırma
 &Bit seviyesinde AND
 | ^Bit seviyesinde OR, bit seviyesinde XOR
 < <= >= >Karşılaştırma
 <=> == != === =~ !~Eşitlik , eşleşme, karşılaştırma✓³
 &&Boolean AND
 ||Boolean OR
 .. ...Inclusive range, Exclusive range
 ? :Ternary operatör
 rescuerescue ifadesi
 = += -=Atamalar
 defined?defined? operatörü
 notBoolean NOT
 or andBoolean OR, boolean AND
 if unless while untilif yapısı elemanları
 { }süslü parantezler
 do enddo end bloğu


+ işareti ve - işareti denen , +nesne veya -nesne veya -(bir hesap) gibi pozitif negatif işareti oluyor. 

Bu tablodaki if, unless gibi if yapısı elemanları, bu kelimelerin modifier versiyonları. Örneğin

a += 1 unless a.zero?


ile belirtilen operatörle metod olarak tanımlıdır ve tanımlanabilir. Birçok metodlar aynen operatör gibi isimlendirilir. Örneğin

class Foo
  def **(x)
    puts "oluşum nesnesinin #{x} üssünü almak"
  end
  def <<(y)
    puts "Oluşum nesnesini #{y} bit sola kaydırmak"
  end
  def !
    puts "oluşum nesnesinin boolean 'değil'i"
  end
end

Foo.new ** 2     #=> "oluşum nesnesinin 2 üssünü almak"
Foo.new << 3     #=> "Oluşum nesnesini 3 bit sola kaydırmak"
!Foo.new         #=> "oluşum nesnesinin boolean 'değil'i"


¹ Braket okuma ve braket set ([] ve []=) operatörlerinin kullanımda braket içinde verilen argümanları , tanımlamada isimden sonra verilir. Örneğin.

class Foo
  def [](x)
    puts "#{x} elemanı okunuyor"
  end
  def []=(x,y)
    puts "#{x} elemanına #{y} değeri atanıyor"
  end
end

f = Foo.new
f[:cats] = 42    #=> "cats elemanına 42 değeri atanıyor"
f[17]            #=> "17 elemanı okunuyor"


² Pozitif işareti ve negatif işaretinin metodları +@ ve -@ olarak tanımlanır. Örneğin.

class Foo
  def -@
    puts "eksi işaretli"
  end
  def +@
    puts "artı işaretli"
  end
end

f = Foo.new
+f               #=> "artı işaretli"
-f               #=> "eksi işaretli"


³ Ruby'nin erkan versiyonlarında eşit değil operatörü != ve eşleşmeme operatörü !~ metod olarak tanımlanamıyordu. Yerine eşittir operatörü == ve eşleşme operatörü =~ kullanılıp Ruby tarafından boolean olarak ters çevriliyordu. 

Eğer kendi != ve !~ operatörlerinizi tanımlamazsanız hala yukarıdaki gibi değerlendirilir. Bununla beraber Ruby 1.9.1'den sonra isterseniz kendi metodlarınızı tanımlayabilirsiniz. 

class Foo
  def ==(x)
    puts "#{x} ile EŞİTLİK false dönüyor"
    false
  end
end

f = Foo.new
x = (f == 42)    #=> "42 ile EŞİTLİK false dönüyor"
puts x           #=> "false"
x = (f != 42)    #=> "42 ile EŞİTLİK false dönüyor"
puts x           #=> "true"

class Foo
  # kendi metodumuzun tanımı
  def !=(x)
    puts "#{x} değeri ile EŞİT DEĞİL test ediliyor"
  end
end

f != 42          #=> "42 değeri ile EŞİT DEĞİL test ediliyor"



-- Durum eşitliği operatörü

Ayrıca üçlü eşitlik (triple equals) olarak da bilinir, ifadesi ==='dir. 

Bu operatör eşitliği test etmez, bunun yerine sağdaki operand ile soldaki arasında bir ilişki olmasını test eder. Bu nedenle bu durum eşitlik falan lafları biraz yanıltıcı olur. 

Şurada verilen cevap bir açıklık getiriyor. a === b 'nin en güzel açıklaması , "eğer a adında bir çekmecem varsa, b nesnesini bunun içine koyabilir miyim?". Diğer bir deyişle a setinin içinde b değeri var mı?


Örnekler:

>> (1..5) === 3             #=> true
>> (1..5) === 6             #=> false

>> Integer === 42           #=> true
>> Integer === 'fourtytwo'  #=> false

>> /ell/ === 'Hello'        #=> true
>> /ell/ === 'Foobar'       #=> false



=== metodunu değiştiren sınıflar

Birçok sınıf case satırlarında anlamlı sonuç elde etmek için === metodunu değiştirir. Bazıları şunlar :

╔═════════════════╦════════════════════╗
║      Class      ║     Aynı işlem     ║
╠═════════════════╬════════════════════╣
Array           ║ ==                 ║
║                 ║                    ║
Date            ║ ==                 ║
║                 ║                    ║
Module          ║ is_a?              ║
║                 ║                    ║
Object          ║ ==                 ║
║                 ║                    ║
Range           ║ include?           ║
║                 ║                    ║
Regexp          ║ =~                 ║
║                 ║                    ║
String          ║ ==                 ║
╚═════════════════╩════════════════════╝


Tavsiye edilen uygulama

Üçlü eşitlik operatörünün özellikle yukarıda belirtilen sınıflar için kullanılması yerine yaptıkları aynı işlemi kullanmak tavsiye edilir. Kafa karışıklığı olmasın, çünkü üçlü eşitlik sanki kapsama operatörü gibi anlaşılıyor. 

# Tavsiye edilmez
Integer === 42
(1..5) === 3
/ell/ === 'Hello'

# Tavsiye edilir
42.is_a?(Integer)
(1..5).include?(3)
/ell/ =~ 'Hello'



-- Güvenli gezinti operatörü

Ruby 3.0 ile güvenli gezinti operatörü &. (safe navigation operator) kullanıma başlandı. Bu operatör çok kullanılan nesne && nesne.özellik && nesne.özellik.metod sorgulamasını kısaltmak için eklendi. Yani önce nesne var mı? diye bakılıyor, varsa özellik var mı? diye bakılıyor, özellik de varsa metod çağrılıyor. Öncekilerden bir nil değer dönerse devam edip hata oluşturmamak için. 

Örneğin bir Ev nesneniz var, onun bir adres özelliği var ve siz cadde_adı değerine ulaşmak istiyorsunuz. Eski Ruby versiyonlarında bunda hata olmasını engellemek için şöyle bir kod yazmanız gerekiyordu.

if ev && ev.adres && ev.adres.cadde_adı
  ev.adres.cadde_adı
end


Güvenli gezinti operatörü ile bu kısaltılabilir.

if ev&.adres&.cadde_adı
  ev.adres.cadde_adı
end


Dikkat:

Güvenli gezinti operatörü tam olarak da zincirleme test ile aynı çalışmaz. Zincileme test yapılırken operandların doğru sonuç vermesi ile devam edilirken &. operatörü operandların nil değer olmamasını test eder. Bu durumda eğer operandlardan biri false değerdeyse, hata oluşacaktır. 

ev = Ev.new(false)

# bu cevap yazmaz
if ev && ev.adres && ev.adres.cadde_adı
  p ev.adres.cadde_adı
end

# hata verir -
if ev&.adres&.cadde_adı
  p ev.adres.cadde_adı
end
# undefined method `cadde_adı' for false:FalseClass (NoMethodError)




-- Karşılaştırma operatörleri


OperatörAçıklaması
==Eğer iki değer eşitse true olur
!=Eğer iki değer eşit değilse true olur
<Soldaki operand sağdakinden küçükse true olur
>Soldaki operand sağdakinden büyükse true olur
>=Soldaki operand sağdakinden büyük veya eşitse true olur
<=Soldaki operand sağdakinden küçük veya eşitse true olur
<=>0 : Soldaki operand sağdakine eşit
1 : Soldaki operand sağdakinden büyük
-1 : Soldaki sağdakinden küçük




-- Atama operatörleri


-- -- Basit atama

Basit atama işlemi = operatörü ile yapılır. Eğer soldaki değişken adı daha tanımlanmamışsa yeni bir değişken oluşturulur. 

x = 3
y = 4 + 5
puts "x değeri #{x}, y değeri #{y}"
#=> x değeri 3, y değeri 9


-- -- Paralel atama

Aynı anda bir'den çok değişkene atama yapılabilir x, y = 3, 9 gibi. Özellikle değerleri değiştirirken işe yarar.

x, y = 3, 9
puts "x değeri #{x}, y değeri #{y}"
#=> x değeri 3, y değeri 9

x, y = y, x
puts "x değeri #{x}, y değeri #{y}"
#=> x değeri 9, y değeri 3


-- -- Kısaltılmış operatör

Operatör ve atamaları birleştirmek mümkündür.

x = 1
y = 2
puts "x değeri #{x}, y değeri #{y}"
x += y    # 'x = x + y' demektir
puts "x değeri şimdi #{x}"

#=> x değeri 1, y değeri 2
#=> x değeri şimdi 3


Kısaltılmış operatörlerde çeşitli kombinasyonlar kullanılabilir.


OperatörAçıklamasıÖrnekEşdeğeri
+=Değişkene değer ekler, değişkene yazar x += y x = x + y
-=Değişkenden değer çıkarır, değişkene yazar x -= y x = x - y
*=Değişkeni değerle çarpar, değişkene yazar x *= y x = x * y
/=Değişkeni değere böler, değişkene yazar x /= y x = x / y
%=Değişkenin değere tamsayı bölümünden artanı değişkene yazar x %= y x = x % y
**=Değişkenin değer kadar üssünü alır, değişkene yazar x **= y x = x ** y





Ruby özel sabitleri

İşimize yarayacak birçok özel sabit vardır

-- FILE

Şu anda çalışan dosyanın içinde bulunulan klasöre göre bağıl adresidir. Görmek için bir deneme yapalım. Diyelim dnm.rb adında bir dosyamız var, içinde de şunlar var.

puts "aynı yerden çağrılınca", __FILE__
require "./dnm/dnm.rb"


Şimdi dnm klasörü içinde bir dnm.rb daha koyalım ve içine şunu yazalım.

puts "klasör içinden çağrılınca",__FILE__


Programı çalıştıralım.

~/rb/temeller# ruby dnm.rb
aynı yerden çağrılınca
dnm.rb
klasör içinden çağrılınca
/root/rb/temeller/dnm/dnm.rb


Dosyanın çağrıldığı yere göre bağıl yolunu veriyor. Özellikle büyük projelerde (Rails gibi) dosyanın nereden çağrıldığını bilmek çok işimize yarayabilir.

>> __FILE__
=> "(irb)"
>> File.dirname __FILE__
=> "."


Buradan da görülüyor ki __FILE__ sabiti sadece bir string değil büyülü bir şekilde içinde başka bilgiler var.



-- dir

__dir__ aslında bir sabit değil , bir metod. 

__dir__ değeri File.dirname(File.realpath(__FILE__)) ile aynıdır. Bulunulan klasörün yolunu verir. Yukarıdaki örnekte __FILE__ yerine __dir__ kullansak

~/rb/temeller# ruby dnm.rb
aynı yerden çağrılınca
/root/rb/temeller
klasör içinden çağrılınca
/root/rb/temeller/dnm

cevabını alırız. 



-- $PROGRAM_NAME veya $0

Şu andan çalışan betik adını verir. Mesela betiğin içinde bulunduğu klasörden çalıştığını test edelim.


dnm.rb

puts "aynı klasörde", __FILE__ == $PROGRAM_NAME
require "./dnm/dnm.rb"


dnm/dnm.rb

puts "başka klasörde", __FILE__ == $0

 ve çalıştırınca

$ ruby dnm.rb
aynı klasörde
true
başka klasörde
false

gibi.

Programı bulunduğu klasörden çalıştırıyorsak hepsi aynı sonuç verir.

puts $PROGRAM_NAME, $0, __FILE__
# dnm.rb
# dnm.rb
# dnm.rb



--$~ değeri

Son gerçekleşen eşleşmenin sonucunu verir. 

>> /ell/ =~ "hello"
>> $~
=> #<MatchData "ell">

>> /h...o/ =~ "hello"
>> $~
=> #<MatchData "hello">




-- ARGV değeri

ARGV programı komut satırından çalıştırılırken programa verilen argümanları sıralar.


dnm.rb

p ARGV


çalıştırırken

$ ruby dnm.rb "bir" "iki"
["bir", "iki"]

$ ruby dnm.rb "file1.data" "file2.data" file3.data
["file1.data", "file2.data", "file3.data"]

gibi.






Modüller

Modüller işimize yarayacak kodları içinde toplayabileceğimiz taşıyıcılardır. Mesela birçok değişik sınıfta kullanabileceğimiz metodları bir modül içinde toplayıp , o sınıfların tanımlarında include ifadesi ile dahil ettiğimizde , modülde tanımlanmış metodlar o sınıflarda da kullanılabilir olacaktır. 

Tanımlama:

module ModülAdı;

    her hangi Ruby kodu;

end


Kullanım :

include ModülAdı


Notlar :

Ruby'de modül isimleri sabit olarak kabul edilir. Bu yüzden büyük harfle başlamalıdır.

>> module dnm;end;
=> class/module name must be CONSTANT (SyntaxError)



-- include ile basit bir mixin

Mixin terimi sınıf tanımları içine modül kodlarını dahil ederek sınıflara özellik kazandırmayı ifade eder.

module BirMixin
  def foo
    puts "foo!"
  end
end

class Bar
  include BirMixin
  def baz
    puts "baz!"
  end
end

b = Bar.new
b.baz         # => "baz!"
b.foo         # => "foo!"


Bar sınıfının kendi metodları ve BirMixin modülü metodlarının oluşturduğu bir karışım var ve Bar sınıfı nesnelerinde modüldeki foo metodu çağrılabiliyor.

Bu karışımın nasıl kullanılacağı sınıfa modülün nasıl eklendiğine göre değişir.

  • include metodu modül kodunu sınıfın kapsamında çalıştırır (mesela metod tanımları sınıfın oluşum metodu olarak tanımlanır). 
  • extend metodu modül içindeki metodları sadece bulunulan kapsamda kullanılabilir yapar. Eğer sınıf tanımı içinde kullanıldıysa sınıf metodu olarak kullanılabilir.  

module BirMixin
  def foo
    puts "foo!"
  end
end

class Bar
  extend BirMixin
  def baz
    puts "baz!"
  end
end

b = Bar.new
b.baz         # => "baz!"
Bar.foo       # => "foo!"
b.foo         # => (NoMethodError)


Sadece bir oluşum nesnesine de extend uygulanabilir.

a = Bar.new
b = Bar.new
b.extend(BirMixin)
b.foo         # => "foo"
a.foo         # (NoMethodError)


Etkili kullanılırsa çok işimizi görebilir. Bir örneğini decoratör tasarım kalıbında görmüştük. 



-- Modüllerin sınıflara birleştirilmesi

Modülleri karmaşık sınıf yapıları hazırlarken kullanabiliriz. include ifadesi ile modül metodlarını sınıfa ekleyebiliriz. 

module Foo
  def foo_metod
    puts 'foo_metod çağrıldı!'
  end
end

module Bar
  def bar_metod
    puts 'bar_metod çağrıldı!'
  end
end

class Baz
  include Foo
  include Bar

  def baz_metod
    puts 'baz_metod çağrıldı!'
  end  
end


Baz sınıfı şimdi hem Foo hem de Bar modülü içindeki tüm metod tanımlarını sanki kendi metod tanımları gibi kullanabilir. 

new_baz = Baz.new
new_baz.baz_metod #=> 'baz_metod çağrıldı!'
new_baz.bar_metod #=> 'bar_metod çağrıldı!'
new_baz.foo_metod #=> 'foo_metod çağrıldı!'




-- Modülleri isim alanı gibi kullanmak

Modüller diğer modülleri ve sınıfları içerebilir.

module Namespace

  module Child

      class Foo; end

  end

  # Foo sınıfına burada erişim:
  #
  Child::Foo

end

# Foo sınıfına burada erişim:
#
Namespace::Child::Foo


Benim sürekli yaptığımı yapıp tek bir iki nokta üst üste koymayın sakın.




Sabitler

Sabitler Ruby programlarında kazara değişmesini istemediğimiz ID değerleri , API key'ler gibi değerleri saklamak için kullanılır. 

BİR_SABİT = "değer"



-- Bir sabit tanımlamak

Aynı bir değişkene değer ataması yapar gibi.

MY_CONSTANT = "Hello, world" # sabit
Constant = 'Bu da bir sabit' # sabit
my_variable = "Hello, venus" # sabit değil


Sabit isimleri büyük harfle başlamalıdır. Ruby'de büyük harfle başlayan her şey sabit kabul edilir. Bu yüzden class ve module'ler de sabittir. Genel eğilim sabit isminin tüm harflerini büyük yazmaktır. 



-- Sabit değeri değişir mi?

Evet, değişebilir.

MY_CONSTANT = "Hello, world"
MY_CONSTANT = "Hullo, world"


Bu kod çalışır ama sabit değeri değiştirildiğine dair bir uyarı verir ve kod içinde sabitin ilk kullanıldığı yeri de belirtir. Tabi ki uzuun bir program içinde aynı sabite iki yerde değer vermek hatasına düşebiliriz. Bu yüzden Ruby bizi uyarıyor.

Bununla beraber uyarı almadan bir sabit içinde bir karakteri değiştirebiliriz. 

MY_CONSTANT = "Hello, world"
MY_CONSTANT[1] = "u"


Bu kod uyarı mesajı vermez ama MY_CONSTANT değeri "Hullo, world" olarak değişmiştir. 



-- Metod içinde sabit tanımlanamaz

Aşağıdaki kod dynamic constant assignment diye hata verir. 

def say_hi
  MESSAGE = "Hello"
  puts MESSAGE
end



-- Sınıf içinde değişken tanımlamak ve değiştirmek

Örnek.

class Mesaj
  DEFAULT_MESAJ = "Merhaba Dünya"

  def initialize(mesaj = nil)
    if mesaj
      puts mesaj
    else
      puts DEFAULT_MESAJ
    end
  end
end

Mesaj.new     #=> Merhaba Dünya
Mesaj.new "Selam millet"


Dışardan Mesaj sınıfı içindeki DEFAULT_MESAJ sabitine ulaşmak için.

Mesaj::DEFAULT_MESAJ = "Merhaba Alem"
Mesaj.new     #=> Merhaba Alem


Tabii ki programı çalıştırınca sabit değerini değiştirdiğinize dair bir uyarı alacaksınız. 





Değişken Kapsamı ve Görünürlüğü

Değişkenlerin kapsamını ifade etmenin yolları.

  • $global_değişken
  • @@sınıf_değişkeni
  • @oluşum_değişkeni
  • yerel_değişken


Sınıf değişkenleri sınıf hiyerarşisi içide paylaşılır. Bu süpriz sonuçlar doğurabilir.

class A
  @@değişken = :x

  def self.değişken
    @@değişken
  end
end

class B < A
  @@değişken = :y
end

p A.değişken # :y


Her şey bir nesne olduğu gibi sınıflar da birer nesnedir , bu yüzden bir oluşum değişkenini sadece o sınıfın bir özelliğini saklamak için kullanabiliriz.

class A
  @değişken = :x

  def self.değişken
    @değişken
  end
end

class B < A
  @değişken = :y
end

p A.değişken # :x
p B.değişken # :y




-- Sınıf değişkenleri

Sınıf değişken isimleri @@ karakterleri ile başlamalıdır. Sınıf değişkenleri tüm sınıf içinde paylaşılır, sınıf içinde herhangi bir yerde tanımlanabilir. 

class Dinozor
  @@sınıflama = "Sürüngen gibi, ama kuş gibi de"
 
  def self.sınıflama
      @@sınıflama
  end

  def sınıflama
      @@sınıflama
  end
end

dino = Dinozor.new
p dino.sınıflama
# => "Sürüngen gibi, ama kuş gibi de"

p Dinozor.sınıflama
# => "Sürüngen gibi, ama kuş gibi de"


Sınıf değişkenleri bağlı sınıflarla da paylaşılır, yani bir alt sınıfta paylaşılan bu değişken değeri değiştirilebilir. 

class TRex < Dinozor
  @@sınıflama = "Kocaman dişli kuş!"
end

p TRex.sınıflama
# => "Kocaman dişli kuş!"

p Dinozor.sınıflama
# => "Kocaman dişli kuş!"


Birçok durumda bu davranış istenmez. Bunun üstesinden gelmek için sınıf seviyesinde tanımlanmış oluşum değişkenleri kullanılır. 

Bir modül içinde tanımlanan sınıf değişkenleri dahil edildikleri sınıfın sınıf değişken değerinin üzerine yazamazlar.

module AcayipBirŞey
  @@sınıflama = "Acayip Bir Şey"
end

class ÖrdekDinozor < Dinozor
  include AcayipBirŞey
end

p ÖrdekDinozor.class_variables
# => [:@@sınıflama]
p AcayipBirŞey.class_variables
# => [:@@sınıflama]

p ÖrdekDinozor.sınıflama
# => "Kocaman dişli kuş!"




-- Yerel değişkenler

Diğer kapsamlardaki değişkenlerden farklı olarak yerel değişkenler önlerine bir özel karakter almaz. 

yerel_değişken = "local"
p yerel_değişken
# => "local"


Kapsamı nerede tanımlandığına bağlıdır. Tanımlandığı yerin kapsamı dışından erişilemez. Örneğin bir yerel değişken , bir metod tanımlaması içinde ilk ataması yapılmışsa, sadece o metod tanımlaması içinde kullanılabilir. 

def bir_metod
  metod_kapsamı_değişken = "Merhaba"
  p metod_kapsamı_değişken
end

bir_metod
# => "Merhaba"

metod_kapsamı_değişken
# NameError: undefined local variable or method `metod_kapsamı_değişken'


Tabii ki olay sadece metodlarla bitmiyor, kural olarak şunu da diyebiliriz, bir değişkeni do...end ya da {  } bloğu içinde tanımlarsak blok dışından erişemeyiz. 

2.times do |n|
  local_var = n + 1
  p local_var
end
# 1
# 2

local_var
# NameError: undefined local variable or method `local_var'


Buna karşılık if ve case bloklarında tanımlanan değişkenlere dışardan erişilebilir. 

if true
  d1 = "vay"
end

p d1  #=> "vay"


Blok içinde tanımlananlar dışarıdan erişilemezken , dışarda tanımlananlara kod bloğu içinde erişilebilir. 

d1 = "foo"

d1.split("").each_with_index do |char, i|
    puts "'#{d1}' içinde #{i} indexli karakter #{char}"
end
# 'foo' içinde 0 indexli karakter f
# 'foo' içinde 1 indexli karakter o
# 'foo' içinde 2 indexli karakter o


Ancak sınıf ve metod tanımları içinde erişilemez.

d1 = "foo"

def bir_metod
    puts "dışarıdaki yerel değişkene burada erişilebilir mi? #{d1}"
end

bir_metod
# NameError: undefined local variable or method `d1'


Metod tanımlamalarında verilen parametreler tabi ki metodun yerel değişkenleridir. Fakat dışarda aynı isimle verilmiş değişken varsa onun üzerine yazmadan gölgede bırakırlar. 

gölgede_kalan = "gün ışığı"

["karanlık"].each do |gölgede_kalan|
    p gölgede_kalan
end
# "karanlık"

p gölgede_kalan
# "gün ışığı"



-- Global değişkenler

 Global değişkenler global kapsama sahiptir, bu yüzden her yerden erişilebilirler. Kapsamlarının tanımlandıkları kapsamla alakası yoktur. Bir global değişken adı $ işareti ile başlamalıdır.

$ben_global = "aman tanrım"

class Dinozor
    def oluşum_metodu
       p "global değişkenler her yerde. Bakın? #{$ben_global}, #{$diğer_global}"
    end

    def self.sınıf_metodu
       $diğer_global = "ciddi misin?"
       p "global değişkenler her yerde. Bakın? #{$ben_global}"
    end
end

Dinozor.sınıf_metodu
# "global değişkenler her yerde. Bakın? aman tanrım"

dinozor = Dinozor.new
dinozor.oluşum_metodu
# "global değişkenler her yerde. Bakın? aman tanrım, ciddi misin?"


Global değişkenler her yerde kullanılabilir olması sebebiyle eğer daha tanımlanmadan önce çağrılırsa hata vermez, sadece nil değer döner.

p $tanımsız
# nil


Global değişkenler kullanımı kolay olduğu için sabitler yerine kullanılması yaygındır. 



-- Oluşum değişkenleri

Oluşum değişkenleri nesne kapsamında geçerlidir. Nesne içinde her hangi bir yerde tanımlanabilir ve örneğin tüm nesne metodlarında kullanılabilirler. Buna karşılık bir oluşum değişkeni sınıf seviyesinde tanımlanmışsa sınıf kapsamında erişilebilir. Oluşum değişkenleri @ karakteri ile başlamalıdır. Oluşum değişkenleri nesnelerin özelliklerini saklamak ve okumak amacıyla kullanılırlar ve tanımlanmamışlarsa nil değer dönerler. 

class Dinozor
  @temel_ses = "rawrr"

  def initialize(ses = nil)
      @ses = ses || self.class.temel_ses
  end

  def konuş
      @ses
  end

  def konuşmaya_çalış
      @temel_ses
  end

  def ses_uzunluğunu_say_ve_sakla
      @ses.chars.each_with_index do |char, i|
          @ses_uzunluğu = i + 1
          p "#{char}: #{ses_uzunluğu}"
      end
  end
 
  def ses_uzunluğu
      @ses_uzunluğu
  end

  def self.temel_ses
      @temel_ses
  end
end

dino_1 = Dinozor.new
dino_2 = Dinozor.new "grrr"

p Dinozor.temel_ses
# => "rawrr"
p dino_2.konuş
# => "grrr"


Sınıf seviyesinde tanımlanan oluşum değişkenine nesne kapsamında erişilemez.

p dino_1.konuşmaya_çalış
# nil


Bununla beraber Dinozor nesnesi oluştururken ses değeri girilmezse @temel_ses değerini kullanmasını istedik.

p dino_1.konuş
# => "rawwr"


Oluşum değişkenleri nesne içinde herhangi bir yerde tanımlanabilir, hatta kod bloğu içinde.

dino_1.ses_uzunluğunu_say_ve_sakla
# "r: 1"
# "a: 2"
# "w: 3"
# "r: 4"
# "r: 5"

p dino_1.ses_uzunluğu
# => 5


Oluşum değişkenleri diğer aynı sınıf oluşumları arasında paylaşılmaz.

p dino_2.ses_uzunluğu
# => nil


Ruby'de sınıflar da bir nesne olduğu için, sınıf seviyesinde tanımladığımız oluşum değişkenlerine, sınıf hiyerarşisinde üretilmiş diğer sınıflardan ulaşılamaz.

class ÖrdekDinozor < Dinozor
  @temel_ses = "vak vak"
end

ördek_dino = ÖrdekDinozor.new
p ördek_dino.konuş
# => "vak vak"
p ÖrdekDinozor.temel_ses
# => "vak vak"
p Dinozor.temel_ses
# => "rawrr"


Bu bölüm de bayağı bir uzadı, burada keselim. Bir sonraki bölüme Singleton sınıf ile başlayacağız inşallah. Şimdilik kalın sağlıcakla..






Hiç yorum yok:

Yorum Gönder