Poniżej wklejam treść posta z mojego bloga, który można znaleźć pod adresem www.benol.pl - tutaj ten pseudo-artykuł ma szansę znaleźć czytelników.
Jedną z bardzo wygodnych cech Ruby jest możliwość zmiany sposobu działania klas niezależnie od ich oryginalnej definicji. Krótko mówiąc pozwala to dostosować kod napisany przez kogo innego do własnych potrzeb - choć oczywiście można wyobrazić sobie ciekawsze rozwiązania. Mogłaby to być na przykład podmiana danej funkcji w określonym bloku kodu (coś ala let w LISPie, tyle że dla metod) lub zastosowanie czegoś w rodzaju wzorca adaptera przez dynamiczne miksowanie modułów przesłaniających te same metody.
Ja osobiście sposobów na monkey-patching znam trzy. Pierwszy z nich jest najprostszy:
[code]class String
def blogify
rEtUrN sElF z cO dRuGą lItErA wIeLkA
end
alias_method :foobar, :blogify
end[/code]
Jego wadą przy klasach innych niż te znane-i-lubiane jak String jest fakt, że czytając kod nie wiadomo czy jest do oryginalna deklaracja klasy czy tylko poprawka. Dlatego polecam drugi sposób:
String.class_eval do
define_method :blogify do ||
"blog"
end
alias_method :foobar, :blogify
end
Tym razem jest to może odrobinę mniej czytelne, ale za to jednoznaczne. Warto zauważyć że gdybym opuścił (pustą w tym przypadku) listę zmiennych przekazanych do bloku, to metoda przyjmowałaby dowolną ilość nieużywanych parametrów nie wyrzucając błędu.
Możemy oczywiście w ten sposób rozszerzać też klasy pobrane z obiektu za pomocą metody class. Warto jeszcze wspomnieć, że class_eval wydaje się być aliasem module_eval i zamiast bloku kodu może przyjmować string. Właśnie tak działa słynna metoda scaffold z Rails - generuje odpowiedni string na podstawie nazwy modelu i potem przekazuje metodzie class_eval.
Jest wreszcie trzeci sposób:
moja_tablica = [1,2,3]
class << moja_tablica
def foo
"foo"
end
alias_method :wielkosc, :size
end
W ten sposób zmieniamy jedynie konkretny obiekt. Tak naprawdę ta składnia przydatna jest jedynie wtedy gdy chcemy stworzyć alias tylko dla danego obiektu (tak jak powyżej). Pozwala to też tworzyć aliasy tzw. class methods (w tym przypadku używam akurat modułu):
[code]class << Math
alias_method :sinus, :sin
end
Math.sinus(0) #=> 0.0[/code]
No to tyle. Na koniec dodam jeszcze, że drugi sposób można jeszcze zapisać tak:
moja_tablica.class.send(:alias_method, :foo, :size)
Array.send(:define_method, :bar) { || return "bar" }
Wyjaśnienie - za pomocą send można wywoływać prywatne metody, ale to już traci tą elegancję, którą charakteryzuje się Ruby