<?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">
 
 <title>Piotr Sarnacki</title>
 
 <updated>2012-03-25T17:21:57-07:00</updated>
 <id>http://piotrsarnacki.com/</id>
 <author>
   <name>Piotr Sarnacki</name>
   <email>drogus@gmail.com</email>
 </author>

 
 <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/piotrsarnacki-pl" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="piotrsarnacki-pl" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
   <title>Ruby debug</title>
   <link href="http://piotrsarnacki.com/2012/03/25/ruby-debug" />
   <published>2012-03-25T00:00:00-07:00</published>
   <updated>2012-03-25T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2012/03/25/ruby-debug</id>
   <content type="html">&lt;p&gt;Debugger to bardzo ciekawe narzędzie. Bywa niezwykle pomocny przy różnego rodzaju problemach ciężkich do rozwiązania manualnie, ale stosunkowo niewiele osób go używa. I nawet jeżeli ktoś wie o jego istnieniu i wie jak z niego korzystać, to bez takiego nawyku, wiedza ta na niewiele się zdaje. Wyobraźmy sobie taką sytuację. Próbuję poprawić buga znalezionego w kodzie, więc piszę padający test i zaczynam wprowadzanie poprawki. Nie widzę żadnej anomalii patrząc w kod, więc zaczynam przyglądać mu się bliżej. I wbrew temu co mogą niektórzy powiedzieć, ten kod wcale nie musi być skomplikowany. Ruby jest językiem dynamicznie typowanym, więc bardzo często błędy w kodzie biorą się stąd, że dostajemy nie to co chcieliśmy. W takim wypadku, nawet przy bardzo prostym kodzie, możemy siedzieć nad rozwiązaniem dłuższą chwilę, bo umysł podświadomie omija problematyczne miejsce zakładając, że dostajemy oczekiwaną wartość. Wracając do poprawki, zaczynam wstawiać w różne miejsca w kodzie metody, które wyświetlą dane pomocne przy debugowaniu, najczęściej robię to w taki sposób:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;method_with_bug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# ....&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Dzięki użyciu metody &lt;code&gt;p&lt;/code&gt; wyświetlona wartość będzie jeszcze potraktowana &lt;code&gt;inspect&lt;/code&gt;. Jeżeli kilka takich linijek załatwi sprawę, to jesteśmy w domu, ale jeżeli nie? Najprawdopodobniej po każdym kolejnym dodanym &lt;code&gt;p&lt;/code&gt; trzeba będzie uruchomić jeszcze raz test i być może okaże się, że będziemy musieli sprawdzić kilkanaście różnych rzeczy.&lt;/p&gt;

&lt;p&gt;Debugger ma nad tego typu podejściem dość dużą przewagę - kiedy idziemy przez kod wykonując kolejne kroki, łatwiej jest zauważyć, że coś jest nie tak. Wyświetlając część danych na ekran, nie wiemy tak naprawdę gdzie siedzi błąd, więc trochę strzelamy. I nie chcę przez to powiedzieć, że przy każdym błędzie opłaca się odpalać debuggera, Warto po prostu mieć nawyk jego używania. Ja z reguły zaczynam od wyświetlenia kilku wartości, które są najbardziej podejrzane i mogą mi pokazać gdzie leży błąd i jeżeli to nic nie daje, to używam debuggera. Nawyk jest tutaj bardzo ważny, bo bez niego najczęściej skończy się na dodawaniu kolejnych &lt;code&gt;puts&lt;/code&gt;, za każdym razem poprzedzonych myślą: "Ok, już wiem o co chodzi, tylko jeszcze wyświetlę ten obiekt, nie ma sensu włączać ruby-debug".&lt;/p&gt;

&lt;p&gt;Jeżeli chodzi o przeciwników debuggera, to jednym z głównych zarzutów jest: "Używając debuggera można uniknąć dyscypliny, która powinna towarzyszyć nam przy programowaniu i tym samym ominąć kroki, które bez debuggera trzeba by wykonać: pisanie testów, refactoring, dodanie dokumentacji". Zgadzam się z tym, że w przypadku kiedy kod jest marnej jakości, nie ma do niego testów i dokumentacji, to użycie debuggera bez poprawy sytuacji jest błędem. Problem w tym, że sytuacja nie zawsze tak wygląda i debugger wcale nie oznacza porzucenia testów (o czym będzie jeszcze za chwilę). Jeżeli chodzi o refaktoryzację, to też jestem zwolennikiem jak najczęstszego jej stosowania, ale to nie zawsze jest możliwe. Czasami ze względu na czas, czasami ze względu na dużą ilość kodu, czasami ze względu na małą wiedzę o kodzie (np. stara aplikacja, którą musimy utrzymywać). Ale żeby nie przedłużać zostawię tego typu rozważania i przejdę do sedna.&lt;/p&gt;

&lt;h2&gt;Do czegu używam debuggera?&lt;/h2&gt;

&lt;p&gt;Użycie debuggera wygląda z reguły w ten sposób, że w momencie kiedy chcemy poprawić jakiś problem w aplikacji, to wstawiamy tam &lt;code&gt;debugger&lt;/code&gt;, odpalamy aplikację (albo w przypadku aplikacji np. w railsach odświeżamy stronę w przeglądarce) i sprawdzamy co się dzieje. Ja prawie nigdy nie robię tego w ten sposób. Najczęściej używam debuggera przy okazji uruchamiania testów. Po znalezieniu problemu piszę padający test i wtedy jeżeli mam problem z poprawieniem kodu, uciekam się do użycia debuggera.&lt;/p&gt;

&lt;p&gt;Kolejny dość ciekawy przypadek, przy którym może się przydać debugger, to sprawdzenie co właściwie robi jakaś metoda z biblioteki, której używamy. Czasami kod zewnętrznych bibliotek nie jest zbyt dobry i wtedy szybko kapituluję, ale bardzo często już na samym początku sprawdzane są różnego rodzaju opcje, co daje dość dużą wiedzę o tym co można do niej przekazać. Jest to też bardzo przydatne przy rozszerzaniu jakiegoś API. Railsy 3 mają na przykład bardzo fajne API w kontrolerach i jeżeli wiemy co tam się dzieje, to można stosunkowo łatwo rozszerzyć dany kontroler. Przydaje się więc możliwość prześledzenia co właściwie dzieje się np. po wykonaniu metody &lt;code&gt;render&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Ostatnią rzeczą, przy której często używam debuggera, jest prototypowanie i wszelkiego rodzaju "szybkie" skrypty. Przypuśćmy na przykład, że chcemy sparsować jakąś stronę w nokogiri i wyciągnąć z niej jakieś informacje. Najczęściej bardzo przydaje się przy takich zadaniach interaktywna konsola, w której możemy wypróbować wyciąganie różnych elementów. Problem w tym, że jeżeli zaczniemy od irba, to musimy tam przekleić część używanego skryptu, który otwiera stronę, tworzy dokument nokogiri i ewentualnie inne kawałki, które są nam potrzebne. Z kolei metoda debugowania polegająca na wyświetlaniu wszystkiego na ekran jest dość uciążliwa, szczególnie po dwudziestym wykonaniu tego samego skryptu. Debugger łączy zalety obu podejść. Możemy skorzystać z przygotowanego kodu i zacząć w miejscu, w którym wszystko jest już ustawione i w tym samym czasie użyć interaktywnej konsoli.&lt;/p&gt;

&lt;h2&gt;Jak używać debuggera?&lt;/h2&gt;

&lt;p&gt;Na początku musimy debugger załadować, tzn. dodajć linijkę &lt;code&gt;require 'ruby-debug'&lt;/code&gt; gdzieś przed miejscem, które chcemy sprawdzić. Następnie w dowolnym miejscu w kodzie możemy wstawić metodę &lt;code&gt;debugger&lt;/code&gt;. Po uruchomieniu programu, w momencie kiedy interpreter dojdzie do tego miejsca, uruchomiona zostanie konsola, w której możemy sprawdzać aktualny stan procesu i poruszać się wykonując kolejne linijki kodu.&lt;/p&gt;

&lt;p&gt;Przed rozpoczęciem pracy polecam stworzyć plik &lt;code&gt;~/.rdebugrc&lt;/code&gt;, w którym można umieścić domyślny config, ja używam:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nb"&gt;set &lt;/span&gt;autolist &lt;span class="c"&gt;# automatyczne wyświetlenie kodu po każdej komendzie&lt;/span&gt;
&lt;span class="nb"&gt;set &lt;/span&gt;autoeval &lt;span class="c"&gt;# automatyczne wykonanie wpisanego kodu rubiego,&lt;/span&gt;
             &lt;span class="c"&gt;# bez tego można używać tylko metody `p` i jako&lt;/span&gt;
             &lt;span class="c"&gt;# argument podawać kod rubiego&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Jeżeli używasz rubiego w wersji 1.9.3, to na końcu tego posta są &lt;a href="#ruby-1-9-3"&gt;instrukcje uruchomić ruby-debug&lt;/a&gt; pod tą wersją.&lt;/p&gt;

&lt;h2&gt;Najczęściej używane komendy&lt;/h2&gt;

&lt;p&gt;Komendy mają z reguły skrócone wersje, dlatego będę tutaj używał wersji skróconych, a rozszerzenie podawał w nawiasie kwadratowym, np. &lt;code&gt;n[ext]&lt;/code&gt;. Jeżeli dana komenda przyjmuje jakieś argumenty, to będą one umieszczone po nazwie metody, nawias kwadratowy oznacza, że są opcjonalne.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;n[ext]&lt;/code&gt; &amp;mdash; wykonanie bieżącej linii przechodząc do następnej, bez wchodzenia głębiej&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s[tep]&lt;/code&gt; &amp;mdash; wejście w metodę znajdującą się w bieżącej linii. Nie jest to zawsze możliwe, gdyż nie możemy zbadać wnętrza metod biblioteki standardowej napisanych w C.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c[continue] [nnn]&lt;/code&gt; &amp;mdash; wznów wykonywanie programu aż do jego końca, napotkania breakpointa albo dojścia do linii &lt;code&gt;nnn&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;b[reak]&lt;/code&gt; &amp;mdash; ustawienie breakpointa (tzn. punktu, w którym wykonanie programu zostanie przerwane po wykonaniu instrukcji &lt;code&gt;continue&lt;/code&gt;), break może również przyjmować różne argumenty:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;b[reak] [file:]line [if expr]&lt;/code&gt; &amp;mdash; możemy jako argument podać linię, w której ma się ustawić breakpoint, domyślnie dotyczy to aktualnie wykonywanego pliku, ale można też użyć innego, np.: &lt;code&gt;break other_file.rb:12&lt;/code&gt;. Można również podać opcjonalny warunek, np. &lt;code&gt;break 12 if i == 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;b[reak] class(.|#)method [if expr]&lt;/code&gt; &amp;mdash; zatrzymanie na wykonaniu wybranej metody na wybranej klasie (wtedy używamy kropki, np. &lt;code&gt;Thread.current&lt;/code&gt;) lub instancji (wtedy używamy &lt;code&gt;#&lt;/code&gt;, np. &lt;code&gt;Foo#foo&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;info&lt;/code&gt; &amp;mdash; wyświetla różnego rodzaju informacje, najczęściej używane jest do wyświetlania breakpointów: &lt;code&gt;info b&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;d[elete][ nnn ]&lt;/code&gt; &amp;mdash; usuwa wskazany breakpoint (jeżeli jest podany numer &lt;code&gt;nnn&lt;/code&gt;) lub wszystkie jeżeli wywołane bez żadnych argumentów&lt;/li&gt;
&lt;li&gt;&lt;code&gt;backtrace&lt;/code&gt; &amp;mdash; nazwa mówi sama za siebie ;)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Pełna lista komend jest dostępna po wykonaniu &lt;code&gt;help&lt;/code&gt;, dokładniejszy opis każdej metody jest dostępny po wywołaniu &lt;code&gt;help nazwa-instrukcji&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To by było na tyle jeżeli chodzi o teorię, teraz przejdźmy do praktyki. Ze względu na to, że opisywanie tego jest lekko uciążliwe i pewnie średnio czytelne, wreszcie miałem okazję, żeby nagrać screencasta. Przejdę w nim przez kilka podstawowych przypadków, które napotkamy przy debugowaniu. Jeżeli będę miał trochę wolnego czasu, to dorzucę jeszcze drugą część, w której pokażę kilka scenariuszy wymienionych powyżej.&lt;/p&gt;

&lt;iframe src="http://player.vimeo.com/video/39151944" width="500" height="313" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen&gt;&lt;/iframe&gt;




&lt;h2 id="ruby-1-9-3"&gt;Ruby 1.9.3&lt;/h2&gt;


&lt;p&gt;Niestety gemy, które są wymagane do działania &lt;code&gt;ruby-debug&lt;/code&gt; nie są cały czas publicznie wypuszczone, więc nie możemy ich prosto wylistować w &lt;code&gt;Gemfile&lt;/code&gt;. Dlatego hostuję mały serwer rubygems, na którym je umieściłem. Jeżeli używasz rubiego w wersji 1.9.3, to używając bundlera można dopisać do &lt;code&gt;Gemfile&lt;/code&gt; taki kod:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# source dla rubygems też powinno zostać&lt;/span&gt;
&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://gems.developers.stoliczku.pl&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:development&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;linecache19&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0.5.13&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ruby-debug-base19&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0.11.26&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ruby-debug19&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:require&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ruby-debug&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Dodakowo, jeżeli używamy RVM, to trzeba podać bundlerowi ścieżkę do źródeł:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;bundle config build.linecache19 --with-ruby-include&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;${MY_RUBY_HOME/rubies/src}&amp;quot;&lt;/span&gt;
bundle config build.ruby-debug-base19 --with-ruby-include&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;${MY_RUBY_HOME/rubies/src}&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Trochę tego niestety jest, ale po wykonaniu tych kroków wszystko działa bardzo dobrze.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Mountable engines</title>
   <link href="http://piotrsarnacki.com/2010/09/09/mountable-engines" />
   <published>2010-09-09T00:00:00-07:00</published>
   <updated>2010-09-09T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2010/09/09/mountable-engines</id>
   <content type="html">&lt;p&gt;Jak prawdopodobnie niektórzy wiedzą, podczas wakacji pracowałem nad projektem &amp;#8220;Rails mountable apps&amp;#8221; w ramach &lt;a href="http://rubysoc.org"&gt;Ruby Summer of Code&lt;/a&gt;. Projekt zakładał umożliwienie montowania aplikacji napisanych w Railsach w innych aplikacjach Railsowych. W tym momencie nie jest to jeszcze niestety możliwe, ale istnieją już solidne podwaliny w postaci &amp;#8220;mountable engines&amp;#8221;. Wspólnie z osobami z rails core team zdecydowaliśmy, że najlepiej będzie na razie przetestować w boju montowalne engine&amp;#8217;y i zobaczyć w jaki sposób programiści ich używają, a później popracować na montowalnymi aplikacjami.&lt;/p&gt;
&lt;p&gt;Czym są montowalne engine&amp;#8217;y? Po co właściwie przez większą część wakacji siedziałem przy kodzie railsów skoro engine&amp;#8217;y już istnieją w railsach 2.x i stosunkowo łatwo można napisać plugin z kontrolerami, modelami i widokami? Implementacja engine&amp;#8217;ów w railsach 2.x ma kilka sporych wad. Pierwszą z nich jest zarządzanie routesami. Użytkownik nie ma żadnego wyboru w kwestii tego pod jakim urlem engine będzie się znajdował. Jeżeli twórca engine&amp;#8217;u użyje routesów, to jedynym prostym sposobem na zmianę zachowania jest sforkowanie i ręczna zmiana kodu. Następnym problemem jest enkapsulacja. Kontrolery w obecnej wersji engine&amp;#8217;ów zachowują się tak, jakby po prostu przekopiować je ręcznie do aplikacji. Oznacza to, że każdy kontroler dostaje automatycznie wszystkie helpery z aplikacji. W niektórych przypadkach może to być pożądane zachowanie, ale przy użyciu engine&amp;#8217;ów napisanych przez innych, wiąże się to z ryzykiem kolizji nazw. Kolejne ograniczenie to rack middlewares. Nie można w prosty sposób dodać middleware&amp;#8217;ów, które będą podpięte tylko do engine&amp;#8217;u. Jedyny sposób to ręczne sprawdzanie ścieżki (np. &lt;code&gt;env["PATH_INFO"] =~ /^\/tolk/&lt;/code&gt;, niezbyt wygodne). W railsach 3.0 można co prawda używać aplikacji racka w kontrolerach, ale jest to dość ograniczone, tzn. wpinamy się już po przejściu przez router i cały middleware stack. Ostatnią rzeczą, o której chciałbym wspomnieć, jest wygoda. Ze względu na konflikty nazw, najbezpieczniej jest umieścić wszystkie modele, kontrolery i helpery w module. Pociąga to za sobą jednak kilka niedogodności. Np. w tym momencie wszystkie helpery generujące urle trzeba prefixować, np. &lt;code&gt;blog_posts_path&lt;/code&gt;. Tak samo jest z modelami, domyślna nazwa wygenerowana przez &lt;code&gt;ActiveModel::Naming&lt;/code&gt;, to &lt;code&gt;blog_post&lt;/code&gt; dla klasy &lt;code&gt;Blog::Post&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Podczas pracy w RSoC próbowałem poprawić te problemy. W efekcie zaszło dość dużo zmian w zachowaniu engine&amp;#8217;ów:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;engine jest teraz aplikacją rack&lt;/li&gt;
	&lt;li&gt;posiada własny middleware stack&lt;/li&gt;
	&lt;li&gt;posiada własne routesy&lt;/li&gt;
	&lt;li&gt;pliki z katalogu public mogą być serwowane z użyciem &lt;code&gt;ActionDispatch::Static&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;może posiadać swoje własne pluginy&lt;/li&gt;
	&lt;li&gt;w prosty sposób można umieścić modele i kontrolery w module&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Co się zmieniło?&lt;/h2&gt;
&lt;h3&gt;Izolowany lub współdzielony engine&lt;/h3&gt;
&lt;p&gt;Do tej pory pliki z katalogu &amp;#8216;app&amp;#8217;, znajdującego się w katalogu z engine&amp;#8217;em, zachowywały się mniej więcej tak, jakby wkleić je do katalogu app w aplikacji. Skutek tego był taki, że dostawały wszystkie helpery (w tym także helpery do routesów) z aplikacji. Dla niektórych engine&amp;#8217;ów jest to pożądane zachowanie (np. Devise), ale istnieje wiele przypadków, w których nie działa to zbyt dobrze. W przypadku użycia jakiegoś dużego engine&amp;#8217;u, takiego jak np. forum, istnieje duża szansa wystąpienia konfliktów (np. takie same nazwy metod w helperach). Dlatego od railsów 3.1 wprowadzona zostanie możliwość oznaczenia, że Engine jest izolowany używając metody isolate_namespace:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Blog&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt;
    &lt;span class="n"&gt;isolate_namespace&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Przy takiej konfiguracji kontrolery znajdujące się w module Blog, dostaną tylko i wyłącznie helpery i routesy znajdujące się w tym samym namespace.&lt;/p&gt;
&lt;h3&gt;Montowanie engine&amp;#8217;u&lt;/h3&gt;
&lt;p&gt;Engine można zamontować w aplikacji tak jak każdą aplikację Rack, używając metody mount:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;mount&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/blog&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:as&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;blog&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Przy takiej definicji engine będzie dostępny w &lt;code&gt;/blog&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Routes&lt;/h3&gt;
&lt;p&gt;Z powodu tego, że Engine może teraz posiadać swoje własne routes&amp;#8217;y, czasami zajdzie potrzeba odwołania się do routesów engine&amp;#8217;u z aplikacji i na odwrót. Dlatego stworzone zostały helpery, ktore to umożliwiają. Helper &lt;code&gt;main_app&lt;/code&gt; służy do odwołania się do routesów aplikacji, a helpery dla zamontowanych engine&amp;#8217;ów zależą od opcji &lt;code&gt;:as&lt;/code&gt; użytej w powyższym przykładzie. Np. dla powyższego przykładu, helper do engine&amp;#8217;u &lt;code&gt;Blog::Engine&lt;/code&gt; będzie nazywał się &lt;code&gt;blog&lt;/code&gt;. Dzięki temu można napisać np.:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posts_path&lt;/span&gt;
&lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root_path&lt;/span&gt;

