Ruby 2.4 ile gelen yenilikler

12 Aralık 2016 tarihinde Ruby 2.4.0-rc1 yayınlandı. rc1 Release Candidate1 yani sürüm için neredeyse aday anlamına geliyor. Stabil sürüm olan 2.3 göre yenilikler neler ? Şimdi bu yeni özelliklerin bir kısmına gözatalım.

binding.irb

Aynı pry’ı takar gibi artık irb’yi de koda takabiliyoruz. binding.pry gibi binding.irb yapmak mümkün artık.

# test.rb
require 'pry'

class A
  def merhaba()
    puts "merhaba"
  end
end

a = A.new
x = 10

# pry session başlasın
binding.pry
# artık bunun yerine: binding.irb

# pry session bitince
puts "Buradan devam ediyoruz, x'in değeri: #{x}."

Float#round

Yuvarlama işlemini duruma göre ayarlayabiliyorsunuz. 5.5 sayısını yuvarla dediğinizde; yukarı, aşağı ya da yarısından büyükse yuları, küçükse aşağı yuvarlayabiliyorsunuz.

5.5.round
# => 6

5.5.round(half: :up) # yukarı. bu default (varsayılan) davranışı

5.5.round(half: :down) # aşağı yuvarla
# => 5

5.5.round(half: :even)
# => 6

5.6.round(half: :even)
# => 6

5.4.round(half: :even)
# => 5

Float’un #floor, #ceil ve #truncate methodları

Bu üç method’da ek olarak dijit parametresi alıyor. Varsayılan değer sıfır. (default: 0) Ondalık sayının tavan/taban ya da belli bir kısmını alırken işe yarıyor:

# taban
5.56789.floor
# => 5

# tabandan itibaren 2 dijit daha al
5.56789.floor(2)
# => 5.56

# tavan
5.56789.ceil
# => 6

# 2 dijit al
5.56789.ceil(2)
# => 5.57

# kesinti yap
5.56789.truncate
# => 5

# 3 dijit alarak kes
5.56789.truncate(3)
# => 5.567

gets.chomp

Artık kullanıcıdan input alırken line-ending yani \n’leri temizlemeye gerek kalmayacak. gets’e opsiyon olarak chop: true geçmek yeterli.

# eski sürümlerde
user_input = gets
# => "merhabalar!\n"

# 2.4.0-rc1
user_input = gets(chomp: true)
# => "merhabalar!"

Method: #empty?

Dir, File ve Pathname için empty? method’u geldi:

Dir.empty?('empty_dir/')
# => true

Dir.empty?('/tmp/')
# => false

# içi boş bir dosya...
File.empty?('/tmp/empty_file')
# => true

Refinements’da #send ve #to_proc

Önceki sürümlerde, sonradan Refinements ile eklenen method’lar, #send ile çağırılınca: method bulunamadı hatası veriyordu. Bu düzeltildi:

module CustomModifications
  refine String do
    def shuffle_letters
      self.split("").shuffle.join("")
    end
  end
end

using CustomModifications

# bu düzeltildi
puts "hello".send(:shuffle_letters) # lhoel

puts "world".shuffle_letters # rwldo

Hash#transform_values

Aynı Array#map gibi bir tür Hash ile map yapma durumu:

point = {x: 10, y: 20, z: -100}
point.transform_values {|value| value * 4}
# => {:x=>40, :y=>80, :z=>-400}

point # orijinal değer değişmedi
# => {:x=>10, :y=>20, :z=>-100}

Ruby’deki bang yani ! notasyonu ile in-place mutaion yani üzerinde değişiklik de yapmak mümkün:

point.transform_values!{|value| value * 4}
# => {:x=>40, :y=>80, :z=>-400}

point # orijinal değer artık değişti...
# => {:x=>40, :y=>80, :z=>-400}

Fixnum ve Bignum artık Integer oldu

Uzun yıllardır kullandığımız sayı tipi Fixnum artık tarih oldu. Keza Bignum da…

5.class
# => Integer

1_000_000_000_000_000_000.class
# => Integer

Fixnum
# warning: constant ::Fixnum is deprecated :)

Integer#digits

