Ruby eval, dynamischer geht es nicht

Ruby macht es möglich, dass man zur Laufzeit Methoden hinzufügt. Ob man das nutzen sollte, es rein stilistisch gesehen klar geht oder nicht, ist dem Anwender überlassen. Und ich find es eigentlich cool, von ner Programmiersprache nicht wie nen Kind behandelt zu werden. Es gibt auf jeden Fall Use-Cases wo man solch ein Verhalten will (wie ActiveRecord in Rails und die sexy dynamischen find-Methoden).

eval

eval fuehrt den uebergebenen String aus

eval "13+37" # => 50 

Damit ist es also unproblematisch code, den ich hier in einem Textfeld eingebe, irgendwo in meinem Programm ausfuehrbar zu machen (bis nen %x{ rm -rf /* kommt}).

Also: aus grosser Macht, folgt grosse Verantwortung :)

instance_eval

instance_eval ist der kleine Bruder von Eval: Sehr geile Eigenschaft: es wird der String oder der Block den du uebergibst ausgefuehrt, self wird aber auf den Empfaenger des Methoden-Aufrufs gesetzt. Boah, ok Bahnhof. Hier kommt nen Beispiel:

class Secret
  private 
  def psst
    puts "ey psst, darf keiner wissen"
  end
end
s = Secret.new
s.psst # => NoMethodError: private method `psst' called for #<Secret:0x10137bd18>
s.instance_eval { psst } # => "ey psst, darf keiner wissen"

Also ist self der Empfaenger und darf damit auf psst zugreifen. Ganz schoen hackisch mal eben self veraendern zu duerfen ;)

class_eval

Und das leckerste zum Schluss: mit class_eval kann man auf den class-definition body zugreifen.

str = "test" # => "test"
str.class.class_eval do
  def to_turkish
     "#{self.gsub(/[ou]/,'ü').gsub(/[OU]/,'Ü')}, ya"
  end
end

str.to_turkish # => "test, ya"
foo = "foo"   # => "foo"
foo.to_turkish # => "füü, ya"
# arkadaslar tuerk, ya, Kreuzberg geliorum!

Zugriff auf Variablen im uebergeordneten Scope

Man kann mit class_eval auch den Scope erweitern:

# in der irb
>> draussen = "du kommst hier nicht rein"
>> class Club
>>   puts draussen
>> end
=> NameError: undefined local variable or method 'draussen' for Club:Class

# wobei das hier geht
>> Club.class_eval { puts draussen }
=> "du kommst hier nicht rein"

# das hier wiederum nicht, da 'def' nen eigenen Scope oeffnet
>> Club.class_eval {def mach_ansage; puts draussen; end; }
=> NameError: undefined local variable or method 'draussen' for Club:Class

# das hier aber wieder funzt:
>> Club.class_eval { define_method("mach_ansage") { puts draussen } }
=> #<Proc:0x0133723@(irb):42>
>> _.call
=> "du kommst hier nicht rein"
>> Club.new.mach_ansage
=> "du kommst hier nicht rein"

Ist doch mal der absolute Kracher, oder? Ich finds sooo geil :)

Short URL for this post: http://tmblr.co/ZGfIHyi1Hq5