&lt;span class="n"&gt;main_app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login_path&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Oczywiście takie helpery muszą być użyte tylko przy odwoływaniu się do innych routesów, np. kiedy chcemy wygenerować ścieżkę do routesów bloga z kontrolera aplikacji.&lt;/p&gt;
&lt;h3&gt;Namespacing&lt;/h3&gt;
&lt;p&gt;Żeby uniknąć konfliktów, montowalny engine musi być zamknięty w namespace. W normalnych warunkach skutkuje to tym, że wszędzie trzeba dodawać prefix (np. nazwa dla modelu &lt;code&gt;Blog::Post&lt;/code&gt;, to w normalnych warunkach &lt;code&gt;blog_post&lt;/code&gt;, route to &lt;code&gt;blog_posts_path&lt;/code&gt; itp.&lt;/p&gt;
&lt;p&gt;Tego typu prefixy w zwykłym enginie są używane w celu uniknięcia konfliktów. W izolowanym enginie najczęściej nie ma takiego problemu. Np. nie opłaca się w środku engine&amp;#8217;u pisać &lt;code&gt;blog_posts_path&lt;/code&gt; zamiast &lt;code&gt;posts_path&lt;/code&gt;. Tak samo podczas wysyłania parametrów z formularza, nie opłaca się używać &lt;code&gt;params[:blog_post]&lt;/code&gt; zamiast &lt;code&gt;params[:post]&lt;/code&gt;. Engine oznaczony jako izolowany pozwala na ominięcie tych prefixów w większości miejsc.&lt;/p&gt;
&lt;h3&gt;Migracje&lt;/h3&gt;
&lt;p&gt;Jeżeli używacie w swojej aplikacji ActiveRecorda, to przydadzą się też migracje. Obecne podejście do migracji engine&amp;#8217;ów, to kopiowanie migracji do katalogu aplikacji z przepisaniem timestampów. Dla przykładu mamy w enginie &lt;code&gt;Blog::Engine&lt;/code&gt; migrację &lt;code&gt;20091212_create_posts.rb&lt;/code&gt;. Po wykonaniu w dniu 2010-10-10 komendy:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;rake railties:copy_migrations
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;zostanie ona przekopiowana do katalogu aplikacji z nazwą &lt;code&gt;20101010_create_posts.blog.rb&lt;/code&gt;. Wybraliśmy ten sposób z kilku powodów:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;przed wykonaniem migracji warto na nią spojrzeć, co po przekopiowaniu do katalogu aplikacji jest dużo łatwiejsze&lt;/li&gt;
	&lt;li&gt;tego typu format jest zgodny z dotychczasowym, więc nie trzeba było zmieniać tabeli &lt;code&gt;schema_migrations&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;dzięki przepisywaniu timestampów nie wystąpi sytuacja, w której po przekopiowaniu migracji, będą one wykonywane przed migracjami aplikacji (ma to znaczenie kiedy w czasie rozwijania aplikacji dodamy jakieś pole do tabeli z engine&amp;#8217;u)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Pliki statyczne&lt;/h3&gt;
&lt;p&gt;Ostatni problem jaki trzeba było rozwiązać, to serwowanie statycznych plików. Są na to obecnie 2 sposoby:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;utworzenie symlinków do katalogów public w enginach, służy do tego task &lt;code&gt;rake railties:create_symlinks&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;wykorzystanie &lt;code&gt;ActionDispatch::Static&lt;/code&gt; (middleware, który serwuje pliki statyczne)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Tutorial&lt;/h2&gt;
&lt;p&gt;Najłatwiej pokazać wszystkie zmiany w praktyce, więc pokuszę się o napisanie prostego engine&amp;#8217;u. Do wygenerowania tego engine&amp;#8217;u użyjemy &lt;a href="http://github.com/josevalim/enginex"&gt;enginex&lt;/a&gt; napisanego przez José Valima. Niech nie zmyli was nazwa. Enginex został co prawda stworzony w celu prostego generowania engine&amp;#8217;ów, ale z powodzeniem można go używać do wygenerowania szkieletu dla dowolnego gema, który będzie miał za zadanie działać z railsami 3. Na początek zainsalujmy enginex:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;gem install enginex
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Po wykonaniu komendy &lt;code&gt;enginex --help&lt;/code&gt; pokażą się dostępne opcje. W chwili obecnej można ustawić jedynie test framework (rspec lub test unit). Zacznijmy z domyślnym test unit:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;enginex blog
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Enginex powinien stworzyć w tym momencie katalog blog, w którym będą znajdować się pliki z reguły pojawiające się w gemach (lib, test, &lt;span class="caps"&gt;README&lt;/span&gt; itp) oraz coś nowego &amp;#8211; aplikacja railsów w katalogu &lt;code&gt;test/dummy&lt;/code&gt;. Po co nam aplikacja railsów? Ze względu na to, że engine (czy też inny rodzaj gemu) będzie pracował z railsami i najłatwiej testować go w realnym środowisku railsów.&lt;/p&gt;
&lt;p&gt;Pierwsze co trzeba będzie zmienić, to plik Gemfile. Obecna wersja enginex zakłada, że użyte będą railsy 3.0. Całkiem słuszne założenie dla większości projektów, ale żeby korzystać z dobrodziejstw najnowszej wersji engine&amp;#8217;ów, trzeba będzie użyć wersji z githuba. Co więcej, trzeba będzie użyć wersji z mojego forka railsów na githubie. Co prawda większość rzeczy, nad którymi pracowałem, jest już w oficjalnym repozytorium railsów, ale cały czas poprawiam różne rzeczy i ze względu na to, że nie mam możliwości commitowania, trzeba czasami poczekać trochę na akceptację i dorzucenie do oficjalnej wersji.&lt;/p&gt;
&lt;p&gt;Dlatego zmieniam Gemfile, żeby wyglądało w ten sposób (przy okazji dorzuciłem też &lt;code&gt;factory_girl_rails&lt;/code&gt;, przyda się później):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;http://rubygems.org&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;rails&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:git&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;git://github.com/drogus/rails.git&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:branch&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;engines&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;arel&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:git&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;git://github.com/rails/arel.git&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;factory_girl&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;capybara&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;gt;= 0.3.9&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;sqlite3-ruby&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:require&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;sqlite3&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;RUBY_VERSION&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;1.9&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;ruby-debug&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;gt;= 0.10.3&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Teraz tylko &lt;code&gt;bundle install&lt;/code&gt; w konsoli i można zacząć pracować. Dla pewności, że środowisko się poprawnie wczytuje, można jeszcze odpalić testy: &lt;code&gt;rake test&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Jeżeli testy przechodzą, to można się zabrać za pisanie kodu. Od czego zacząć? Od testów oczywiście ;-) Po przejrzeniu katalogu test, łatwo zauważyć, że znajduje się tam katalog integration. Wygenerowane przez enginex testy korzystają z Capybary, dzięki czemu można łatwo pisać testy integracyjne. Zacznijmy od wyświetlania postów na blogu. Stwórzmy plik &lt;code&gt;test/integration/posts_integration_test.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;test_helper&amp;#39;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostsIntegrationTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IntegrationCase&lt;/span&gt;
  &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;I can see blog posts on blog&amp;#39;s root page&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Factory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Awesome!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:body&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;All work and no fun makes Jack a dull boy&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;visit&lt;/span&gt; &lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root_path&lt;/span&gt;
    &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_content?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Awesome!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_content?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;All work and no fun makes Jack a dull boy&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Po uruhomieniu rake test zobaczymy: &lt;code&gt;ArgumentError: No such factory: post&lt;/code&gt;. Brakuje nam wpisu post w factories. Dodajmy go więc:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# test/factories.rb&lt;/span&gt;

&lt;span class="no"&gt;Factory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;define&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:class&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Blog::Post&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;My post&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;It&amp;#39;s just a post, man...&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Do pliku &lt;code&gt;test/test_helper.rb&lt;/code&gt; dodajemy również &lt;code&gt;require "factories"&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Tym razem po uruchomieniu testów, błąd powinien wyglądać mniej więcej tak: &lt;code&gt;NameError: uninitialized constant Blog::Post&lt;/code&gt;. Nic dziwnego, nie ma jeszcze w kodzie żadnego modelu. Model jest oczywiście namespace&amp;#8217;owany i dlatego używamy &lt;code&gt;Blog::Post&lt;/code&gt; zamiast normalnego &lt;code&gt;Post&lt;/code&gt;. Nie pozostaje nic innego jak dodać taki model:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# app/models/blog/post.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Blog&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Musimy dodać także migrację i sprawić, żeby była uruchamiana przy każdym odpaleniu testów (między innymi przez takie problemy uważam, że mongodb, to przyszłość modularnych aplikacji ;-). Powstają tutaj dwa problemy. Pierwszym z nich są generatory. W tej chwili niestety generatory nie działają w katalogu engine&amp;#8217;u. Drugi problem jest związany z uruchamianiem migracji, nie ma obecnie żadnego mechanizmu, który automatyzuje wykonywanie migracji dla engine&amp;#8217;u w testach. Ale co to dla nas?&lt;/p&gt;
&lt;p&gt;Stwórzmy migrację:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# db/migrate/20100631120813_create_blog_posts.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateBlogPosts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;up&lt;/span&gt;
    &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:blog_posts&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ss"&gt;:body&lt;/span&gt;

      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamps&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;down&lt;/span&gt;
    &lt;span class="n"&gt;drop_table&lt;/span&gt; &lt;span class="ss"&gt;:blog_posts&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Teraz wystarczy stworzyć plik &lt;code&gt;test_helper&lt;/code&gt; i wkleić pod komentarzem &amp;#8220;#Run any available migrations&amp;#8221; poniższy kod:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# Run any available migrations&lt;/span&gt;
&lt;span class="no"&gt;FileUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;../dummy/db/test.sqlite3&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;FileUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;../dummy/db/migrate/*.blog.rb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;FileUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkdir_p&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;../dummy/db/migrate/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;../dummy/db/migrate/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:blog&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;../../db/migrate/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migrator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;migrate&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;../dummy/db/migrate/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Kod ten usuwa wszystkie wcześniej skopiowane migracje i bazę sqlite3, po czym kopiuje aktualne migracje z katalogu db/migrate i je uruchamia.&lt;/p&gt;
&lt;p&gt;Ok&amp;#8230; Uruchamiany testy i: &lt;code&gt;NameError: uninitialized constant Blog::Post&lt;/code&gt;. Pomimo tego, że model jest już stworzony, w testach dalej go nie widać. Dlaczego tak się dzieje? Nigdzie nie powiedzieliśmy, że aplikacja ma wczytać nasz Engine&amp;#8230; a właściwie nawet nie mamy jeszcze engine&amp;#8217;u! Po sprawdzeniu pliku &lt;code&gt;lib/blog.rb&lt;/code&gt; można zauważyć, że jest tam jedynie:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Blog&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Enginex nie robi żadnych założeń co do tego jakiego rodzaju gem chcemy zrobić. Trzeba to skorygować, w pliku &lt;code&gt;lib/blog/engine.rb&lt;/code&gt; wstawmy kod engine&amp;#8217;u:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Blog&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt;
    &lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;a w &lt;code&gt;lib/blog.rb&lt;/code&gt; załadujmy powyższy kod:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;blog/engine&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Pojawia się tutaj opisywana wcześniej metoda &lt;code&gt;namespace&lt;/code&gt;, która sprawia, że Engine jest izolowany.&lt;/p&gt;
&lt;p&gt;Tym razem po uruchomieniu testów dostaniemy taki błąd: &lt;code&gt;NameError: undefined local variable or method `blog' for #&amp;lt;PostsIntegrationTest:0x000001041136e0&amp;gt;&lt;/code&gt;. Chodzi o linijkę &lt;code&gt;blog.root_path&lt;/code&gt; w teście. Brakuje nam helperow do routesów zamontowanych engine&amp;#8217;ów. W przyszłości takie helpery będą prawdopododbnie automatycznie dołączane do testów, ale w tym momencie niestety trzeba będzie zrobić to samemu. W pliku &lt;code&gt;test/support/integration_case.rb&lt;/code&gt; w ciele klasy &lt;code&gt;ActiveSuppoer::IntegrationCase&lt;/code&gt; należy wkleić:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mounted_helpers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;W tym momencie helpery powinny już być dostępne, ale po uruchomieniu testów dalej dostaniemy ten sam błąd. Dlaczego? Jak już pisałem są to helpery do wszystkich &lt;strong&gt;zamontowanych&lt;/strong&gt; engine&amp;#8217;ów. A tak się składa, że &lt;code&gt;Blog::Engine&lt;/code&gt; nie jest jeszcze nigdzie zamontowany. Poprawmy to montując engine w aplikacji w pliku &lt;code&gt;test/dummy/config/routes.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;mount&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/blog&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:as&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;blog&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Po uruchomieniu testów powyższy błąd powinien zniknąć, ale za to dostajemy kolejny związany z routesami: &lt;code&gt;NoMethodError: undefined method `root_path' for #&amp;lt;ActionDispatch::Routing::RoutesProxy:0x00000101e9bd10&amp;gt;&lt;/code&gt;. Wynika to z faktu, że nie zdefiniowaliśmy jeszcze żadnych routesów w enginie. Aby to zrobić wystarczy wkleić taki oto kod w pliku &lt;code&gt;config/routes.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="ss"&gt;:to&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;posts#index&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Routesy powinny już działać poprawnie, ale testy podpowiadają nam, że brakuje kontrolera: &lt;code&gt;ActionController::RoutingError: uninitialized constant Blog::PostsController&lt;/code&gt;. Kontroler powinien wyglądać mniej więcej tak:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Blog&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
      &lt;span class="vi"&gt;@posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;i znajdować się w pliku &lt;code&gt;app/controllers/blog/posts_controller.rb&lt;/code&gt;. Ciekawe w tym kodzie jest to, że nie musimy używać &lt;code&gt;Blog::Post&lt;/code&gt;, wystarczy samo &lt;code&gt;Post&lt;/code&gt;, ponieważ już jesteśmy w odpowiednim namespace. Dlatego właśnie używam dłuższej formy: &lt;code&gt;module Foo; class Bar; end; end&lt;/code&gt;. Przy zastosowaniu skróconego zapisu nie dałoby się tak zrobić:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PostsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="vi"&gt;@posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; ArgumentError: Blog is not missing constant Post!&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;A wracając do samego engine&amp;#8217;u&amp;#8230; Jak pewnie niektórzy się domyślą, tym razem testy będą krzyczeć o braku template&amp;#8217;u. Stwórzmy więc &lt;code&gt;app/views/blog/posts/index.html.erb&lt;/code&gt;, a w nim wyświetlmy pobrane w kontrolerze posty:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="no"&gt;Posts&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&amp;gt;&lt;/span&gt;

&lt;span class="sr"&gt;&amp;lt;% @posts.each do |post| %&amp;gt;&lt;/span&gt;
&lt;span class="sr"&gt;  &amp;lt;h3&amp;gt;&amp;lt;%= post.title %&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;post&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="sx"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="sx"&gt;  &amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;% end &lt;/span&gt;&lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;W tym momencie testy powinny przechodzić! Aplikacja już działa, ale przydałby się jakiś layout. Do pliku &lt;code&gt;app/views/layouts/blog.html.erb&lt;/code&gt; skopiujcie poniższy kod:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="o"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="n"&gt;doctype&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt; &lt;span class="n"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/title&amp;gt;&lt;/span&gt;
&lt;span class="sr"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="sr"&gt;    &amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;main&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="sx"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="sx"&gt;    &amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;footer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/footer&amp;gt;&lt;/span&gt;
&lt;span class="sr"&gt;  &amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/body&amp;gt;&lt;/span&gt;
&lt;span class="sr"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Teraz trzeba ten layout ustawić w kontrolerze. Z reguły robi się to w &lt;code&gt;ApplicationController&lt;/code&gt; i żeby pozostać &lt;span class="caps"&gt;DRY&lt;/span&gt;, w enginie też można tak zrobić (z tą różnicą, że tutaj będzie to &lt;code&gt;Blog::ApplicationController&lt;/code&gt;, który będzie wyglądał tak:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Blog&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
    &lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;blog&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Wystarczy jeszcze tylko zmienić &lt;code&gt;Blog::PostsController&lt;/code&gt;, żeby dziedziczył z ApplicationController i layout powinien poprawnie się wyświetlać:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Ok, super, ale nie można przecież rozwijać aplikacji opierając się tylko na testach. Tutaj również możemy skorzystać z testowej aplikacji. Wejdźcie do katalogu test/dummy, wykonajcie migracje &lt;code&gt;rake db:migrate&lt;/code&gt;, włączcie konsolę &lt;code&gt;script/rails c&lt;/code&gt;, a w konsoli stwórzcie jakieś posty:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;First post&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:body&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;This is awesome!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Missed missy&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:body&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Missy needs your help... :(&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Teraz można już włączyć serwer, zobaczyć jak to wygląda i dodać style. Wystarczy uruchomić serwer (&lt;code&gt;script/rails s&lt;/code&gt;) i wejść na &lt;a href="http://localhost:3000/blog"&gt;http://localhost:3000/blog&lt;/a&gt;. Powinniście zobaczyć tam dodane przed chwilą posty. W layoucie &lt;code&gt;blog.html.erb&lt;/code&gt; można teraz wstawić link do cssów:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="erb"&gt;&lt;span class="x"&gt;  &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;stylesheet_link_tag&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;style&amp;quot;&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;a w pliku &lt;code&gt;public/stylesheets/style.css&lt;/code&gt; (w katalogu engine&amp;#8217;u, nie aplikacji) na przykład:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="css"&gt;&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#A80000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Po uruchomieniu serwera tytuł bloga powinien zmienić kolor na czerwony. Serwowanie plików statycznych działa bez wprowadzania żadnych zmian, ponieważ w trybie development domyślnie uruchamiany jest &lt;code&gt;ActionDispatch::Static&lt;/code&gt;. W środowisku production, trzeba ustawić &lt;code&gt;config.serve_static_assets&lt;/code&gt; na true, lub wykonać &lt;code&gt;rake railties:create_symlinks&lt;/code&gt; w celu stworzenia symlinków do katalogów public w engine&amp;#8217;ach.&lt;/p&gt;
&lt;p&gt;Na tym chciałbym zakończyć ten tutorial. W razie czego aplikację umieściłem na W kolejnych odsłonach napiszę trochę więcej o tym jak dalej obchodzić się z tego typu engine&amp;#8217;ami. Dla podsumowania, po przeczytaniu tej notki powinniście wiedzieć jakie są różnice pomiędzy starszą wersją engine&amp;#8217;ów, a tym co obecnie siedzi w &lt;code&gt;rails/master&lt;/code&gt; oraz umieć stworzyć prosty montowalny engine.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Sprostowanie</title>
   <link href="http://piotrsarnacki.com/2010/05/16/zsprostowanie" />
   <published>2010-05-16T00:00:00-07:00</published>
   <updated>2010-05-16T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2010/05/16/zsprostowanie</id>
   <content type="html">&lt;p&gt;Ze względu na dziwne właściwości google readera w ostatnich godzinach zrobiło się niezłe zamieszanie z postami w RSSie. Zmieniłem adres bloga i naiwnie pomyślałem, że jeżeli podmienię w feedburnerze adres do starego feeda, to google reader zupdate&amp;#8217;uje po prostu treść. Nic bardziej mylnego. Okazuje się, że google reader nie kasuje z cache&amp;#8217;a żadnych postów. Dodatkowo nic go nie obchodzą daty, które są ustawione w RSSie, posty układa tak jak mu się podoba, a nie tak jak mu mówi atrybut &amp;#8216;published&amp;#8217; w XMLu. Z tego powodu jakieś stare posty sprzed 3 lat pojawiły się na samej górze w czytniku. Pech sprawił, że wśród kilkudziesięciu postów na drugim miejscu był post sprzed 3 lat, w którym pisałem o rzuceniu pracy i studiów. Pragnę wyjaśnić, że jest to już daleka przeszłość i moje obecne studia i praca mają się niezwykle dobrze. Przepraszam za zamieszanie, od teraz będę miał na uwadze, że cache google readera jest nieugięty i choćbyście nawet bardzo chcieli, to nie uda się usunąć postów, które się w nim znajdują.&lt;/p&gt;
&lt;p&gt;Jeżeli ktoś natrafi na ten problem, to od razu mogę podrzucić rozwiązanie. W googlu można znaleźć w ciągu kilku sekund przynajmniej kilkanaście tematów na forach i postów dotyczących tego problemu. W supporcie googla w jednym z tematów tego typu odpowiada nawet pracownik googla, który twierdzi, że jeżeli post już dodał się do cache&amp;#8217;u, to &amp;#8220;niezwykle trudno go stamtąd usunąć&amp;#8221;. Prawdopodobnie jestem za cienki w łokciach, żeby się wypowiadać negatywnie o inżynierach googla, ale trochę mnie dziwi fakt, że bez problemu można w google web developer tools usunąć cache całej strony, która ma kilkadziesiąt tysięcy zaindeksowanych plików, ale nie można co jakiś czas odświeżyć cache&amp;#8217;a RSSów. Przechodząc do samego rozwiązania. Posty nie mogą zostać usunięte, ale za to można zmienić ich treść. Dlatego jeżeli zachowane zostanie id posta, to google weźmie to za update. W ten sposób można podmienić zawartość niechcianych postów na coś w stylu &amp;#8220;treść usunięta&amp;#8221;. Życzę powodzenia w nierównej walce z cache&amp;#8217;em googla!&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Zmiana adresu</title>
   <link href="http://piotrsarnacki.com/2010/05/16/zmiana-adresu" />
   <published>2010-05-16T00:00:00-07:00</published>
   <updated>2010-05-16T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2010/05/16/zmiana-adresu</id>
   <content type="html">&lt;p&gt;Od dłuższego czasu nie pisałem nic na blogu. Było to częściowo spowodowane tym, że chciałem zmienić jego adres i połączyć z drugim blogiem (polski blog miał adres blog.drogomir.com, angielski drogomir.com/blog, kto w ogóle wymyśla taką dziwną konfigurację? nawet ja się najczęściej gubiłem pod którym adresem jest który blog). Ilekroć siadałem do edytora, żeby napisać coś sensownego, do głowy przychodziła mi myśl: przenieś w końcu te blogi! Zamykałem edytor, otwierałem internety, żeby zobaczyć na co zmigrować oba blogi i&amp;#8230; po 10 minutach dawałem sobie spokój. W ostatnich dniach zebrałem się wreszcie, żeby przenieść oba blogi na nowy adres. Jak łatwo możecie zauważyć nowy adres to &lt;a href="http://piotrsarnacki.com"&gt;piotrsarnacki.com&lt;/a&gt;. Posty w języku polskim są dostępne pod adresem &lt;a href="http://piotrsarnacki.com/pl"&gt;piotrsarnacki.com/pl&lt;/a&gt;. Nowe adresy dla RSSów to odpowiednio:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://feeds.feedburner.com/piotrsarnacki-all"&gt;feeds.feedburner.com/piotrsarnacki-all&lt;/a&gt; &amp;#8211; wszystkie posty&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://feeds.feedburner.com/piotrsarnacki"&gt;feeds.feedburner.com/piotrsarnacki&lt;/a&gt; &amp;#8211; posty angielskie&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://feeds.feedburner.com/piotrsarnacki-pl"&gt;feeds.feedburner.com/piotrsarnacki-pl&lt;/a&gt; &amp;#8211; posty polskie&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Posty powinny być też dostępne pod obecnym adresem &lt;span class="caps"&gt;RSS&lt;/span&gt;, ale zachęcam do zmiany na nowy adres :)&lt;/p&gt;
&lt;p&gt;Od strony technicznej: blog jest &lt;a href="http://github.com/drogus/drogus.github.com"&gt;hostowany na githubie&lt;/a&gt; z pomocą &lt;a href="http://github.com/mojombo/jekyll"&gt;jekylla&lt;/a&gt;. Na początku miałem wątpliwości czy będzie to dobry pomysł. Jekyll jest niezwykle prosty w użyciu i ma przy tym spore możliwości, ale przy okazji traci się trochę elastyczności na rzecz wygenerowanego statycznego HTMLa. Tylko&amp;#8230; tak naprawdę, po co mi coś więcej? Całego bloga trzymam w repozytorium gita, więc łatwo jest śledzić historię zmian. Mam darmowy hosting na githubie, więc nie muszę się już martwić o utrzymanie dla blogów jakiegokolwiek serwera. Posty mogę łatwo dodawać i edytować będąc offline. Wspaniała sprawa. :)&lt;/p&gt;
&lt;p&gt;Layout bloga jest w tym momencie skopiowany ze strony &lt;a href="http://tom.preston-werner.com/"&gt;Toma Preston-Werner&lt;/a&gt; (tak, to ten od githuba ;-) ze względu na to, że wszystko oprócz postów jest na licencji &lt;span class="caps"&gt;MIT&lt;/span&gt;, a dzięki temu mogłem praktycznie w 10 minut postawić bloga (więcej czasu zajęło przeniesienie postów z typo i mephisto, ale dzięki skryptom przygotowanym specjalnie dla jekylla, także ta operacja była prawie bezbolesna). Część postów poszło do kosza ze wzgłedu na ich przedawnienie (przez te kilka lat zmieniło się w railsach całkiem dużo i nikogo już pewnie nie obchodzi, że właśnie wyszły railsy w wersji 1.2-rc2), część postaram się oznaczyć &lt;a href="http://piotrsarnacki.com/2009/06/16/cucumber-and-celerity-testing-unobtrusive-javascript"&gt;jako deprecated&lt;/a&gt; (swoją drogą zrobienie prostego mechanizmu do oznaczania w ten sposób postów jest w jekyllu banalnie proste, skrobnę później o tym posta).&lt;/p&gt;
&lt;p&gt;Na koniec muszę się jeszcze pochwalić. Prawdodpodobnie większość z was wie o tegorocznym &lt;a href="http://rubysoc.org"&gt;Ruby Summer of Code&lt;/a&gt;. Dla tych którzy nie wiedzą: w tym roku żadne projekty związane bezpośrednio z Ruby nie dostały się na Google Summer of Code, więc społeczność (mocno wspierana przez &lt;a href="http://engineyard.com"&gt;Engine Yard&lt;/a&gt;, który z tego co pamiętam jest inicjatorem pomysłu) zorganizowała sobie własne Summer of Code. W 3 dni zebrano 100 tysięcy dolarów, co pozwoli sfinansować 20 projektów. Wśród owych 20 projektów znalazło się miejsce dla 2 Polaków:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Marcin Raczkowski&lt;/strong&gt; &amp;#8211; będzie pracował nad Identity Map dla ActiveRecord&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Piotr Sarnacki&lt;/strong&gt; (czyli skromny autor tego oto blogusia ;-) &amp;#8211; będę pracował nad mountable apps i panelem admin do Railsów 3 (w zasadzie sytuacja jest trochę bardziej skomplikowana, ponieważ moje zgłoszenia zostały połączone ze zgłoszeniami &lt;a href="http://github.com/hurrycane"&gt;Bogdana Gazy&lt;/a&gt; i pracujemy w jednym projekcie, przy czym ja jestem bardziej odpowiedzialny za część dotyczącą mountable apps, a Bogdan za rails-admin)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pracę nad projektem zacznę prawdopodobnie w połowie czerwca i na blogu postaram się informować na bieżąco o postępach.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Google chce przyśpieszyć internet</title>
   <link href="http://piotrsarnacki.com/2009/06/26/google-chce-przys%CC%81pieszyc%CC%81-internet" />
   <published>2009-06-26T00:00:00-07:00</published>
   <updated>2009-06-26T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2009/06/26/google-chce-przyśpieszyć-internet</id>
   <content type="html">&lt;p&gt;Nie od dziś wiadomo, że prędkość ładowania strony ma wpływ na akcje użytkowników. Badania prowadzone miedzy innymi przez Yahoo, Google i Amazon wskazały, że zmniejszenie czasu ładowania strony ma bezpośredni wpływ na ilość akcji wykonanych przez użytkowników (wyszukiwań lub w wypadku Amazona zakupionych przedmiotów).&lt;/p&gt;
&lt;p&gt;Google odpalił niedawno stronę, na której &lt;a href="http://code.google.com/speed/index.html"&gt;zachęca i uczy jak przyśpieszyć działanie naszych stron i serwisów&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Dodatkowo Google wypuścił narzędzie &lt;a href="http://code.google.com/speed/page-speed/"&gt;Page Speed&lt;/a&gt;, które jest rozszerzeniem do Firebuga i podobnie jak &lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt; pomaga zbadać stronę pod kątem prędkości ładowania się poszczególnych komponentów. Oba te narzędzia dostarczają od razu praktycznych porad i opisów pomagających zrozumieć jak zoptymalizować stronę. Dla mnie są to niezbędne narzędzia przy pracy z serwisami internetowymi. Do tej pory używałem głównie YSlow, ale przy następnych optymalizacjach na pewno skorzystam z Page Speed.&lt;/p&gt;
&lt;p&gt;Jeżeli jeszcze nie znacie tych narzędzi, gorąco polecam zapoznanie się z nimi.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>YUI 3 beta</title>
   <link href="http://piotrsarnacki.com/2009/06/25/yui-3-beta" />
   <published>2009-06-25T00:00:00-07:00</published>
   <updated>2009-06-25T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2009/06/25/yui-3-beta</id>
   <content type="html">&lt;p&gt;We wczorajszym poście na blogu &lt;span class="caps"&gt;YUI&lt;/span&gt; &lt;a href="http://www.yuiblog.com/blog/2009/06/24/yui3b1/"&gt;ogłoszono wersję beta YUI3&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;YUI&lt;/span&gt;&lt;/strong&gt; (Yahoo User Interface) jest frameworkiem javascript rozwijanym przez &lt;span class="caps"&gt;YAHOO&lt;/span&gt;, który dostarcza nie tylko niskopoziomowe funkcje pomagające przy pisaniu kodu javascript, ale też zestaw komponentów (takich jak różnego rodzaju okienka dialog, color picker, czy nawet &lt;a href="http://developer.yahoo.com/yui/editor/"&gt;bardzo dobry edytor &lt;span class="caps"&gt;WYSIWYG&lt;/span&gt;&lt;/a&gt;). &lt;span class="caps"&gt;YUI&lt;/span&gt; jest dojrzałą biblioteką używaną na stronach &lt;span class="caps"&gt;YAHOO&lt;/span&gt;, co zapewnia bardzo dobrą jakość. Sam team &lt;span class="caps"&gt;YUI&lt;/span&gt; to jedni z najlepszych specjalistów od frontendu &amp;#8211; zachęcam do czytania wpisów na &lt;a href="http://developer.yahoo.com/yui/editor/"&gt;ich blogu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To co najbardziej przekonywało mnie do używania &lt;span class="caps"&gt;YUI&lt;/span&gt;, to fakt dostarczenia całego zestawu narzędzi do budowania interfejsów użytkownika w javascripcie. W przypadku np. jQuery lekko może denerwować bardzo duże rozstrzelenie poszczególnych bibliotek. Są oczywiście takie projekty jak &lt;a href="http://jqueryui.com/"&gt;jqueryUI&lt;/a&gt;, ale większość pluginów trzeba ściągnąć z innych źródeł, a ich kod często pozostawia wiele do życzenia. W przypadku &lt;span class="caps"&gt;YUI&lt;/span&gt; mamy zwarty zestaw komponentów, łatwy do rozszerzania i rozwijany przez specjalistów w celu wykorzystania na stronach odwiedzanych codziennie przez miliony internautów.&lt;/p&gt;
&lt;p&gt;Z drugiej strony to co było banalne w jQuery często było ciężkie do uzyskania w &lt;span class="caps"&gt;YUI&lt;/span&gt;. A jak wszyscy wiedzą jeżeli chodzi o zabawę z &lt;span class="caps"&gt;DOM&lt;/span&gt;, animacjami i ajaxem z jQuery jest ciężko wygrać. Dlatego gdy używałem komponentów &lt;span class="caps"&gt;YUI&lt;/span&gt; w jednym z projektów, w którym przy okazji używałem jQuery, to do manipulacji elementów &lt;span class="caps"&gt;DOM&lt;/span&gt; używałem jQuery, a z &lt;span class="caps"&gt;YUI&lt;/span&gt; wybierałem tylko komponenty.&lt;/p&gt;
&lt;p&gt;Wersja trzecia &lt;span class="caps"&gt;YUI&lt;/span&gt; może to zmienić. Zespół z Yahoo postanowił przepisać bibliotekę zupełnie od nowa z naciskiem na lekkość i szybkość. Najważniejsze zmiany&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;zmienione &lt;span class="caps"&gt;API&lt;/span&gt; do manipulacji &lt;span class="caps"&gt;DOM&lt;/span&gt;, czyli &amp;#8220;jQueryzacja&amp;#8221; &amp;#8211; łańcuchy komend, proste funkcje typu addClass, remove, append itp.&lt;/li&gt;
	&lt;li&gt;ułatwiona praca z eventami&lt;/li&gt;
	&lt;li&gt;modularyzacja biblioteki&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Najbardziej obiecująco wygląda ostatni punkt. Do tej pory poszczególne komponenty nie były modularne, co prowadziło do powtarzającego się  kodu w różnych komponentach. Dodatkowo jeżeli ktoś chciał stworzyć Tooltip dziedziczący po Overlay, to musiał wykorzystać wszystkie elementy, które Overlay zawiera. Jest to oczywiście zbędne wykorzystywanie pamięci i &lt;a href="http://developer.yahoo.com/yui/container/tooltip/index.html"&gt;Tooltip, który jest obecnie dostępny w &lt;span class="caps"&gt;YUI&lt;/span&gt;&lt;/a&gt; właśnie z tego powodu nie jest najlepszą możliwą implementacją.&lt;/p&gt;
&lt;p&gt;W YUI3 poszczególne funkcjonalności są rozbite do modułów. Dlatego, żeby zbudować wspomniany wyżej tooltip nie trzeba dziedziczyć po Overlay, tylko zmiksować (czy to nie brzmi znajomo?) na przykład: moduł pozycjonowania i moduł animacji. Dodatkowo można wmiksować poszczególne moduły nie tylko do klas, ale także instancji poszczególnych obiektów. Jeżeli będzie to rzeczywiście działało tak fajnie jak mówią programiści z &lt;span class="caps"&gt;YUI&lt;/span&gt; (polecam obejrzeć video ze &lt;a href="http://www.yuiblog.com/blog/2009/06/24/yui3b1/"&gt;zlinkowanego wcześniej posta&lt;/a&gt;), to YUI3 na pewno zagości w moich projektach.&lt;/p&gt;
&lt;p&gt;To co najbardziej denerwuje mnie obecnie w różnego rodzaju bibliotekach do jQuery czy prototype, to całkowite oderwanie poszczególnych bibliotek od siebie. Używam na przykład jqueryUI i żeby użyć tooltipa, muszę ściągnąć zupełnie inną bibliotekę, która jest napisana zupełnie inaczej i nie dzieli żadnego kodu z innymi bibliotekami (tutaj też wychodzi swoboda javascriptu, jeżeli ktoś próbował porównywać implementacje różnych bibliotek, to na pewno wie na ile różnych sposobów można zaimplementować te same funkcje). Jeszcze gorzej jest gdy trzeba napisać własne komponenty &amp;#8211; lekkość jQuery i brak oficjalnych bibliotek pomagających w takich wypadkach jest dość dużą przeszkodą.&lt;/p&gt;
&lt;p&gt;W tym momencie &lt;span class="caps"&gt;YUI&lt;/span&gt; team zachęca programistów do używania YUI3 w nowych projektach, szczególnie tych, które nie planują wykorzystywać dużej ilości widgetów obecnych w YUI2. Oficjalna wersja ma być dostępna w 3 kwartale tego roku. Rozwój można biblioteki śledzić &lt;a href="http://yuilibrary.com/projects/yui3/roadmap"&gt;tutaj&lt;/a&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Co nowego w Rails3?</title>
   <link href="http://piotrsarnacki.com/2009/05/03/co-nowego-w-rails3" />
   <published>2009-05-03T00:00:00-07:00</published>
   <updated>2009-05-03T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2009/05/03/co-nowego-w-rails3</id>
   <content type="html">&lt;p&gt;Ponad 4 miesiące temu światem Ruby&amp;#8217;iego zatrzęsła wiadomość o połączeniu się 2 popularnych frameworków &amp;#8211; Ruby on Rails i Merba. Dzisiaj, tylko kilka dni dzieli nas od ujrzenia &lt;a href="http://en.oreilly.com/rails2009/public/schedule/detail/7785"&gt;pierwszych aplikacji, które będą napisane w Rails 3&lt;/a&gt;. Carl Lerche (twórca między innymi routera do merba) oraz Yehuda Katz na tegorocznym RailsConf będą mówić o Rails3 i mountable apps.&lt;/p&gt;
&lt;p&gt;Na stabilną wersję będzie trzeba jeszcze trochę poczekać, ale trzymam kciuki, żeby na RailsConf rzeczywiście udało im się pokazać działającą aplikację.&lt;/p&gt;
&lt;p&gt;W tym poście chciałbym krótko opisać to co ma się znaleźć w Rails 3. Z niemałą niecierpliwością śledzę newsy i doniesienia o nowych railsach i nie mogę się już doczekać niektórych usprawnień.&lt;/p&gt;
&lt;h3&gt;ActionORM (wcześniej ActiveORM)&lt;/h3&gt;
&lt;p&gt;Zwolennicy Merba bardzo podkreślają w swoim frameworku niezależność od konkretnych technologii. Było to reklamowane głównie jako &amp;#8220;&lt;span class="caps"&gt;ORM&lt;/span&gt; agnosticism&amp;#8221; czyli możliwość podpięcia do Merba jednego z wielu &amp;#8220;ORMów&amp;#8221; (DataMapper, ActiveRecord, Sequel) w porównaniu do Railsowej monolityczności w postaci jednego słusznego ActiveRecorda.&lt;/p&gt;
&lt;p&gt;Jaki właściwie jest problem w używaniu innego ORMa w naszej aplikacji? Na poziomie modeli i kontrolerów nie ma to z reguły większego znaczenia. Przecież Railsów nie obchodzi czy klasa w katalogu app/models dziedziczy po &lt;code&gt;ActiveRecord::Base&lt;/code&gt;, czy dołącza moduł: &lt;code&gt;include DataMapper::Resource&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Kłopot zaczyna się kiedy chce się korzystać z wielu helperów, które jako argument przyjmują obiekt ActiveRecord. Najprostszy przykład:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;% form_for &lt;/span&gt;&lt;span class="vi"&gt;@article&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="sx"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="sx"&gt;  &amp;lt;%= f.text_field :title %&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;% end &lt;/span&gt;&lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Jeżeli &lt;code&gt;@article&lt;/code&gt; nie jest obiektem AR, to poprawność działania tej metody zależy tylko od tego czy interfejs danego ORMa tutaj używany jest taki sam. W tym akurat przypadku problemem może być sprawdzanie błędów w walidacji. Railsy domyślnie dodają wrapper do pól z błędami, więc jeżeli &lt;span class="caps"&gt;ORM&lt;/span&gt; ma inny interfejs do pobrania informacji o błędach, to metoda po prostu nie zadziała.&lt;/p&gt;
&lt;p&gt;W tym momencie jest to w Railsach zrobione &lt;a href="http://github.com/rails/rails/blob/17e712d3a3d3934bb1f694d449d9a76a3ac715c1/actionpack/lib/action_view/helpers/active_record_helper.rb#L247"&gt;z użyciem alias_method_chain&lt;/a&gt; i jeżeli obiekt nie ma zaimplementowanej metody errors, to tag pozostaje niezmieniony, więc można samemu dołączyć swoją własną implementację lub dodać potrzebne metody do modelu. Jednak wszyscy chyba się zgodzą, że nie jest to najbardziej elegancki sposób.&lt;/p&gt;
&lt;p&gt;Jeżeli chodzi o Merba, to sprawa wygląda tylko trochę lepiej niż w railsach. Jest co prawda wsparcie dla 3 najpopularniejszych ORMów, ale implementacja nie pozwala na łatwe dodanie kolejnych ORMów bez grzebania w kodzie. Jeżeli &lt;span class="caps"&gt;ORM&lt;/span&gt;, którego chcesz używać ma interfejs zgodny z DataMapperem, Sequelem lub ActiveRecordem, to wszystko jest ok, ale jeżeli jest to coś innego, to masz problem.&lt;/p&gt;
&lt;p&gt;Odpowiedzią na to ma być ActionORM. Czym jest ActionORM? Jest to proste proxy dla obiektów. ActionORM udostępnia &lt;span class="caps"&gt;API&lt;/span&gt; takie jak ActiveRecord (dzięki temu w kodzie railsów nie będzie trzeba wiele zmieniać). W prostych słowach ActionORM ma sprawić, że obiekty innych ORMów będą wyglądały jak obiekty ActiveRecord (czyli klasyczne wykorzystanie &lt;a href="http://en.wikipedia.org/wiki/Duck_typing"&gt;duck typing&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;a href="http://github.com/lancecarlson/rails/blob/0faae5b971c3dbf3b1c4ead19504580233bbc7fa/activeorm/lib/active_orm/proxies/datamapper_proxy.rb"&gt;Tutaj jest przykład proxy dla Datamappera&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Różnice są jak widać niewielkie, ale podejrzewam, że będzie można dodać więcej metod, które się różnią pomiędzy implementacjami (oczywiście w granicach rozsądku, sprowadzanie całego interfejsu do jednego przypadku było by pozbawione sensu, chodzi o proste operacje, które każdy &lt;span class="caps"&gt;ORM&lt;/span&gt; ma rozwiązane podobnie, ale różni się &lt;span class="caps"&gt;API&lt;/span&gt;).&lt;/p&gt;
&lt;h3&gt;Rack Middleware&lt;/h3&gt;
&lt;p&gt;Railsy od jakiegoś czasu wykorzystują w pełni dobrodziejstwa &lt;a href="http://radarek.jogger.pl/2008/06/27/rack-niech-aplikacje-przemowia-wspolnym-jezykiem/"&gt;Rack&lt;/a&gt;. Co więcej, różne części frameworka są zaimplementowane jako rack middlewares. Kontroler, router, obsługa sesji i wiele innych rzeczy to teraz po prostu kolejne middlewares (przepraszam za te anglojęzyczne wstawki, ale naprawdę nie mam pojęcia jak to ładnie przetłumaczyć). Dzięki temu i dodaniu &lt;a href="http://weblog.rubyonrails.org/2008/12/17/introducing-rails-metal"&gt;Rails metal&lt;/a&gt; aplikacje są coraz bardziej modularne.&lt;/p&gt;
&lt;p&gt;Programiści railsów dążą w tej chwili do jeszcze szerszego wykorzystania racka. Wszystko to idzie w stronę jak najłatwiejszego połączenia komponentów różnego typu. Na przykład możliwość podłączenia do routera kontrolerów railsowych, aplikacji sintatry i aplikacji merba (jako jedną aplikację oczywiście).&lt;/p&gt;
&lt;h3&gt;Optymalizacja i refaktoryzacja&lt;/h3&gt;
&lt;p&gt;Największą i najlepszą według mnie zmianą w railsach jest refaktoryzacja kodu ActionPack. Na czym ona polega? W bardzo prostych słowach: stworzony jest AbstractController, z którego dziedziczyć będą inne kontrolery, a w samym kodzie jest zastosowana dużo większa enkapsulacja. Jakie to ma znaczenie dla użytkowników railsów? Będzie istniał jeden interfejs dla wszystkich kontrolerów, dzięki czemu na przykład ActionController i ActionMailer będą mogły współdzielić funkcjonalność (żeby nie iść daleko, dopiero niedawno do mailera dodana została obsługa layoutów). Bardzo łatwe będzie też dodanie Parts znanych z merba (PartsController również dziedziczy w Merbie z AbstractControllera).&lt;/p&gt;
&lt;p&gt;W chwili obecnej podobną funkcjonalność do Merb Parts udostępniają &lt;a href="http://cells.rubyforge.org/"&gt;Rails Cells&lt;/a&gt;, ale nie jest to rozwiązanie idealne (implementacja jest zagmatwana, w cellach widoczne są zmienne instancji z kontrolerów i innych celli, podejrzewam, że jest to wolniejsze niż parts).&lt;/p&gt;
&lt;p&gt;Co jeszcze? Yehuda Katz wziął się między innymi za &lt;a href="http://yehudakatz.com/2009/01/16/status-update-a-fresh-look-at-callbacks/"&gt;optymalizację callbacków&lt;/a&gt;, czy bloku respond_to. Polecam również inne artykuły na temat refaktoryzacji na jego blogu.&lt;/p&gt;
&lt;p&gt;Na pewno szybszy będzie też router. W chwili obecnej największym problemem jest to, że router generuje bardzo dużo metod (wszystkie metody _url i &lt;em&gt;path), co w rezultacie przyczynia się do bardzo wolnego startu aplikacji. W czasach mongreli nie miało to dużego znaczenia, bo aplikacje były odpalane raz i tylko sporadycznie restartowane. Teraz kiedy na wielu serwerach zainstalowany jest mod&lt;/em&gt;passenger, czas startu aplikacji jest dużo ważniejszy &amp;#8211; instancje, które nie dostają requestów przez 5 minut (domyślne ustawienie) są wyłączane dzięki czemu nie zajmują zasobów.&lt;/p&gt;
&lt;p&gt;Nowy router można przetestować już teraz. Po więcej odsyłam na &lt;a href="http://merbist.com/2009/04/14/merb-11-delayed/"&gt;the merbist&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Mountable apps&lt;/h3&gt;
&lt;p&gt;Jest to jedna z tych rzeczy, na które czekam w Railsach 3 najbardziej. Dosłownie liczę godziny do rails conf i prezentacji na ich temat (mam nadzieję, że będą nagrywane, bo niestety nie miałem wolnych kilku tysięcy złotych, żeby się pojawić osobiście na konferencji ;-).&lt;/p&gt;
&lt;p&gt;Jakieś namiastki mountable apps są już dostępne w postaci Rails Engines i Merb Slices. Jest to jakieś rozwiązanie, ale na pewno dalekie od idealnego. Żeby skorzystać z którejś z tych opcji trzeba napisać specyficzny rodzaj aplikacji, a każde z tych rozwiązań jest w niektórych miejscach niedopracowane. Celem mountable apps będzie możliwość łatwego połączenia 2 aplikacji bez żadnych zmian w kodzie.&lt;/p&gt;
&lt;h3&gt;&lt;span class="caps"&gt;API&lt;/span&gt; dla pluginów&lt;/h3&gt;
&lt;p&gt;Jedną z rzeczy, którą wypromował Merb jest publiczne i prywatne &lt;abbr title="Application Programming Interface"&gt;api&lt;/abbr&gt;. Dzięki temu pisanie pluginów nie wiąże się tam z używaniem alias_method_chain i ogólnie pojętym monkey patchingiem na każdym kroku. A jeżeli czegoś nie da się zrobić używając tylko publicznego api, to jest to bug. Jest to chyba jedna z trudniejszych rzeczy w całej refaktoryzacji. Nie wiadomo dokładnie czego będą potrzebować twórcy pluginów, niektóre metody trzeba będzie dopiero stworzyć.&lt;/p&gt;
&lt;p&gt;Ogromnym plusem takiego podejścia jest to, że twórcy frameworka nie muszą się martwić o prywatne metody, ważne żeby publiczne &lt;span class="caps"&gt;API&lt;/span&gt; się nie zmieniało. Dzięki temu pluginy napisane zgodnie z wytycznymi, czyli nie używając prywatnego &lt;span class="caps"&gt;API&lt;/span&gt; nie będą się wysypywały na każdej kolejnej wersji (co bardzo często można zaobserwować w tym momencie), a jeżeli coś się zmieni w publicznym &lt;span class="caps"&gt;API&lt;/span&gt; przy okazji większych zmian, to twórcy pluginów będą mogli się o tym łatwo dowiedzieć bez śledzenia commitów na githubie.&lt;/p&gt;
&lt;h3&gt;Podsumowanie&lt;/h3&gt;
&lt;p&gt;Jestem zdania, że połączenie merba i railsów to jedna z najlepszych rzeczy, która mogła się przytrafić programistom webowym. Railsy będą szybsze, bardziej modularne i łatwiejsze w rozszerzaniu. Tylko czekać na wyniki refaktoryzacji :)&lt;/p&gt;
&lt;p&gt;Postaram się w najbliższych tygodniach pisać o tym co dzieje się z Railsami3, jak idą prace i co jeszcze ciekawego będzie można zobaczyć.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Refaktoryzacja Railsów</title>
   <link href="http://piotrsarnacki.com/2009/01/03/refaktoryzacja-railso%CC%81w" />
   <published>2009-01-03T00:00:00-08:00</published>
   <updated>2009-01-03T00:00:00-08:00</updated>
   <id>http://piotrsarnacki.com/2009/01/03/refaktoryzacja-railsów</id>
   <content type="html">&lt;p&gt;Niestety nie będzie to wpis o tym jak refaktoryzować swoją aplikację, ale raczej post o refaktoryzacji samego frameworka w ramach przygotowań do Rails 3.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://railsenvy.com/2008/12/31/rails-envy-podcast-episode-061-12-31-2008"&gt;Railsy pożarły mózg Merba i posiądą jego tajemne moce&lt;/a&gt; (zachęcam do wysłuchania tego podcastu, chłopaki od railsenvy genialnie mówią o tym co się dzieje w świecie railsów) &amp;#8211; o tym już &lt;a href="http://nhw.pl/pl/2008/12/rails-i-merb-w-jednym-staly-domu"&gt;każdy na pewno wie&lt;/a&gt;. Czy to dobrze? Oczywiście, że tak :)&lt;/p&gt;
&lt;p&gt;Jeżeli ktoś dalej ma problemy z tą decyzją (podejrzewam, że może tak być w wypadku fanów merba), polecam prezentacją &lt;a href="http://railsconfeurope.blip.tv/file/1555560/"&gt;Living with legacy software&lt;/a&gt; wygłoszoną przez Davida Heinemeiera Hanssona &amp;#8211; nie jest w żaden sposób bezpośrednio związana z połączeniem, ale zmienia podejście do aplikacji i ich kodu.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.youtube.com/watch?v=UwI307EC63c&amp;amp;feature=related"&gt;Ale nie o to, nie o to, nie o to&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://yehudakatz.com/"&gt;Yehuda Katz&lt;/a&gt; ostro wziął się do pracy jeszcze podczas świąt i co jakiś czas wrzuca na bloga informacje o tym jak refaktoryzuje i jakie ma plany wobec Railsów. Informacje traktujące o projektowaniu, refaktoryzacji i architekturze ciężko jest znaleźć w sieci, więc gorąco polecam śledzenie tego co pisze Yehuda &amp;#8211; do tej pory można przeczytać między innymi o enkapsulacji ActionView i ActionController czy o optymalizacji respond_to. Dodajcie sobie tego bloga do czytnika, naprawdę warto.&lt;/p&gt;
&lt;p&gt;Co najlepsze, wiele z tych zmian dotyka tylko wewnętrznej implementacji railsów, więc można liczyć, że będą one dostępne dla ruby on rails 2.3.0.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>To self or not to self?</title>
   <link href="http://piotrsarnacki.com/2008/11/15/to-self-or-not-to-self" />
   <published>2008-11-15T00:00:00-08:00</published>
   <updated>2008-11-15T00:00:00-08:00</updated>
   <id>http://piotrsarnacki.com/2008/11/15/to-self-or-not-to-self</id>
   <content type="html">&lt;p&gt;Czasami przeglądając kod różnych aplikacji można zauważyć kawałki wyglądające mniej więcej tak:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;  &lt;span class="kp"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:price&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;total_price&lt;/span&gt;
    &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;# więcej kodu&lt;/span&gt;
    &lt;span class="nb"&gt;p&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;W prawdziwej aplikacji można to zobaczyć &lt;a href="http://github.com/schof/spree/tree/master/app/models/order.rb#L100"&gt;na przykład w Spree&lt;/a&gt; (użycie tot zamiast total)&lt;/p&gt;
&lt;p&gt;p oznacza tutaj zapewne price, ale autor kodu intuicyjnie stwierdził, że pisząc &amp;#8220;price =&amp;#8221;, zamiast stworzenia nowej zmiennej przypisze wartość na atrybut price.&lt;/p&gt;
&lt;p&gt;Rzeczywiście jest to intuicyjne i całkiem bezpieczne, ale zupełnie niepotrzebne. Spokojnie i bez żadnego problemu można użyć price. Dlaczego?&lt;/p&gt;
&lt;p&gt;W rubim można opuścić słowo self jeżeli chodzi o odczytanie wartości metody. Jeżeli więc bez zadeklarowania zmiennej price ktoś napisałby puts(price), to ruby zinterpretowałby to jako chęć wyświetlenia self.price. Inaczej jest z zapisywaniem. Jeżeli wywołana jest metoda price=, to ruby stworzy nową zmienną.&lt;/p&gt;
&lt;p&gt;Na przykład taki kod:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;
    &lt;span class="kp"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:price&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_something_with_price&lt;/span&gt;
      &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;do_something_with_price&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Wypisze:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="mi"&gt;20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Na koniec napiszę tylko, że mam mieszane uczucia co do korzystania z tych właściwości języka. Z jednej strony nie lubię jak w kodzie pałętają się skrótowe nazwy zmiennych, ale z drugiej strony taki zapis pokazuje, że chodzi nam o coś innego niż self.costam.&lt;/p&gt;
&lt;p&gt;Ja z reguły staram się nie skracać nazw w takich wypadkach. Jakie jest wasze zdanie?&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Named scope</title>
   <link href="http://piotrsarnacki.com/2008/10/03/named-scope" />
   <published>2008-10-03T00:00:00-07:00</published>
   <updated>2008-10-03T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2008/10/03/named-scope</id>
   <content type="html">&lt;p&gt;W railsach od jakiegoś czasu można używać &lt;a href="http://ar.rubyonrails.com/classes/ActiveRecord/NamedScope/ClassMethods.html"&gt;named_scope&lt;/a&gt;. Jest to bardzo fajny mechanizm umożliwiający łatwiejsze skonstruowanie zapytania używając wcześniej zdefiniowanych metod. Wcześniej był dostępny jako plugin has_finder.&lt;/p&gt;
&lt;p&gt;Wygląda to mniej więcej tak:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;  &lt;span class="n"&gt;named_scope&lt;/span&gt; &lt;span class="ss"&gt;:active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:active&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;named_scope&lt;/span&gt; &lt;span class="ss"&gt;:paid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:paid&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;named_scope&lt;/span&gt; &lt;span class="ss"&gt;:recent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;created_at &amp;gt;= ?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;week&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ago&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Po zdefiniowaniu takich sope&amp;#8217;ów (macie pomysł jak to spolszczyć?) można ich używać w ten sposób (zakładam, że scope&amp;#8217;y zostały zadeklarowane w modelu Product):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;  &lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Jak można łatwo zauważyć, scope&amp;#8217;y można łączyć w łańcuchy. Dzięki takiej konstrukcji otrzymujemy prosty i treściwy kod, który łatwo zrozumieć. Teraz coś lekko trudniejszego:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;  &lt;span class="n"&gt;named_scope&lt;/span&gt; &lt;span class="ss"&gt;:in_category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:category_id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  
  &lt;span class="c1"&gt;# i użycie&lt;/span&gt;
  &lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in_category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;O co chodzi? Jako 2 argument można przekazać lambdę, która zostanie wywołana przy użyciu scope&amp;#8217;a i powinna zwrócić Hash.&lt;/p&gt;
&lt;p&gt;Ale to nie są jedyne zastosowania scope&amp;#8217;ów. Można do nich przekazać też inne parametry, które może przyjąć metoda find.&lt;/p&gt;
&lt;p&gt;Na przykład order, offset i limit:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;  &lt;span class="n"&gt;named_scope&lt;/span&gt; &lt;span class="ss"&gt;:order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:order&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;created_at DESC&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
  &lt;span class="n"&gt;named_scope&lt;/span&gt; &lt;span class="ss"&gt;:offset&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:offset&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
  &lt;span class="n"&gt;named_scope&lt;/span&gt; &lt;span class="ss"&gt;:limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:limit&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
  
  &lt;span class="c1"&gt;# i użycie:&lt;/span&gt;
  &lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;id DESC&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;W pierwszym i trzecim scope&amp;#8217;ie zastosowałem trick, który pozwala na użycie zdefiniowanej wartości domyślnej w razie gdy żadna nie zostanie podana. `*args` pakuje argumenty do tablicy. Jeżeli args.first zwróci nil, to znaczy, że tablica jest pusta, czyli nie zostały podane żadne argumenty i trzeba zaaplikować wartość domyślną.&lt;/p&gt;
&lt;p&gt;Coś jeszcze? Fajnie by było, żeby można było sortować nie tylko po wartościach z jednej tablicy&amp;#8230; a do tego wypadałoby użyć joins/include. Nic prostszego.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;  &lt;span class="n"&gt;named_scope&lt;/span&gt; &lt;span class="ss"&gt;:joins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;joins&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:joins&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;joins&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
  
  &lt;span class="c1"&gt;# a teraz można tak:&lt;/span&gt;
  &lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;joins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;users.email&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;How cool is that?&lt;/p&gt;
&lt;p&gt;No i na koniec coś co ostatecznie przekonało mnie, że named_scope jest genialnym wynalazkiem. Jeżeli ktoś robił kiedyś formularze, w których można wybrać kilka opcji i na ich podstawie trzeba zbudować zapytanie, zapewne wie, że jest to nieco upierdliwe. Używając `named_scopes` można to zrobić bardzo łatwo.&lt;/p&gt;
&lt;p&gt;Ryan Bates napisał plugin &lt;a href="http://github.com/ryanb/scope-builder/tree/master"&gt;scope_builder&lt;/a&gt;, dzięki któremu można to zrobić jeszcze łatwiej. Przypuśćmy, że mamy formę, w której można zaznaczyć kilka pól i po ich wysłaniu należy na ich podstawie pobrać odpowiednie rekordy z bazy.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;  &lt;span class="c1"&gt;# przygotowujemy listę parametrów, które użytkownik może ustawić&lt;/span&gt;
  &lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:paid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:recent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="c1"&gt;# Tworzymy scope&lt;/span&gt;
  &lt;span class="vi"&gt;@products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scope_builder&lt;/span&gt;
  &lt;span class="c1"&gt;# teraz można sprawdzić, które pola zostały zazanczone&lt;/span&gt;
  &lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="vi"&gt;@products&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="c1"&gt;# na koniec można na przykład dodać paginację&lt;/span&gt;
  &lt;span class="vi"&gt;@products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@products&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:per_page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Dla mnie genialne. :) Można dzięki temu łatwiej tworzyć zaawansowane wyszukiwanie, sortowanie i inne tego typu rzeczy.&lt;/p&gt;
&lt;p&gt;Oczywiście najlepiej jest zamknąć taki kawałek kodu jako metodę modelu, zgodnie ze &lt;a href="http://www.youtube.com/watch?v=91C7ax0UAAc"&gt;skinny controller, fat model&lt;/a&gt;, ale to pozostawię jako ćwiczenie ;-)&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Po dłuższej przerwie</title>
   <link href="http://piotrsarnacki.com/2008/09/30/po-d%C5%82uz%CC%87szej-przerwie" />
   <published>2008-09-30T00:00:00-07:00</published>
   <updated>2008-09-30T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2008/09/30/po-dłuższej-przerwie</id>
   <content type="html">&lt;p&gt;Przez jakiś czas nie pisałem nic na blogu. Długo zbierałem się do napisania czegokolwiek, od dawien dawna miałem przetłumaczyć wpisy na moim &lt;a href="http://drogomir.com/blog"&gt;angielskojęzycznym blogusiu&lt;/a&gt;. Tak bardzo mi się nie chciało, że jak tylko siadałem do kompa z zamiarem przetłumaczenia czy napisania czegoś podobnego coś mnie odrzucało.&lt;/p&gt;
&lt;p&gt;Dlatego, żeby więcej się nie męczyć podsumuję ten temat i podam linki &amp;#8211; większość ludzi, którzy tutaj trafią i tak na pewno zna angielski.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://github.com/drogus/apache-upload-progress-module/tree/master"&gt;Napisałem apache upload progress module&lt;/a&gt; do apacha. Moduł, dzięki któremu można pobrać dane o wysyłanych plikach. Format odpowiedzi oparłem o moduły tego typu dla &lt;a href="http://wiki.codemongers.com/NginxHttpUploadProgressModule"&gt;nginxa&lt;/a&gt; i &lt;a href="http://upload.lighttpd.net/index.html"&gt;lighttpd&lt;/a&gt; dlatego jeżeli wcześniej ktoś używał któregoś z nich, przesiadka będzie całkowicie bezbolesna. Sam używałem wcześniej nginx upload progress (głównie dlatego napisałem moduł do apacha) i po przerzuceniu aplikacji na apacha z nowym modułem nie trzea było zmieniać ani jednej linijki. Na angielskim blogu zamieściłem opis &lt;a href="http://drogomir.com/blog/2008/6/18/upload-progress-bar-with-mod_passenger-and-apache"&gt;instalacji i konfiguracji modułu&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Żeby można było w miarę łatwo używać modułu napisałem także pluginy do &amp;quot;prototype&amp;#8217;a&amp;quot;http://github.com/drogus/prototype-upload-progress/tree/master i &lt;a href="http://github.com/drogus/jquery-upload-progress/tree/master"&gt;jquery&lt;/a&gt;, które obsługują pasek postępu. Przykłady są w repozytoriach, umieściłem je także &lt;a href="http://drogomir.com/files/blog/jquery-upload-progress/example/"&gt;na serwerze&lt;/a&gt; dla obczajenia w akcji. Popełniłem także tekst o tym &lt;a href="http://drogomir.com/blog/2008/6/30/upload-progress-script-with-safari-support"&gt;jak hackowałem plugin, żeby działał w safari&lt;/a&gt; &amp;#8211; safari w tym momencie nie było wcale lepsze od IE&amp;#8230; nawet powiedziałbym, że gorsze. Swoją drogą plugin nie działa w najnowszej operze (działał w 9.5 zdaje się), więc jeżeli ktoś ma chwile czasu i mógłby sprawdzić dlaczego i co można zrobić, żeby działał, to może wnieść swój wkład w rozwój (dodam, że nie jest to ta sama kwestia co dla safari, bo to już sprawdziłem) &amp;#8211; nie sądzę żebym miał teraz czas sam na tym siedzieć.&lt;/p&gt;
&lt;p&gt;Dodatkowo napisałem jeszcze tekst o tym jak można fajnie &lt;a href="http://drogomir.com/blog/2008/7/3/tweaking-rails-app-with-jquery-part-i"&gt;uatrakcyjnić aplikację używając jQuery&lt;/a&gt;. Część pierwsza. Część druga będzie jak będę miał więcej czasu, czyli pewnie niezbyt szybko.&lt;/p&gt;
&lt;p&gt;Pozdrowienia ze słonecznego Wrocławia, gdzie mam zamiar mieszkać przez najbliższy rok (miła odmiana od Pruszkowa :).&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>2 przydatne rzeczy w gicie</title>
   <link href="http://piotrsarnacki.com/2008/06/03/2-przydatne-rzeczy-w-gicie" />
   <published>2008-06-03T00:00:00-07:00</published>
   <updated>2008-06-03T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2008/06/03/2-przydatne-rzeczy-w-gicie</id>
   <content type="html">&lt;p&gt;Usuwanie gałęzi ze zdalnego repozytorium:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;git push &lt;span class="o"&gt;{&lt;/span&gt;repository&lt;span class="o"&gt;}&lt;/span&gt; :heads/&lt;span class="o"&gt;{&lt;/span&gt;your_branch_here&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Usuwanie pliku z indexu bez usuwania go z systemu plików (przydatne gdy dodamy do .gitignore plik, który już istnieje):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;git-update-index --force-remove sciezka/do/pliku
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Taki szybki pościk ;-)&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Git</title>
   <link href="http://piotrsarnacki.com/2008/04/08/git" />
   <published>2008-04-08T00:00:00-07:00</published>
   <updated>2008-04-08T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2008/04/08/git</id>
   <content type="html">&lt;p&gt;Jeżeli ktoś jeszcze nie zauważył społeczność frameworków &lt;a href="http://rubyonrails.org"&gt;Ruby on Rails&lt;/a&gt; i &lt;a href="http://merbivore.org"&gt;Merb&lt;/a&gt; przesiada się powoli na rozproszony system kontroli wersji &lt;a href="http://git.or.cz/"&gt;Git&lt;/a&gt;. Na gita przechodzą Railsy, przeszedł już Merb, Rspec, sporo koderów wypuszczających popularne pluginy. Jednym słowem coś w tym musi być.&lt;/p&gt;
&lt;p&gt;I rzeczywiście coś w tym jest.&lt;/p&gt;
&lt;p&gt;O co chodzi? Jarosław Zabiełło na swoim blogu &lt;a href="http://blog.zabiello.com/articles/2008/03/19/distributed-vcs"&gt;przedstawił kilka rozproszonych systemów kontroli wersji&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Jakie są zalety gita?&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;każdy posiadający kopię aplikacji posiada też całe repozytorium. Coś się rozwaliło? Trzeba zmienić serwer? Serwis,a na którym hostowałeś SVN&amp;#8217;a padł? Smuteczek. Ale nie z gitem &amp;#8211; repozytorium, które masz na dysku wystarczy.&lt;/li&gt;
	&lt;li&gt;Git jest dużo szybszy od SVNa&lt;/li&gt;
	&lt;li&gt;&amp;#8220;Mobilność&amp;#8221; &amp;#8211; bardzo ważna dla mnie kwestia. Często wyjeżdżam i zdarza mi się pracować bez dostępu do netu. W przypadku SVNa musiałem wrzucić jeden mega commit z pierdylionem zmian. W przypadku gita commity lecą do repozytorium na dysku i później można je ewentualnie pchnąć (push) do publicznego repo.&lt;/li&gt;
	&lt;li&gt;Bardzo łatwa obsługa gałęzi. Pracując z gitem najlepiej wyrobić sobie nawyk tworzenia gałęzi przy każdej zmianie, lub grupie połączonych ze sobą zmian. Dzięki  temu można pracować równolegle nad wieloma rzeczami nie zaśmiecając aplikacji. Później tylko łączymy gałęzie, które rzeczywiście są potrzebne, jeżeli coś nie wyjdzie można po prostu daną gałąź wyrzucić i po krzyku&lt;/li&gt;
	&lt;li&gt;dużo łatwiejsze wrzucanie zmian do projektów open source&amp;#8217;owych, szczególnie z pomocą githuba (ale o tym za chwilę).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ale kod leżący cały czas na dysku będzie mało przydatny. Dlatego można założyć sobie publiczne repozytorium. Stwierdziłem jakiś czas temu, że w trosce o swój czas, którego zawsze za mało, będę musiał ograniczyć wszelkie prace, których mogę stosunkowo łatwo uniknąć. Dlatego nie bawiłem się nawet w tworzenie swojego własnego publicznego repozytorium. Wygodniej skorzystać pracy innych ludzi ;-)&lt;/p&gt;
&lt;p&gt;Obecnie najbardziej popularne (jedyne?) serwisy oferujące hosting repozytoriów gita to: &lt;a href="http://github.org"&gt;GitHub&lt;/a&gt; i &lt;a href="http://gitorius.org"&gt;Gitorius&lt;/a&gt;. Gitorius jest w pełni darmowy, ale nie można na nim trzymać prywatnych repozytoriów. Na githubie za darmo można trzymać publiczne repozytoria (ogranicza nas 100mb &amp;#8211; jeżeli chodzi o gita to bardzo dużo), za prywatne trzeba będzie zapłacić. Na razie można jeszcze korzystać za darmo, bo serwis jest w fazie beta.&lt;/p&gt;
&lt;p&gt;Zarejestrowałem się na Githubie kilka dni temu i przeniosłem tam 2 ze swoich projektów. Github to bardzo fajny pomysł na stworzenie społeczności koderów (napisany oczywiście w railsach). Co daje taki serwis?&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;dostęp do rss&amp;#8217;ów poszczególnych użytkowników, projektów, feed z wiadomościami z obserwowanych projektów&lt;/li&gt;
	&lt;li&gt;można jednym kliknięciem zrobić kopię aplikacji (fork) &amp;#8211; jakie są tego korzyści opisał na blogu jeden z developerów merba Michael Ivey :&lt;a href="http://gweezlebur.com/2008/2/9/contributing-to-merb-part-2"&gt;Contributing to merb&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;można wysłać znajomym &amp;#8220;pull request&amp;#8221; &amp;#8211; czyli prośbę o uaktualnienie, na przykład po ważnej aktualizacji&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dodatkowo dostajemy wszystkie zalety gita.&lt;/p&gt;
&lt;p&gt;No to co? Przesiadka na jasną stronę mocy, z tego fuj fuj obleśnego i niefajnego SVNa, który tak chwaliłem kiedy się z nim zapoznałem ;-)&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>HotRuby</title>
   <link href="http://piotrsarnacki.com/2008/04/05/hotruby-ruby-i-javascript" />
   <published>2008-04-05T00:00:00-07:00</published>
   <updated>2008-04-05T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2008/04/05/hotruby-ruby-i-javascript</id>
   <content type="html">&lt;p&gt;Jakiś czas temu pojawiły się &lt;a href="http://ajaxian.com/archives/hotruby-run-ruby-on-a-javascript-interpreter"&gt;wzmianki&lt;/a&gt; &lt;a href="http://www.rubyinside.com/hotruby-a-javascript-flash-virtual-machine-that-runs-ruby-821.html"&gt;o hotruby&lt;/a&gt; (tylko wtedy nie miałem czasu o tym napisać). Krótko mówiąc jest to &amp;#8220;implementacja rubiego w javascripcie&amp;#8221;. Możemy używać rubiego w przeglądarce i we flashu.&lt;/p&gt;
&lt;p&gt;Kod rubiego jest wysyłany do skompilowania przez skrypt cgi, skrypt cgi kompiluje go do bytecodu, po czym javascript wykonuje ów bytecode.&lt;/p&gt;
&lt;p&gt;Na stronie &lt;a href="http://hotruby.accelart.jp/"&gt;HotRuby&lt;/a&gt; jest wzmianka o tym, że składnia języka jest w większości zaimplementowana (na pewno nie ma wyjątków), ale jak na razie większość funkcji i bibliotek nie. Na razie nie wiem do czego mogłoby mi się takie połączenie przydać, ale patrząc na dema łatwo można zauważyć, że całkiem fajne rzeczy da się stworzyć z pomocą hotrubiego. Oby tak dalej.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Rails 2.0 i js.erb</title>
   <link href="http://piotrsarnacki.com/2008/01/15/rails-2-0-i-js-erb" />
   <published>2008-01-15T00:00:00-08:00</published>
   <updated>2008-01-15T00:00:00-08:00</updated>
   <id>http://piotrsarnacki.com/2008/01/15/rails-2-0-i-js-erb</id>
   <content type="html">&lt;p&gt;Niedawno pisałem o &lt;a href="http://blog.drogomir.com/articles/2007/12/25/nieinwazyjny-javascript-razem-z-ruby-on-rails"&gt;nieinwazyjnym kodzie javascript w railsach&lt;/a&gt;. Wspomniałem tam o pluginie MinusMor, który dodaje pliki ejs. Czyli kod javascript parsowany erbem. Zainstalowałem plugin w mojej aplikacji przeniesionej na rails 2.0. Kod pluginu nie działa&amp;#8230; zacząłem przeglądać kod railsów, żeby poprawić minusmor (w internecie nie mogłem znaleźć poprawki). Po chwili przemyślenia sprawdziłem co dzieje się gdy wyrzucę plik rjs. Komunikat o błędzie:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;Missing&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;erb&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Git! W railsach 2.0 możemy przecież wskazać jakiego parsera mają użyć.&lt;/p&gt;
&lt;p&gt;Tworzymy plik index.js.erb. I jeszcze mała zmiana, ustawiamy brak layoutu, żeby renderował się sam template:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;respond_to&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;
  &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:layout&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;I to wszystko! Po prostu działa.&lt;/p&gt;
&lt;p&gt;Nawet lepiej. Można zdefiniować layout, na przykład:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:layout&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;jquery&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;a w nim można na przykład napisać:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="nx"&gt;yield&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;})(&lt;/span&gt;&lt;span class="nx"&gt;jQuery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;I można używać w template&amp;#8217;ach js.erb $ pomimo tego, że wcześniej użyło się jQuery.noConflict.&lt;/p&gt;
&lt;p&gt;Przeszukałem później jeszcze net w poszukiwaniu informacji na ten temat i znalazłem artykuł na mad.ly o &lt;a href="http://mad.ly/2007/11/02/javascript-embedded-ruby-templates-with-rails-out-of-the-box/#more-7"&gt;dodaniu helperów z MinusMor&lt;/a&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Web3.0 - boję się!</title>
   <link href="http://piotrsarnacki.com/2008/01/13/web30-boje-sie" />
   <published>2008-01-13T00:00:00-08:00</published>
   <updated>2008-01-13T00:00:00-08:00</updated>
   <id>http://piotrsarnacki.com/2008/01/13/web30-boje-sie</id>
   <content type="html">&lt;p&gt;Pan Urbanowicz, znawca internetu, &lt;a href="http://urbanowicz.salon24.pl/54761,index.html"&gt;napisał tekst&lt;/a&gt; o (uwaga uwaga, fanfary) Web3.0! A w nim przestraszył mnie nieźle:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ale Web 3.0 niesie ze sobą również wiele zagrożeń: naruszanie swobód obywatelskich, kradzież danych, stopniową dehumanizację czy czipy biometryczne, będące w stanie śledzić wszystkie nasze ruchy. Jak w „Raporcie mniejszości&amp;quot;, każdy nasz krok będzie rejestrowany przez kamery, które będą komunikować z komputerami, a te porozumiewać się z serwisami identyfikującymi twarze. Kto wie, jak daleko może posunąć się inwigilacja ze strony agencji rządowych?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Oba światy: wirtualny i rzeczywisty będę się wzajemnie przenikały, automatycznie wzrośnie ilość spamu, wirusów i hakerów. Będzie więcej dezinformacji, więcej manipulacji i więcej cenzury. Web 3.0 nie będzie nas chronił bardziej niż Web 2.0, wprost przeciwnie, będziemy jeszcze bardziej narażeni na niebezpieczeństwa. Tworzymy świat, który trudno nam zrozumieć, a co dopiero mówić o jego kontroli.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Szkoda, że nie ma nic o głodzie, wojnach, najeźdźcach z kosmosu i pani Czesi spod piątki, która awanturuje się o każdą rzecz.&lt;/p&gt;
&lt;p&gt;Boże, miej nas w swojej opiece! Albowiem Armagedon 3.0 nadchodzi!&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Przyjazne adresy w Ruby on Rails</title>
   <link href="http://piotrsarnacki.com/2008/01/12/przyjazne-adresy-w-ruby-on-rails" />
   <published>2008-01-12T00:00:00-08:00</published>
   <updated>2008-01-12T00:00:00-08:00</updated>
   <id>http://piotrsarnacki.com/2008/01/12/przyjazne-adresy-w-ruby-on-rails</id>
   <content type="html">&lt;p&gt;W dzisiejszych czasach, kiedy &lt;a href="http://en.wikipedia.org/wiki/Search_engine_optimization"&gt;&lt;span class="caps"&gt;SEO&lt;/span&gt;&lt;/a&gt; dla niektórych jest ważniejsze od treści przyjazne adresy można zobaczyć na większości stron. Dlaczego nie wrzucić ich do naszej aplikacji? Nawet jeżeli komuś nie zależy na &lt;span class="caps"&gt;SEO&lt;/span&gt;, przyjazne adresy są&amp;#8230; przyjazne! Użytkownik dostający linka http://foo.com/article/kolejny-artykul-o-naszej-klasie będzie miał szansę zastanowić się sekundę i nie kliknąć (nie to żebym miał coś do naszej klasy, ale boję się ostatnio lodówkę otworzyć, jeszcze szynka zacznie nawijać kogo dodała do znajomych). Same korzyści!&lt;/p&gt;
&lt;p&gt;Dodanie przyjaznych adresów jest w aplikacji railsowej bardzo proste jeżeli trzymamy się kilku zasad. Jak to działa? ActiveRecord::Base, po którym dziedziczą udostępnia metodę to_param, która jest wykorzystywana przy generowaniu adresów.&lt;/p&gt;
&lt;p&gt;W praktyce wygląda to tak, że jeżeli napiszemy:&lt;br /&gt;
&lt;code&gt;&amp;lt;pre&amp;gt;
link_to @article.name, articles_path(@article)
#albo
link_to @article.name, :controller =&amp;gt; "articles", :action =&amp;gt; "show", :id =&amp;gt; @article
&amp;lt;/pre&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;wywołana zostanie metoda to_param i @article zostanie zamieniony na jego id. Można to wykorzysta do naszych niecnych celów.&lt;/p&gt;
&lt;p&gt;Można nadpisać to_param&lt;br /&gt;
&lt;code&gt;&amp;lt;pre&amp;gt;
  def to_param
    "#{id}-#{name.gsub(/[^a-z0-9]+/i, '-')}"
  end
&amp;lt;/pre&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Od teraz zamiast id będzie generował się string zawierający name, na przykład: &amp;#8220;11-tytuł-artykułu&amp;#8221;. Hamerykanie mają mniejszy problem, bo nie mają znaków diakrytycznych i mogą to tak zostawić. gsub zamieni wszystkie znaki nie wchodzące w skład alfabetu na myślnik. U nas jest gorzej, bo nie chcemy mieć adresu: &amp;#8220;11-tytu-artyku-u&amp;#8221;. I weź teraz zgaduj gdzie dorzucić ogonki. Z pomocą przychodzi Obie Fernandez, który napisał najfajniejszą jaką do tej pory widziałem metodą &lt;a href="http://www.jroller.com/obie/entry/fix_that_tranny_add_to"&gt;zamieniającą znaki diakrytyczne na odpowiadające im litery alfabetu&lt;/a&gt;&lt;br /&gt;
Wrzuciłem na serwer &lt;a href="http://drogomir.com/ruby/ascii.rb"&gt;wersję z polskimi znakami diakrytycznymi&lt;/a&gt;. Taki plik wystarczy wrzucić do folderu initializers dla Railsów 2.0.x, albo do katalogu lib dla 1.2.x (w tym wypadku trzeba też w environment.rb dodać linijkę require &amp;#8216;ascii&amp;#8217;). Zrobiłem jeszcze jedną modyfikację &amp;#8211; to_url_format powinien moim zdaniem w miejsce spacji i innych znaków wrzucać myślniki. Lepiej wygląda &amp;#8220;tytul-artykulu&amp;#8221; niż &amp;#8220;tytulartykulu&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Dzięki temu String udostępnia 2 nowe metody: `to_url_format` i `to_ascii`:&lt;br /&gt;
&lt;code&gt;&amp;lt;pre&amp;gt;
"zażółć gęślą jaźń".to_ascii #=&amp;gt; "zazolc gesla jazn"&lt;/p&gt;
&lt;p&gt;&amp;#8220;zażółć gęślą jaźń&amp;#8221;.to_url_format #=&amp;gt; &amp;#8220;zazolc-gesla-jazn&amp;#8221;&lt;br /&gt;
&lt;/pre&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Teraz wystarczy zmodyfikować lekko to_param:&lt;br /&gt;
&lt;code&gt;&amp;lt;pre&amp;gt;
  def to_param
    "#{id}-#{name.to_url_format}"
  end
&amp;lt;/pre&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Dzięki temu urle będą miały upragnioną formę: &amp;#8220;12-tytul-artykulu&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Tylko po co to id na początku? Dzięki temu obędzie się bez żadnych zmian w kontrolerach. Taki string zostanie przed wrzuceniem do bazy automatycznie skonwertowany na liczbę całkowitą. Czyli kolejna rzecz, która &amp;#8220;po prostu działa&amp;#8221; automagicznie. Jeżeli id będzie potrzebne w jakimś innym miejscu, w którym nie nastąpi konwersja można to zrobić ręcznie: params[:id].to_i, bo:&lt;br /&gt;
&lt;code&gt;&amp;lt;pre&amp;gt;
"11-jakis-napis".to_i #=&amp;gt; 11
&amp;lt;/pre&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Jaki jest minus takiej metody? Adresy mają w sobie id i tylko na podstawie tego id jest pobierany artykuł, więc czy wpiszemy /articles/11-tytul-artykulu, czy /articles/11-tralalala pobierze się ten sam artykuł.&lt;/p&gt;
&lt;p&gt;Żeby temu zapobiec najlepiej zrobić dodatkową kolumnę, na przykład &amp;#8220;permalink&amp;#8221; i wpisywać do niej przekształcony adres. Np. article.permalink = article.name.to_url_format . I zamiast po id szukać po kolumnie permalink: Article.find_by_permalink(params[:id]). Oczywiście z metody to_param też trzeba usunąć id :)&lt;/p&gt;
&lt;p&gt;Nie każdemu to drugie podejście będzie potrzebne. Dla mnie wersja z id jest o tyle lepsza, że jak nawet ktoś nie skopiuje całego adresu, albo pomyli się przy wpisywaniu (tak, czasami zdarza się, że ktoś dyktuje jakiś adres), to jest duża szansa, że dotrze na dobrą stronę. Poza tym istnieje wtedy możliwość wklejenia adresu bez dalszej części, która może być dość długa &amp;#8211; może nie trzeba będzie korzystać z serwisów typu tinyurl.&lt;/p&gt;
&lt;p&gt;&lt;ins&gt;Wrzuciłem &lt;a href="http://drogomir.com/ruby/ascii.rb"&gt;nową wersję skryptu&lt;/a&gt; działającą z nowszymi wersjami Ruby on Rails&lt;/ins&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Otwarte klasy w Rubim i ich praktyczne wykorzystanie</title>
   <link href="http://piotrsarnacki.com/2008/01/08/otwarte-klasy-w-rubim-i-ich-praktyczne-wykorzystanie" />
   <published>2008-01-08T00:00:00-08:00</published>
   <updated>2008-01-08T00:00:00-08:00</updated>
   <id>http://piotrsarnacki.com/2008/01/08/otwarte-klasy-w-rubim-i-ich-praktyczne-wykorzystanie</id>
   <content type="html">&lt;p&gt;Ruby posiada bardzo fajną właściwość nazwaną otwartymi klasami. Znaczy to tyle, że jeżeli nikt nie zamroził danej klasy/metody, można w dowolnym miejscu w kodzie nadpisać ją klasę, metodą, dodać nowe metody. Można tak nadpisać nawet klasy wbudowane w język!&lt;/p&gt;
&lt;p&gt;Na przykład taki kod (och jak ja lubię ten przykład):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Fixnum&lt;/span&gt;
  &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="n"&gt;old_plus&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;+&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;old_plus&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;4+4=1 ? nie tego się spodziewaliśmy. A właściwie nie tego się spodziewali ludzie, którzy nie rozumieją jeszcze powyższego przykładu (celowo dodałem słowo jeszcze, będę na tyle cywilizowanym i miłym człowiekiem, że spróbuję wytłumaczyć o co chodzi).&lt;/p&gt;
&lt;p&gt;Liczby całkowite są klasy Fixnum. Powyższy kod modyfikuje tą klasę. Najpierw tworzony jest alias dla metody +, żeby można było jej później używać, a następnie owa metoda zostaje nadpisana w taki sposób, żeby zwracała wynik &lt;a href="http://pl.wikipedia.org/wiki/Arytmetyka_modularna"&gt;dodawania modulo7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Do napisania tego artykułu natchnął mnie Daniel Owsiański pisząc o zjawisku roboczo nazwanym &lt;a href="http://jarmark.org/post/nie-ma-darmowych-obiadow/"&gt;version lock-in&lt;/a&gt;. Daniel ma oczywiście dużo racji i moja paranoja, o której pisałem u niego w komentarzach jest objawem przewrażliwienia mojej mózgoczaszki w pewnych kwestiach. Są jednak wypadki, w których naprawdę warto zachować zgodność z nowymi wersjami. Pomaga tutaj powyższa właściwość języka Ruby. Pisał o tym kiedyś autor bloga &lt;a href="http://errtheblog.com/"&gt;Err the Blog&lt;/a&gt; w kontekście &lt;a href="http://errtheblog.com/posts/67-evil-twin-plugin"&gt;rozszerzania możliwości pluginów&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Często w rozmowach o Ruby on Rails na różnych listach dyskusyjnych można usłyszeć, że gdy chcemy coś zmienić w danej metodzie, najlepiej przekopiować kod metody, nadpisać ją, zmienić to co trzeba i voilla. Ale nie tędy droga panie i panowie :)&lt;/p&gt;
&lt;p&gt;Wyglądałoby to mniej więcej tak. Chcemy na przykład nadpisać metodę find. Wchodzimy na &lt;a href="http://api.rubyonrails.org"&gt;Rails &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/a&gt;, znajdujemy ActiveRecord::Base#find, wklejamy kod w modelu i zmieniamy:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;#w modelu:&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;#jakiś zmodyfikowany kod finda&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Jakie minusy ma takie podejście? Gdy mamy zainstalowaną dużą liczbę pluginów nigdy nie wiadomo czy któryś z nich nie nadpisuje już metody find i wtedy nadpisując ją stracimy funkcjonalność dodaną przez plugin. Smuteczek. Poza tym gdy zmieni się kod metody find w samym frameworku również u nas będziemy musieli go zmienić.&lt;/p&gt;
&lt;p&gt;Jak to zrobić Ruby Way&amp;#8482;? Przypuśćmy, że chcemy się popastwić nad wspomnianą metodą find:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;#w modelu:&lt;/span&gt;

&lt;span class="c1"&gt;# metoda find jest metodą klasy&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:conditions&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; 
    &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:conditions&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:conditions&lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:conditions&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:conditions&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
        &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:conditions&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;merge!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:deleted&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:conditions&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blank?&lt;/span&gt;
          &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:conditions&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;deleted = ?&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
          &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:conditions&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;(&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:conditions&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;deleted = ?&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; AND &amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:conditions&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;       
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Powyższy kod dodaje do conditions warunek &amp;#8220;deleted = false&amp;#8221;, po czym wywołuje metodę find z tak zmodyfikowanymi argumentami. Czasami trzeba jednak namieszać coś w kodzie metody. Można wtedy dodać dodatkowy argument. Następnie dajemy ifa &amp;#8211; jeżeli argument zwraca true wykonujemy zmodyfikowany kod, a jeżeli nie, wykonujemy kod oryginalnej metody.&lt;/p&gt;
&lt;p&gt;Dzięki takiemu podejściu możemy w miarę łatwo upgradować pluginy i Railsy bez większego stresu :)&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Thin. Nowy lepszy mongrel. Sphinx. Nowy lepszy ferret.</title>
   <link href="http://piotrsarnacki.com/2008/01/06/thin-nowy-lepszy-mongrel-sphinx-nowy-lepszy-ferret" />
   <published>2008-01-06T00:00:00-08:00</published>
   <updated>2008-01-06T00:00:00-08:00</updated>
   <id>http://piotrsarnacki.com/2008/01/06/thin-nowy-lepszy-mongrel-sphinx-nowy-lepszy-ferret</id>
   <content type="html">&lt;p&gt;Jak donosi &lt;a href="http://rubyinside.com"&gt;Ruby Inside&lt;/a&gt;, wypuszczony został ostatnio &lt;a href="http://www.rubyinside.com/thin-a-ruby-http-daemon-thats-faster-than-mongrel-688.html"&gt;serwer szybszy od mongrela&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Chodzi o Thin, czyli połączenie EventMachine, Racka (interfejs do komunikacji z serwerem) i Mongrela (a dokładniej jego bibliotek do parsowania). Instalacja i użycie jest równie łatwe co użycie samego mongrela. `gem install thin`, a później w katalogu aplikacji `thin start`. Jedna z moich aplikacji wyciągała na mongrelu 110req/s, a na thinie około 150req/s (ab -c 10 -n 100). Różnica całkiem spora. Warto się przesiąść&lt;/p&gt;
&lt;p&gt;Sprawdziłem też ostatnio sphinxa. O &lt;a href="http://blog.zabiello.com/articles/2007/11/30/sphinx-merb-vs-ferret-rails"&gt;sphinxie pisał już Jarosław Zabiełło&lt;/a&gt; i bardzo mnie powyższym artykułem zainteresował. Do tej pory używałem ferreta, ale odpalanie nie do końca stabilnych i niezbyt szybkich serwerów dla każdej aplikacji było trochę uciążliwe. &lt;a href="http://brainspl.at"&gt;Ezra Zygmuntowicz&lt;/a&gt;, twórca frameworka &lt;a href="http://merbivore.com"&gt;Merb&lt;/a&gt; wypowiadał się, że miał dość duże kłopoty ze stabilnością ferreta, które skończyły się po przejściu na sphinxa.&lt;/p&gt;
&lt;p&gt;Instalacja opisana jest &lt;a href="http://blog.evanweaver.com/files/doc/fauna/ultrasphinx/files/README.html"&gt;w dokumentacji&lt;/a&gt;, więc nie będę powtarzał :) Mogę dodać jedynie, że czasami używając bazy postgresql trzeba ręcznie dodać language poleceniem `createlang plpgsql nazwa_bazy` (chodzi o nowy język proceduralny dla postgresa). Serwer odpalony przez sphinxa jest bardzo wydajny i zajmuje kilkadziesiąt razy mniejsze ilości pamięci operacyjnej. Jedynym minusem jest to, że nie ma niektórych opcji, które posiada ferret &amp;#8211; jeżeli ktoś ich potrzebuje proponuję sprawdzenie Solr.&lt;/p&gt;
&lt;p&gt;&lt;ins&gt;Zapomniałem wspomnieć, że ultrasphinx działa z bazami postgresql &amp;gt;= 8.2&lt;/ins&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>God.rb</title>
   <link href="http://piotrsarnacki.com/2008/01/05/god-rb" />
   <published>2008-01-05T00:00:00-08:00</published>
   <updated>2008-01-05T00:00:00-08:00</updated>
   <id>http://piotrsarnacki.com/2008/01/05/god-rb</id>
   <content type="html">&lt;p&gt;Wpadłem jakiś czas temu na bardzo fajną aplikację wspomagającą deployment &lt;a href="http://rubyonrails.org"&gt;Ruby on Rails&lt;/a&gt;. &lt;a href="http://god.rubyforge.org/"&gt;god.rb&lt;/a&gt; monitoruje procesy i w razie potrzeby (out of memory, zbyt dużo pożartego cpu, pad serwera) restartuje je. Config, jak i sam god oczywiście, jest napisany w Ruby&amp;#8217;im, więc można sobie nieco ułatwić pracę (wspominałem kiedyś, że nigdy nie miałem dość samozaparcia, żeby się nauczyć pisać skrypty powłoki pod linuxem?).&lt;/p&gt;
&lt;p&gt;Aby go zainstalować należy jako root (pod ubuntu sudo) wykonać polecenie:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;gem install god
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Na stronie goda są podane różne configi, ale nie zakładają one scenariusza: &amp;#8220;sporo aplikacji, taki sam config, jak to zrobić automatycznie?&amp;#8221;. Pomyślałem sobie, że napiszę prosty config, który będzie pobierał listę aplikacji Rails z pliku i dodawał mongrele do monitorowania. Gdy na serwerze siedzi więcej aplikacji głupio byłoby kopiować config dla każdej z nich i ręcznie ustawiać numery portów i katalogi.&lt;/p&gt;
&lt;p&gt;Pełny config z komentarzami wyjaśniającymi o co kaman (&lt;a href="http://drogomir.com/god.txt"&gt;wersja tekstowa&lt;/a&gt;):&lt;br /&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# run with:  god -c /path/to/config.god&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&amp;lt;span class="c1"&amp;gt;# run with:  god -c /path/to/config.god&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yaml&amp;#39;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class="c1"&gt;# otwieramy plik ze spisem aplikacji i robimy z nich tablicę&lt;/span&gt;&lt;br /&gt;
&lt;span class="n"&gt;rails_apps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/etc/god/applications&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;r&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readlines&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;collect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rails_app&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rails_apps&lt;/span&gt;&lt;br /&gt;
  &lt;span class="c1"&gt;# załadujmy ustawienia danej aplikacji&lt;/span&gt;&lt;br /&gt;
  &lt;span class="c1"&gt;# korzystać będziemy z konfiguracji dla clusterów&lt;/span&gt;&lt;br /&gt;
  &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;&lt;span class="caps"&gt;YAML&lt;/span&gt;&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rails_app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;config/mongrel_cluster.yml&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;br /&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;servers&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# domyślnie startujemy jeden serwer&lt;/span&gt;&lt;/p&gt;
&lt;span class="c1"&gt;# teraz stawiamy mongrela dla każdego portu&lt;/span&gt;
&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;port&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;to_i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;port&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;to_i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;servers&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;to_i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="no"&gt;God&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;watch&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="n"&gt;pid_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rails_app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;pid_file&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\.pid$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.pid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rails_app&lt;/span&gt;&lt;span class="o"&gt;[/[^&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="o"&gt;/]*&lt;/span&gt;&lt;span class="vg"&gt;$/&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-mongrel-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# dodajemy mongrele danej aplikacji do grupy o nazwie takiej samej jak nazwa katalogu&lt;/span&gt;
&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rails_app&lt;/span&gt;&lt;span class="o"&gt;[/[^&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="o"&gt;/]*&lt;/span&gt;&lt;span class="vg"&gt;$/&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt; &lt;span class="c1"&gt;# default      &lt;/span&gt;
&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;mongrel_rails start -c &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rails_app&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -p &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; \&lt;/span&gt;
&lt;p&gt;&lt;span class="s2"&gt;        -P &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;pid_file&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;  -e &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;environment&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;  -d&amp;quot;&lt;/span&gt;&lt;br /&gt;
      &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;mongrel_rails stop -P &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;pid_file&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;
      &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;restart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;mongrel_rails restart -P &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;pid_file&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;
      &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_grace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;&lt;br /&gt;
      &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;restart_grace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;&lt;br /&gt;
      &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;behavior&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:clean_pid_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_if&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:process_running&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;restart_if&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;restart&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="n"&gt;restart&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:memory_usage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;above&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;megabytes&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# 3 out of 5 intervals&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;restart&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cpu_usage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;above&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;percent&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# lifecycle&lt;/span&gt;
&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lifecycle&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:flapping&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;within&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minute&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:unmonitored&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retry_in&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retry_times&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retry_within&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hours&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;p&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;kuniec&amp;quot;&lt;/span&gt;&lt;br /&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;/notextile&gt;&lt;/p&gt;
&lt;p&gt;Aplikacje w pliku, który jest odczytywany na początku to ścieżki wpisane w kolejnych liniach. gdy wystartujemy goda poleceniem `god -c /sciezka/do/configu` uruchomią się mongrele dla wszystkich aplikacji. Ważne: powyższy config korzysta z konfiguracji stworzonych dla clusterów. Aby ją stworzyć należy wykonać w katalogu aplikacji:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;mongrel_rails cluster::configure -e production -p 8000 -N 2
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Powyższa komenda wygeneruje plik `mongrel_cluster.yml` w katalogu config aplikacji Ruby on Rails. Zostanie ustawione środowisko production, port 8000 i 2 serwery (na portach 8000 i 8001).&lt;/p&gt;
&lt;p&gt;Żeby szczęście było pełne i niczym niezmącone można jeszcze pokusić się o napisanie skryptu startowego init.d. Oczywiście w ruby&amp;#8217;im. A jakie to proste &lt;a href="http://blog.zabiello.com/articles/2006/09/29/uniksowy-skrypt-etc-init-d-w-ruby"&gt;pokazywał kiedyś Jarek Zebiełło&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;W razie wątpliwości pytajcie o szczegóły w komentarzach.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Nieinwazyjny javascript razem z Ruby on Rails</title>
   <link href="http://piotrsarnacki.com/2007/12/25/nieinwazyjny-javascript-razem-z-ruby-on-rails" />
   <published>2007-12-25T00:00:00-08:00</published>
   <updated>2007-12-25T00:00:00-08:00</updated>
   <id>http://piotrsarnacki.com/2007/12/25/nieinwazyjny-javascript-razem-z-ruby-on-rails</id>
   <content type="html">&lt;p&gt;Jakiś czas temu Riddle napisał o tym dlaczego &lt;a href="http://perfectionorvanity.com/2007/11/07/kto-normalny-wylacza-javascript/"&gt;nie można zakładać, że ktoś ma włączony javascript&lt;/a&gt;. Temat był już wcześniej wiele razy poruszany, mi bardzo podobał się artykuł Chrisa Heilmanna &lt;a href="http://icant.co.uk/articles/seven-rules-of-unobtrusive-javascript/"&gt;The seven rules of Unobtrusive JavaScript&lt;/a&gt;. Zachęcam do zapoznania się z tymi dwoma tekstami &amp;#8211; pomogą zrozumieć dlaczego &amp;#8220;tracić&amp;#8221; czas na rozwiązywanie problemów, o których napiszę poniżej. Streszczając krótko artykuły mogę napisać, że jeżeli to możliwe, należy pisać aplikację tak, żeby działała bez włączonego javascriptu. Skrypty mogą nie działać z kilku powodów:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;użytkownik ma wyłączony javascript w przeglądarce&lt;/li&gt;
	&lt;li&gt;wystąpi błąd w kodzie (nie możemy sprawdzić kodu na wszystkich urządzeniach)&lt;/li&gt;
	&lt;li&gt;strona nie załaduje się do końca&lt;/li&gt;
	&lt;li&gt;boty wyszukiwarek nie używają javascriptu, więc w skrajnym wypadku możemy uniemożliwić zindeksowanie naszej strony&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Jak to wygląda w Railsach?&lt;/p&gt;
&lt;p&gt;Railsy są wyposażone w zestaw helperów generujących różnego rodzaju kawałki kodu javascript. Pomysł jest z pozoru bardzo fajny. Początkujący mogą szybko zacząć używać javascriptu razem z dobrodziejstwami, które daje nam Ajax bez znajomości samego języka. Jest jednak sporo minusów używania helperów:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;generują one dodatkowy niepotrzebny kod javascript. jeżeli na stronie mamy kilkadziesiąt linków, a każdy z nich ma wklejony kod `onclick=&amp;#8220;new Ajax.Request(&amp;#8216;/controller/action?n=33&amp;#8217;, {asynchronous:true, evalScripts:true, onComplete:function(request){undoRequestCompleted(request)}}); return false;&amp;#8221;` strona będzie ważyć dużo więcej.&lt;/li&gt;
	&lt;li&gt;javascript w nich użyty jest &amp;#8220;inwazyjny&amp;#8221;. Jeżeli javascript nie będzie działał, to element wygenerowany w taki sposób również nie zadziała^1^&lt;/li&gt;
	&lt;li&gt;ciężko jest się przy nich trzymać zasady &lt;span class="caps"&gt;DRY&lt;/span&gt;. Mając 5 linków wygenerowanych metodą link_to_remote z takimi samymi opcjami, za każdym razem gdy coś musimy zmienić, zmieniamy to w 5 miejscach. Powinno się oczywiście napisać helpera, który wygeneruje link z danymi opcjami. Tylko chyba nie tędy droga &amp;#8211; w dalszej części artykułu postaram się pokazać dlaczego nieinwazyjny javascript jest lepszy w tego typu zadaniach.&lt;/li&gt;
	&lt;li&gt;z mojego doświadczenia wynika, że bardzo często, gdy ilość kodu się powiększa i javascript generowany przez railsy miesza się z tym z plików js można łatwo się pogubić. Tyczy się to także różnego rodzaju api &amp;#8211; na przykład google maps. Najłatwiej działać, gdy javascript jest odseparowany od kodu railsów&lt;/li&gt;
	&lt;li&gt;jesteśmy związani z jedną biblioteką (w tym wypadku prototype+script.aculo.us) &amp;#8211; jeżeli chcemy zamienić ją na coś innego (ja ostatnio przesiadłem się na jQuery, zastanawiałem się też nad &lt;span class="caps"&gt;YUI&lt;/span&gt;) helpery przestaną działać &amp;#8211; można je oczywiście przepisać, ale komu by się chciało. &lt;span class="caps"&gt;DHH&lt;/span&gt; nie zamierza nic w tej kwestii zmieniać, więc na zmianę tego w Railsach nie ma co czekać.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Jakie są minusy? Trzeba lepiej poznać javascript (właściwie dla mnie to nie jest minus, ale dla niektórych być może tak). Nie jest to jednak przeszkoda nie do pokonania dla początkujących. Javascript, który jest potrzebny do zadań możliwych do wykonania z użyciem samych helperów nie jest z reguły przesadnie trudny do nauczenia. Ratunkiem dla osób, które nadal chcą korzystać z helperów jest &lt;a href="http://www.ujs4rails.com/resources/quickstart-guide"&gt;plugin &lt;span class="caps"&gt;UJS&lt;/span&gt;&lt;/a&gt;. Jeżeli bardzo nie chcesz pisać wszystkiego w czystym javascripcie, to jest to bardzo fajne połączenie prostoty helperów i zalet nieinwazyjnego javascriptu. Jest ona jednak pisana dla Prototype&amp;#8217;a, więc tak jak w ostatnim punkcie z powyższej listy można o niej zapomnieć, jeżeli używana jest jakakolwiek inna biblioteka.&lt;/p&gt;
&lt;p&gt;&lt;ins&gt;W komentarzach apohllo zauważył, że plugin &lt;span class="caps"&gt;UJS&lt;/span&gt; nie jest już rozwijany. Używacie na własną odpowiedzialność. :)&lt;/ins&gt;&lt;/p&gt;
&lt;p&gt;Przejdę do przykładów, bo przecież nie samą teorią człowiek żyje.&lt;/p&gt;
&lt;p&gt;Przerzuciłem się ostatnio na jQuery i chyba przy niej zostanę. Rozumiem jednak, że większość użytkowników railsów jest związana z Prototype&amp;#8217;em, więc kod będę podawał w dwóch wersjach, dla Prototype&amp;#8217;a i jQuery.&lt;/p&gt;
&lt;p&gt;Na początek wprowadzenie. Co zrobić, żeby wyrzucić z htmla (i railsów) wstawki Javascript? Wszystko wstawiamy do aplikacji używając zdarzeń. Przypuśćmy, że mamy linka o id=&amp;#8220;someLink&amp;#8221;. Zamiast dopisania onclick:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;onclick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;alert(&amp;#39;Klik!&amp;#39;); return false;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;należy użyć:&lt;/p&gt;
&lt;p&gt;Prototype:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;  &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;someLink&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;click&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Klik!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;jQuery:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#someLink&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Klik!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Oba przykłady dodają zdarzenie uaktywniane kliknięciem w linka. Ostatnia linijka w obu funkcjach, które są wykonywane po kliknięciu (nazywane są z reguły handlerami) jest wstawiona po to, żeby kliknięcie linka nie przeładowało strony.&lt;/p&gt;
&lt;p&gt;Kod taki w aplikacji Rails można wrzucić do pliku application.js, lub jakiegoś specyficznego pliku js ładowanego na danej stronie. Należy też go załadować dopiero po wczytaniu się całego dokumentu. Normalnie coś takiego uzyskiwało się wpisując w body: `onload=&amp;#8220;jakasFunkcjaJavascript();&amp;#8221;`, ale takie dodawanie jest passe, więc:&lt;/p&gt;
&lt;p&gt;Prototype:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;  &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;load&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//kod który wykona się po załadowaniu strony&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// lub zdefiniowana wcześniej funkcja, która wykona się po załadowaniu strony&lt;/span&gt;
  &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;load&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jakasFunkcjaJavascript&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;jQuery:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//kod który wykona się po załadowaniu strony&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  
  &lt;span class="c1"&gt;// lub zdefiniowana wcześniej funkcja, która wykona się po załadowaniu strony&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jakasFunkcjaJavascript&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;//powyższe przykłady, to skrócone wersje document.ready:&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Dzięki tym konstrukcjom mamy pewność, że kod wykona się dopiero gdy załaduje się cały dokument, a nie w momencie, gdy dołączony jest plik js.&lt;/p&gt;
&lt;p&gt;Teraz przykład prostego zapytania ajax (przykład z dokumentacji railsów):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;  &lt;span class="n"&gt;link_to_remote&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:url&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:action&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;action&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; 
    &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;alert(&amp;#39;Not found...? Wrong URL...?&amp;#39;)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;:failure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;alert(&amp;#39;HTTP Error &amp;#39; + request.status + &amp;#39;!&amp;#39;)&amp;quot;&lt;/span&gt;
  &lt;span class="c1"&gt;# Wygeneruje: &amp;lt;a href=&amp;quot;#&amp;quot; onclick=&amp;quot;new Ajax.Request(&amp;#39;/testing/action&amp;#39;, {asynchronous:true, evalScripts:true,&lt;/span&gt;
  &lt;span class="c1"&gt;#            on404:function(request){alert(&amp;#39;Not found...? Wrong URL...?&amp;#39;)},&lt;/span&gt;
  &lt;span class="c1"&gt;#            onFailure:function(request){alert(&amp;#39;HTTP Error &amp;#39; + request.status + &amp;#39;!&amp;#39;)}}); return false;&amp;quot;&amp;gt;hello&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Jak widać powyżej wygenerowanego kodu jest całkiem sporo. Jeżeli będzie trzeba wstawić taki link w paru miejscach dobrze by było napisać swojego własnego helpera, który automatycznie będzie wklejał komunikaty o błędach.&lt;/p&gt;
Jak można to zrobić lepiej? Na początek wystarczy stworzyć zwykłego linka z jakąś klasą, lub id:
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;  &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:action&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;action&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;:class&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ajax&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Teraz trzeba użyć trochę javascriptu :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;        &lt;span class="nx"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a.ajax&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;click&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Ajax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;href&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;asynchronous&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;evalScripts&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
              &lt;span class="nx"&gt;on404&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Not found...? Wrong URL...?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt; 
              &lt;span class="nx"&gt;onFailure&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;HTTP Error &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)}});&lt;/span&gt; 
            &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Na początku pobieramy wszystkie linki z klasą ajax i dla każdego z nich wywołujemy funkcję `Event.observe(element, &amp;#8216;click&amp;#8217;&amp;#8230;.`. Dalszy kod wykona się więc po kliknięciu w danego linka. W tym wypadku wykonujemy zapytanie ajaxowe (`new Ajax.Request`). Pierwszy argument to atrybut href linka (uwaga, kod ten nie zadziała w starszych wersjach prototype&amp;#8217;a, które niepoprawnie obsługiwały this w tego typu funkcji). Reszta kodu to standardowe opcje, po więcej odsyłam do dokumentacji Prototype.&lt;/p&gt;
&lt;p&gt;A w jQuery wyglądać to będzie tak:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a.ajax&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(){&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ajax&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;dataType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;script&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;beforeSend&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setRequestHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Accept&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);},&lt;/span&gt;
        &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
          &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Error loading page&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;     
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Kod zasadniczo robi to samo, co poprzedni przykład. Można przy okazji porównać prostotę jQuery i porównać ją z Prototype&amp;#8217;em (ostatnio dużo się w tej bibliotece pozmieniało, a ja nie jestem na bieżąco, więc jeżeli ktoś zna lepszy sposób na napisanie czegoś takiego, to proszę o komentarz). Skomentuję tylko atrybuty dataType i beforeSend w funkcji ajax(). Ustawiając je w taki sposób przekazujemy serwerowi, że chcemy dostać odpowiedź jako skrypt i akceptujemy typ &lt;span class="caps"&gt;MIME&lt;/span&gt; &amp;#8220;text/javascript&amp;#8221;. Należy te 2 rzeczy dodać, ponieważ inaczej nie będzie renderować się plik &lt;span class="caps"&gt;RJS&lt;/span&gt;. Więcej na ten temat w artykule &lt;a href="http://mad.ly/2007/05/17/jquery-ajax-rails/"&gt;jQuery Ajax + Rails&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Rozwiązanie proste i efektywne. Żeby link wykonał javascript wystarczy dodać do niego klasę ajax. Jeżeli strona i kody javascript nie wczytają się, link dalej będzie działał poprawnie. Przy założeniu, że poprawnie obsłużymy wszystko w kontrolerze. Służy do tego metoda `respond_to`&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;  &lt;span class="n"&gt;respond_to&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt; &lt;span class="c1"&gt;# jeżeli to zapytanie wykonane ajaxem uruchomi się plik RJS&lt;/span&gt;
    &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="c1"&gt;# w przeciwnym wypadku wyrenderowany zostanie template rhtml&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Więcej o takim sposobie renderowania templatów pisał na przykład &lt;a href="http://weblog.jamisbuck.org/2006/3/27/web-services-rails-style"&gt;Jamis Buck&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Można też w podobny sposób zamienić zwykłą formę na taką wysyłaną ajaxem:&lt;/p&gt;
&lt;p&gt;Prototype:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;     &lt;span class="nx"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;form.ajax&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;submit&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Ajax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;action&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nx"&gt;asynchronous&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="nx"&gt;evalScripts&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt; 
          &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Powyższy kod jest bardzo podobny do poprzedniego przykładu. Różnica polega na tym, że zdarzeniem nie jest &amp;#8216;click&amp;#8217; tylko &amp;#8216;submit&amp;#8217; i jako parametry podajmy wynik funkcji `Form.serialize(this)` &amp;#8211; zbiera ona wartości pól i zwraca string typu: &amp;#8220;pole1=wartosc1&amp;amp;pole2=wartosc2&amp;#8221;&lt;/p&gt;
&lt;p&gt;jQuery:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;form.ajax&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;ajaxForm&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;dataType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;beforeSend&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setRequestHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Accept&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);},&lt;/span&gt;
    &lt;span class="nx"&gt;resetForm&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;W jQuery najłatwiej skorzystać z pluginu &lt;a href="http://www.malsup.com/jquery/form/"&gt;jQuery Form&lt;/a&gt; &amp;#8211; załatwia on za nas wszelkie formalności ;-)&lt;/p&gt;
&lt;p&gt;W ten sposób zmiana jakiegoś linka lub formy na jego ajaxową formę to kwestia dodania jednej klasy. Można oczywiście napisac wiele takich funkcji dla różnych przypadków, dowiązanych do tagów z innymi klasami, lub z konkretnym id.&lt;/p&gt;
&lt;p&gt;Na koniec krótkie podsumowanie.&lt;/p&gt;
&lt;p&gt;Kod javascript dodajemy do aplikacji tak, żeby nie zablokować dostępu w wypadku braku jego wykonania. Zapomnieć można o wszelakich &amp;#8220;onclick&amp;#8221; i innych tego typu sprawach. Wszystko powinno być dołączone jako zdarzenia. Dzięki temu zmniejsza i upraszcza się kod railsów i ten przez nie generowany.&lt;/p&gt;
&lt;p&gt;Warto obejrzeć również plugin &lt;a href="http://www.danwebb.net/2006/11/24/minusmor-released"&gt;MinusMOR&lt;/a&gt;, który zmienia trochę podejście do javascriptu. Zamiast plików rjs, w których używamy rubiego zamienianego później na javascript, mamy pliki ejs, w których wpisujemy kod javascript z możliwością wstawiania kodu rubiego. Tak samo jak w rhtmlu poprzez &amp;lt;% %&amp;gt;.&lt;/p&gt;
&lt;p&gt;&lt;del&gt;W komentarzach apohllo zauważył, że plugin MinusMOR nie jest już rozwijany. Trzeba o tym pamiętać zaczynając go używać. Z drugiej strony widziałem kod pluginu i rejestruje on tylko nowe rozszerzenie &amp;#8220;ejs&amp;#8221;. Na początku szukane będą pliki z tym rozszerzeniem, a jeżeli ich nie będzie Railsy wyrenderują &lt;span class="caps"&gt;RJS&lt;/span&gt;. W każdym razie zaczynacie używać na własną odpowiedzialność. :)&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;Zapraszam do komentowania &amp;#8211; prosiłbym o opinie dotyczące tego typu artykułów. Czy są zrozumiałe? Czy przydają się wam? Konstruktywna krytyka mile widziana. :)&lt;/p&gt;

&lt;hr/&gt;
&lt;ol&gt;
	&lt;li&gt;W tym miejscu trzeba zaznaczyć, że jest możliwość wygenerowania linku, czy formy, która będzie działała przy wyłączonym javascripcie, ale niewiele osób o tym wie i z tego korzysta. I trzeba dopisać 2 url, który z reguły jest taki sam &amp;#8211; łamana jest zasada &lt;span class="caps"&gt;DRY&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;</content>
 </entry>
 
 <entry>
   <title>jQuery vs Prototype</title>
   <link href="http://piotrsarnacki.com/2007/09/04/jquery-vs-prototype" />
   <published>2007-09-04T00:00:00-07:00</published>
   <updated>2007-09-04T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2007/09/04/jquery-vs-prototype</id>
   <content type="html">&lt;p&gt;Kilka dni temu wrzuciłem &lt;a href="http://blog.drogomir.com/articles/2007/08/31/dlaczego-jquery"&gt;notkę promującą jQuery&lt;/a&gt;. Często podczas porównywania narzędzi przeżywam pewien rodzaj euforii, która nie pozwala mi na obiektywne rozpatrzenie wszystkich za i przeciw. Przez chwilę wydaje mi się, że znalazłem narzędzie idealne (co oczywiście jest okropnym nadużyciem. jak ogólnie wiadomo idealny jest tylko polski rząd i jego sukcesy na przestrzeni ostatnich 2 lat &amp;#8211; widziałem w reklamach).&lt;/p&gt;
&lt;p&gt;W każdym razie ochłonąłem trochę i ze spokojem buddyjskiego mnicha (mówiłem już, że jestem oazą spokoju?) przetrząsnąłem cały internet w poszukiwaniu informacji. Próbowałem nawet wywołać flamewara na google groups. I biednie jest. Niewiele znalazłem. Wszędzie jakieś artykuły, których sens jest mniej więcej taki: &amp;#8220;Przez długi czas używałem prototype(jQuery) i stwierdziłem, że muszę dać jQuery(prototype) szansę. jQuery jest prototype killerem!&amp;#8221;. Nie licząc fajnego artykułu &lt;a href="http://blogs.pathf.com/agileajax/2007/08/jquery-vs-proto.html"&gt;na agile ajax&lt;/a&gt;. Stety albo niestety autor nie do końca chce wyjawić, które podejście jest dla niego lepsze. Aczkolwiek pewną wskazówką może być przesiadka na jQuery, a nie odwrotnie.&lt;/p&gt;
&lt;p&gt;No i &lt;a href="http://alternateidea.com/blog/articles/2006/05/23/prototype-inheritance-madness"&gt;krytyka modelu dziedziczenia zastosowanego w prototype&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Pozwoliłem sobie zrobić własne porównanie. Mam nadzieję, że chociaż w małym procencie obiektywne. ;-)&lt;/p&gt;
&lt;h3&gt;Manipulacja &lt;span class="caps"&gt;DOM&lt;/span&gt; i &lt;span class="caps"&gt;CSS&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;Na tym polu zdecydowanie wygrywa jQuery. Twórca zapewnił coś, co nazywa się &amp;#8220;chainability&amp;#8221;. Metody manipulujące elementami HTMLa można wywoływać w łańcuchach, na przykład &lt;code&gt;jQuery('#some').show('slow').css('background-color', 'white').add('&amp;lt;p&amp;gt;Dodatkowy paragraf!&amp;lt;/p&amp;gt;')&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Jeżeli chodzi o same selektory, to od wersji 1.5.0 prototype ma obsługę selektorów &lt;span class="caps"&gt;CSS&lt;/span&gt;, więc nie ma już problemu z wybieraniem elementów w bardzo fikuśnych konfiguracjach ;-)&lt;/p&gt;
&lt;p&gt;jQuery ma jeszcze jedną ciekawą właściwość. Wybrane elementy zawsze zwracane są w tablicy dzięki czemu biblioteka sama zadba o przeiterowanie po kolekcji i zaaplikowaniu metody do każdego jej elementu. Na przykład zamiast napisać (prototype):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;click&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Możemy użyć:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;jQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Wydajność&lt;/h3&gt;
&lt;p&gt;Na tym polu prototype mocno dawał w kość jQuery, ale sytuacja się poprawiła. W poprzednim poście zamieściłem 2 screeny, na których można zauważyć, że obecnie jQuery jest jakieś 2 razy wolniejsza (oczywiście jeżeli chodzi o same selektory). Dla innych metod podejrzewam, że będzie podobnie (dla each i map na pewno &amp;#8211; jeżeli ktoś się bardzo nudzi może się pobawić i zrobić więcej benchmarków).&lt;/p&gt;
&lt;p&gt;Ale statystyki na szczęście nie zawsze oddają to, co dzieje się w rzeczywistości. Jeżeli ktoś dysponuje odpowiednio wolnym komputerem (kilkaset mhz najlepiej), łatwo może sprawdzić, że efekty napisane w &lt;a href="http://script.aculos.us"&gt;script.aculo.us&lt;/a&gt; bardziej &amp;#8220;mulą&amp;#8221; kompa niż ich odpowiedniki w jQuery (linux, firefox2 &amp;#8211; wydaje mi się, że na innych systemach i przeglądarkach będzie podobnie). Szczególnie widać tą różnicę przy wszelakich tworach drag&amp;amp;drop.&lt;/p&gt;
&lt;h3&gt;Rozszerzenia&lt;/h3&gt;
&lt;p&gt;W tym miejscu wystarczy spojrzeć na &lt;a href="http://docs.jquery.com/Plugins"&gt;listę rozszerzeń dla jQuery&lt;/a&gt;. Tego chyba nic w tej chwili  nie pobije. Rozszerzenia z zakresu UI dla prototypa (a właściwie script.aculo.us) wyglądają biednie w takim porównaniu.&lt;/p&gt;
&lt;p&gt;Duży plus dla jQuery.&lt;/p&gt;
&lt;h3&gt;Społeczność&lt;/h3&gt;
&lt;p&gt;jQuery jest niewątpliwie biblioteką popularniejszą. Niektórzy mówią, że to chwilowy buzz (znowu ulegam modzie frameworkowej? :).&lt;/p&gt;
&lt;p&gt;Prototype z kolei, chociaż mniej popularny, ma mocne wsparcie w społeczności RoR i wsparcie Rails Core Team. Podobno niektórzy pracują nad czymś co można nazwać &amp;#8220;jQuery on Rails&amp;#8221;, ale nadal prototype jako jedyny jest dostępny &amp;#8220;out of the box&amp;#8221;.&lt;/p&gt;
&lt;h3&gt;Rozszerzenia języka.&lt;/h3&gt;
&lt;p&gt;Prototype dodaje do javascriptu bardzo dużo metod, wzorowanych na metodach rubiego,  ułatwiających pracę z wbudowanymi typami. Między innymi moduł Enumerable, Hash, sporo metod do String, Date i dużo dużo więcej.&lt;/p&gt;
&lt;p&gt;jQuery z kolei udostępnia tylko kilka metod (each, map, trim, grep&amp;#8230;). Oczywiście można dodać swoje metody, ale w tym miejscu trzeba wspomnieć o filozofii jQuery.&lt;/p&gt;
&lt;p&gt;Prototype dodaje nowe metody do poszczególnych klas, dzięki czemu można napisać na przykład:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;W jQuery wyglądałoby to w ten sposób:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;jQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Jeszcze nie jestem do końca pewien, które podejście mi bardziej pasuje. Być może połączenie jQuery z podejściem prototype?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;jQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iterator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;jQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;iterator&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;I już można użyć konstrukcji analogicznej do prototype. Po paru zmianach można dodać też resztę funkcji z Enumerable &amp;#8211; po odpowiednim przygotowaniu potrzeba niewiele więcej niż copy&amp;amp;paste. Podobnie łatwo będzie z resztą rozszerzeń.&lt;/p&gt;
&lt;p&gt;Można również zostać przy podejściu jQuery i napisać metody działające mniej więcej tak:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;jQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* jakiś kod */&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;ins&gt;Chwilę po dodaniu tego posta przypadkiem trafiłem na fajny artykuł, który pokazuje dlaczego &lt;a href="http://mg.to/2006/05/20/prototype-vs-web-2-0"&gt;podejście a&amp;#8217;la prototype jest po ciemnej stronie mocy&lt;/a&gt; ;-)&lt;/ins&gt;&lt;/p&gt;
&lt;p&gt;W następnym poście opiszę jak można to szybko, łatwo i przyjemnie zrobić. (o ile starczy mi czasu i samozaparcia)&lt;/p&gt;
&lt;p&gt;Od wersji 1.6.0 prototype dostanie też parę bardzo przydatnych funkcji i narzędzi. Między innymi DOMBuilder, custom events, wrap() &amp;#8211; bardzo podobają mi się takie smaczki. Więcej informacji w &lt;a href="http://www.prototypejs.org/2007/8/15/prototype-1-6-0-release-candidate"&gt;notce na stronie prototype&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Podsumowując ten punkt, jeżeli chcemy intensywnie korzystać z wymienionych powyżej metod (polecam przejrzeć &lt;a href="http://www.prototypejs.org/api/"&gt;dokumentację prototype&lt;/a&gt;) lepszym wyborem będzie prototype. Z zaznaczeniem, że sporo z tych funkcji można bardzo łatwo przenieść do jQuery ;-)&lt;/p&gt;
&lt;h3&gt;Materiały&lt;/h3&gt;
&lt;p&gt;Przez pewien czas, dawno dawno temu, prototype nie miał prawie dokumentacji, co mogło niektórych zniechęcać. Aktualnie można powiedzieć, że zarówno prototype, jak i script.aculo.us są dobrze udokumentowane i raczej nikt nie będzie miał problemu z nauką. Tak samo zresztą jest z jQuery.&lt;/p&gt;
&lt;p&gt;Oba framweorki doczakały się książek.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;jQuery:
	&lt;ul&gt;
		&lt;li&gt;&lt;a href="http://www.amazon.com/Learning-jQuery-Interaction-Development-JavaScript/dp/1847192505/ref=pd_bbs_sr_1/102-4639170-4767356?ie=UTF8&amp;amp;s=books&amp;amp;qid=1188905165&amp;amp;sr=8-1"&gt;Learning jQuery&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://www.amazon.com/jQuery-Reference-Guide-Karl-Swedberg/dp/1847193811/ref=pd_bbs_sr_2/102-4639170-4767356?ie=UTF8&amp;amp;s=books&amp;amp;qid=1188905165&amp;amp;sr=8-2"&gt;jQuery Reference Guide&lt;/a&gt;&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
	&lt;li&gt;prototype:
	&lt;ul&gt;
		&lt;li&gt;&lt;a href="http://www.amazon.com/Prototype-Scriptaculous-Action-Dave-Crane/dp/1933988037/ref=pd_bbs_sr_1/102-4639170-4767356?ie=UTF8&amp;amp;s=books&amp;amp;qid=1188905195&amp;amp;sr=8-1"&gt;Prototype and Scriptaculous in Action&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://www.amazon.com/Prototype-script-aculo-us-Never-JavaScript-Could/dp/1934356018/ref=pd_bbs_2/102-4639170-4767356?ie=UTF8&amp;amp;s=books&amp;amp;qid=1188905195&amp;amp;sr=8-2"&gt;Prototype and script.aculo.us: You Never Knew JavaScript Could Do This!&lt;/a&gt;&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Podsumowanie&lt;/h3&gt;
&lt;p&gt;Ciężko mi wybrać ten lepsiejszy framework. I na pewno wybór ten nie będzie obiektywny &amp;#8211; każdy musi sam rozważyć czego oczekuje po bibliotece.&lt;/p&gt;
&lt;p&gt;Wydaje mi się, że przy projekcie, który w tym momencie zaczynam bardziej sprawdzi się jQuery. Mam w głowie ułożony zaawansowany interfejs użytkownika i nie chciałbym żadnych zgrzytów, a jak pokazuje przykład scriptaculous, płynność różnych efektów nie ma wiele wspólnego z szybkością biblioteki.&lt;/p&gt;
&lt;p&gt;Z drugiej strony jeżeli ktoś uważa, że będzie wykonywał po stronie użytkownika sporo obliczeń, pętli, działania na dużych tablicach itp. itd. prawdopodobnie dobrym wyborem będzie prototype &amp;#8211; nie dość, że jest łatwiej wykonywać te czynności, to jeszcze wszystko działa szybciej.&lt;/p&gt;
&lt;p&gt;Mam nadzieję, że powyższy opis pomoże w wybraniu biblioteki lepszej do rozwiązania danego problemu. Jeżeli pominąłem jakieś soczyste właściwości którejś z bibliotek proszę o komentarz &amp;#8211; uzupełnię porównanie :)&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Książki</title>
   <link href="http://piotrsarnacki.com/2007/09/01/ksia%CC%A8z%CC%87ki" />
   <published>2007-09-01T00:00:00-07:00</published>
   <updated>2007-09-01T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2007/09/01/książki</id>
   <content type="html">&lt;p&gt;Zdarza mi się ostatnimi czasy przeczytać komentarz, że książki programistyczne są już niepotrzebne, bo i tak wszystko jest w necie. A nawet, cytuję: &amp;#8220;w książkach to jest naćkane, jak chcesz konkretów to nie kupuj książek&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Całkiem głupie to ;-)&lt;/p&gt;
&lt;p&gt;Dla mnie książki programistyczne są niezastąpione. I najchętniej kupiłbym sobie połowę pozycji dostępnych na rynku. Po czym prawdopodobnie 1/3 bym nie przeczytał, ale to już inna kwestia. Ogólnie rzecz biorąc większości rzeczy zawartych w książkach można pewnie nauczyć się samemu, z lekką pomocą wujka Googla. Ale o ile przyjemniej przeczytać jak to robią profesjonaliści (o ile nikt nie da się nabrać niektórym &lt;a href="http://helion.pl/ksiazki/cruby.htm"&gt;profesjonalistom inaczej&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Po takim wstępie nie mogę się nie pochwalić, że jakiś czas temu kupiłem wspólnie z ludźmi od nowego projektu (na razie nie zapeszam. za jakiś czas pewnie coś napiszę) parę bardzo fajnych książek.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://www.amazon.com/Dont-Make-Me-Think-Usability/dp/0321344758/ref=pd_bbs_sr_1/103-1673275-3760642?ie=UTF8&amp;amp;s=books&amp;amp;qid=1188647369&amp;amp;sr=8-1"&gt;Don&amp;#8217;t make me think&lt;/a&gt; &amp;#8211; genialna książka o usability. Bardzo krótka i treściwa. Autor starał się napisać książkę, którą będzie się dało przeczytać w parę godzin. I muszę przyznać, że bardzo dobrze mu to wyszło. Idealna dla ludzi, którzy nie są specami w tej kwestii, a chcą poprawić użyteczność swoich stron.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://www.amazon.com/Agile-Development-Rails-Pragmatic-Programmers/dp/0977616630/ref=pd_bbs_sr_2/103-1673275-3760642?ie=UTF8&amp;amp;s=books&amp;amp;qid=1188647391&amp;amp;sr=8-2"&gt;Agile development with Rails&lt;/a&gt; &amp;#8211; Pozycja obowiązkowa dla uczących się RoR. Co prawda teraz niewiele mi się przyda, ale czułbym się źle jakby jej nie kupił. Korzystałem dość sporo z ebooka z amule&amp;#8217;a. No i przyda się &lt;a href="http://sarniak.blogspot.com"&gt;bliźniemu&lt;/a&gt;, który właśnie zaczyna się uczyć RoR.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://www.amazon.com/Ruby-Rails-Techniques-Developers/dp/1932394699/ref=pd_bbs_sr_1/103-1673275-3760642?ie=UTF8&amp;amp;s=books&amp;amp;qid=1188647391&amp;amp;sr=8-1"&gt;Ruby for Rails&lt;/a&gt; &amp;#8211; jeszcze nie przeczytałem, ale po przekartkowaniu zapowiada się bardzo ciekawie. Napiszę coś więcej jak wreszcie się za nią wezmę ;-)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://www.amazon.com/Ruby-Way-Second-Addison-Wesley-Professional/dp/0672328844/ref=pd_bbs_sr_1/103-1673275-3760642?ie=UTF8&amp;amp;s=books&amp;amp;qid=1188647417&amp;amp;sr=8-1"&gt;The Ruby Way&lt;/a&gt; &amp;#8211; bardzo dobra książka. Jestem właśnie w trakcie czytania. Podaje bardzo dużo rozwiązań często spotykanych problemów zgddnie z &amp;#8220;ruby way&amp;#8221;. Ponad 600 stron esencji programowania ;-)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://www.amazon.com/JavaScript-Definitive-Guide-David-Flanagan/dp/0596101996/ref=pd_bbs_sr_1/103-1673275-3760642?ie=UTF8&amp;amp;s=books&amp;amp;qid=1188647405&amp;amp;sr=8-1"&gt;JavaScript: The Definitive Guide&lt;/a&gt; &amp;#8211; niektórzy narzekają, że książka bardziej zbliżona jest do dokumentacji niż do książki &amp;#8220;do nauki&amp;#8221;. Dla mnie to plus. Javascripta znam całkiem dobrze. Pewnie dzięki temu, że w poprzednim stuleciu, kiedy o bibliotekach takich jak jQuery nikt nie słyszał, bawiłem się w pisanie różnego rodzaju latających warstw, wysuwanych menu i innych arkanoidów (nawet fajnie się grało ;-). W tym momencie potrzebuję właśnie takiej dokumentacji, do której mogę spojrzeć podczas pisania i zobaczyć jakie metody udostępnia dana klasa, albo jak powinien się zachowywać jakiś kod. Jeżeli ktoś chce pisać coś więcej niż efekty z użyciem jQuery (czy innego script.aculo.us), to naprawdę zachęcam do kupna.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Podejrzewam, że gdyby początkujący programiści więcej inwestowali w wiedzę książkową to byłoby dużo mniej partactwa. Szczególnie, że książki związane z Rubim i RoR oprócz nauki programowania próbują od początku wpajać dobre praktyki programistyczne. I koniecznie trzeba przeczytać &lt;a href="http://www.merlin.com.pl/frontend/browse/product/1,299928.html"&gt;Pragmatycznego programistę&lt;/a&gt; :)&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Dlaczego jQuery?</title>
   <link href="http://piotrsarnacki.com/2007/08/31/dlaczego-jquery" />
   <published>2007-08-31T00:00:00-07:00</published>
   <updated>2007-08-31T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2007/08/31/dlaczego-jquery</id>
   <content type="html">&lt;p&gt;Jako javascriptowego frameworka (z góry przepraszam &lt;em&gt;ukrytych purystów językowych&lt;/em&gt; za brutalne &lt;a href="http://www.nierobcietegowdomu.pl/wpisy/o-jezyku-polskich-blogow"&gt;wrzucanie spolszczonych angielszczyzmów&lt;/a&gt; &amp;#8211; w razie czego proszę o wersję przetłumaczoną w komentarzu, trochę humoru zawsze się przyda) przez długi czas używałem zestawu &lt;a href="http://www.prototypejs.org/"&gt;prototype&lt;/a&gt; + &lt;a href="http://script.aculo.us/"&gt;script.aculo.us&lt;/a&gt;. Po krótkim czasie przywiązałem się i stwierdziłem, że nie ma sensu sprawdzać innych możliwości&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;bo i tak większość frameworków oferuje podobne możliwości&lt;/li&gt;
	&lt;li&gt;będę musiał stracić czas na nauczenie się czegoś nowego&lt;/li&gt;
	&lt;li&gt;Ruby on Rails wspiera powyżej wymieniony zestaw&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Właściwie to &amp;#8220;wybranie&amp;#8221; prototype&amp;#8217;a było bezpośrednim następstwem ostatniego punktu. Pomyślałem, że skoro &lt;span class="caps"&gt;DHH&lt;/span&gt; wybrał ten framework, to coś w tym musi być. Podejrzewam, że w rzeczywistości było to spowodowane tym, że jQuery miał w tamtym okresie małą popularność (o ile w ogóle istniał). Patrząc na to jak wyglądają obie biblioteki można odnieść wrażenie, że filozofię dużo bliższą Railsom realizuje jQuery.&lt;/p&gt;
&lt;p&gt;Najlepiej to widać patrząc na przykłady:&lt;/p&gt;
&lt;p&gt;prototype:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Ajax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Updater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;get&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;par&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;anchors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;anchors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;anchors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;relAttribute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rel&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;relAttribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;history&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;click&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
              &lt;span class="c1"&gt;//siakaś funkcja&lt;/span&gt;
             &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;jQuery:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;par&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;a[@rel=&amp;#39;history&amp;#39;]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="c1"&gt;//siakaś funkcja&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Od razu widać po której stronie stoi przejrzystość (i jasna strona mocy). Ten drugi przykład może nie być już aktualny, bo ostatnio programiści prototype wzięli się do roboty i co jakiś czas zamieszczają wiadomości o zmianach w &lt;span class="caps"&gt;API&lt;/span&gt;, ale takich kawałków można wkleić dużo więcej. Wszystko idzie w dobrą stronę, ale nie wiem czy uda się w niedalekiej przyszłości uzyskać lekkość jQuery.&lt;/p&gt;
&lt;p&gt;Ruby on Rails niestety nie mają wsparcia dla jQuery, ale &lt;a href="http://mad.ly/2007/05/17/jquery-ajax-rails/"&gt;nie jest tak źle&lt;/a&gt;. Selektor elementów w RJS&amp;#8217;ach dalej będzie działał, resztę kodu można wrzucać bezpośrednio poprzez `page &amp;lt;&amp;lt;`, a brak helperów? Możnaby je przepisać (i zapewne ktoś już to zaczął robić). Aczkolwiek pojawiają się głosy, że &lt;a href="http://jlaine.net/2007/8/3/from-rails-ajax-helpers-to-low-pro-part-i"&gt;domyślne helpery w RoR są krnąbrne oraz złe&lt;/a&gt;. Po części całkiem słusznie. Z drugiej strony w niektórych aplikacjach nie ma sensu robić obsługi bez javascriptu (jakieś rozbudowane interfejsy użytkownika), a podobno &lt;a href="http://ajaxian.com/archives/joseph-smarr-%e2%80%9chigh-performance-javascript-why-everything-you%e2%80%99ve-been-taught-is-wrong%e2%80%9d"&gt;używanie event handlerów jest wolniejsze niż zwykły onclick&lt;/a&gt;. Jak zwykle wszystko zależy od zastosowań i potrzeb. Ciężkie jest życie programisty. Kiedyś padnę na zawał od takiego nawału trudnych decyzji do podjęcia ;)&lt;/p&gt;
&lt;p&gt;Jako dalszą lekturę polecam &lt;a href="http://jquery.com/blog/2006/08/20/why-jquerys-philosophy-is-better/"&gt;propagandę na blogu jQuery&lt;/a&gt; i &lt;a href="http://jquery.com/blog/2007/08/24/jquery-114-faster-more-tests-ready-for-12/"&gt;doniesienia o nowej wersji&lt;/a&gt;, gdzie można podziwiać wyniki zabiegów optymalizacyjnych i sporo nowych mechanizmów. Całkiem niedawno &lt;a href="http://jquery.com/blog/2007/04/18/learning-jquery-book-details/"&gt;jQuery doczekało się książki&lt;/a&gt;. Dużym plusem jest też &lt;a href="http://docs.jquery.com/Plugins"&gt;ogromna ilość rozszerzeń&lt;/a&gt;. Lista wygląda imponująco.&lt;/p&gt;
&lt;p&gt;Jedyne co mnie ostatnio zaniepokoiło to przesiadka z jQuery na prototype, o której mówi &lt;a href="http://jarmark.org/post/druga-twarz-blipa/"&gt;Zbigniew Sobiecki w wywiadzie&lt;/a&gt;. Jego doświadczenie widać najlepiej patrząc na blipa, więc jakieś powody musi mieć. Będę musiał to zbadać ;)&lt;/p&gt;
&lt;p&gt;W każdym razie w tym momencie moim frameworkiem javascriptowym &amp;#8220;of choice&amp;#8221; jest jQuery. Jeżeli ktoś zna argumenty przemawiające za pozostaniem przy prototype i script.aculo.us niech się nie krępuje i zostawi komentarz.&lt;/p&gt;
&lt;p&gt;Dopisane:&lt;br /&gt;
Znalazłem wczoraj fajne &lt;a href="http://mootools.net/slickspeed/"&gt;narzędzie do benchmarków&lt;/a&gt;. Niestety wersje bibliotek nie są najnowsze, co owocuje &lt;a href="http://blog.drogomir.com/files/benchmark.old.png"&gt;cienkimi wynikami jQuery&lt;/a&gt;, ale na szczęście jest link do źródeł, więc można &lt;a href="http://blog.drogomir.com/files/benchmark.new.png"&gt;wrzucić swoje wersje&lt;/a&gt; (ostatnie stabilne wersje jQuery i prototype). Jak widać na obrazkach twórcy jQuery włożyli ogromną ilość pracy w optymalizację, ale cały czas jeszcze trochę brakuje, żeby dogonić prototype.&lt;/p&gt;
&lt;p&gt;Rozmawiałem wczoraj ze &lt;a href="http://stifflog.com"&gt;sztywnym&lt;/a&gt; na &lt;a href="http://beta.blip.pl"&gt;blipie&lt;/a&gt; (jest teraz jednym z developerów blipa). Zwrócił mi uwagę na parę rzeczy, które mi umknęły. Jedną z nich jest brak rozszerzeń dla obiektów istniejących już w javascripcie. Prototype udostępnia bardzo dużo metod, wzorowanych głównie na metodach rubiego, które ułatwiają pracę z typami wbudowanymi (wszelkie iteratory typu collect, each, any, all, różne metody dla klasy String, hashe itp. itd.). Szczerze mówiąc nawet nie pomyślałem, że nic podobnego nie ma w jQuery &amp;#8211; przerzuciłem się dosłownie parę dni temu i nie pisałem nic większego. W takim momencie trzeba zadać sobie pytanie co jest priorytetem w danym projekcie. Oczywiście warto znać obie biblioteki, ale niestety do danego zadania trzeba wybrać jedną z nich (teoretycznie nic nie stoi na przeszkodzie, żeby załączyć obie, dzięki jQuery.noConflict, ale trzeba się liczyć z dodatkowymi kilobajtami).&lt;/p&gt;
&lt;p&gt;Przejrzałem dodatkowo bloga prototypa i pojawia się cały czas sporo fajnych rzeczy. Myślę, że przy wyborze między tymi dwoma bibliotekami trzeba pomyśleć czy bardziej przydatne będą efekty wizualne i ogólnie grzebanie w &lt;span class="caps"&gt;CSS&lt;/span&gt; i &lt;span class="caps"&gt;DOM&lt;/span&gt;, czy stawiamy na pisanie różnych funkcji &amp;#8220;niższego poziomu&amp;#8221;. Najgorsze jest to, że wydaje mi się, że w projekcie, który zaczynam będzie się to rozkładało mniej więcej po połowie. Coś czuję, że wrócę jeszcze do tego tematu.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Kilka przydatnych linków</title>
   <link href="http://piotrsarnacki.com/2007/08/25/kilka-przydatnych-linko%CC%81w" />
   <published>2007-08-25T00:00:00-07:00</published>
   <updated>2007-08-25T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2007/08/25/kilka-przydatnych-linków</id>
   <content type="html">&lt;p&gt;Dawno nic nie pisałem, przez co mogło się niektórym wydawać, że blog umarł śmiercią naturalną. Bądź też ja umarłem (śmiercią niekoniecznie naturalną). Jak można dość łatwo zauważyć myśli o owych śmierciach (dwóch! chwila nieuwagi i sytuacja ociera się o żałobę narodową) były mocno przesadzone.&lt;/p&gt;
&lt;p&gt;Na dobry początek reanimacji ledwo dychającego już blogusia tytułowe przydatne linki:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://toggl.com"&gt;Toggl&lt;/a&gt; &amp;#8211; podczas pracy, szczególnie w domu, można bardzo łatwo zacząć zajmować się tysiącem rzeczy, które przerwą kodzenie. Toggl jest serwisem, który pozwala zmierzyć realny czas pracy. Dodaję nowy projekt, klikam &amp;#8220;start&amp;#8221; i zegar zaczyna tykać. I tyka tak sobie licząc godziny pracy. Od kilku dni takie liczenie pomaga mi bardzo w skupieniu nad pisaniem. Jak już chcę kliknąć na zakładkę z google readerem, to zaraz przychodzi myśl, że będę musiał przystopować toggl i realny czas pracy będzie dużo mniejszy.&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://jquery.com/blog/2007/08/24/jquery-114-faster-more-tests-ready-for-12/"&gt;jQuery doczekało się kolejnej już wersji&lt;/a&gt;. Jak zwykle dużo nowych bajerów i jeszcze szybsze działanie (a po ostatnim &amp;#8220;800% faster&amp;#8221; myślałem, że dużo więcej nie wyciągną). Właściwie to na jQuery przesiadłem się dosyć niedawno i jako były-wielki-fan&amp;#8482; script.aculo.us czuję się zobligowany do napisania artykułu o tym co otworzyło mi oczy. Beware!&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://mashable.com"&gt;Mashable&lt;/a&gt; przygotowało &lt;a href="http://mashable.com/2007/08/23/google-reader-firefox/"&gt;spis narzędzi do google readera&lt;/a&gt;. Wreszcie jakaś lista &amp;#8220;x tools for&amp;#8230;&amp;#8221;, która mi się przydała. Polecam &lt;a href="http://userscripts.org/scripts/show/8564"&gt;skin w stylu OS X&lt;/a&gt;. Naprawdę cieszy oko. Aż chce się zacząć przeglądać pomimo straszącego 100+&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Na dzisiaj tyle. Oby to nie był ostatni post w przeciągu kolejnych kliku miesięcy. Trzymajcie kciuki.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Postanowienia na rok 2007</title>
   <link href="http://piotrsarnacki.com/2006/12/31/postanowienia-na-rok-2007" />
   <published>2006-12-31T00:00:00-08:00</published>
   <updated>2006-12-31T00:00:00-08:00</updated>
   <id>http://piotrsarnacki.com/2006/12/31/postanowienia-na-rok-2007</id>
   <content type="html">&lt;p&gt;A jakże ;-)&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;napisać wreszcie i wypuścić jeden (może więcej?) z serwisów, na które mam pomysły. sporo tego jest, w większości serwisy niszowe i dość hermetyczne połączone z moimi zainteresowaniami. głównie na zasadzie: a gdybym tak miał możliwość&amp;#8230;. (takie moje egoistyczne pobudki :) )&lt;/li&gt;
	&lt;li&gt;uporządkować sprawy na uczelni (czyt. zaliczyć zaległe egzaminy, laborki, ćwiczenia)&lt;/li&gt;
	&lt;li&gt;w kwestii nauki i poznawania nowych narzędzi:
	&lt;ul&gt;
		&lt;li&gt;zostać guru w Rubim i Railsach ;)&lt;/li&gt;
		&lt;li&gt;może Python i Django?&lt;/li&gt;
		&lt;li&gt;Java &amp;#8211; w stronę programowania na &lt;span class="caps"&gt;PDA&lt;/span&gt; itp.&lt;/li&gt;
		&lt;li&gt;poznać lepiej JavaScript &amp;#8211; żeby nie ograniczać się tylko do tego co oferują biblioteki typu Prototype/Scriptaculous, czy niezbyt wyrafinowane skrypty napisane przezemnie&lt;/li&gt;
		&lt;li&gt;Google Maps&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
	&lt;li&gt;wyremontować &lt;a href="http://www.mis.org.pl/misie-w-polsce/yogi/"&gt;Misia Yogiego&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;więcej czasu poświęcać bliskim :) (coś czuję, że ostatnio zaniedbuję znajomych bardziej niż zwykle)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Na razie nic więcej nie przychodzi mi do głowy. Pozostaje mi tylko życzyć wszystkiem szczęśliwego nowego roku :)&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>WAT on Rails</title>
   <link href="http://piotrsarnacki.com/2006/12/07/wat-on-rails" />
   <published>2006-12-07T00:00:00-08:00</published>
   <updated>2006-12-07T00:00:00-08:00</updated>
   <id>http://piotrsarnacki.com/2006/12/07/wat-on-rails</id>
   <content type="html">&lt;p&gt;W ramach pierwszego sympozjum CyberGuru na Wojskowej Akademii Technicznej w Warszawie wygłosiłem dzisiaj &lt;a href="http://drogomir.com/rubyonrails.pdf"&gt;prezentację&lt;/a&gt; &lt;em&gt;Przedstawienie framework'a Ruby on Rails&lt;/em&gt;. CeyberGuru skupia studentów, którzy chcą rozwijać swoje zainteresowania informatyczne. Do tej pory grupy wchodzące w skład CyberGuru były w większości mało aktywne, o ile w ogóle miały jakichś członków. Dość niedawno powstała inicjatywa odświeżenia owej organizacji, owocem której jest pierwsze (i mam nadzieję nie ostatnie) sympozjum.&lt;/p&gt;