Sayıların kaç dijiti var ? 1234 için; 1’ler basamağında 4, 10’lar basamağında 3, yüzler basamağında 2 ve son olarak binler basamağında 4 bulunuyor:

1234.digits # => [4, 3, 2, 1]

Freeze etmeden Clone yap!

Ruby 2.1 ile gelen nesnelerin korunması durumu (immutability) bu yeni versiyonda nesnin kopyalanması (clone) durumuna da dahil olmuş:

# varsayılan
mesaj = "merhaba".freeze
mesaj_clone = mesaj.clone
mesaj.object_id == mesaj_clone.object_id # => false
mesaj.frozen?       # => true
mesaj_clone.frozen? # => true

# yeni özellikle
mesaj_clone_2 = mesaj.clone(freeze: false)
mesaj_clone_2.frozen? # => false
mesaj_clone_2.object_id == mesaj_clone.object_id # => false

Thread.report_on_exception

Önceki sürümlerde Thread içinde oluşan exception’lar sessizdi:

# ruby 2.3
a = Thread.new do
  puts  "Ben thread içindeyim..."
  raise "Exception fırladı!"
end

sleep(1)
puts "Dıştaki ana thread..."

# Ben thread içindeyim...
# Dıştaki ana thread...

Bu kod parçası çalıştığında Exception fırladı! hiçbir zaman görünmez… Eğer yeni Ruby ile çalışırsa;

# ruby 2.4.0-rc1
Thread.report_on_exception = true

a = Thread.new do
  puts  "Ben thread içindeyim..."
  raise "Exception fırladı!"
end

sleep(1)
puts "Dıştaki ana thread..."

# Ben thread içindeyim...
# #<Thread:0x007f9a7423cf60@examples-2.rb:3 run> terminated with exception:
# examples-2.rb:5:in `block in <main>': Exception fırladı! (RuntimeError)
# Dıştaki ana thread...

şeklinde exception yakalanabilir.


Set ve #compare_by_identity

Veri yapılarından olan Set ile unique yani eşi olmayan değerleri saklama işleri yaparken, önceki sürümler, değere göre bu tekilliği analiz ederken yeni sürümle gelen compare_by_identity methodunu kullanarak #object_id’ye göre bu ayrıştırmayı yapabiliriz:

# ruby 2.2

require 'set'
set = Set.new
set << 5678 << 5678 << "merhaba" << "merhaba"
# => #<Set: {5678, "merhaba"}>
set.count # => 2

# ruby 2.4.0-rc1

set = Set.new().compare_by_identity
set << 5678 << 5678 << "merhaba" << "merhaba"
# => #<Set: {5678, "merhaba", "merhaba"}>
set.count # => 3

Regexp#match? methodu

Yeni gelen bu #match? methodu hem çok hızlı hem de çok işe yarar bir özellik:

/^foo (\w+)$/.match?('foo wow') # => true

Regexp#named_captures ve Regexp#values_at methodları

# Regexp#named_captures

pattern  = /(?<first_name>Tony) (?<last_name>\w+)/
pattern.match('Tony Hawk').named_captures
# => {"first_name"=>"Tony", "last_name"=>"Hawk"}

# Regexp#values_at
pattern.match('Tony Hawk').values_at(:first_name)
# => ["Tony"]

pattern.match('Tony Hawk').values_at(0) # => ["Tony Hawk"]
pattern.match('Tony Hawk').values_at(1) # => ["Tony"]
pattern.match('Tony Hawk').values_at(2) # => ["Hawk"]

Enumerable#sum

Sıralanabilir nesneler için #sum methodu geldi. Keza başlangıç değeri de atamak mümkün. Varsayılan değer sıfır (0).

[1,2,3,4].sum      # => 10
[1,2,3,4].sum(20)  # => 30

Son olarak, Türkçe desteği çok ciddi şekilde ilerlemiş:

"uğur ışğüiöç".upcase               # => "UĞUR IŞĞÜIÖÇ"
"uğur özyılmazel".upcase            # => "UĞUR ÖZYILMAZEL"
"UĞUR ÖZYILMAZEL".downcase(:turkic) # => "uğur özyılmazel"