<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="en"><title type="text">rubedo -- alchemy_of :ruby, :rails</title><link rel="alternate" type="text/html" href="http://blog.razorjack.net/" /><subtitle type="html">Wpisy z dziennika internetowego Jogger, wspomaganego przez Jabbera</subtitle><updated>2009-11-14T12:37:39+00:00</updated><generator>JoggerPL</generator><link rel="self" href="http://feeds.feedburner.com/rubedo" type="application/atom+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry><title type="text">Benchmark: Ruby 1.8, Ruby 1.9, JRuby 1.3 RC2</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/rubedo/~3/nStSKt-eHy8/" /><category term="Rails" /><category term="Railsfera" /><category term="Ruby" /><category term="Techblog" /><category term="ruby performance benchmark ruby19 jruby programming" /><updated>2009-06-01T14:35:07-07:00</updated><id>http://blog.razorjack.net/2009/06/01/ruby-1-8-ruby-1-9-jruby-1-3-rc2-porownanie-wydajnosci/</id><content type="html">&lt;p&gt;Wiele się mówi o szybkości Rubiego. Na pewno dzięki temu jest coraz mniej osób, które uważają, że jest to język zbyt wolny do aplikacji webowych - Merb w końcu pokazał, że potrafi być szybszy niż pythonowi konkurenci (&lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt;, &lt;a href="http://pylonshq.com/"&gt;Pylons&lt;/a&gt;), a PHP (także pod względem wydajności frameworków) to już nie margines, a zwykła patologia.&lt;/p&gt;
&lt;p&gt;Ostatnio modne są microbenchmarki, testujące pojedynczy aspekt języka - wyjątki, rekurencję itp. Jest to oczywista bzdura, która tak naprawdę&amp;nbsp;niczego nie mówi, dlatego postanowiłem zmierzyć wydajność tych języków w czymś realnym.&lt;/p&gt;
&lt;h3&gt;Co na pokładzie?&lt;/h3&gt;
&lt;p&gt;C2D 2.4 GHz, 4GB RAM. System operacyjny: Mac OS X 10.5.7&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;
$ ruby -v
ruby 1.8.6 (2008-08-11 patchlevel 287) [i686-darwin9.7.0]
Ruby Enterprise Edition 20090520
$ ruby19 -v
ruby 1.9.1p129 (2009-05-12 revision 23412) [i386-darwin9.6.0]
$ jruby -v
jruby 1.3.0RC2 (ruby 1.8.6p287) (2009-05-27 46598e4) 
(Java HotSpot(TM) 64-Bit Server VM 1.6.0_07) [x86_64-java]
&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;Runda pierwsza: rozpoznawanie samogłosek w głosie ludzkim&lt;/h3&gt;
&lt;p&gt;Coś całkiem mocno wykraczające poza zwykłą pętlę&amp;nbsp;&lt;code&gt;for&lt;/code&gt;. Rozpoznawanie następuje oczywiście poprzez sztuczne sieci neuronowe, a konkretniej: wielowarstwowe perceptrony ze &lt;a href="http://en.wikipedia.org/wiki/Backpropagation"&gt;wsteczną propagacją błędów&lt;/a&gt;. Pomijając szczegóły implementacyjne (w końcu chodzi o wydajność), warto tylko zaznaczyć, że czas uczenia sieci został wydłużony do absurdalnej liczy setek tysięcy cykli uczenia się całkiem niemałej paczki danych treningowych. Wszystko po to, by JVM od JRubiego zdążyła się "rozgrzać" i pokazać pazurki. Jak ostre są te pazurki? Porównacie w testach wydajności poniżej:&lt;/p&gt;
&lt;h4&gt;Ruby 1.8&lt;/h4&gt;
&lt;pre&gt;
real    25m11.525s
user    25m6.682s
sys     0m1.949s
&lt;/pre&gt;
&lt;h4&gt;Ruby 1.9&lt;/h4&gt;
&lt;pre&gt;
real    12m39.138s
user    12m38.201s
sys     0m1.073s
&lt;/pre&gt;
&lt;h4&gt;JRuby&lt;/h4&gt;
&lt;pre&gt;
real    8m15.320s
user    8m12.577s
sys     0m4.128s
&lt;/pre&gt;
&lt;p&gt;Tak jak się mówi w wielu miejscach, Ruby 1.9 jest dwukrotnie szybszy niż Ruby 1.8. Natomiast &lt;strong&gt;JRuby jest szybszy od Ruby 1.9 o 37%&lt;/strong&gt;, co jest dość znaczną przewagą.&lt;/p&gt;
&lt;h3&gt;Runda druga: Rails&lt;/h3&gt;
&lt;p&gt;Nic wielkiego, testowana jest aplikacja z jedną akcją renderującą zwykły szablon. Nie używamy bazy danych. Całość&amp;nbsp;serwowana przez pojedynczego Mongrela. W przypadku Ruby 1.9 należało wprowadzić parę zmian w kodzie C i .rb, aby rozszerzenie dało radę się skompilować. W przypadku JRubiego serwowanie aplikacji na Mongrelu jest, delikatnie mówiąc, nietypowe, ale potrzebne przy równych (w miarę miarodajnych) testach.&lt;/p&gt;
&lt;p&gt;Parametr &lt;code&gt;c&lt;/code&gt; mówi o ilości jednoczesnych połączeń podczas testów.&lt;/p&gt;
&lt;h4&gt;Ruby 1.8&lt;/h4&gt;
&lt;pre&gt;
-c 1: 582.73 [#/sec]
-c 5: 548.83 [#/sec]
-c 20: 524.57 [#/sec]
&lt;/pre&gt;
&lt;h4&gt;Ruby 1.9&lt;/h4&gt;
&lt;pre&gt;
-c 1: 548.06 [#/sec]
-c 5: 501.29 [#/sec]
-c 20: 476.33 [#/sec]
&lt;/pre&gt;
&lt;h4&gt;JRuby&lt;/h4&gt;
&lt;pre&gt;
-c 1: 365.03 [#/sec]
-c 5: 493.89 [#/sec]
-c 20: 486.92 [#/sec]
&lt;/pre&gt;
&lt;p&gt;Bardzo dziwną rzeczą jest to, że &lt;strong&gt;Ruby 1.9 okazał się wolniejszy niż Ruby 1.8&lt;/strong&gt;! JRuby delikatnie przewyższa 1.9 szybkością przy dużych obciążeniach, ale nadal jest to wolniej niż 1.8.&lt;/p&gt;
&lt;h3&gt;Runda trzecia: Merb&lt;/h3&gt;
&lt;p&gt;Typ aplikacji taki sam jak w przypadku Railsów. Pamiętajmy, że tylko Merb 1.1 (edge!) działa z wersją 1.9.&lt;/p&gt;
&lt;h4&gt;Ruby 1.8&lt;/h4&gt;
&lt;pre&gt;
-c 1: 953.38 [#/sec]
-c 5: 922.15 [#/sec]
-c 20: 915.56 [#/sec]
&lt;/pre&gt;
&lt;h4&gt;Ruby 1.9&lt;/h4&gt;
&lt;pre&gt;
-c 1: 1146.85 [#/sec]
-c 5: 1110.52 [#/sec]
-c 20: 1079.74 [#/sec]
&lt;/pre&gt;
&lt;h4&gt;JRuby&lt;/h4&gt;
&lt;pre&gt;
-c 1: 772.88 [#/sec]
-c 5: 1109.98 [#/sec]
-c 20: 1079.29 [#/sec]
&lt;/pre&gt;
&lt;p&gt;Tutaj wyniki Ruby 1.9 i JRuby są&amp;nbsp;porównywalne. Nie da się ukryć, że Merb jest lepiej napisany niż Railsy, dlatego im lepsza implementacja tym większy skok wydajności. Rails3 ma mieć ulepszone bebechy, dlatego możemy się spodziewać, że wydajność Rails 3 będzie porównywalna z Merbem.&lt;/p&gt;
&lt;h3&gt;Co to wszystko znaczy?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="http://jruby.codehaus.org/"&gt;JRuby&lt;/a&gt; jest w pełni gotowy do produkcji&lt;/strong&gt; i jest całkowicie kompatybilny (poza paroma gemami z rozszerzeniami w C) z wersją 1.8. Oznacza to mniej więcej tyle, że w większości przypadków, o ile mamy dużo RAM-u na naszym serwerze, możemy &lt;strong&gt;za darmo znacząco zwiększyć wydajność aplikacji&lt;/strong&gt; zwyczajnie poprzez zmianę implementacji języka na Javową&lt;/li&gt;
&lt;li&gt;JRuby 1.4 będzie jeszcze szybszy&lt;/li&gt;
&lt;li&gt;Java 7 będzie posiadała w swoich bebechach wiele ficzerów, które ułatwiają tworzenie na jej bazie języków dynamicznych - oznaczać to będzie prawdopodobne wyrzucenie części kodu z JRubiego, bo będzie on już w samej platformie Java. Takie zabieg prawdopodobnie zwiększy wydajność JRubiego.&lt;/li&gt;
&lt;li&gt;Tradycyjnie, nowa edycja Javy może zwiększyć jej wydajność, na czym skorzysta JRuby.&lt;/li&gt;
&lt;li&gt;JRuby nie wymaga zgodności gemów z wersją 1.9, a daje porównywalny kop wydajnościowy, co wersja 1.9. Mam nadzieję, że te słowa nie zdemotywują nikogo do reimplementacji swoich bibliotek pod wersję 1.9... Więcej info na &lt;a href="http://isitruby19.com"&gt;isitruby19.com&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Testy dotyczyły tylko trzech implementacji. Niedługo może się pojawić &lt;a href="http://maglev.gemstone.com/"&gt;MagLev&lt;/a&gt;, &lt;a href="http://rubini.us/"&gt;Rubinius&lt;/a&gt; i &lt;a href="http://www.macruby.org/"&gt;MacRuby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MacRuby jest projektem wyjątkowo ciekawym. Docelowo ma być kompatybilny z każdym POSIXowym systemem (a więc nie tylko Mac OS X), a także rezygnować z wirtualnej maszyny YARV na rzecz &lt;a href="http://en.wikipedia.org/wiki/Low_Level_Virtual_Machine"&gt;LLVM&lt;/a&gt;. Będzie to olbrzymi kop wydajnościowy. Już teraz trunkowa wersja 0.5 potrafi być paręnaście razy szybsza od konkurentów, a skompilowany (tak jest, będzie można kompilować do binarki!) generator ciągu Fibonacciego &lt;a href="http://pastie.org/485095"&gt;jest szybszy niż...&lt;/a&gt; implementacja w &lt;a href="http://en.wikipedia.org/wiki/Objective-C"&gt;Objective-C&lt;/a&gt;! Do tego dochodzi dojrzały generational garbage collector oraz obsługa natywnych wątków. Nie byle jaka implementacja!&lt;/li&gt;
&lt;/ul&gt;</content><feedburner:origLink>http://blog.razorjack.net/2009/06/01/ruby-1-8-ruby-1-9-jruby-1-3-rc2-porownanie-wydajnosci/</feedburner:origLink></entry><entry><title type="text">nginx + Phusion Passenger™</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/rubedo/~3/l9fmcqt9m30/" /><category term="Ogólne" /><category term="Rails" /><category term="Techblog" /><category term="rails rack nginx" /><updated>2009-04-16T17:34:37-07:00</updated><id>http://blog.razorjack.net/2009/04/17/nginx-phusion-passenger/</id><content type="html">&lt;p&gt;To nie jest spóźnione prima aprilis, aczkolwiek sam nie mogłem uwierzyć w to, co przeczytałem &lt;a href="http://blog.phusion.nl/2009/04/16/phusions-one-year-anniversary-gift-phusion-passenger-220/"&gt;na blogu Phusion&lt;/a&gt;. Nie owijając w bawełnę, serwer &lt;a href="http://nginx.net/"&gt;nginx&lt;/a&gt; ma już pasażera. I to jakiego!&lt;/p&gt;
&lt;p&gt;Instalacja jest tak prosta, jak obiecywano.&lt;/p&gt;
&lt;p&gt;Niestety, kompilacja modułu wg. domyślnej konfiguracji wyłącza jawnie &lt;i&gt;http_rewrite_module&lt;/i&gt;, nie działają także wyrażenia regularne (których także potrzebowałem w swojej konfiguracji, aby migracja była bezbolesna). Problemu jednak żadnego nie było, skompilowałem ręcznie nginksa z --with-pcre i paroma innymi modułami, nie wyłączając przy tym rewrite'ow - i wszystko poszło gładko. Zobaczymy, czy gładko będzie chodziło - bardzo dziwi mnie domyślne wyłączenie modułu przepisywania...&lt;/p&gt;
&lt;p&gt;Tak czy inaczej, mój VPS już śmiga na nginx+passenger. A Wasz? :-)&lt;/p&gt;</content><feedburner:origLink>http://blog.razorjack.net/2009/04/17/nginx-phusion-passenger/</feedburner:origLink></entry><entry><title type="text">Edytor WYSIWYG w adminie Django (newforms-admin)</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/rubedo/~3/tN6ytFHo6-s/" /><category term="django" /><category term="newforms-admin" /><category term="Python" /><category term="Techblog" /><updated>2008-05-13T16:44:44-07:00</updated><id>http://blog.razorjack.net/2008/05/14/edytor-wysiwyg-w-adminie-django-newforms-admin/</id><content type="html">&lt;p&gt;Pisząc systemy CMS, możemy się spotkać z powtarzającym się wymaganiem: ma być edytor WYSIWYG do tekstu. Widywałem aplikacje Django, w których główny admin był budowany na tym, co oferuje framework, a drugi - pisany ręcznie z edytorem WYSIWYG do większych tekstów. Zawsze jakieś rozwiązanie, ale da się lepiej. Jak?&lt;/p&gt;
&lt;h3&gt;Przede wszystkim: gałąź newforms-admin&lt;/h3&gt;
&lt;p&gt;Jeśli istnieje ktoś, kto jeszcze jej nie używa, niech zapozna się z jej &lt;a href="http://code.djangoproject.com/wiki/NewformsAdminBranch"&gt;opisem&lt;/a&gt;. W skrócie - bardziej elastyczny i podatny na własną rozbudowę admin budowany na bazie &lt;a href="http://www.djangoproject.com/documentation/newforms/"&gt;newforms&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Instalacja front-endu&lt;/h3&gt;
&lt;p&gt;Wybrałem &lt;a href="http://www.fckeditor.net/"&gt;FCKeditor&lt;/a&gt;, ze względu na duże możliwości i prostotę w implementacji, ale sposób działa dla dowolnie innego edytora. Należy ściągnąć paczkę i wrzucić ją tak, aby katalog z całością był dostępny pod ścieżką /fckeditor/. Tak jest domyślnie, ale nikt nam nie przeszkadza, by to zmienić, jeśli chcemy inaczej.&lt;/p&gt;
&lt;h3&gt;Drobna modyfikacja skryptu integrującego z Pythonem&lt;/h3&gt;
&lt;p&gt;Twórcy FCKeditora byli na tyle mili, by udostępniać go razem z pythonową klasą. Kopiujemy &lt;i&gt;fckeditor.py&lt;/i&gt; do wybranego przez nas katalogu i przycinamy funkcje &lt;code&gt;IsCompatible&lt;/code&gt; do takiej postaci:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="python"&gt;def IsCompatible(self):
    return True&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;Widget dla newforms&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://www.djangosnippets.org/snippets/576/"&gt;Kod&lt;/a&gt; z &lt;a href="http://djangosnippets.org"&gt;Django snippets&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="python"&gt;from django.newforms.widgets import Widget
from django.utils.safestring import mark_safe
from django.utils.encoding import force_unicode
import myapp.core.fckeditor as fck
# ........
class FCKeditor(Widget):
   def __init__(self, attrs=None):
           if attrs is not None:
                   self.attrs = attrs.copy()
           else:
                   self.attrs = {}

           self.__widget = fck.FCKeditor("FCKeditor1")
           super(FCKeditor, self).__init__(attrs)

           self.setAtters(self.attrs)

   def setAtters(self, attrs):
           if attrs is None:
                   return

           if 'basepath' in attrs:
                   self.__widget.BasePath = attrs['basepath']

           if 'width' in attrs:
                   self.__widget.Width = attrs['width']

           if 'height' in attrs:
                   self.__widget.Height = attrs['height']

           if 'toolbar' in attrs:
                   self.__widget.ToolbarSet = attrs['toolbar']

   def render(self, name, value, attrs=None):
           if value is None: value = ''
           value = force_unicode(value)

           self.__widget.InstanceName = name
           self.__widget.Value = value
           self.setAtters(attrs)

           final_attrs = self.build_attrs(attrs, name=name)
           return mark_safe(self.__widget.CreateHtml())
&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;Podczepienie widgetu do modelu&lt;/h3&gt;
&lt;p&gt;W moim przypadku jest to model Clip, zmieniam formularz dla textfield-u description:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="python"&gt;class ClipOptions(admin.ModelAdmin):
        # .........
        
        def formfield_for_dbfield(self, db_field, **kwargs):
                if db_field.name == 'description':
                        kwargs['widget'] = FCKeditor
                return super(ClipOptions,self).formfield_for_dbfield(db_field,**kwargs)

# ...
global_admin = admin.AdminSite()
global_admin.register(Clip, ClipOptions)&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;Gotowe&lt;/h3&gt;
&lt;p&gt;Oto jak wygląda w realnej aplikacji: &lt;img src="http://img.skitch.com/20080513-p416185jng3xgfuqpsfig19xjc.jpg" alt="Zmie0144 klip | Administracja stron0105 Django"&gt;&lt;/p&gt;</content><feedburner:origLink>http://blog.razorjack.net/2008/05/14/edytor-wysiwyg-w-adminie-django-newforms-admin/</feedburner:origLink></entry><entry><title type="text">Translashun gem... iz translating</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/rubedo/~3/XSAXecuA8l0/" /><category term="Humor" /><category term="Ruby" /><category term="gem" /><category term="lolcat" /><category term="programming" /><category term="fun" /><updated>2008-03-10T12:48:35-07:00</updated><id>http://blog.razorjack.net/2008/03/10/translashun-gem-iz-translating/</id><content type="html">&lt;p&gt;&lt;a href="http://www.dribin.org/dave/lolspeak/"&gt;Dave Dribin&lt;/a&gt; wypuścił gema, na którego wszyscy czekali i nawet o tym nie wiedzieli.&lt;/p&gt;
&lt;p&gt;Gem nazywa się &lt;a href="http://lolspeak.rubyforge.org/api/"&gt;LOLspeak&lt;/a&gt; i jak nietrudno się domyślić, umożliwia tłumaczenie zdań z języka normalnego na &lt;a href="http://icanhascheezburger.com"&gt;lolcatowy&lt;/a&gt;. Użycie jest bardzo proste:&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;gem install lolspeak&lt;/kbd&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;require 'rubygems'
require 'lolspeak'

puts "hi cat, i have something for you".to_lolspeak
#               oh hai kitteh, i has somethin 4 u&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Słownik zapewne będzie szybko i intensywnie rozbudowywany, tak więc jeśli planujesz internacjonalizację swoich rubinowych aplikacji, pamiętaj, że kot też człowiek.&lt;/p&gt;
&lt;div style="margin: auto 0;"&gt;&lt;img src="http://icanhascheezburger.files.wordpress.com/2008/02/funny-pictures-cat-bubble-bath-trust.jpg"&gt;&lt;/div&gt;</content><feedburner:origLink>http://blog.razorjack.net/2008/03/10/translashun-gem-iz-translating/</feedburner:origLink></entry><entry><title type="text">RuPy - Ruby &amp; Python Conference</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/rubedo/~3/nziNI9DoaC0/" /><category term="Polska społeczność" /><category term="Python" /><category term="Ruby" /><category term="Techblog" /><category term="conference" /><updated>2008-01-26T10:59:01-08:00</updated><id>http://blog.razorjack.net/2008/01/26/rupy-ruby-python-conference/</id><content type="html">&lt;p&gt;Konferencja RuPy 2008, która odbędzie się 12-13 kwietnia 2008 r., w Poznaniu to już druga konferencja poświęcona językom programowania Python i Ruby. Pierwszy raz odbyła się w kwietniu 2007 r. Doszło wówczas do spotkania entuzjastów tych technologii z Polski i innych krajów. Ta pierwsza konferencja nie była dużym przedsięwzięciem, ale okazała się ogromnym sukcesem i wartościowym doświadczeniem zarówno dla uczestników jak i prelegentów. Zachęceni tych faktem, organizatorzy w tym roku celują w wydarzenie o jeszcze większej skali.&lt;/p&gt;
&lt;p&gt;Komitet organizacyjny konferencji RuPy 2008 poszukuje prelegentów chętnych wygłosić referat dotyczący języków Python i Ruby. Jeśli macie jakieś interesujące pomysły, nie zwlekajcie i wyślijcie e-maila ze swoją propozycję złożoną z tytułu referatu, krótkiego opisu zagadnienia oraz kilku słów o sobie pod adres: talks@rupy.eu. Dla zaproszonych prelegentów oferowane są darmowe zakwaterowania i wyżywienie oraz możliwość poznania bardzo energicznej społeczności IT. Zachęcamy ponadto wszystkich do uczestnictwa w dodatkowych atrakcjach przygotowanych przez organizatorów konferencji. Zeszłoroczne 'Geek party' dla wielu było niezapomnianym wrażeniem. Macie również możliwość także wspierać inicjatywę RuPy poprzez propagowanie adresu strony konferencji i informacji o niej.&lt;/p&gt;
&lt;p&gt;Osoby chętne do wygłoszenia referatu proszone są do nadsyłania propozycji na powyższy adres w terminie do końca stycznia 2008!&lt;/p&gt;
&lt;p&gt;Więcej informacji o konferencji na stronie: &lt;a href="http://www.rupy.eu"&gt;http://www.rupy.eu&lt;/a&gt;&lt;/p&gt;</content><feedburner:origLink>http://blog.razorjack.net/2008/01/26/rupy-ruby-python-conference/</feedburner:origLink></entry><entry><title type="text">Zed Shaw: "Rails Is A Ghetto"</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/rubedo/~3/pffJVZHoJP8/" /><category term="Rails" /><category term="Railsfera" /><category term="Ruby" /><updated>2008-01-06T05:47:01-08:00</updated><id>http://blog.razorjack.net/2008/01/06/zed-shaw-rails-is-a-ghetto/</id><content type="html">&lt;p&gt;Każdy programista Rails zna to nazwisko. Twórca &lt;a href="http://mongrel.rubyforge.org/"&gt;Mongrela&lt;/a&gt; postanowił odejść ze społeczności, pozostawiając jako uzasadnienie &lt;a href="http://www.zedshaw.com/rants/rails_is_a_ghetto.html"&gt;bardzo długi, ale interesujący post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Z miejsca ostrzegam, że post jest cieżki, napisany ciętym językiem. Ujawnia smutne fakty i historie, które miały miejsce w społeczności, również wśród core developerów Railsów. O krytykę Rubiego także nietrudno. Lektura dość przykra dla zwolenników Rails.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When I hang out with the Nitro, Factor, Django, Lua, Python, Lisp, or Mongrel crews I get to talk to super smart guys who have egos for sure, but have mutual respect. They craft nifty shit I love to play with, are fun to talk with even when I disagree, and don’t say mean shit when I’m not around.&lt;/p&gt;
&lt;p&gt;With Rails I get scrawny cocksuckers with carpal tunnel syndrome talking to me like they’re gonna eat my young. Their feeble PHP infected minds can’t grasp advanced shit like objects or closures. When you combine stupid businesses with stupid people using a stupid framework based on a big fat fucking lie on a shitty platform you get the perfect storm of dumbfuck where a man like me can’t find work.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Po paru dniach od napisania artykułu, autor otrzymał ponad 400 maili. Na ich podstawie będzie pisał FAQ, tak więc kto myśli, że sprawa cichnie, ten się myli...&lt;/p&gt;
&lt;p&gt;Zdaję sobie sprawę, że większa część polskiej społeczności zdążyła się już zapoznać z całym postem, ale bardzo ciekawi mnie jak Wy widzicie tę sprawę. Prosiłbym tylko o język choć minimalnie ładniejszy niż ten, którego użył Zed ;-).&lt;/p&gt;</content><feedburner:origLink>http://blog.razorjack.net/2008/01/06/zed-shaw-rails-is-a-ghetto/</feedburner:origLink></entry><entry><title type="text">Obiekty w Rubim, część 2: singletony, moduły, miksiny</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/rubedo/~3/Ub6xGqkJSSU/" /><category term="Ruby" /><category term="Ruby tutorial" /><category term="Techblog" /><category term="programming" /><category term="OOP" /><category term="mixins" /><category term="modules" /><category term="namespaces" /><category term="singleton pattern" /><updated>2007-10-28T17:20:48-07:00</updated><id>http://blog.razorjack.net/2007/10/29/obiekty-w-rubim-czesc-2-singletony-moduly-miksiny/</id><content type="html">&lt;p&gt;W tej części kursu omówimy wzorzec singletonu, dynamicznie dodawane metody oraz moduły i miksiny w języku Ruby.&lt;/p&gt;
&lt;h3&gt;Wzorzec singletonu&lt;/h3&gt;
&lt;p&gt;Założenia są proste: dana klasa może występować w co najwyżej &lt;strong&gt;jednym&lt;/strong&gt; egzemplarzu.&lt;/p&gt;
&lt;p&gt;Stwórzmy dla przykładu klasę implementującą konsolę - taką, jaką znamy choćby z Quake'a. Ponieważ w całym silniku gry nie potrzebujemy więcej, niż jednej konsoli - zastosujemy tutaj wzorzec singletonu:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;class Console
  def self.instance
    return @@instance if defined? @@instance
    @@instance = new
  end
  
  private_class_method :new, :allocate
end

c1 = Console.instance
c2 = Console.instance

c1.object_id    # =&amp;gt; 22691800
c2.object_id    # =&amp;gt; 22691800&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Widać tutaj, że aby zrealizować singleton w Rubim, należy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;zablokować metody &lt;code&gt;new&lt;/code&gt; i &lt;code&gt;allocate&lt;/code&gt; - uczynić je prywatnymi, by mogły być wywoływane tylko w odrębie klasy&lt;/li&gt;
&lt;li&gt;udostępnić metodę, która zwraca (a wcześniej ewentualnie tworzy) instancję naszego singletonu&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Oczywiście Ruby nie byłby sobą, gdyby nie pozwolił nam na realizację tego wszystkiego za pomocą paru stuknięć w klawiaturę :-).&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;require 'singleton'

class Console
  include Singleton
end

c1 = Console.instance
c2 = Console.instance

c1.object_id    # =&amp;gt; 22691800
c2.object_id    # =&amp;gt; 22691800&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;Metody singletonu&lt;/h3&gt;
&lt;p&gt;Nazwa może być nieco myląca, gdyż nie do końca jest związana ze wzorcem singletonu.&lt;/p&gt;
&lt;p&gt;Ruby pokazuje swoją dynamikę w przykładzie, w którym rozszerzamy wbudowaną w język klasę:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;class Integer
  def odd?
    (self % 2) == 1
  end
end

puts 5.odd?       # =&amp;gt; true
puts 6.odd?       # =&amp;gt; false
puts 7.odd?       # =&amp;gt; true&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;To nie koniec, można posunąć się nawet dalej. Ruby oferuje nam możliwość dopisania metody do &lt;strong&gt;egzemplarza&lt;/strong&gt; dowolnej klasy. Taką metodę nazywamy metodą singletonu.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;class Dog
  def bark
    puts "Woof, woof!"
  end
  
  def method_missing(name)
    puts "Hey, man. I can't #{name}!"
  end
end

Rex, Azor = Dog.new, Dog.new

def Azor.kill
  puts "/me is killing"
end

Rex.kill
Azor.kill&lt;/code&gt;
&lt;/pre&gt;
&lt;pre&gt;
&lt;samp&gt;Hey, man. I can't kill!
/me is killing&lt;/samp&gt;
&lt;/pre&gt;
&lt;p&gt;Metoda &lt;code&gt;method_missing&lt;/code&gt; wywoływana jest w momencie, gdy następuje próba wywołania nieistniejącej metody. Więcej takich sztuczek postaram się omówić w kolejnej części tutoriala.&lt;/p&gt;
&lt;h3&gt;Przestrzenie nazw&lt;/h3&gt;
&lt;p&gt;...czyli gdy klasa niezbyt dobrze modeluje problem.&lt;/p&gt;
&lt;p&gt;Przestrzenie nazw to jedno z dwóch zastosowań modułów (o drugim powiemy za chwilę) w Rubim. Poza umożliwieniem programiście logicznej organizacji struktury projektu, pozwalają dodatkowo uniknąć konfliktów nazw, które tak bardzo potrafią dokuczać w innych językach. O przykłady nietrudno. &lt;code&gt;log(10)&lt;/code&gt; może oznaczać zarówno logarytm z dziesięciu, jak i zalogowanie błędu o kodzie 10. Takich niejednoznaczności należy unikać.&lt;/p&gt;
&lt;p&gt;Jak?&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;module MyMath
  # ...
  def self.log(x)
    # ...
  end
end

module CriticalErrorLogger
  def self.log(error_num)
    # ...
  end
end

y = MyMath.log(10)                      # Logarytm
CriticalErrorLogger.log(10)             # Logowanie błędu
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Pamiętajmy, że konieczne jest poprzedzenie &lt;code&gt;selfem&lt;/code&gt; nazwy metody, by zdefiniować metodę modułu, a nie instancji. Metody instancji mają zastosowanie w miksinach, o których dosłownie za chwilę.&lt;/p&gt;
&lt;p&gt;Czasem jednak chcemy napisać program czysto matematyczny, a wtedy rzeczą naturalną jest, że sinus to &lt;code&gt;sin(x)&lt;/code&gt; a nie &lt;code&gt;Math.sin(x)&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;puts Math.sin(Math::PI/2)

include Math
puts sin(PI/2)&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Widać tutaj, że includowaniu ulegają nie tylko metody, ale także stałe.&lt;/p&gt;
&lt;h3&gt;require&lt;/h3&gt;
&lt;p&gt;Chcąc użyć klas i metod z zewnętrznego pliku, konieczne jest użycie metody &lt;code&gt;require&lt;/code&gt; (np. &lt;code class="ruby"&gt;require 'complex'&lt;/code&gt; - jeśli chcemy użyć w programie liczb zespolonych). Jak widać, nie ma potrzeby pisania rozszerzenia ładowanego pliku.&lt;/p&gt;
&lt;h3&gt;A ścieżki?&lt;/h3&gt;
&lt;p&gt;Są, wszystkie dostępne w magicznej zmiennej &lt;code class="ruby"&gt;$:&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;irb(main):001:0&amp;gt; $:&lt;br&gt;
=&amp;gt; ["c:/ruby/lib/ruby/site_ruby/1.8", "c:/ruby/lib/ruby/site_ruby/1.8/i386-msvcrt", "c:/ruby/lib/ruby/site_ruby", "c:/ru by/lib/ruby/1.8", "c:/ruby/lib/ruby/1.8/i386-mswin32", "."&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Tablica (a raczej kobyła) z plikami, które includowaliśmy (wiednie lub bezwiednie) dostępna jest pod magiczną nazwą &lt;code&gt;$"&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class="ruby"&gt;irb(main):002:0&amp;gt; $"&lt;br&gt;
=&amp;gt; ["rbconfig.rb", "rubygems/rubygems_version.rb", "thread.so", "thread.rb", "rbconfig/datadir.rb", "rubygems/user_inter action.rb", "socket.so", "timeout.rb", "net/protocol.rb", "uri/common.rb", "uri/generic.rb", "uri/ftp.rb", "uri/http.rb" , "uri/https.rb", "uri/ldap.rb", "uri/mailto.rb", "uri.rb", "net/http.rb", "stringio.so", "yaml/error.rb", "syck.so", "y aml/ypath.rb", "yaml/basenode.rb", "yaml/syck.rb", "yaml/tag.rb", "yaml/stream.rb", "yaml/constants.rb", "rational.rb", "date/format.rb", "date.rb", "yaml/rubytypes.rb", "yaml/types.rb", "yaml.rb", "zlib.so", "rubygems/remote_fetcher.rb", " forwardable.rb", "digest.so", "digest.rb", "digest/sha2.rb", "parsedate.rb", "time.rb", "rubygems/source_index.rb", "rub ygems/version.rb", "rubygems/specification.rb", "openssl.so", "openssl/bn.rb", "openssl/cipher.rb", "openssl/digest.rb", "openssl/buffering.rb", "fcntl.so", "openssl/ssl.rb", "openssl/x509.rb", "openssl.rb", "rubygems/gem_openssl.rb", "ruby gems/security.rb", "rubygems/custom_require.rb", "rubygems.rb", "ubygems.rb", "e2mmap.rb", "irb/init.rb", "irb/workspace .rb", "irb/context.rb", "irb/extend-command.rb", "irb/output-method.rb", "irb/notifier.rb", "irb/slex.rb", "irb/ruby-tok en.rb", "irb/ruby-lex.rb", "readline.so", "irb/input-method.rb", "irb/locale.rb", "irb.rb"]&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Ups...&lt;/h3&gt;
&lt;p&gt;Metoda &lt;code&gt;require&lt;/code&gt; powoduje załadowanie pliku docelowego tylko raz. Niestety, przed nieopatrznym powtórzeniem procesu Ruby broni się niezbyt mądrze.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;irb(main):001:0&amp;gt; require 'mathn'
=&amp;gt; true
irb(main):002:0&amp;gt; require 'mathn'
=&amp;gt; false
irb(main):003:0&amp;gt; require 'C:/ruby/lib/ruby/1.8/mathn.rb'
C:/ruby/lib/ruby/1.8/mathn.rb:118: warning: already initialized constant Unify
C:/ruby/lib/ruby/1.8/mathn.rb:306: warning: already initialized constant Unify
=&amp;gt; true&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;W tym przypadku wygląda to niewinnie, ale bardzo łatwo można sobie zrobić tym krzywdę. Prawdopodobnie zostanie to poprawione w przyszłych wersjach, ale póki co - beware!&lt;/p&gt;
&lt;h3&gt;Miksiny&lt;/h3&gt;
&lt;p&gt;Drugie zastosowanie modułów. Wspaniała możliwość będąca doskonałym połączeniem zalet dziedziczenia wielobazowego (znanego chociażby z C++) z przejrzystością i jednoznacznością definicji klas.&lt;/p&gt;
&lt;p&gt;Prześledźmy poniższy przykład:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;module Witness
  def create_witness
    @witness = "Been there. Seen it. Got the scars."
  end
  
  def has_witness?
    (defined? @witness) ? true : false
  end
end

class Accident
  include Witness
  
  def to_s
    "An accident with " + (self.has_witness? ? 
            "a witness saying \'#{@witness}\'" : "no witness")
  end
end

acc1, acc2 = Accident.new, Accident.new
acc2.create_witness

puts acc1, acc2&lt;/code&gt;
&lt;/pre&gt;
&lt;pre&gt;
&lt;samp&gt;An accident with no witness
An accident with a witness saying 'Been there. Seen it. Got the scars.'&lt;/samp&gt;
&lt;/pre&gt;
&lt;p&gt;Jak to wszystko funkcjonuje?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;moduły nigdy nie mają instancji - mogą co najwyżej zostać włączone do klasy; wywołać można jedynie metody modułu (np. &lt;code&gt;Math.sin(0)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;argumentem metody &lt;code&gt;include&lt;/code&gt; jest zawsze nazwa widocznego z danego miejsca modułu; jeśli moduł nie jest widoczny, należy załadować odpowiedni plik za pomocą metody &lt;code&gt;require&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;klasa, do której dołączany jest moduł, przejmuje także jego zmienne instancji, modułu oraz stałe&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pamiętać należy o kolejności poszukiwania metody w momencie wywołania:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;metody oryginalnej klasy&lt;/li&gt;
&lt;li&gt;metody wmiksowane&lt;/li&gt;
&lt;li&gt;metody przodka&lt;/li&gt;
&lt;li&gt;metody wmiksowane w przodka&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Oczywiście w momencie znalezienia metody, jest ona wywoływana, a samo przeszukiwanie przerywane.&lt;/p&gt;
&lt;h3&gt;Rubinowe miksiny&lt;/h3&gt;
&lt;p&gt;Ruby dostarcza parę ciekawych modułów, które wmiksowane są w wiele standardowych klas. Tylko czekają, aż wmiksujemy je i my :). Na przykładzie omówimy &lt;code&gt;Comparable&lt;/code&gt; i &lt;code&gt;Enumerable&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Enumerable&lt;/h3&gt;
&lt;p&gt;Zasada jest prosta. Implementujemy w naszej klasie metodę &lt;code&gt;each&lt;/code&gt;, a Ruby zajmuje się resztą. Znaczy się: dopisuje za nas takie metody, jak &lt;code&gt;all?&lt;/code&gt;, &lt;code&gt;any?&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;min&lt;/code&gt;, &lt;code&gt;sort&lt;/code&gt; itp. (&lt;kbd&gt;ri Enumerable&lt;/kbd&gt;!).&lt;/p&gt;
&lt;p&gt;Dla przykładu zaimplementujmy kolekcję, która zawiera ciągi kropek. Ilość kropek kolejnych elementów jest równa wartości kolejnych elementów tablicy podanej do konstruktora.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;class Dotter
  include Enumerable
  
  def initialize(val)
    @collection = val.to_a
  end
  
  def each
    @collection.each { |e| yield '.'*e }
  end
  
end

d1 = Dotter.new(2..7)
# ..
# ...
# ....
# .....
# ......
# .......

d2 = Dotter.new([5, 4, 6, 1, 7, 3])
# .....
# ....
# ......
# .
# .......
# ...


d1.all? { |v| v.length &amp;gt; 8} # false
d2.any? { |v| v.length%2}   # true
d2.include?('..')           # false
d2.max                      # "......."
d2.min                      # "."
d2.sort                     # [".", "...", "....", ".....", 
                            # "......", "......."]&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;Comparable&lt;/h3&gt;
&lt;p&gt;Użycie tego modułu jest równie proste. Implementujemy metodę &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt;, a w efekcie otrzymujemy metody porównania oraz &lt;code&gt;between?&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Dla przypomnienia, metoda &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; zwraca -1, gdy pierwszy element jest mniejszy od drugiego, 0 - gdy oba elementy są równe oraz 1, gdy pierwszy element jest większy od drugiego.&lt;/p&gt;
&lt;p&gt;Czas na praktykę. Weźmy przykład z dokumentacji Rubiego. Poza tym, że rozmiar jednak ma znaczenie, jeszcze coś innego jest w nim interesujące.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;class SizeMatters
  include Comparable
  
  attr :str
  def &amp;lt;=&amp;gt;(anOther)
    str.size &amp;lt;=&amp;gt; anOther.str.size
  end
  
  def initialize(str)
    @str = str
  end
  
  def inspect
    @str
  end
end

s1 = SizeMatters.new("Z")
s2 = SizeMatters.new("YY")
s3 = SizeMatters.new("XXX")
s4 = SizeMatters.new("WWWW")
s5 = SizeMatters.new("VVVVV")

s1 &amp;lt; s2                       # =&amp;gt; true
s4.between?(s1, s3)           # =&amp;gt; false
s4.between?(s3, s5)           # =&amp;gt; true
[ s3, s2, s5, s4, s1 ].sort   # =&amp;gt; [Z, YY, XXX, WWWW, VVVVV]&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;Czy wiesz, że...&lt;/h3&gt;
&lt;p&gt;Gdy zdefiniujemy pustą klasę:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;class Empty
end

e = Empty.new
puts e.methods.sort.join(", ")&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;samp&gt;==, ===, =~, __id__, __send__, class, clone, display, dup, eql?, equal?, extend, freeze, frozen?, gem, hash, id, inspect, instance_eval, instance_of?, instance_variable_defined?, instance_variable_get, instance_variable_set, instance_variables, is_a?, kind_of?, method, methods, nil?, object_id, private_methods, protected_methods, public_methods, require, require_gem, respond_to?, send, singleton_methods, taguri, taguri=, taint, tainted?, to_a, to_s, to_yaml, to_yaml_properties, to_yaml_style, type, untaint&lt;/samp&gt;&lt;/p&gt;
&lt;p&gt;zauważamy sporą ilości metod, których nie definiowaliśmy. Tak naprawdę są to metody klas nadrzędnych do naszej: &lt;code&gt;Object&lt;/code&gt;, &lt;code&gt;Module&lt;/code&gt;, &lt;code&gt;Class&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Za to zamieszanie Ruby jest krytykowany przez wielu sceptyków. Okazuje się, że może być jeszcze gorzej :).&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;module Kernel
  def are_you_there?
    "Yeah, fuc*in' everywhere!"
  end
end

5.are_you_there?        # "Yeah, fuc*in' everywhere!"
"test".are_you_there?   # "Yeah, fuc*in' everywhere!"&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Publiczna metoda modułu Kernel jest wmiksowana w każdą klasę. Zdefiniowanie metody poza jakąkolwiek klasą definiuje ją w module Kernel. Includowanie modułu poza jakąkolwiek klasą powoduje inkludowanie go do modułu Kernel.&lt;/p&gt;
&lt;p&gt;Podany wyżej przykład pokazuje skalę tego, co dopiszemy do Kernela. Niby nic... ale nie bawmy się w Lenina!&lt;/p&gt;</content><feedburner:origLink>http://blog.razorjack.net/2007/10/29/obiekty-w-rubim-czesc-2-singletony-moduly-miksiny/</feedburner:origLink></entry><entry><title type="text">Zabawa w języki i skojarzenia :)</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/rubedo/~3/Ngo-gmyCRIA/" /><category term="Humor" /><category term="Języki" /><category term="programming" /><category term="fun" /><updated>2007-10-13T14:18:19-07:00</updated><id>http://blog.razorjack.net/2007/10/13/zabawa-w-jezyki-i-skojarzenia/</id><content type="html">&lt;p&gt;&lt;a href="http://radarek.jogger.pl"&gt;Radarek&lt;/a&gt; wymyślił &lt;a href="http://radarek.jogger.pl/2007/10/13/zabawa-w-jezyki-i-skojarzenia/"&gt;zabawę w języki i skojarzenia&lt;/a&gt;. Zasady są proste: do pewnych popularnych języków dopisujemy skojarzenia, zarówno te pozytywne, jak i negatywne. Let Mortal Kombat begin!&lt;/p&gt;
&lt;table class="langs"&gt;
&lt;tr&gt;
&lt;th&gt;Język&lt;/th&gt;
&lt;th&gt;Pozytywy&lt;/th&gt;
&lt;th&gt;Negatywy&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PHP&lt;/td&gt;
&lt;td&gt;dobry na stronę domową, znalazłem go nawet w konserwie&lt;/td&gt;
&lt;td&gt;wymusza siłą fizyczną burdel w kodzie&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;dorobek, enterprise, powaga&lt;/td&gt;
&lt;td&gt;hello world: piszę do tego 3 frameworki, 1254 linie konfiguracji w XML-u i obsługę 23 WyjątkówOCholernieDługichNazwach; smutni niewyspani programiści; Eclipse wolno działa!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pascal&lt;/td&gt;
&lt;td&gt;mój prawie pierwszy język, &lt;a href="http://razorjack.net/rubedo/images/posts/hacker.gif"&gt;sentyment&lt;/a&gt;, przejrzystość&lt;/td&gt;
&lt;td&gt;DJ Ango wylał go do zlewu :(&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;dzieciństwo, szybki, prawie zwięzły&lt;/td&gt;
&lt;td&gt;prawie zwięzły, "k*rwa, ten kod &lt;strong&gt;musi&lt;/strong&gt; działać!"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C++&lt;/td&gt;
&lt;td&gt;szybki, new + delete, obiektowy&lt;/td&gt;
&lt;td&gt;wcale nie jest obiektowy, "k*rwa, ten kod &lt;strong&gt;musi&lt;/strong&gt; działać!"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ruby&lt;/td&gt;
&lt;td&gt;pasja, elegancik, ma Rails i dobry marketing&lt;/td&gt;
&lt;td&gt;dobry marketing, "Twitter doesn't scale", każda metoda ma 5 aliasów, kaskady tysięcy end-ów&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Perl&lt;/td&gt;
&lt;td&gt;wyrażenia regularne&lt;/td&gt;
&lt;td&gt;język tylko do zapisu, równie czytelny co wyrażenia regularne&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;indentacje! (coś &lt;strong&gt;wspaniałego&lt;/strong&gt;), elegancik, DJ Ango&lt;/td&gt;
&lt;td&gt;prawie jak Ruby; tuple, dictionary - normalniej się tego nie dało nazwać?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C#&lt;/td&gt;
&lt;td&gt;podobno dobre IDE (Visual Studio)&lt;/td&gt;
&lt;td&gt;gdzie jest znak 'sharp' na klawiaturze?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JavaScript&lt;/td&gt;
&lt;td&gt;AJAX, jQuery, mało ma wspólnego z Javą :D&lt;/td&gt;
&lt;td&gt;alert("Prawy przycisk został zablokowany. Złodzieju.");&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assembler&lt;/td&gt;
&lt;td&gt;kilka rozkazów na krzyż a napiszę wszystko&lt;/td&gt;
&lt;td&gt;kilka książek z mapami portów, DMA i opisem przerwań a napiszę hello world&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;A jakie skojarzenia mają &lt;a href="http://offtopic.jogger.pl"&gt;Riddle&lt;/a&gt; i &lt;a href="http://blog.zabiello.com"&gt;Jarosław Zabiełło&lt;/a&gt;? :-)&lt;/p&gt;</content><feedburner:origLink>http://blog.razorjack.net/2007/10/13/zabawa-w-jezyki-i-skojarzenia/</feedburner:origLink></entry><entry><title type="text">Ruby on Rails vs .NET, PHP, Django</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/rubedo/~3/IGpLru8NCXQ/" /><category term="Humor" /><category term="Rails" /><category term="Techblog" /><category term="ruby" /><category term="railenvy" /><category term="humour" /><updated>2007-10-03T11:46:32-07:00</updated><id>http://blog.razorjack.net/2007/10/03/ruby-on-rails-vs-net-php-django/</id><content type="html">&lt;p&gt;Swojego czasu &lt;a href="http://radarek.jogger.pl"&gt;Radarek&lt;/a&gt; zapostował &lt;a href="http://radarek.jogger.pl/2007/05/22/hi-i-m-ruby-on-rails/"&gt;cztery przezabawne reklamówki&lt;/a&gt; Railsów. Dorzucam kolejne trzy. Miłej zabawy :)&lt;/p&gt;
&lt;h3&gt;Ruby on Rails vs .NET&lt;/h3&gt;
&lt;div class="yt-video"&gt;&lt;object width="425" height="350"&gt;&lt;param name="movie" value="http://www.youtube.com/v/528BCJiRkks"&gt;
&lt;param name="wmode" value="transparent"&gt;
&lt;embed src="http://www.youtube.com/v/528BCJiRkks" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"&gt;&lt;/object&gt;&lt;/div&gt;
&lt;h3&gt;Ruby on Rails vs PHP&lt;/h3&gt;
&lt;div class="yt-video"&gt;&lt;object width="425" height="350"&gt;&lt;param name="movie" value="http://www.youtube.com/v/GQXqWkWqnSw"&gt;
&lt;param name="wmode" value="transparent"&gt;
&lt;embed src="http://www.youtube.com/v/GQXqWkWqnSw" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"&gt;&lt;/object&gt;&lt;/div&gt;
&lt;h3&gt;Ruby on Rails vs Django&lt;/h3&gt;
&lt;div class="yt-video"&gt;&lt;object width="425" height="350"&gt;&lt;param name="movie" value="http://www.youtube.com/v/PLUS00QrYWw"&gt;
&lt;param name="wmode" value="transparent"&gt;
&lt;embed src="http://www.youtube.com/v/PLUS00QrYWw" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"&gt;&lt;/object&gt;&lt;/div&gt;
&lt;p&gt;Boskie :).&lt;/p&gt;</content><feedburner:origLink>http://blog.razorjack.net/2007/10/03/ruby-on-rails-vs-net-php-django/</feedburner:origLink></entry><entry><title type="text">Destruktory w Rubim, czyli ObjectSpace w akcji</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/rubedo/~3/YfhQcLgVetA/" /><category term="Ruby" /><category term="Ruby tricks" /><category term="Techblog" /><category term="tutorial" /><category term="programming" /><category term="object" /><category term="oop" /><category term="destructor" /><category term="objectspace" /><updated>2007-08-13T12:35:46-07:00</updated><id>http://blog.razorjack.net/2007/08/13/destruktory-w-rubim-czyli-objectspace-w-akcji/</id><content type="html">&lt;p&gt;Ruby (podobnie jak Java) nie posiada destruktorów. Może to się wydawać dziwne, ale okazuje się, że garbage collector załatwia za nas większość przykrych czynności, a pozostałe można zrealizować poprzez odpowiednią budowę aplikacji.&lt;/p&gt;
&lt;p&gt;A co, jeśli się uprzemy na destruktor lub po prostu metodę, która zostanie wykonana podczas niszczenia obiektu?&lt;/p&gt;
&lt;h3&gt;"Destruktory" dla egzemplarza&lt;/h3&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;one = "jeden"
two = "dwa"
three  = "trzy"

ObjectSpace.define_finalizer(one, lambda {|id| puts "\"one\" (#{id}) umarł w męczarniach" })
ObjectSpace.define_finalizer(two, lambda do |id|
                                    puts "\"two\" (#{id}) umarł w męczarniach"
                                  end)&lt;/code&gt;
&lt;/pre&gt;
&lt;pre&gt;
&lt;samp&gt;"two" (22660860) umarł w męczarniach
"one" (22660870) umarł w męczarniach&lt;/samp&gt;
&lt;/pre&gt;
&lt;p&gt;Trzeci też umarł, ale bardziej po cichu.&lt;/p&gt;
&lt;h3&gt;"Destruktory" dla klasy&lt;/h3&gt;
&lt;p&gt;Należy tutaj pamiętać, że definiujemy metodę klasy, a nie instancji.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;class TestClass
    def initialize
        ObjectSpace.define_finalizer(self, self.class.method(:finish_him!).to_proc)
    end
    def TestClass.finish_him!(id)
        puts "Flawless victory (#{id})"
    end
end

test = TestClass.new&lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;Wymuszenie garbage collectora&lt;/h3&gt;
&lt;p&gt;Moduł &lt;code&gt;ObjectSpace&lt;/code&gt; to nic innego jak interakcja z garbage collectorem, który działa w pełni poprawnie, ale automatycznie - nigdy nie wiemy, kiedy zostanie wywołany. Możemy jednak wywołać metodę &lt;code&gt;garbage_collect&lt;/code&gt;, by bardziej kontrolować pracę kolektora. Niestety, niszczymy wówczas wszystkie &lt;strong&gt;nieużywane&lt;/strong&gt; obiekty, ale to chyba nie jest duży (o ile w ogóle) mankament.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;class TestClass
    def initialize
        ObjectSpace.define_finalizer(self, self.class.method(:finish_him!).to_proc)
    end
    def TestClass.finish_him!(id)
        puts "Flawless victory (#{id})"
    end
end

def create_garbage
  TestClass.new
end

puts "Tworzę śmieci"
create_garbage
puts "Usuwam śmieci."
ObjectSpace.garbage_collect
puts "Gotowe."&lt;/code&gt;
&lt;/pre&gt;
&lt;pre&gt;
&lt;samp&gt;Tworzę śmieci
Usuwam śmieci.
Flawless victory (22660260)
Gotowe.&lt;/samp&gt;
&lt;/pre&gt;
&lt;p&gt;Warto pamiętać, że sprzątaniu ulegną śmieci powstałe w innych blokach niż ten, w którym poprzez wymuszenie wywoływany jest kolektor.&lt;/p&gt;
&lt;p&gt;Pozostaje tylko pytanie: &lt;strong&gt;po co wymuszać?&lt;/strong&gt; Jest to uzasadnione w naprawdę niewielu przypadkach. Innymi słowy: łapy precz. To tylko ciekawostka ;).&lt;/p&gt;
&lt;h3&gt;Ups...&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ObjectSpace&lt;/code&gt; posiada metodę &lt;code&gt;_id2ref&lt;/code&gt;, która zwraca referencję obiektu o podanym id.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="ruby"&gt;ObjectSpace._id2ref("test".object_id) # =&amp;gt; "test"&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Kiedy wywoływany jest finalizer, przekazywany jest mu id obiektu, który właśnie został zniszczony. Szukanie referencji po takim identyfikatorze nie zadziała, bo obiektu już prawdopodobnie nie ma. Warto o tym pamiętać, by uniknąć frustrujących błędów na przyszłość.&lt;/p&gt;</content><feedburner:origLink>http://blog.razorjack.net/2007/08/13/destruktory-w-rubim-czyli-objectspace-w-akcji/</feedburner:origLink></entry></feed>