&lt;p&gt;Jeżeli chodzi o przebieg sympozjum to było bardzo przyjemnie. Wygłoszone prezentacje:
* Bezepieczeństwo aplikacji webowych, Krzysztof Wilkos
* Przedstawienie framework'a Ruby on Rails, Piotr Sarnacki
* Java w zastosowaniach wbudowanych, Andrzej Olszak
* Wprowadzenie do C# - Jakub Bańkowski&lt;/p&gt;

&lt;p&gt;Wszystko nieco się przedłużyło - nie mamy dużego doświadczenia w orgaznizowaniu takich spotkań, więc prezentacje trwały dłużej niż zakładaliśmy. Uczestnicy sympozjum wypełnili ankiety, dzięki czemu będziemy wiedzieli co zmienić. Z tego co widziałem przydałoby się więcej przykładów, a mniej teorii. Prawdopodobnie będziemy dążyli wtedy do zmniejszenia liczby referatów podczas jednego sympozjum, co automatycznie wydłuży czas trwania pojedyńczego referatu.&lt;/p&gt;

&lt;p&gt;Korzystając z okazji, po prezentacji, powiedziałem o swoim pomyśle stworzenia &lt;em&gt;grupy webowej&lt;/em&gt;. Chciałbym zebrać ludzi zainteresowanych pisaniem i rozwijaniem aplikacji internetowych i rozpocząć pracę nad (bliżej jeszcze neisprecyzowanym) wspólnym projektem. Jedyne co na razie mogę powiedzieć na pewno - będziemy pisać używając Ruby on Rails. Jeżeli coś z tego wyniknie na pewno o tym napiszę.&lt;/p&gt;
</content>
 </entry>
 
 
</feed>

