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