Meta Programming
Ruby’deki Meta Programming, yazılan kodun run-time’da pek çok şeyi değiştirmesi, Kernel’dan gelen sistem fonksiyonlarını manipule etmesi (Class, Module, Instance ile ilgili şeyler) şeklindedir.
Hatta bazen yazdığınız programı restart etmeden bile kodu değişikliği yapmak mümkün olur.
Bazı komutlar, kullanım şekilleri gerçekten de çok tehlikeli olabilir! Özellikle dış dünyadan gelecek input’ların run-time’da yorumlanması pek de önerilen bir yöntem değildir. Yani burada göreceğimiz bazı yöntemleri bilelim ama gerçek dünyada pek fazla uygulamayalım!
Class’lar Değiştirilebilir!
İster Kernel ister dışarıdan eklenen, her tür Class modifiye edilebilir:
class String
def foo
"foo: #{self}"
end
end
a = "hello"
a.foo # => "foo: hello"
String
Class’ına kafamıza göre foo
method’u ekledik.
Class’ların Birden Fazla initialize
Yöntemi Olabilir!
Bu Class Overloading
yani Class ya da method’u ezmek olarak düşünülebilir.
Class’ın bir tane initialize
method’u olduğu için, koşullu olarak Class’a
başlangıç seviyesinde müdahale edebiliriz:
# Dortgen.new([sol_ust_x, sol_ust_y], boy, en)
# Dortgen.new([sol_ust_x, sol_ust_y], [sag_alt_x, sag_alt_y])
class Dortgen
def initialize(*args)
if args.size < 2 || args.size > 3
"Bu sınıf en az 2 en fazla 3 parametre alır"
else
"Doğru parametre kullanımı"
end
end
end
Dortgen.new([0, 0], 10, 10) # => #<Dortgen:0x007fe3ea1330f0>
Dortgen.new([0, 0], [10, 10]) # => #<Dortgen:0x007fe3ea132d58>
İster 2, ister 3 parametre ile initialize
ettiğimiz Dortgen
sınıfı,
parametre kullanımına göre farklı çıktılar üretebilir.
Anonim Class
Anonim Class’lar, Singleton, Ghost ya da Metaclass diye de adlandırılır. Aslında her Ruby Class’ı kendine ait anonim bir sınıfa ve method’lara sahiptir. Tek farkı kendisine ait olmasıdır.
class Developer
class << self
def personality
"Awesome"
end
end
end
Developer.new # => #<Developer:0x007fc9048a2738>
Developer.personality # => "Awesome"
a = Developer.new
a # => #<Developer:0x007fc9048a2120>
a.class # => Developer
a.class.personality # => "Awesome"
a.personality # => undefined method `personality’ for #<Developer:0x007fd3ca0a0e10> (NoMethodError)
Developer
sınıfının kendi class
’ına anonim bir method taktık. Class’dan
instance üretmeden Developer.personality
şeklinde erişebilirken, a
instance’ından gitmek istediğimizde yani a.personality
dediğimizde hata
mesajı aldık. Oysa o methods sadece a.class
a ait :)
Yaptığımız iş aslında bir Singleton oluşturma oldu.
define_method
Class içinde run-time yani dinamik olarak method oluşturabilirsiniz:
class Developer
define_method :personality do |arg|
"You are #{arg} developer!"
end
end
Developer.new.personality("an awesome") # => "You are an awesome developer!"
a = Developer.new
a.personality("an awesome") # => "You are an awesome developer!"
a.class.instance_methods(false) # => [:personality]
send
send
method’u Object
sınıfından gelen bir method’dur. Sınıfa
göndereceğimiz mesaj ilk parametre olup bu da aslında çağıracağımız method
adıdır.
class Developer
def hello(*args)
"Hello #{args.join(" ")}"
end
end
d = Developer.new
d.send(:hello, "vigo", "how are you?") # => "Hello vigo how are you?"
Unutmayın, sadece public
method’lara erişebilirsiniz!
remove_method ve undef_method
Adından da anlaşılacağı gibi method’u yoketmek için kullanılır ama eğer
remove_method
ile iptal edilmek istenilen method, türediği üst sınıfında var
ise ne yazık ki yok edilemez. Bu durumda da undef_method
devreye girer:
class Developer
def method_missing(m, *args, &block)
"#{m} is not available!"
end
def hello
"Hello from class Developer"
end
end
class TurkishDeveloper < Developer
def hello
"Hello from class TurkishDeveloper"
end
end
d = TurkishDeveloper.new
d.hello # => "Hello from class TurkishDeveloper"
class TurkishDeveloper
remove_method :hello
end
d.hello # => "Hello from class Developer" # üst sınıfta varolduğu için çalıştı!
Eğer;
class TurkishDeveloper
undef_method :hello
end
d.hello # => "hello is not available!"
yaparsak, method komple uçar ve method_missing
ile yakaladığımız kod bloğu
çalışır.
eval
Pek çok programlama dilinde evaluate etmekten gelen, yani String
formundaki metnin çalışabilir kod parçası haline gelmesi olayıdır eval
:
eval("5 + 5") # => 10
eval(’"Hello".downcase’) # => "hello"
Aslında çok tehlikelidir. Yani programatik hiçbir kontrol olmadan dümdüz metnin executable hale getirilmesidir ve hiçbir zaman önerilmez. Güvenlik zafiyeti doğurabilir.
instance_eval
Yazılan kod bloğunu sanki Class’ın bir method’uymuş gibi çalıştırır:
class Developer
def initialize
@star = 10
end
end
d = Developer.new
d.instance_eval do
puts self
puts @star
end
# #<Developer:0x007fe549a91e70>
# 10
ya da;
class Developer
end
Developer.instance_eval do
def who
"vigo"
end
end
Developer.who # => "vigo"
şeklinde kullanılır. Aynı şekilde sadece public
olan method’lar için
geçerlidir.
module_eval ve class_eval
İkisi de aynı işi yapar. Dışarıdan Class değişkenlerine erişmek için kullanılır:
class Developer
@@geek_rate = 10
end
Developer.class_eval("@@geek_rate") # => 10
Aynı şekilde method tanımlamak için;
class Developer
end
Developer.class_eval do
def who
"vigo"
end
end
Developer.new.who # => "vigo"
class_variable_get ve class_variable_set
Class konusunda Class ve Instance Variables arasındaki farkı görmüştük. Bu iki method yardımıyla sınıf değişkenine erişmek ve değerini değiştirmek mümkün:
class Developer
@@geek_rate = 10
end
Developer.class_variable_set(:@@geek_rate, "100") # => "100"
Developer.class_variable_get(:@@geek_rate) # => "100"
instance_variable_get ve instance_variable_set
Aynı önceki gibi, bu method’lar da sadece Instance Variable için çalışır:
class Developer
def initialize(name, star)
@name = name
@star = star
end
def show
"Name: #{@name}, Star: #{@star}"
end
end
d = Developer.new("vigo", 10)
d.instance_variable_get(:@name) # => "vigo"
d.instance_variable_get(:@star) # => 10
d.show # => "Name: vigo, Star: 10"
d.instance_variable_set(:@name, "lego") # => "lego"
d.show # => "Name: lego, Star: 10"
const_get ve const_set
Constant yani sabitleri Class ve Module konusunda görmüştük. const_set
ile
Class’a sabit değer atıyoruz, const_get
ile de ilgili değeri okuyoruz:
class Box
end
Box.const_set("NAME", "web") # => "web"
Box.const_get("NAME") # => "web"
Box::NAME # => "web"
a = Box.new
a.class.constants # => [:NAME]
a.class::NAME # => "web"