infinity_test

This is my test suite setup:

For a rather large Rails project:

Looking at the Gemfile, I feel the need to clean it up. But this works pretty good at the moment :)

Setting up infinity test was soooo easy and well documented, it really was fun: https://github.com/tomas-stefano/infinity_test

Thanks to watchr, and rvm, who made this project possible!

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 :)

Ruby singleton klassen

Ich mache mal ne kleine Serie über Ruby, insbesondere über dynamische Aspekte. Ich stelle mal die Singleton-Klassen an den Anfang.

Singleton

Ein singleton ist nen Design-Pattern, bei dem die Kernaussage wohl kurz und knapp wie folgt definiert werden kann Eine Klasse, deren Anzahl an Instanzen zu jeder Zeit maximal 1 beträgt, ist ein Singleton.

In Ruby ist alles Objekt. Jedes Objekt hat eigentlich zwei Klassen:

  1. Die Klasse von der sie instanziiert wird
  2. seine Singleton-Klasse

Beispiel: singleton-Methoden auf Objekten definieren

Bevor ich hier gross anfange zu erklären, zeig ich erstmal bisserl Code:

# ist das gleiche wie
# str = "test"
str = String.new "test"  # => "test"

# hier wird eine singleton-Methode definiert
# oder anders ausgedrueckt: der singleton-Klasse von str wird die Methode length
# hinzugefuegt
def str.length
  "#{super} Zeichen"
end

str.length  # => "4 Zeichen"
str.class    # => String

foo = String.new # => ""
foo.length           # => 0

die Methode length existiert also lediglich fuer das Objekt str.

Beispiel: Singleton-Context oeffnen

Man kann Methoden auch im Singleton-Context eines Objektes definieren.

str = "test"
# den singleton-context oeffnet man dann mit diesem Konstrukt:
class << test
  def zweimal
    "#{self} #{self}"
  end
end

str.zweimal # => "test test"

Beispiel Klassenmethoden

Es gibt unterschiedliche Varianten Singleton-Methoden zu definieren. Da alles Objekt ist, kannst du auch den Singleton-Kontext einer Klasse beschreiben:

# oeffnen des Definitions-contextes der Klasse Array
class Array
  class << self
    def say
      puts "hi from array"
    end
  end
end
# ist dasselbe wie
def Array.say
  puts "hi from array"
end
# ist dasselbe wie
class Array
  def self.say
    puts "hi from array"
  end
end