<?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-all" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="piotrsarnacki-all" /><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 apps tutorial</title>
   <link href="http://piotrsarnacki.com/2010/12/21/mountable-apps-tutorial" />
   <published>2010-12-21T00:00:00-08:00</published>
   <updated>2010-12-21T00:00:00-08:00</updated>
   <id>http://piotrsarnacki.com/2010/12/21/mountable-apps-tutorial</id>
   <content type="html">&lt;p&gt;I finished my work on RubySOC a few months ago and I wanted to write this post quickly after that, but life, as usual, verified my plans. Unfortunately, attending a few great conferences left me with no money, so I needed some time of hard work to pay all the debts (nevertheless, all those trips were really worth it, especially rubyconf – I really felt in love with New Orleans!). I want to apologize to everyone who really waited for that, especially those who heard that this blog post will be written &amp;#8220;very soon&amp;#8221; ;) Now&amp;#8230; let&amp;#8217;s cut the emo talk and get to serious business!&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ll start with a short introduction for those, who aren&amp;#8217;t familiar with the concept of mountable apps in Rails 3 (edge) yet. The goal for implementing mountable apps was to allow running 2 rails applications in one process. Unfortunately, that goal was not achieved and we settled on a simpler approach. If you want to create a mountable application, you can use rails engines, which will be much more powerful in Rails 3.1. For the list of major changes, please check my &lt;a href="http://piotrsarnacki.com/2010/09/14/mountable-engines"&gt;last post on that topic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Although almost all of the changes made to engines were usable shortly after summer, the process of setting up a new engine, suitable for mounting, was really rough. Of course, there is a great gem, &lt;a href="https://github.com/josevalim/enginex"&gt;enginex&lt;/a&gt;, by José, but at that time it only generated bare structure for any kind of rails extension and there were no things specific to mountable apps (as mountable apps haven&amp;#8217;t existed during the time of writing enginex). That&amp;#8217;s why I ported enginex to rails, with some changes that were needed to merge it to the core, to make that task simpler.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s start with the tutorial. First things first. As the new APIs work only on rails edge, you need to get rails from github:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;git clone https://github.com/rails/rails.git
&lt;span class="nb"&gt;cd &lt;/span&gt;rails
bundle install
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To generate any rails extension in rails 3.1 you can use &lt;code&gt;rails plugin new&lt;/code&gt; command. It generates a directory with lib directory, gemspec, tests and dummy application for testing. There are 2 really important things here:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;gemspec is automatically generated, unless you use &lt;code&gt;--skip-gemspec&lt;/code&gt; option &amp;#8211; I hope that this will help to move more people to gem plugins&lt;/li&gt;
	&lt;li&gt;dummy application, which by default lives in test/dummy &amp;#8211; this is standard rails application that you can test your plugin&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To generate a mountable engine, we can use:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;bundle &lt;span class="nb"&gt;exec&lt;/span&gt; ./bin/rails plugin new ../blog --edge --mountable
&lt;span class="nb"&gt;cd&lt;/span&gt; ../blog
bundle install
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We need to add &lt;code&gt;--dev&lt;/code&gt; or &lt;code&gt;--edge&lt;/code&gt; option to ensure that Gemfile will point to rails edge version, either from local clone or github. I used edge here, cause it&amp;#8217;s better for most of you &amp;#8211; you can get newest rails version with simple &lt;code&gt;bundle update&lt;/code&gt;. Mountable option will add files that will come in handy for developing a mountable engine, such as: integration tests, &lt;code&gt;Blog::ApplicationController&lt;/code&gt;, &lt;code&gt;config/routes.rb&lt;/code&gt; and so on.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s see how the key files look like.&lt;/p&gt;
&lt;p&gt;Probably the most important file is &lt;code&gt;lib/blog/engine.rb&lt;/code&gt;, which keeps definition of the engine:&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="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;The thing that differs from what we could see in Rails 3.0 engine, is &lt;code&gt;isolate_namespace&lt;/code&gt; which makes engine isolated from host application. If you don&amp;#8217;t remember how it works exactly, please check &lt;a href="https://github.com/rails/rails/blob/master/railties/lib/rails/engine.rb#L210"&gt;the documentation&lt;/a&gt; or &lt;a href="http://piotrsarnacki.com/2010/09/14/mountable-engines"&gt;my last post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next important thing is &lt;code&gt;config/routes.rb&lt;/code&gt; file:&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;These are empty routes, but as you can see, they belong to the engine, not to the host application.&lt;/p&gt;
&lt;p&gt;The last thing that you may not be familiar with is the dummy application located in &lt;code&gt;test/dummy&lt;/code&gt;. This is a standard rails application that will be used to test engine, both with automated tests and manually during development. The nice thing about the new plugin generator is that with &lt;code&gt;--mountable&lt;/code&gt; option, it automatically mounts engine in &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="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;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Ok, let&amp;#8217;s write some code! Or&amp;#8230; actually don&amp;#8217;t write code, I&amp;#8217;m too lazy, I&amp;#8217;m gonna use scaffold generator:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;rails g scaffold post title:string body:text
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;While the scaffold should behave exactly the same as the one generated in regular app, there are some differences. Notice that migration for that is called &lt;code&gt;create_blog_posts&lt;/code&gt; instead of &lt;code&gt;create_posts&lt;/code&gt;. Also almost all of the files are places in &lt;code&gt;blog/&lt;/code&gt; subdirectory. When you open &lt;code&gt;app/models/blog/post.rb&lt;/code&gt;, you will see:&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;Post&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;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;Everything is namespaced for a good reason: we want to avoid conflicts between engine and host application.&lt;/p&gt;
&lt;p&gt;Ok, now we can migrate our database (sqlite3 by default):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;rake db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Engine&amp;#8217;s Rakefile includes all the tasks that are needed to manage database. Migrations are run from both engine&amp;#8217;s and dummy&amp;#8217;s application directories to make development and testing easier. Finally, it&amp;#8217;s time to check it!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;rails s
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now point your browser to &lt;a href="http://localhost:3000/blog/posts"&gt;http://localhost:3000/blog/posts&lt;/a&gt;. You should see the standard rails scaffold working – the only difference is that it&amp;#8217;s namespaced with &lt;code&gt;/blog&lt;/code&gt; path. It was easy, wasn&amp;#8217;t it?&lt;/p&gt;
&lt;p&gt;As you can see, creating a mountable application using engines is pretty straightforward. I haven&amp;#8217;t shown any way to better integrate a mountable application with the host application (e.g. using User model from the host application or adding some configuration options to the engine). It&amp;#8217;s on purpose ;) I haven&amp;#8217;t got time to develop anything good yet. You can start playing with it yourself! Be sure to let me know if you come up with some good patterns.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Lightweight controllers with rails 3</title>
   <link href="http://piotrsarnacki.com/2010/12/12/lightweight-controllers-with-rails3" />
   <published>2010-12-12T00:00:00-08:00</published>
   <updated>2010-12-12T00:00:00-08:00</updated>
   <id>http://piotrsarnacki.com/2010/12/12/lightweight-controllers-with-rails3</id>
   <content type="html">&lt;p&gt;Some time ago &lt;a href="http://piotrsarnacki.com/2010/07/31/rails3-modularity/"&gt;I showed you&lt;/a&gt;, how easily you can reuse rails 3 modules. Today I want to present lightweight controllers, which is also really really easy in rails 3.&lt;/p&gt;
&lt;p&gt;Why would you want controller to be more lightweight? Speed of course. We all know that rails can&amp;#8217;t scale and ruby is slow, let&amp;#8217;s make it a little bit faster ;)&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s start with getting familiar with new controller architecture. The base for all the controllers in rails is &lt;a href="https://github.com/rails/rails/blob/v3.0.3/actionpack/lib/abstract_controller/base.rb"&gt;&lt;code&gt;AbstractController::Base&lt;/code&gt;&lt;/a&gt;. If you want to build controllers without support for handling requests, you can start with that as a base. What if we want to create something looking more like standard rails controllers? We can check how does implementation of &lt;code&gt;ActionController::Base&lt;/code&gt; look like: &lt;a href="https://github.com/rails/rails/blob/v3.0.3/actionpack/lib/action_controller/base.rb#L169"&gt;&lt;code&gt;ActionController::Base&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As you can see, it inherits from &lt;code&gt;ActionController::Metal&lt;/code&gt; and &lt;a href="https://github.com/rails/rails/blob/v3.0.3/actionpack/lib/action_controller/base.rb#L180-221"&gt;includes bunch of modules&lt;/a&gt;. Looks like Metal will be a good start for our purpose. Let&amp;#8217;s say that we want to render a simple &lt;span class="caps"&gt;JSON&lt;/span&gt; response generated from a model:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# app/controllers/api_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiController&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;Metal&lt;/span&gt;
  &lt;span class="kp"&gt;include&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;Rendering&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Good morning!&amp;quot;&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;As you can see I created controller that inherits from &lt;code&gt;ActionController::Metal&lt;/code&gt; and included &lt;code&gt;ActionController::Rendering&lt;/code&gt;. To render simple &lt;span class="caps"&gt;JSON&lt;/span&gt; response, we will not need anything more.&lt;/p&gt;
&lt;p&gt;How fast is it comparing to &lt;code&gt;ActionController::Base&lt;/code&gt;? For comparison I&amp;#8217;ve created controller with exactly the same action:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# app/controllers/home_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomeController&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="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Good morning!&amp;quot;&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;
I&amp;#8217;ve done a few benchmarks, but don&amp;#8217;t take it too seriously, these are not scientific, I just wanted to show you how approximately can it differ. Here are ab results: &lt;a href="https://gist.github.com/738168"&gt;https://gist.github.com/738168&lt;/a&gt;. As you can see, &lt;code&gt;ActionController::Metal&lt;/code&gt; is almost 40% faster. This can of course vary, depending on version of ruby, concurrency (I used 1) and other factors, but clearly it can give you nice boost.
&lt;p&gt;Of course trained eye can easily spot an issue here. Do you think that someone will actually return a response like that? Hardcoded string?Srsly? I guess not. But there certainly are situations, when you can profit from using that kind of controllers. If all you need to do, is to return text response, that most of the time is fetched from cache and is hit frequently, this should work pretty well. Some of you may also ask, &lt;del&gt;why can&amp;#8217;t we use &lt;a href="http://weblog.rubyonrails.org/2008/12/17/introducing-rails-metal"&gt;Rails Metal&lt;/a&gt;, which is available for quite some time now&lt;/del&gt; &lt;ins&gt;rack middleware&lt;/ins&gt; (rails metal was removed in rails 3). We could, but then we loose possibility to use router and include other modules to &lt;code&gt;ActionController::Metal&lt;/code&gt; (like &lt;code&gt;ActionController::Helpers&lt;/code&gt; or other useful things).&lt;/p&gt;
&lt;p&gt;I hope that this post will help you to experiment with new Rails 3 cool features! If you want more of that stuff, in better form and with much better explanation and depth, I highly recommend reading José Valim&amp;#8217;s book &lt;a href="http://pragprog.com/titles/jvrails/crafting-rails-applications"&gt;Crafting Rails Applications&lt;/a&gt;!&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Mountable engines - RSoC Wrap Up</title>
   <link href="http://piotrsarnacki.com/2010/09/14/mountable-engines" />
   <published>2010-09-14T00:00:00-07:00</published>
   <updated>2010-09-14T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2010/09/14/mountable-engines</id>
   <content type="html">&lt;p&gt;As some of you probably know, during the summer I was working on &amp;#8220;Rails mountable applications&amp;#8221; project thanks to Ruby Summer of Code. It was great experience, I learned a lot new things and I hope that I delivered something useful for the community. I tweeted that the other day, but I must emphasize: it would not be possible without great support from my mentors: Carl Lerche, Yehuda Katz and José Valim! They helped me a lot with both ideas and implementation. The biggest internet hug should go to José, who spent enormous amount of his time on discussions, reviewing my commits and helping me to set the goals. I also want to thank all the sponsors and people that helped to organize Ruby Summer of Code.&lt;/p&gt;
&lt;p&gt;Getting back to my work. My main task was to extend capabilities of rails engines. Although at first I wanted to get straight to mountable full rails applications, after discussions with Carl and José, I knew that starting with the smaller target will be more sane way to go. The biggest problem with implementing mountable applications (that is running more than one rails application in same process) is configuration and application initialization in general. Right now config values are shared between all the railties classes, because they&amp;#8217;re kept in class variables. The other problem with mountable apps is that they&amp;#8217;re pretty new concept in rails community and we will need some time to settle standards. With those problems in mind, it is much better to test the concept with engines and implement truly mountable applications later, based on results and feedback. Right now, engines are almost as powerful as applications. The main difference is that engine can&amp;#8217;t be run without an application. The best thing about such strategy is that &lt;span class="caps"&gt;API&lt;/span&gt; for using more than one application is already here, so if it is needed, converting engine to application will be as easy as changing a bunch of configuration files.&lt;/p&gt;
&lt;p&gt;In that post, I would like to briefly describe my changes.&lt;/p&gt;
&lt;h2&gt;Extending engines&lt;/h2&gt;
&lt;p&gt;First thing needed to allow build entire applications on engines, was to add some of the application&amp;#8217;s features to engines. That&amp;#8217;s why engines got:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;its own middleware stack&lt;/li&gt;
	&lt;li&gt;routes&lt;/li&gt;
	&lt;li&gt;plugins support&lt;/li&gt;
	&lt;li&gt;config/environment.rb&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With all the capabilities that engines had before, engine is now almost as powerful as application.&lt;/p&gt;
&lt;h2&gt;Mounting engine&lt;/h2&gt;
&lt;p&gt;Since engine is now a rack app, you can simply mount it in your application&amp;#8217;s routes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&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;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This will mount &lt;code&gt;Blog::Engine&lt;/code&gt; at &lt;code&gt;/blog&lt;/code&gt; path. There are 2 huge benefits of such method:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;you can change the engine&amp;#8217;s path, which was much harder before&lt;/li&gt;
	&lt;li&gt;you can use all of the routes magic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Want to mount your engine with dynamic scope? No problem:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/:username&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:username&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;default&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also use devise to force authentication for the engine:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;authenticate&lt;/span&gt; &lt;span class="ss"&gt;:admin&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;mount&lt;/span&gt; &lt;span class="no"&gt;Tolk&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;/tolk&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;It&amp;#8217;s easy, isn&amp;#8217;t it?&lt;/p&gt;
&lt;h2&gt;Cross applications routes &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;Since you can mount engine with its own router inside application, there is a possibility of having more than one router in your app. To handle all the routers easily, there are new helpers providing access for each router. Application&amp;#8217;s router is always available as &lt;code&gt;main_app&lt;/code&gt;. That said, you can call any application route from engine just like that:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;main_app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logout_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;root_path&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Helpers for other routers are available after mounting an engine:&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="c1"&gt;# default helper for such engine is &amp;quot;blog&amp;quot;:&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;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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you need to change the helper name, just pass the &lt;code&gt;:as&lt;/code&gt; attribute:&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;my_blog&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# now helper is called my_blog:&lt;/span&gt;
&lt;span class="n"&gt;my_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;my_blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root_path&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also use those helpers in polymorphic url (which is used, among the other places, in &lt;code&gt;form_for&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;polymorphic_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;form_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@post&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;Note that you have to explicitly add those helpers to &lt;code&gt;polymorphic_url&lt;/code&gt; only when you need to call an engine route from your application or from another engine.&lt;/p&gt;
&lt;h2&gt;Namespacing&lt;/h2&gt;
&lt;p&gt;Having more than one source of controllers, models and helpers can cause conflicts. Imagine you have &lt;code&gt;Post&lt;/code&gt; model in your application and you would like to install blog engine having model with the same name. To avoid that, you can put your engine inside the namespace. Although it will help you with conflicts, it&amp;#8217;s not enough level of separation for some engines. There are basically two possible scenarios here. One of them is shared engine, when you want to share helpers between engine and application. A good example of such engine is &lt;a href="http://github.com/plataformatec/devise"&gt;Devise&lt;/a&gt;. The other use case is when you want to make isolated engine, which will not likely share anything. It could be an engine that provides tools for application like &lt;a href="http://github.com/dhh/tolk"&gt;Tolk&lt;/a&gt; or the other app like blog or &lt;span class="caps"&gt;CMS&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Engines are shared by default, therefore you must explicitly mark it as isolated with &lt;code&gt;isolate_namespace&lt;/code&gt; method:&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;With such engine definition, only the engine&amp;#8217;s helpers and routes will be included inside engine&amp;#8217;s controllers and views.&lt;/p&gt;
&lt;h2&gt;Migrations&lt;/h2&gt;
&lt;p&gt;If you are one of NoSQL (or rather schema less) database users, you can probably skip that point. In other cases you will probably need migrations. We decided that the easiest way to handle engine&amp;#8217;s migrations is to copy them to application&amp;#8217;s db/migrate directory and change their timestamps for not breaking the migrations timeline. It can be done by simply calling rake task:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;rake railties:copy_migrations
&lt;span class="c"&gt;# or to copy only selected engines&lt;/span&gt;
rake railties:copy_migrations &lt;span class="nv"&gt;RAILTIES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;foo,bar
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The nice side effect of copying migrations is that you can easily review them before applying.&lt;/p&gt;
&lt;h2&gt;Assets&lt;/h2&gt;
&lt;p&gt;The chances are that you will have some assets in your engine&amp;#8217;s public directory. The default way of serving assets in development mode in Rails is &lt;code&gt;ActionDispatch::Static&lt;/code&gt; middleware. If you have mounted any engine, it will automagically serve their assets. In production you have 2 options:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;stay with &lt;code&gt;ActionDispatch::Static&lt;/code&gt; by turning it on with: &lt;code&gt;config.serve_static_assets = true&lt;/code&gt; in your &lt;code&gt;environment.rb&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;create symlinks to engine&amp;#8217;s public directories&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can automatically create symlinks with a rake task:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;rake ralties:create_symlinks
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you want to change the default asset path, you can set it in &lt;code&gt;Engine&lt;/code&gt; definition:&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;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asset_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/my_blog_assets%s&amp;quot;&lt;/span&gt; &lt;span class="c1"&gt;# note %s at the 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;This will change both &lt;code&gt;ActionDispatch::Static&lt;/code&gt;&amp;#8217;s and create_symlinks rake behavior.&lt;/p&gt;
&lt;h2&gt;Why does it matter?&lt;/h2&gt;
&lt;p&gt;Although engines are available since Rails 2.3 and even ealier as a plugin, building isolated applications using them can be hard. With new &lt;span class="caps"&gt;API&lt;/span&gt;, building entire apps (like forum, blog or &lt;span class="caps"&gt;CMS&lt;/span&gt;) that can be reused, will be much easier. But hey, big components are evil, right? I would say that it depends on your needs. There is huge space for engines as tools to help develop your application. If you use i18n, there is &lt;a href="http://github.com/dhh/tolk"&gt;Tolk&lt;/a&gt;. Most of applications could probably benefit from &lt;a href="http://github.com/sferik/rails_admin"&gt;Rails Admin&lt;/a&gt; (it&amp;#8217;s also one of the RSoC projects). Also the new Carlhuda&amp;#8217;s ;-) work on &lt;a href="http://github.com/wycats/rails_assets"&gt;Rails 3.1 assets&lt;/a&gt; is based on engines.&lt;/p&gt;
&lt;p&gt;Another topic are applications. You can argue that making flexible application that would suit needs of many developers will be hard and inefficient&amp;#8230; and you are probably right. However there are many situations when you need to quickly add some &lt;span class="caps"&gt;CMS&lt;/span&gt; or small forum to your application and you pretty much do not care about features. It&amp;#8217;s not your core product, you just need something simple. Of course you can always do it by yourself, but what are the benefits? If you&amp;#8217;re not trying to revolutionize &lt;span class="caps"&gt;CMS&lt;/span&gt; systems and just want an easy way to add a few articles to your site, you can use something generic. If it&amp;#8217;s really needed, you can always write your own better suited engine later on and replace the previous one.&lt;/p&gt;
&lt;h2&gt;What&amp;#8217;s next?&lt;/h2&gt;
&lt;p&gt;My work is already merged to rails master and most of the things are finished, but there are still some places that need more work. If you have any suggestions on engines, feel free to comment, add a ticket on lighthouse or send a pull request with changes.&lt;/p&gt;
&lt;p&gt;There is also need for engines creators that will battle test the new &lt;span class="caps"&gt;API&lt;/span&gt; and new concepts. If you have any questions feel free to contact me on &lt;a href="http://twitter.com/drogus"&gt;twitter&lt;/a&gt;, &lt;a href="http://github.com/drogus"&gt;github&lt;/a&gt; or &lt;a href="mailto:drogus@gmail.com"&gt;by email&lt;/a&gt;. I will also prepare a guide shortly, so stay tuned!&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>RSoC status: Namespacing engines</title>
   <link href="http://piotrsarnacki.com/2010/09/06/rsoc-status-namespacing-engines" />
   <published>2010-09-06T00:00:00-07:00</published>
   <updated>2010-09-06T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2010/09/06/rsoc-status-namespacing-engines</id>
   <content type="html">&lt;p&gt;It&amp;#8217;s been a while since my last post. After finalizing my work on routes and few other things I went to Croatia for 2 weeks, to rest from the computer and programming. But fear not, I will not count that 2 weeks into RSoC time and I will work for 2 weeks longer to not shorten actual development time.&lt;/p&gt;
&lt;p&gt;The big news is: my work has been merged to rails official repository recently! That means that you can start using the things that I described right away (you just need to use edge version of rails).&lt;/p&gt;
&lt;p&gt;The next thing that needed to be done to allow mounting engines easily is namespacing. Namespacing is crucial to avoid conflicts between engines and application itself. Imagine that you have application with Article and Comment models and you want to mount blog engine into it. The chances are, blog engine will also have Comment model and corresponding controller and helper. Without namespacing such configuration would end up with conflicts. To avoid that, we can put all of the things in an engine into namespace:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# ENGINE/app/models/blog/comment.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;Comment&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;Base&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;# ENGINE/app/controllers/blog/comments_controller.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="c1"&gt;# note that ApplicationController here is in fact Blog::ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CommentsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&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;At first, it may seem that this will not require any changes to Rails, it&amp;#8217;s standard Ruby technique, right? To some degree it&amp;#8217;s true, but there are some places that made it hard to use namespaced engines before.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s look at controllers as an example. In rails 3.0, when you create any class that inherits from &lt;code&gt;ActionController::Base&lt;/code&gt;, it will get all of the application helpers. It&amp;#8217;s default behavior and it&amp;#8217;s perfectly ok when you have only one application. Problems appear when you want to mount engine with its own controllers and helpers. With Rails 3.0 it will be a bit hard. Even if you namespace your controllers and helpers, you will get all application&amp;#8217;s helpers instead of engine&amp;#8217;s ones. The simple solution would be to load only helpers within current namespace, but that would break some of the existing engines.&lt;/p&gt;
&lt;p&gt;There are basically 2 types of engines. Some of the engines are considered to be a part of application, they provide helpers and controllers that will be used within application and there is no need to isolate them (take &lt;a href="http://github.com/plataformatec/devise"&gt;Devise&lt;/a&gt; as example of such engine). I will call it &amp;#8220;shared engine&amp;#8221;. The second use case is engine that should not share anything, like blog engine. I will call such constructs &amp;#8220;isolated engines&amp;#8221;.&lt;/p&gt;
&lt;p&gt;The problem with having shared and isolated engines is the fact that in both situations you can namespace stuff inside, but the behavior should be slightly different. While you would probably like to share helpers from shared engine, even if they&amp;#8217;re namespaced, you would not want that in case of isolated engine. To distinguish this situations you need to explicitly say that engine is isolated. Otherwise it will be shared. To make engine isolated, just use &lt;code&gt;isolate_namespace()&lt;/code&gt; method in Engine definition:&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="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="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Blog&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;With such code, all of the stuff namespaced with &lt;code&gt;Blog&lt;/code&gt; will be isolated. There are quite a few effects of that. As I mentioned before, one of the problems are controllers. With engine explicitly marked as namesapaced, controllers will include only helpers from within the same namespace and they will include only an engine&amp;#8217;s routes.&lt;/p&gt;
&lt;p&gt;The next thing is router itself. When you have every controller inside the module, normally you would have to reflect that in routes:&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;scope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:module&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:blog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;: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;This code will ensure that &lt;code&gt;posts&lt;/code&gt; resource points to &lt;code&gt;Blog::PostsController&lt;/code&gt;. With isolated engine this is not needed at all. You can ommit that scope, cause it will be applied automagically behind the scene. With engine marked as isolated you can just do:&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="c1"&gt;# everything here is namespaced with :blog namespace&lt;/span&gt;
  &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:posts&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The next thing to change was &lt;code&gt;ActiveModel::Naming&lt;/code&gt;. It is used in many places in rails and by default it leaves namespace, e.g. it would convert &lt;code&gt;Blog::Post&lt;/code&gt; to &lt;code&gt;blog_post&lt;/code&gt;. While in some places it&amp;#8217;s ok, it can be a major &lt;span class="caps"&gt;PITA&lt;/span&gt; in other ones. The first pain point is &lt;code&gt;polymorphic_url&lt;/code&gt;, which is used among the other things in &lt;code&gt;form_for&lt;/code&gt;. Let&amp;#8217;s consider example of &lt;code&gt;Blog::Post&lt;/code&gt; model:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# @post = Blog::Post.new&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= form_for(@post) do |f|%&amp;gt;&lt;/span&gt;
&lt;span class="sx"&gt;  &amp;lt;%=&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;text_field&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt; &lt;span class="sx"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="sx"&gt;  &amp;lt;%= f.submit %&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;Without any changes in &lt;code&gt;ActiveModel::Naming&lt;/code&gt;, such code would try to use &lt;code&gt;blog_posts_path&lt;/code&gt; helper. Using namespace here is not needed at all, as you can&amp;#8217;t have a conflict with helpers from other routers. That&amp;#8217;s why in isolated engine &lt;code&gt;form_for(@post)&lt;/code&gt; will use &lt;code&gt;posts_path&lt;/code&gt; and thanks to that, we didn&amp;#8217;t need to add that prefix to all the routes.&lt;/p&gt;
&lt;p&gt;The next thing connected with forms and model name are param names. Normally, the last piece of code would generate field &lt;code&gt;&amp;lt;input type="text" name="blog_post[title]" id="blog_post_title" /&amp;gt;&lt;/code&gt;. That kind of prefixes are also not needed as it&amp;#8217;s highly improbable that you would need &lt;code&gt;params[:post]&lt;/code&gt; for some other thing (and even if it is the case for you, you can use &lt;code&gt;:as =&amp;gt; :some_other_name&lt;/code&gt; to change the default). The &lt;code&gt;blog_&lt;/code&gt; is also omitted here and in isolated engine the field would look just like that: &lt;code&gt;&amp;lt;input type="text" name="post[title]" id="post_title"/&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;All of the other places where &lt;code&gt;ActiveModel::Naming&lt;/code&gt; is used, like partials, generating dom elements or i18n, take the namespace into account.&lt;/p&gt;
&lt;p&gt;As you can see, there was quite a few changes in Rails code to handle namespaces, but in the end they&amp;#8217;re pretty transparent to for the developer.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Rails3 modularity</title>
   <link href="http://piotrsarnacki.com/2010/07/31/rails3-modularity" />
   <published>2010-07-31T00:00:00-07:00</published>
   <updated>2010-07-31T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2010/07/31/rails3-modularity</id>
   <content type="html">&lt;p&gt;Since I spend a lot more time with Rails 3 codebase, I am more and more amazed by its modularity. Rails 3 is much more extensible than I would have ever thought. As a part of my rubysoc project I wanted to port merb-parts to Rails 3. Some of you may never heard of merb parts and ask what the hell are parts? Do I need it? Parts are a bit like controllers, but much more lightweight and without overhead of handling requests. I like to think about parts as powerful partials. If you have a partial and you would like to add a bit more logic to it or fetch something from database, you could probably benefit from using parts. Let&amp;#8217;s go with a simple example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# app/parts/articles_part.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArticlesPart&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Parts&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;@articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&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;:limit&lt;/span&gt;&lt;span class="o"&gt;]&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="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;created_at DESC&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;div class="highlight"&gt;&lt;pre&gt;&lt;code class="erb"&gt;&lt;span class="x"&gt;# app/parts/views/articles_part/index.html.erb&lt;/span&gt;
&lt;span class="x"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
&lt;span class="x"&gt;  &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="vi"&gt;@articles&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;article&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;    &amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="x"&gt;  &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This simple part will fetch last &lt;code&gt;params[:limit]&lt;/code&gt; articles or 10 if limit is not provided. Let&amp;#8217;s use it in our view:&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;part&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ArticlesPart&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:index&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="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&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;Such call will render list of last 5 articles.&lt;/p&gt;
&lt;p&gt;The question is: how much lines of code does it take to implement something like that with ability to render views, layouts, &lt;code&gt;:inline&lt;/code&gt;, use helpers, filters and much more? Over 100 lines of code including &lt;code&gt;part()&lt;/code&gt; helper and a railtie (railtie is used to plug it into rails) &amp;#8211; if you don&amp;#8217;t believe me grab the &lt;a href="http://github.com/drogus/rails-parts"&gt;repo&lt;/a&gt; and check it yourself.&lt;/p&gt;
&lt;p&gt;How is it possible? Let&amp;#8217;s look at the implementation of &lt;code&gt;Parts::Base&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;parts/default_layout&amp;#39;&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Parts&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;AbstractController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
    &lt;span class="kp"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:params&lt;/span&gt;

    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;AbstractController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Layouts&lt;/span&gt;
    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;AbstractController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Translation&lt;/span&gt;
    &lt;span class="kp"&gt;include&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;Helpers&lt;/span&gt;
    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;AbstractController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rendering&lt;/span&gt;
    &lt;span class="kp"&gt;include&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;ImplicitRender&lt;/span&gt;
    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;DefaultLayout&lt;/span&gt;
    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;AbstractController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Callbacks&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;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&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;dup&lt;/span&gt;
      &lt;span class="vi"&gt;@params&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="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unless&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;empty?&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;formats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;formats&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;inherited&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;super&lt;/span&gt;
      &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;helper&lt;/span&gt; &lt;span class="ss"&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;That&amp;#8217;s all&amp;#8230; ? Yes!&lt;/p&gt;
&lt;p&gt;As you cans see &lt;code&gt;Parts::Base&lt;/code&gt; inherits from &lt;code&gt;AbstractController::Base&lt;/code&gt;, which gives it really basic functionality. Additionaly a few helpers are included to add a bit more behavior. The only mixin that I needed to create myself is &lt;code&gt;Parts::DefaultLayouts&lt;/code&gt; which ensures that layout with the name of the part is rendered by default, unless &lt;code&gt;:layout =&amp;gt; false&lt;/code&gt; or there is no such layout in layouts directory.&lt;/p&gt;
&lt;p&gt;Ok, that all is nice and dandy, but how does it work internally?&lt;/p&gt;
&lt;p&gt;The implementation of such pattern can be demonstrated with such code:&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;Foo&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;foo&amp;#39;&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;module&lt;/span&gt; &lt;span class="nn"&gt;Bar&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;super&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;module&lt;/span&gt; &lt;span class="nn"&gt;Baz&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;super&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;class&lt;/span&gt; &lt;span class="nc"&gt;Omg&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Foo&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Bar&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Baz&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;omg&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Omg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; omg&lt;/span&gt;
            &lt;span class="c1"&gt;#   baz&lt;/span&gt;
            &lt;span class="c1"&gt;#   bar&lt;/span&gt;
            &lt;span class="c1"&gt;#   foo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There are 2 modules (Bar and Baz) and 2 classes (Foo and Omg) implementing foo method here. Omg class inherits from Foo and additionaly modules Bar and Baz are included in Omg. This code takes advantage of ruby object model. How does it work?&lt;/p&gt;
&lt;p&gt;When you instantinate Omg object with &lt;code&gt;Omg.new&lt;/code&gt; and call method foo on it, it looks for foo method in current class and its superclasses. So first method that will be actually called is Omg#foo. This method is calling super, so ruby will look for method foo also in Omg&amp;#8217;s superclass. At first you could think that it&amp;#8217;s Foo, but internally Ruby treats modules as superclasses. That said, the next superclass will be the last included module, which is Baz. After that Bar&amp;#8217;s and Foo&amp;#8217;s methods will be invoked.&lt;/p&gt;
&lt;p&gt;This one of the best patterns I have seen in Ruby so far. Not only it allows to extend objects easily, but also to reuse small chunks of code making the whole thing more modular. If Rails would stop at standard inheritance that we know from Java and other similar OO languages, creating something like parts would be much harder. In &lt;code&gt;Parts::Base&lt;/code&gt; I used a few modules from AbstractController, but also a module from ActionController. With implementation without mixins, I would need to take all or nothing approach.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re a Ruby developer, please think about that pattern while writing your next library. It can really help to make your code easier to extend and reuse. I was amazed how easy it was to implement all that stuff and I love the way that Rails is going!&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>RSoC status: routes (a.k.a OMG it's hard)</title>
   <link href="http://piotrsarnacki.com/2010/07/20/rsoc-status-routes-aka-omg-it-s-hard" />
   <published>2010-07-20T00:00:00-07:00</published>
   <updated>2010-07-20T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2010/07/20/rsoc-status-routes-aka-omg-it-s-hard</id>
   <content type="html">&lt;p&gt;In my last post I briefly described some of the RSoC changes and plans. One of the things that I left is router. The topic is much harder and I think it deserves separate blog post.&lt;/p&gt;
&lt;p&gt;Basically, the problem with engines and routes is that now you can have more than one router. That leads to many issues that need to be solved. As with my last post, things described here are not in Rails core yet, so you will have to wait a bit to actually use it.&lt;/p&gt;
&lt;p&gt;For the good start, let&amp;#8217;s identify things to consider:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;recognition&lt;/li&gt;
	&lt;li&gt;generation&lt;/li&gt;
	&lt;li&gt;named routes from more than one router (for example &lt;code&gt;posts_path&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Recognition&lt;/h2&gt;
&lt;p&gt;At the beginning I would like to explain how does recognition work. Here is a simple example of engine mounted in application:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# APP/config/routes.rb&lt;/span&gt;
&lt;span class="no"&gt;Foo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&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;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&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="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# ENGINE/config/routes.rb&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;.&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;resources&lt;/span&gt; &lt;span class="ss"&gt;:posts&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you can see engine provides :posts resource and it&amp;#8217;s mounted at /blog path. Let&amp;#8217;s study simple request to our blog:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;GET&lt;/span&gt;&lt;span class="sr"&gt; /blog/&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Application&amp;#8217;s router will match the first part of the path, which is &lt;code&gt;/blog&lt;/code&gt;, with &lt;code&gt;/blog&lt;/code&gt; mount point and pass the request to rack app mounted there (which in that case is &lt;code&gt;Blog::Engine&lt;/code&gt;). To allow engine recognizing path properly it needs to pass &lt;code&gt;/posts&lt;/code&gt; as a path. The &lt;code&gt;/blog&lt;/code&gt; (which I will call prefix later on) is not a part of mounted engine, but we don&amp;#8217;t want to loose that information by simply removing it from &lt;span class="caps"&gt;PATH&lt;/span&gt;. In that case &lt;code&gt;/blog&lt;/code&gt; prefix is attached to &lt;code&gt;env["SCRIPT_NAME"]&lt;/code&gt; (&lt;code&gt;SCRIPT_NAME&lt;/code&gt; is part of &lt;a href="http://rack.rubyforge.org/doc/files/SPEC.html"&gt;rack&amp;#8217;s spec&lt;/a&gt;). That way, engine will get only &lt;code&gt;/posts&lt;/code&gt; part as &lt;span class="caps"&gt;PATH&lt;/span&gt;, which will allow to properly recognize it.&lt;/p&gt;
&lt;p&gt;The only one problem with recognition is connected with rack middlewares. Consider such example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# APP/config/routes.rb&lt;/span&gt;
&lt;span class="no"&gt;Foo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&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;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&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="n"&gt;match&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/blog/omg&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;omg#index&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# APP/app/controllers/omg_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OmgController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;SomeMiddleware&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# ENGINE/lib/blog/engine.rb&lt;/span&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;Engine&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;SomeMiddleware&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;What will happen when you will request &lt;code&gt;/blog/omg&lt;/code&gt; path? It will call the &lt;code&gt;Blog::Engine&lt;/code&gt; first, as it has higher priority than &lt;code&gt;/blog/omg&lt;/code&gt;. Request will pass through the Engine&amp;#8217;s middleware stack firing SomeMiddleware and hit the Engine&amp;#8217;s router. If &lt;code&gt;/omg&lt;/code&gt; is not a valid route for &lt;code&gt;Blog::Engine&lt;/code&gt; it will return 404 and Application&amp;#8217;s router will try next route, that is &lt;code&gt;/blog/omg&lt;/code&gt; pointing to &lt;code&gt;OmgController&lt;/code&gt;. Middleware stack for that controller also includes &lt;code&gt;SomeMiddleware&lt;/code&gt; so it will be fired again, which is probably not something that we want.&lt;/p&gt;
&lt;p&gt;The solution is to mount apps with lower priority than other routes.&lt;/p&gt;
&lt;h2&gt;Generation&lt;/h2&gt;
&lt;p&gt;The problem with generation is related to mount point, the place where engine is mounted. Consider such example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# APP/config/routes.rb&lt;/span&gt;
&lt;span class="no"&gt;MyRailsApp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&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;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;users#index&amp;quot;&lt;/span&gt;

  &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/:user&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;drogus&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&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="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# ENGINE/lib/blog/engine.rb&lt;/span&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;Engine&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# ENGINE/config/routes.rb&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;.&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;resources&lt;/span&gt; &lt;span class="ss"&gt;:posts&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;Blog::Engine&lt;/code&gt; is mounted at &lt;code&gt;"/:user/blog"&lt;/code&gt; (let&amp;#8217;s just suppose that blog implements multi user setup) and it provides &lt;code&gt;posts&lt;/code&gt; resources. As you can remember from recognition part, &lt;code&gt;/:user/blog&lt;/code&gt; part is engine&amp;#8217;s prefix. Additionally, default user is set to &amp;#8220;drogus&amp;#8221;, so when :user is not specified it will be set to drogus. Imagine that you need to generate path to posts. Something that you would normally achieve by calling &lt;code&gt;posts_path&lt;/code&gt; method. With more than one router the situation is not so simple. We can&amp;#8217;t simply use named url helpers (like &lt;code&gt;posts_path&lt;/code&gt;) inapplication&amp;#8217;s controllers, so we need some other way to handle that (if you&amp;#8217;re wondering why can&amp;#8217;t we just use &lt;code&gt;posts_path&lt;/code&gt;, it&amp;#8217;s explained in named routes section of this post).&lt;/p&gt;
&lt;p&gt;First problem connected with that is &lt;span class="caps"&gt;API&lt;/span&gt;. Jeremy Kemper came up with helper that will allow using mounted engine&amp;#8217;s url helpers by simply calling &lt;code&gt;some_engine.posts_path&lt;/code&gt;. The name of that helper is taken from Engine&amp;#8217;s name or from &lt;code&gt;:as&lt;/code&gt; option used in mount method. Because &lt;code&gt;:as&lt;/code&gt; option is not provided in my example, helper&amp;#8217;s name will be &lt;code&gt;blog_engine&lt;/code&gt; (based on &lt;code&gt;Blog::Engine&lt;/code&gt;). Using that helper we can generate paths for Engine:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;blog_engine&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_engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url_for&lt;/span&gt; &lt;span class="vi"&gt;@post&lt;/span&gt;
&lt;span class="n"&gt;blog_engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;polymorphic_path&lt;/span&gt; &lt;span class="vi"&gt;@post&lt;/span&gt;
&lt;span class="n"&gt;blog_engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url_for&lt;/span&gt; &lt;span class="ss"&gt;:controller&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;posts&amp;quot;&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;index&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We can also generate application&amp;#8217;s urls with &lt;code&gt;app&lt;/code&gt; helper:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root_url&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The next problem to solve is behavior of such helpers. At first, it seems that it&amp;#8217;s fairly easy, but in reality there is quite a few cases to handle. The thing that is most important while considering url generation is context. We can generate urls in application&amp;#8217;s controllers, engine&amp;#8217;s controller&amp;#8217;s, ActionMailer or regular classes not related to Rails itself. Let&amp;#8217;s go through each case.&lt;/p&gt;
&lt;h4&gt;Generating engine&amp;#8217;s url inside application&amp;#8217;s controller&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# APP/app/controllers/foo_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FooController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="n"&gt;blog_engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posts_path&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; &amp;quot;/drogus/blog/posts&amp;quot;&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;Generating posts path inside one of application controllers (and consequently views and helpers) should generate prefix according to mount point. In that particular situation, default user will be inserted in place of :user, so the url will be &lt;code&gt;"/drogus/blog/posts"&lt;/code&gt;. You could also do &lt;code&gt; blog_engine.posts_path(:user =&amp;gt; "john")&lt;/code&gt;, which would generate &lt;code&gt;"/john/blog/posts"&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;Generating engine&amp;#8217;s url inside engine&amp;#8217;s controller&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# ENGINE/app/controllers/posts_controller.rb&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="kp"&gt;include&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;.&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;url_helpers&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="n"&gt;posts_path&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;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;This situation is a bit different. The first thing you will probably notice is that I used posts_path instead of blog_engine.posts_path. This is possible because I included url_helpers from engine&amp;#8217;s routes. From various reasons it can&amp;#8217;t be included automagically for engine&amp;#8217;s controllers, but the plan is to make it work without any explicit includes. Why can&amp;#8217;t we just use &lt;code&gt;blog_engine.posts_path&lt;/code&gt;? In that example it would work, but note that engine can be mounted with different &lt;code&gt;:as&lt;/code&gt; option. With mount looking like:&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;you would have to use &lt;code&gt;blog.posts_path&lt;/code&gt; instead of &lt;code&gt;blog_engine.posts_path&lt;/code&gt;. Basically engine should not need to know how it&amp;#8217;s mounted. That said, we don&amp;#8217;t have any information about options used to mount it, all that we know is what&amp;#8217;s the request that was used to reach the engine.&lt;/p&gt;
&lt;p&gt;But what about the generated path? Someone could say that it should also generate prefix, but that would not work as expected. Imagine that someone requested one of your users blog with path &lt;code&gt;"/dhh/blog/posts/1"&lt;/code&gt;. When you click on link with url generated by &lt;code&gt;posts_path&lt;/code&gt;, you should stay in the same scope, so url should depend on you current path. This is achieved by using &lt;code&gt;env["SCRIPT_NAME"]&lt;/code&gt; value. In request to &lt;code&gt;"/dhh/blog/posts/1"&lt;/code&gt;, the script name would be set to &lt;code&gt;"/dhh/blog"&lt;/code&gt;, as this is the part of path that does not belong to engine. It should be clear now, that example above will generate &lt;code&gt;"/dhh/blog/posts"&lt;/code&gt; path for such request.&lt;/p&gt;
&lt;p&gt;The next thing that is worth mentioning is &lt;code&gt;_routes&lt;/code&gt; method. When you call posts_path directly, it must use routes object. This object is available through &lt;code&gt;_routes&lt;/code&gt; method. This method is defined when you include &lt;code&gt;url_helpers&lt;/code&gt;, so it points to application&amp;#8217;s routes by default. When &lt;code&gt;Blog::Engine.routes.url_helpers&lt;/code&gt; are included in PostsController, &lt;code&gt;_routes&lt;/code&gt; is changed to use engine&amp;#8217;s routes, and because of that we can use posts_path safely.&lt;/p&gt;
&lt;h4&gt;Generating application&amp;#8217;s url inside engine&amp;#8217;s controller&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# ENGINE/app/controllers/posts_controller.rb&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="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root_path&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;We would like to generate root_path from our application&amp;#8217;s router. Obviously it should generate &lt;code&gt;"/"&lt;/code&gt; path, without Engine&amp;#8217;s prefix. The only exception is situation when app is hosted in a sub path (eg /myapp). This can be done with Phusion Passenger, using RailsBaseURI option. &lt;code&gt;/myapp&lt;/code&gt; part would be passed as &lt;code&gt;SCRIPT_NAME&lt;/code&gt; in such case. Nothing complicated, we already know how to use &lt;code&gt;SCRIPT_NAME&lt;/code&gt;, right? Not really (wouldn&amp;#8217;t it be to simple? ;-). The problem is, &lt;code&gt;SCRIPT_NAME&lt;/code&gt; is kept as a string. Let&amp;#8217;s see how the request to &lt;code&gt;"/myapp/user/blog/posts"&lt;/code&gt; looks like.&lt;/p&gt;
&lt;p&gt;At first it hits the application. &lt;code&gt;SCRIPT_NAME&lt;/code&gt; is set to &lt;code&gt;/myapp&lt;/code&gt; by Passenger and the path is &lt;code&gt;/user/blog/posts&lt;/code&gt;. Now, application&amp;#8217;s router recognizes that this request should get to engine, so &lt;code&gt;Blog::Engine&lt;/code&gt; is called. As engine needs only &lt;code&gt;/posts&lt;/code&gt; as path, the prefix (&lt;code&gt;/user/blog&lt;/code&gt;) will be attached to &lt;code&gt;env["SCRIPT_NAME"]&lt;/code&gt; resulting in &lt;code&gt;/myapp/user/blog&lt;/code&gt;. As this is one string and we need to get just application&amp;#8217;s script name, solution is not obvious. How do we get the original script name? Right now our approach is to use whatever is set in &lt;code&gt;Rails.application.routes.default_url_options[:script_name]&lt;/code&gt;, so it should be set to &amp;#8220;/myapp&amp;#8221; in that case.&lt;/p&gt;
&lt;h4&gt;Generating engine&amp;#8217;s url in any other class (including ActionMailer)&lt;/h4&gt;
&lt;p&gt;In that case, url should be generated with prefix (which would be &lt;code&gt;/user/blog&lt;/code&gt; in my example). In ActionMailer we are not inside request, so &lt;code&gt;script_name&lt;/code&gt; is not available and with that in mind we need to generate the full path with &lt;code&gt;"/user/blog/"&lt;/code&gt; at the beginning.&lt;/p&gt;
&lt;h4&gt;Solution&lt;/h4&gt;
&lt;p&gt;Although it may look complicated to handle all those use cases, solution is relatively easy. The first thing to do is to assume that prefix should be always generated. This gets all cases with prefix out of our way. What&amp;#8217;s not so obvious, it also makes generating application&amp;#8217;s url in engine work. As application&amp;#8217;s routes are not mounted, prefix is nil, so we can safely add it to &amp;#8220;generate prefix&amp;#8221; case. What&amp;#8217;s left? Generating engine&amp;#8217;s url inside engine. We need to use &lt;code&gt;env["SCRIPT_NAME"]&lt;/code&gt; here. How to check if we should attach script name? We need to check if routes used to generate url are the same as routes connected with current request.&lt;/p&gt;
&lt;p&gt;Right now, to make it possible, router object is passed via &lt;code&gt;env["action_dispatch.routes"]&lt;/code&gt;. When application is called, it sets it to &lt;code&gt;Rails.application.routes&lt;/code&gt; and then when engine is called, it sets its own router there. That way, we always now in which controller are we. If &lt;code&gt;_routes&lt;/code&gt; method points to the same routes as the &lt;code&gt;env["action_dispatch.routes"]&lt;/code&gt; it means that we try to generate engine&amp;#8217;s url inside engine and we should use the &lt;code&gt;SCRIPT_NAME&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Named routes&lt;/h2&gt;
&lt;p&gt;The initial idea was to allow using named routes from 2 routers in one scope, just like that:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# APP/app/controllers/foo_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FooController&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="c1"&gt;# Rails.application.routes_url_helpers are included by default&lt;/span&gt;
  &lt;span class="kp"&gt;include&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;.&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;url_helpers&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="n"&gt;posts_path&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; &amp;quot;/blog/posts&amp;quot; - path from engine&amp;#39;s router&lt;/span&gt;
    &lt;span class="n"&gt;root_path&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; &amp;quot;/&amp;quot; - path from application&amp;#39;s router&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;Although it would be handy if you will have to use paths from mounted engine a lot, it is the cause of many issues:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;named route method must know the router that it was generated by, as now we could have multiple routers&lt;/li&gt;
	&lt;li&gt;there is a collision problem if 2 routers have the same helpers (situations when both routers will have for example posts_path is not so common, but problem will exist for root_path for sure)&lt;/li&gt;
	&lt;li&gt;currently, after including url_helpers twice &lt;code&gt;_routes&lt;/code&gt; method will point to second routes (&lt;code&gt;Blog::Engine.routes&lt;/code&gt; in last example), which will cause problems with using url helpers directly&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are some solutions for all of those problems, but as they&amp;#8217;re all a bit hacky and complicated, we decided to leave that topic and not allow to use helpers from 2 routers in one scope. It means that you will have to explicitly say that you want to use router other then the default one (for example by using &lt;code&gt;blog_engine.posts_path&lt;/code&gt; helper). It also means that including url_helpers from engine will overwrite current routes, so in case above, you can&amp;#8217;t directly use application routes any more. In such case you should use &lt;code&gt;app&lt;/code&gt; helper.&lt;/p&gt;
&lt;p&gt;I hope that this post is good introduction to current status of router usage in mountable apps. As usual: if you have any ideas, feature requests, critique or any other thoughts that could help bringing mountable apps to life, you&amp;#8217;re more than welcome ;-)&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>RSoC status: bringing engines closer to application</title>
   <link href="http://piotrsarnacki.com/2010/07/06/rsoc-status-briging-engine-closer-to-application" />
   <published>2010-07-06T00:00:00-07:00</published>
   <updated>2010-07-06T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2010/07/06/rsoc-status-briging-engine-closer-to-application</id>
   <content type="html">&lt;p&gt;I&amp;#8217;ve been working for Ruby Summer of Code for last 2 weeks and so far it&amp;#8217;s great! In this post I will try to sum up the work on engines and outline a couple of problems that are still not solved.&lt;/p&gt;
&lt;p&gt;The first idea for RSoC was to bring Rails::Engine closer to Rails::Application. One of the long term targets is to allow to run more than one Application instance in one process. &lt;a href="http://piotrsarnacki.com/2010/06/18/rails-internals-railties/"&gt;As I described in my last post&lt;/a&gt;, application is a bit more specialized engine, so while moving most of the functionalities from Application to Engine, I could identify and solve most of the problems with running several apps in one process.&lt;/p&gt;
&lt;p&gt;First things first. What can Engine do right now and where is it used in Rails? When you drop anything in vendor/plugins directory, it will implicitly be declared as Engine. The features of engine are:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;everything in app/* works as in application&lt;/li&gt;
	&lt;li&gt;it can load config/routes.rb&lt;/li&gt;
	&lt;li&gt;config/locales/* are automatically picked by I18n&lt;/li&gt;
	&lt;li&gt;config/initializers works as in application&lt;/li&gt;
	&lt;li&gt;engines also have initializers blocks inside the Rails::Engine class to customize rails booting&lt;/li&gt;
	&lt;li&gt;you can customize paths like in application (for example change where controllers are)&lt;/li&gt;
	&lt;li&gt;it can define custom generators&lt;/li&gt;
	&lt;li&gt;rake tasks are loaded from lib/tasks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All these features are great, but we can take it even further. Here is the plan for bringing Rails::Engine closer to Rails::Application. Rails::Engine should:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;be a Rack app&lt;/li&gt;
	&lt;li&gt;have a middleware stack&lt;/li&gt;
	&lt;li&gt;have its own routes&lt;/li&gt;
	&lt;li&gt;allow to store assets in public/&lt;/li&gt;
	&lt;li&gt;be able to run its own migrations&lt;/li&gt;
	&lt;li&gt;be able to load plugins&lt;/li&gt;
	&lt;li&gt;allow to do more configuration&lt;/li&gt;
	&lt;li&gt;allow to namespace models and controller without problems&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A few things from that list are already finished (not in rails master yet, on &lt;a href="http://github.com/drogus/rails/tree/engines"&gt;my fork&lt;/a&gt; for now). I will describe my changes, but beware, this is code that&amp;#8217;s not currently a part of rails and it can be changed before merging it to rails. Its here for getting feedback mainly.&lt;/p&gt;
&lt;p&gt;Engine can be now a rack application by providing rack endpoint:&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;Engine&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;endpoint&lt;/span&gt; &lt;span class="no"&gt;AnyRackApp&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;That code would create engine with AnyRackApp as endpoint. Now you can mount it with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;MyRailsApp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&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;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Mount method will tell application router that &lt;code&gt;Blog::Engine&lt;/code&gt; is located at &amp;#8220;/blog&amp;#8221; path. Let&amp;#8217;s investigate a request to &amp;#8220;/blog/posts&amp;#8221;. At first, it will hit the application and it will be passed through entire application&amp;#8217;s middleware stack. The last middleware in application is the router. Router will recognize that &amp;#8220;/blog&amp;#8221; should point to &lt;code&gt;Blog::Engine&lt;/code&gt; app, so it will pass the request to &lt;code&gt;Blog::Engine&lt;/code&gt;. Then it will be passed through Engine&amp;#8217;s middleware stack and finally it will hit Engine&amp;#8217;s rack endpoint.&lt;/p&gt;
&lt;p&gt;What’s the point of providing rack app inside engine? Probably this will not be a common scenario, but it allows you to add all functionalities provided by Engines, like initializers, generators, to your rack app. Besides, Engine has its own middleware stack now, so you can easily add some specific middlewares that will be fired only on requests for engine:&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;Engine&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Subdomain&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;By default endpoint is set to &lt;code&gt;routes&lt;/code&gt;, probably a more common scenario.&lt;/p&gt;
&lt;p&gt;Currently if you want to add your own routes, you have to hardcode it in application&amp;#8217;s routes. It&amp;#8217;s a bit limited, as you cannot easily change the place where the engine is mounted. With Engine being able to use its own router, you can mount it anywhere you want, even using dynamic scopes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# APP/config/routes.rb&lt;/span&gt;
&lt;span class="no"&gt;MyRailsApp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&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;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&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="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# ENGINE/lib/blog/engine.rb&lt;/span&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;Engine&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# ENGINE/config/routes.rb&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;.&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;resources&lt;/span&gt; &lt;span class="ss"&gt;:posts&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With such setup, &lt;code&gt;Blog::Engine&lt;/code&gt; is mounted at &amp;#8220;/blog&amp;#8221;. A request to &amp;#8220;/blog/posts&amp;#8221; will fire posts controller inside the engine. It’s simple as that! However, as we are going to see in another blog post, if you want to mount an engine using a dynamic route, as &amp;#8220;/:company/blog&amp;#8221;, we need to consider different scenarios on &lt;span class="caps"&gt;URL&lt;/span&gt; Recognition and Generation. Currently if you use &lt;code&gt;posts_path&lt;/code&gt;, it will generate &lt;code&gt;/posts&lt;/code&gt;. The problem is, if you&amp;#8217;re in application you should prepend prefix for mounted app, so it would be &amp;#8220;/company/blog/posts&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Next important thing for engines working as full rails apps is public folder. Currently you can for example symlink engine&amp;#8217;s public folder in your application. It&amp;#8217;s ok, but there is much cleaner way to do it: use ActionDispatch::Static, which will serve static files from given directory. At first when I heard that idea, I thought that it will much slower than serving those files with web server, but according to Yehuda Katz, it should be really fast in practice and many server setups could actually cache it (for example with Varnish). If it&amp;#8217;s still not fast enough you can just not enable serving static files, make a symlink and serve it with webserver.&lt;/p&gt;
&lt;p&gt;In addition to all that features, engines can load its own set of plugins now. Right now, it&amp;#8217;s really simple implementation without any check for duplicating plugins, but I think we will consider such thing. Personally I hope that more and more people will just start using gem plugins with its own dependencies, but there are still many apps with &lt;code&gt;vendor/plugins&lt;/code&gt; full of plugins.&lt;/p&gt;
&lt;p&gt;One of the things that has not been implemented yet is migration support. There is quite long &lt;a href="https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/2058"&gt;discussion&lt;/a&gt; on it on lighthouse and a &lt;a href="http://interblah.net/plugin-migrations"&gt;blog post with a few solutions described&lt;/a&gt;, but there is sill no consensus on that one. If you have any thoughts on that topic, you can add a comment on lighthouseapp.&lt;/p&gt;
&lt;p&gt;The last problem that I will talk about (but certainly not last problem that will appear) is namespacing. For better encapsulation all of the controllers, helpers and models should be namespaced. The problem is, currently if you would namespace a controller or model it will not work as intended. Given:&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;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ApplicationController&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;it will affect application in many places. Using that controller in routes will also require namespacing it with: &amp;#8220;blog/posts#index&amp;#8221;, which is probably not something that we want for all the routes. In case of models, most of ORMs will name table for &lt;code&gt;class Blog::Post&lt;/code&gt; as &amp;#8220;blog_posts&amp;#8221;. It could be actually ok, as it will help to avoid name conflicts, but it&amp;#8217;s not always desired behavior.&lt;/p&gt;
&lt;p&gt;I will appreciate any ideas and thoughts on that topic. Anything, like &lt;span class="caps"&gt;API&lt;/span&gt; ideas, feature requests or solutions to some problems will be welcome and will help me with delivering better mountable apps. Stay tuned for the next post on RSoC status: routes. I promise you will not have to wait for it for the next 2 weeks ;-)&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Rails internals: Railties</title>
   <link href="http://piotrsarnacki.com/2010/06/18/rails-internals-railties" />
   <published>2010-06-18T00:00:00-07:00</published>
   <updated>2010-06-18T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2010/06/18/rails-internals-railties</id>
   <content type="html">&lt;p&gt;I&amp;#8217;m going to start my RSoC work next week, but I as a preparation for real work I started reading through Rails codebase. I will try to document my findings on this blog, I hope it will be helpful for people that would like to know Rails internals.&lt;/p&gt;
&lt;p&gt;One of the first findings of reading Rails 3 code is that it&amp;#8217;s really simple compared to previous Rails versions. Boot process doesn&amp;#8217;t need rubygems magic anymore. Ruby features are used to make Rails flexible without need of some crazy hacks. alias_method_chain is not used anymore, which makes it much easier to understand code. These are really great news for all Rails devs, especially plugins maintainers. But hey, enough already known facts, let&amp;#8217;s get to the point!&lt;/p&gt;
&lt;p&gt;The core class of Rails is Railtie. If you are a plugin developer, you will probably want to take a closer look at that. Although Railtie is not required in every plugin, it&amp;#8217;s handy if you want to hook into Rails boot process, create intializers or add a rake tasks. For more information you can read about &lt;a href="http://edgeapi.rubyonrails.org/classes/Rails/Railtie.html"&gt;Railtie in docs&lt;/a&gt; So what&amp;#8217;s Railtie? It&amp;#8217;s a simple class that is capable of loading generators, tasks, middlewares and adding subscribers. One of the things that I&amp;#8217;ve noticed in Railtie (and almost any other class in Rails) is usage of autoload method. I&amp;#8217;ve never heard of autoload before and it seems that&amp;#8217;s very handy method. From rubydocs:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Registers filename to be loaded (using Kernel::require) the first time that module (which may be a String or a symbol) is accessed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;  &lt;span class="nb"&gt;autoload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
  &lt;span class="nb"&gt;autoload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:MyModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/usr/local/lib/modules/my_module.rb&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;The great thing in autoload is its laziness. It will not load given file, if module is not accessed. How does Rails use autoload in Railtie? It autoloads Configurable and Configuration modules. As Railtie doesn&amp;#8217;t need configuration on its own, it includes it in classes that inherit from Railtie (&lt;a href="http://github.com/rails/rails/blob/v3.0.0.beta4/railties/lib/rails/railtie.rb#L170"&gt;code&lt;/a&gt;). That way, those 2 files will not be loaded unless Railtie subclass is created. Someone could argue that Railtie will not be probably used alone, without any subclasses, as Railtie is much more like abstract class, but it&amp;#8217;s really good pattern and it&amp;#8217;s consistently used across other classes in Rails.&lt;/p&gt;
&lt;p&gt;&lt;ins&gt;I need to rectify one thing. Although autoload is very nice, it&amp;#8217;s also known to be thread unsafe and according to Carl Lerche it will be removed from Rails. Use with consideration ;-) &lt;/ins&gt;&lt;/p&gt;
&lt;p&gt;Getting back to Rails itself. The next class in hierarchy is &lt;a href="http://edgeapi.rubyonrails.org/classes/Rails/Engine.html"&gt;Engine&lt;/a&gt;. Engine is much closer to full stack Rails app. In fact application is a bit more specialized Engine. That makes sharing an application as easy as converting it to engine. So, what&amp;#8217;s Engine? Basically it&amp;#8217;s Railtie with ability to set load paths for views, controllers, helpers, locales etc. That&amp;#8217;s it? Nothing more? That simple? Yep, that&amp;#8217;s right.&lt;/p&gt;
&lt;p&gt;Now it&amp;#8217;s time for Application. Application is a subclass of Engine and it&amp;#8217;s capable of booting the Rails app. As you can &lt;a href="http://edgeapi.rubyonrails.org/classes/Rails/Application.html"&gt;read in the docs&lt;/a&gt;, Application is singletone and that&amp;#8217;s why 2 Rails apps can&amp;#8217;t be run in a single process. So what does exactly Rails application do?&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;it loads default middleware stack (it&amp;#8217;s amazing how much things can be handled by rack middlewares, allowing to keep Rails code simpler)&lt;/li&gt;
	&lt;li&gt;it loads plugins&lt;/li&gt;
	&lt;li&gt;it sets bunch of other things: database, logging, sessions, environment config, cache&lt;/li&gt;
	&lt;li&gt;it loads activesupport (interesting thing, you can set config.active_support.bare to not load &amp;#8216;active_support/all&amp;#8217;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&amp;#8217;s also interesting how does Rails use OO model to make things simpler. After generating Rails 3 apps you can notice that frameworks are loaded by just requiring their railties:&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="s2"&gt;&amp;quot;action_mailer/railtie&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;active_resource/railtie&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The same goes for engines. Ok&amp;#8230; but how will Rails know all the railties and engines loaded? As railties and engines are subclasses of Rails::Railtie and Rails::Engine, it&amp;#8217;s as easy as getting their subclasses: ::Rails::Railtie.subclasses or  ::Rails::Engine.subclasses. Quick note on &lt;code&gt;subclasses&lt;/code&gt;: it&amp;#8217;s not ruby method, subclasses are gathered using &lt;code&gt;inherited&lt;/code&gt; method (&lt;a href="http://github.com/rails/rails/blob/v3.0.0.beta4/railties/lib/rails/railtie.rb#L171"&gt;code&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Now let&amp;#8217;s look how is that all used to boot rails application. As you can see in Rails 3 application, config.ru file looks like:&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="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="s1"&gt;&amp;#39;../config/environment&amp;#39;&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="n"&gt;run&lt;/span&gt; &lt;span class="no"&gt;MyApplication&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you probably know, object passed to &amp;#8220;run&amp;#8221; method must respond to :call. Application class does not have &amp;#8220;call&amp;#8221; method defined, though. How does it work? When you send &amp;#8220;call&amp;#8221; message to MyApplication::Application, method_missing will be fired and message will be send to application instance. In fact all of the missing methods on MyApplication::Application are delegated to instance:&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_missing&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;instance&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="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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&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;What is instance? As Application is singleton, call to instance method returns instantinated application or instantinates it and then returns the instance. Application instance is built on initialization, such behavior is triggered in &lt;a href="http://github.com/rails/rails/blob/master/railties/lib/rails/application/finisher.rb"&gt;Finisher&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The next question is: how does request&amp;#8217;s path look like? call() method runs call() on @app object, which is basically the middleware stack. Then, request goes through all the middlewares up to ActionDispatch::Routing::RouteSet, which is responsible for routing request to one of the controllers or other rack apps.&lt;/p&gt;
&lt;p&gt;After reading this blog post you should have a pretty good overview of core classes of Rails. I strongly encourage you to dive into Rails code yourself, it&amp;#8217;s a good way to learn new things about design, object oriented programming in ruby and to pick some cool patterns.&lt;/p&gt;
&lt;p&gt;If anyone is interested, I plan to continue Rails internals posts with action pack and routing system. Feedback highly appreciated! Please let me know if you find this kind of information useful or if you think that I missed something or should give more details on any of the parts.&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>New address</title>
   <link href="http://piotrsarnacki.com/2010/05/15/new-address" />
   <published>2010-05-15T00:00:00-07:00</published>
   <updated>2010-05-15T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2010/05/15/new-address</id>
   <content type="html">&lt;p&gt;I haven&amp;#8217;t written anything here for a while now. It was partially caused by a little mess in my blogs. I had 2 blogs with addresses blog.drogomir.com (for Polish blog) and drogomir.com/blog (for English blog)&amp;#8230; I didn&amp;#8217;t quite like addresses (hey? which one is in which language?) and keeping them on separate engines (typo and mephisto). Each time I wanted to start new post, I felt that I should change that right away. The problem is, I never had that much will to actually do something and after few minutes of checking solutions I usually stopped without any effects.&lt;/p&gt;
&lt;p&gt;Finally I&amp;#8217;ve merged my blogs and moved it to current address: &lt;a href="http://piotrsarnacki.com"&gt;piotrsarnacki.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The blog is &lt;a href="http://github.com/drogus/drogus.github.com"&gt;hosted on github&lt;/a&gt; with help of &lt;a href="http://github.com/mojombo/jekyll"&gt;jekyll&lt;/a&gt;. Jekyll is great so far. Its simplicity is impressive and I don&amp;#8217;t have to worry about any server related issues. Layout is borrowed from &lt;a href="http://tom.preston-werner.com"&gt;Tom Preston-Werner&amp;#8217;s site&lt;/a&gt; (layout is &lt;span class="caps"&gt;MIT&lt;/span&gt; licensed). It helped me start within a few minutes, but I will try to change it in a few days.&lt;/p&gt;
&lt;p&gt;Although old &lt;span class="caps"&gt;RSS&lt;/span&gt; feed still works, I recommend to use new one: &lt;a href="http://feeds.feedburner.com/piotrsarnacki"&gt;feeds.feedburner.com/piotrsarnacki&lt;/a&gt;. All the feeds available for this blog are:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://feeds.feedburner.com/piotrsarnacki"&gt;feeds.feedburner.com/piotrsarnacki&lt;/a&gt; &amp;#8211; English content&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; Polish content (available on &lt;a href="http://piotrsarnacki.com/pl"&gt;piotrsarnacki.com/pl&lt;/a&gt;)&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://feeds.feedburner.com/piotrsarnacki-all"&gt;feeds.feedburner.com/piotrsarnacki-all&lt;/a&gt; &amp;#8211; All posts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I deleted some of the posts and marked some &lt;a href="http://piotrsarnacki.com/2009/06/16/cucumber-and-celerity-testing-unobtrusive-javascript/"&gt;as deprecated&lt;/a&gt;. I try to not abandon that blog this time and post some new stuff.&lt;/p&gt;
&lt;p&gt;On the side note. I have managed to be one of 20 students working on &lt;a href="http://rubysoc.org"&gt;Ruby Summer of Code&lt;/a&gt; projects. I will work in the team with &lt;a href="http://github.com/hurrycane"&gt;Bogdan Gaza&lt;/a&gt; on Rails 3 mountable apps and Rails admin panel (I will focus more on mountable apps part and Bogdan will work on rails admin panel). &lt;a href="http://github.com/carllerche"&gt;Carl Lerche&lt;/a&gt; and &lt;a href="http://github.com/sferik"&gt;Erik Michaels-Ober&lt;/a&gt; will be our primary mentors.&lt;/p&gt;
&lt;p&gt;I will try to post progress updates on this blog. Stay tuned!&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>Cucumber and Celerity - testing unobtrusive javascript</title>
   <link href="http://piotrsarnacki.com/2009/06/16/cucumber-and-celerity-testing-unobtrusive-javascript" />
   <published>2009-06-16T00:00:00-07:00</published>
   <updated>2009-06-16T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2009/06/16/cucumber-and-celerity-testing-unobtrusive-javascript</id>
   <content type="html">&lt;p&gt;In the world of javascript and ajax web apps I often find that writing applications supporting both javascript enabled and disabled is hard. For the sake of simplicity and due to deadlines I often write only javascript version of some of the features. But there are many situations where both scenarios should be supported.&lt;/p&gt;
&lt;p&gt;The most important parts of app, especially the ones that must be crawled by google, should be written with unobtrusive style. And here comes the problem&amp;#8230; I rarely see javascript testing and till today I haven&amp;#8217;t done it myself. Celerity and cucumber (with help of culerity) can solve this problem. Here is short guide introducing this technique.&lt;/p&gt;
&lt;p&gt;I will use &lt;a href="http://cukes.info/"&gt;Cucumber&lt;/a&gt;, &lt;a href="http://celerity.rubyforge.org/"&gt;celerity&lt;/a&gt; (which is &lt;a href="http://htmlunit.sourceforge.net/"&gt;HtmlUnit&lt;/a&gt; wrapper with &lt;span class="caps"&gt;API&lt;/span&gt; compatible with watir) and &lt;a href="http://github.com/langalex/culerity/tree/master"&gt;culerity&lt;/a&gt;. Culerity is a proxy between celerity and your app. Celerity requires JRuby and probably your app need &lt;span class="caps"&gt;MRI&lt;/span&gt; or &lt;span class="caps"&gt;REE&lt;/span&gt; &amp;#8211; culerity resolves this problem.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s get started.&lt;/p&gt;
&lt;p&gt;You need to &lt;a href="http://kenai.com/projects/jruby/pages/GettingStarted#Installing_a_JRuby_Binary_Fille"&gt;install JRuby&lt;/a&gt; in order to run celerity. After installing and adding jruby to your &lt;span class="caps"&gt;PATH&lt;/span&gt; install celerity gem (probably as a root):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;jruby&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt; &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;celerity&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now you can create rails app and configure the environment:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;culerity&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;
&lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;cucumber&lt;/span&gt; &lt;span class="n"&gt;rspec&lt;/span&gt; &lt;span class="n"&gt;rspec&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;haml&lt;/span&gt;
&lt;span class="c1"&gt;# add config.gem &amp;quot;haml&amp;quot; to environment.rb&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;langalex&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;culerity&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="ss"&gt;:/&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;gems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="n"&gt;culerity&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;
&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;/script&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;generate&lt;/span&gt; &lt;span class="n"&gt;cucumber&lt;/span&gt;
&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;/script&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;generate&lt;/span&gt; &lt;span class="n"&gt;rspec&lt;/span&gt;
&lt;span class="c1"&gt;# now edit database.yml and set database options&lt;/span&gt;
&lt;span class="n"&gt;rake&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="ss"&gt;:migrate&lt;/span&gt;
&lt;span class="n"&gt;rake&lt;/span&gt; &lt;span class="n"&gt;features&lt;/span&gt;
&lt;span class="n"&gt;rm&lt;/span&gt; &lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;step_definitions&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;webrat_steps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt; &lt;span class="c1"&gt;# cause we will be using celerity&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;At this point you should have cucumber configured and you should be able to run &amp;#8220;rake features&amp;#8221; with output similar to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;scenarios&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mo"&gt;000&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Let&amp;#8217;s add some tests! You will need step definitions and hooks. Culerity provides some basic step definitions and hooks which you can generate with &amp;#8220;./script/generate culerity&amp;#8221; but I&amp;#8217;ve changed them a bit for my needs, so you can find them on &lt;a href="http://github.com/drogus/culerity-javascript-example"&gt;this example repository&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://github.com/drogus/culerity-javascript-example/blob/master/features/step_definitions/common_celerity.rb"&gt;features/step_definitions/common_celerity.rb&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://github.com/drogus/culerity-javascript-example/blob/master/features/support/hooks.rb"&gt;features/support/hooks.rb&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Copy those files to your app.&lt;/p&gt;
&lt;p&gt;The first file is just rewrite of webrat steps and the second file adds hooks for firing celerity server and browser. Let me explain the hooks.rb file:&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;culerity&amp;#39;&lt;/span&gt;
&lt;span class="sb"&gt;`mongrel_rails start -e cucumber -p 3001 -d`&lt;/span&gt;

&lt;span class="no"&gt;Before&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vg"&gt;$server&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;Culerity&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;run_server&lt;/span&gt;
  &lt;span class="vg"&gt;$browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Culerity&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RemoteBrowserProxy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="vg"&gt;$server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:browser&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:firefox&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="vg"&gt;$browser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;webclient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setJavaScriptEnabled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
  &lt;span class="vi"&gt;@host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://localhost:3001&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;@js&amp;quot;&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;scenario&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="vg"&gt;$browser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;webclient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setJavaScriptEnabled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;at_exit&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vg"&gt;$browser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;
  &lt;span class="vg"&gt;$server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;
  &lt;span class="sb"&gt;`mongrel_rails stop`&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&amp;#8216;Before&amp;#8217; hooks are run before each scenerio. In first &amp;#8216;before&amp;#8217; hook server and browser are set up and host is set to &amp;#8220;http://localhost:3001&amp;#8221; (change it if you want to run app on other address or port). Notice the line: $browser.webclient.setJavaScriptEnabled(false) &amp;#8211; it disables javascript by default.&lt;/p&gt;
&lt;p&gt;Second Before hook is fired only for scenarios tagged with @js tag. It will be useful for explicitly saying which scenarios should be tested with  javascript.&lt;/p&gt;
&lt;p&gt;&lt;ins&gt;I&amp;#8217;ve also added lines to start mongrel before the tests and stop it at exit. It&amp;#8217;s handy if you don&amp;#8217;t want to run and restart mongrel manually&lt;/ins&gt;&lt;/p&gt;
&lt;p&gt;Now it is time to write some scenarios. File &lt;a href="http://github.com/drogus/culerity-javascript-example/blob/master/features/javascript.feature"&gt;features/javascript.feature&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;Feature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Javascript&lt;/span&gt;
  &lt;span class="no"&gt;In&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="n"&gt;javascript&lt;/span&gt;
  &lt;span class="no"&gt;As&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;developer&lt;/span&gt;
  &lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;need&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;way&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="n"&gt;scenarios&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;javascript&lt;/span&gt; &lt;span class="n"&gt;enabled&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;disabled&lt;/span&gt;

  &lt;span class="vi"&gt;@js&lt;/span&gt;
  &lt;span class="no"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;With&lt;/span&gt; &lt;span class="n"&gt;javascript&lt;/span&gt;
    &lt;span class="no"&gt;Given&lt;/span&gt; &lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;am&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;homepage&lt;/span&gt;
    &lt;span class="no"&gt;And&lt;/span&gt; &lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;follow&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Click me!&amp;quot;&lt;/span&gt;
    &lt;span class="no"&gt;Then&lt;/span&gt; &lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;see&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Javascript rocks!&amp;quot;&lt;/span&gt;

  &lt;span class="no"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Without&lt;/span&gt; &lt;span class="n"&gt;javascript&lt;/span&gt;
    &lt;span class="no"&gt;Given&lt;/span&gt; &lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;am&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;homepage&lt;/span&gt;
    &lt;span class="no"&gt;And&lt;/span&gt; &lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;follow&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Click me!&amp;quot;&lt;/span&gt;
    &lt;span class="no"&gt;Then&lt;/span&gt; &lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;see&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;I am also working without javascript!&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Both scenerios rely on &amp;#8220;Click me!&amp;#8221; link but have different expectations. &lt;del&gt;To run those tests start mongrel (or any other web server):&lt;/del&gt;&lt;br /&gt;
&lt;del&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;mongrel_rails&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;e&lt;/span&gt; &lt;span class="n"&gt;cucumber&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;3001&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;/del&gt;&lt;br /&gt;
&lt;del&gt;This will fire mongrel in background on port 3001.&lt;/del&gt;&lt;br /&gt;
&lt;ins&gt;It is not necessary as mongrel control commands are in hooks.rb file&lt;/ins&gt;&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s best to operate on 2 console tabs while using celerity. One of the big drawbacks of using it, is lack of displaying rails exceptions. Because of that I run cucumber on one tab and &amp;#8220;tail -f log/cucumber.log&amp;#8221; on the other &amp;#8211; I can see errors in the log without opening the browser.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s run tests: &lt;pre&gt;rake features&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Of course both tests should fail with &lt;pre&gt;Unable to locate Link, using :text and /Click me!/&lt;/pre&gt; and rails error in log file: &lt;pre&gt;ActionController::RoutingError (No route matches &amp;#8220;/&amp;#8221; with {:method=&amp;gt;:get}):&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Now we can fix it. We need some controller to show the link: &lt;br /&gt;
&lt;pre&gt;script/generate rspec_controller home&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Add &lt;pre&gt;map.root :action =&amp;gt; &amp;#8220;show&amp;#8221;, :controller =&amp;gt; &amp;#8220;home&amp;#8221;&lt;/pre&gt; to routes file.&lt;br /&gt;
Next copy &lt;a href="http://github.com/drogus/culerity-javascript-example/raw/master/app/views/layouts/main.html.haml"&gt;app/views/layouts/main.html.haml&lt;/a&gt; (it just yields action and includes jquery.js and application.js) and &lt;a href="http://github.com/drogus/culerity-javascript-example/raw/master/public/javascripts/jquery.js"&gt;jquery.js&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You need to also set layout for home controller: &lt;pre&gt;layout &amp;#8216;main&amp;#8217;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;And here comes html and javascript.&lt;br /&gt;
app/views/home/show.html.haml&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Click me!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;?clicked=1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;click_me&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;#text&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;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:clicked&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;am&lt;/span&gt; &lt;span class="n"&gt;also&lt;/span&gt; &lt;span class="n"&gt;working&lt;/span&gt; &lt;span class="n"&gt;without&lt;/span&gt; &lt;span class="n"&gt;javascript!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;It displays a link and a text if params[:clicked] is present. So after clicking on that link page will be reloaded with parameter clicked=1 and the text will be displayed.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s check if it&amp;#8217;s passing:&lt;br /&gt;
&lt;pre&gt;mongrel_rails restart; rake features&lt;/pre&gt;&lt;br /&gt;
We need to restart mongrel before &amp;#8220;rake features&amp;#8221; in order to load changes because of &amp;#8220;cache_classes = true&amp;#8221;. It&amp;#8217;s one of the drawbacks of using this method, but I&amp;#8217;m sure that someone will find out better way to do that.&lt;/p&gt;
&lt;p&gt;If you did everything properly the second scenario should pass now. We&amp;#8217;re green! :D&lt;/p&gt;
&lt;p&gt;Add javascript code to your application in order to make the second scenario pass:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;jQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#click_me&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;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#text&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;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Javascript rocks!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&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;Now both scenarios should pass.&lt;/p&gt;
&lt;p&gt;To sum up, now you should be able to:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;configure rails app with cucumber and celerity&lt;/li&gt;
	&lt;li&gt;specify how your tests should be run by placing @js tag on top of javascript scenarios&lt;/li&gt;
	&lt;li&gt;test unobtrusive javascript with ease&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;TODOs:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;del&gt;figure out better way to reload rails app while testing&lt;/del&gt;&lt;/li&gt;
	&lt;li&gt;provide better error explanations in cucumber with celurity to test without tailing logs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cheers!&lt;/p&gt;
&lt;h3&gt;Additional resources&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://github.com/alvinschur/celerity-examples"&gt;Celerity ajax examples by Alvin Schur&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content>
 </entry>
 
 <entry>
   <title>Fixing safe_erb with memcached improved</title>
   <link href="http://piotrsarnacki.com/2009/05/12/fixing-safe_erb-with-memcached-improved" />
   <published>2009-05-12T00:00:00-07:00</published>
   <updated>2009-05-12T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2009/05/12/fixing-safe_erb-with-memcached-improved</id>
   <content type="html">&lt;p&gt;Ruby has many structures that are weird and dangerous to some programmers that works with Java or other more &amp;#8220;traditional&amp;#8221; OO languages. For me it&amp;#8217;s great and metaprogramming capabilities makes ruby killer in case of DSLs, nice looking APIs and avoiding repeating yourself.&lt;/p&gt;
&lt;p&gt;But many problems that are solved with alias_method_chain or method_missing can be solved easier without the code smell. I sometimes find myself designing some weird complex solutions and then, after looking at it saying: &amp;#8220;Hey! Why not just use OO features!&amp;#8221;. I encourage everybody to look critically at every piece of your code that seems to be overkill and can be replaced with standard OO design.&lt;/p&gt;
&lt;p&gt;One of examples is &lt;a href="http://www.nfjsone.com/blog/david_bock/2009/03/fixing_safe_erb_with_memcached_fragments.html"&gt;fixing memcached and safe_erb duet with alias_method_chain&lt;/a&gt; I don&amp;#8217;t know if this solution worked in older versions of rails, but in 2.3 I got stack level too deep error. After some thinking I&amp;#8217;ve just created subclass of MemCacheStore:&lt;/p&gt;
&lt;script src="http://gist.github.com/110488.js"&gt;&lt;/script&gt;&lt;p&gt;And then you can simply set it as cache store: config.cache_store = :my_mem_cache_store&lt;/p&gt;
&lt;p&gt;And that&amp;#8217;s it, no alias method chain :)&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>My git workflow</title>
   <link href="http://piotrsarnacki.com/2009/04/26/my-git-workflow" />
   <published>2009-04-26T00:00:00-07:00</published>
   <updated>2009-04-26T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2009/04/26/my-git-workflow</id>
   <content type="html">&lt;p&gt;I know that this post may be obvious for experienced git users, but it may be useful for some of you.&lt;/p&gt;
&lt;p&gt;Github has &lt;a href="http://github.com/blog/421-pick-your-default-branch"&gt;added default branch picking&lt;/a&gt; recently. It&amp;#8217;s great, as you don&amp;#8217;t have to use master branch as your main branch. I usually have three &amp;#8220;main&amp;#8221; branches in my repo &amp;#8211; production, staging and development.&lt;/p&gt;
&lt;p&gt;The main point of it is to have separate branch for each environment (sometimes I skip staging, but it&amp;#8217;s often wise to have some point between development and production). My main problem with git (or maybe rather with my lack of knowledge about git) in the very beginning of using git was to learn how to use branches efficiently. After some time with git I&amp;#8217;ve noticed that it&amp;#8217;s best to keep each fix or idea in separate branch.&lt;/p&gt;
&lt;p&gt;One crucial thing is to inherit from production when it&amp;#8217;s possible. Let&amp;#8217;s say you have those main branches I mentioned before. There is a bug #345 in bug tracker and you want to fix it and merge into production after showing results to the client. The best way is to begin with production:&lt;/p&gt;
&lt;pre&gt;&lt;code class="console"&gt;
git checkout -b bug345 production
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will create new branch bug345 with production as a parent branch. Now:&lt;/p&gt;
&lt;pre&gt;&lt;code class="console"&gt;
# fix the bug
git commit -m "Fixed the nasty bug #345"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sometimes client doesn&amp;#8217;t need reviewing the changes but when he does you should have ability to show it and merge it to the production branch later.&lt;/p&gt;
&lt;pre&gt;&lt;code class="console"&gt;
git checkout staging
git merge bug345
# deploy staging
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After the client approval of your solution you can merge the bugfix to the production branch:&lt;/p&gt;
&lt;pre&gt;&lt;code class="console"&gt;
git checkout production
git merge bug345
# deploy production
# you can remove bug345 branch now
git branch -d bug345
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The most important thing in this process is to always inherit from production branch, unless you need changes from other branches. Otherwise you could merge some needless junk from other branches or have to cherry-pick individual commits. With above solution everything is clean and you are sure that that you will merge only what you need.&lt;/p&gt;
&lt;p&gt;Hope it will help :)&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>JSON-P support for Apache upload progress</title>
   <link href="http://piotrsarnacki.com/2009/02/10/json-p-support-for-apache-upload-progress" />
   <published>2009-02-10T00:00:00-08:00</published>
   <updated>2009-02-10T00:00:00-08:00</updated>
   <id>http://piotrsarnacki.com/2009/02/10/json-p-support-for-apache-upload-progress</id>
   <content type="html">&lt;p&gt;Just a quick note about new feature added to apache upload progress and jquery upload progress libs.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://deadprogrammersociety.blogspot.com/"&gt;Ron Evans aka deadprogrammer&lt;/a&gt; has added support for &lt;span class="caps"&gt;JSON&lt;/span&gt;-P in &lt;a href="http://github.com/drogus/apache-upload-progress-module/commit/20fed47119d3ea70b91ccb6a91a9338c8f127304"&gt;this commit&lt;/a&gt;. What does it mean? Cross domain requests are now possible, so if you need such a functionality pull the newest changes.&lt;/p&gt;
&lt;p&gt;Here is Ron&amp;#8217;s description from &lt;span class="caps"&gt;README&lt;/span&gt;:&lt;br /&gt;
&lt;blockquote&gt;&lt;br /&gt;
- &lt;span class="caps"&gt;JSON&lt;/span&gt;-P Support&lt;/p&gt;
&lt;p&gt;You can also request progress updates by using &lt;span class="caps"&gt;JSON&lt;/span&gt;-P, if you are uploading the file from a different domain or  subdomain than the web server that is handling your original request. Adding a &amp;#8220;callback=yourCallbackFunction&amp;#8221; parameter  to your request to the progress server will activate this functionality.&lt;/p&gt;
&lt;p&gt;For example, a request like:&lt;br /&gt;
  http://uploads.yourdomain.com/progress?callback=jsonp123&amp;amp;X-Progress-ID=1234&lt;/p&gt;
&lt;p&gt;Would return the &lt;span class="caps"&gt;JSON&lt;/span&gt;-P function: &lt;br /&gt;
  jsonp123(new Object({ &amp;#8216;state&amp;#8217; : &amp;#8216;uploading&amp;#8217;, &amp;#8216;received&amp;#8217; : 35587, &amp;#8216;size&amp;#8217; : 716595, &amp;#8216;speed&amp;#8217; : 35587 }));&lt;/p&gt;
&lt;p&gt;The normal &lt;span class="caps"&gt;JSON&lt;/span&gt; request:&lt;br /&gt;
  http://www.yourdomain.com/progress?X-Progress-ID=1234&lt;/p&gt;
&lt;p&gt;Would return the &lt;span class="caps"&gt;JSON&lt;/span&gt; data: &lt;br /&gt;
  new Object({ &amp;#8216;state&amp;#8217; : &amp;#8216;uploading&amp;#8217;, &amp;#8216;received&amp;#8217; : 35587, &amp;#8216;size&amp;#8217; : 716595, &amp;#8216;speed&amp;#8217; : 35587 })&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Remember to update &lt;a href="http://github.com/drogus/jquery-upload-progress/tree/master"&gt;jquery upload progress&lt;/a&gt; also, to use jsonp.&lt;/p&gt;
&lt;p&gt;Enjoy :)&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>Tweaking Rails app with jQuery, part I</title>
   <link href="http://piotrsarnacki.com/2008/07/03/tweaking-rails-app-with-jquery-part-i" />
   <published>2008-07-03T00:00:00-07:00</published>
   <updated>2008-07-03T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2008/07/03/tweaking-rails-app-with-jquery-part-i</id>
   <content type="html">&lt;div class="quick-links"&gt;
&lt;p&gt;Quick links:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://mugshots.drogomir.com/js/step2/mugshots"&gt;completed demo&lt;/a&gt; (demo is reseted every few hours)&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://github.com/drogus/mugshots/tree/master"&gt;sources of application on github&lt;/a&gt; (&lt;a href="http://github.com/drogus/mugshots/tarball/master"&gt;or download tarball&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;I&amp;#8217;m in the train from &lt;a href="http://maps.google.com/maps?f=d&amp;amp;hl=en&amp;amp;geocode=&amp;amp;saddr=Zgorzelec,+Poland&amp;amp;daddr=warsaw,+poland&amp;amp;sll=37.0625,-95.677068&amp;amp;sspn=58.470251,108.984375&amp;amp;ie=UTF8&amp;amp;ll=51.604372,17.578125&amp;amp;spn=5.815072,13.623047&amp;amp;z=6"&gt;Zgorzelec to Warsaw&lt;/a&gt; returning from my girlfriend&amp;#8217;s place. Polish trains are like turtles, so I will have pretty much time for writing ;-)&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve wrote (or maybe it&amp;#8217;s better to say copy&amp;amp;paste) little rails app like in &lt;a href="http://clarkware.com/cgi/blosxom/2007/02/24"&gt;Mike Clark&amp;#8217;s tutorial for attachment_fu&lt;/a&gt;. A few months ago there was &lt;a href="http://www.yoursgallery.pl/exhibitions.php?action=details&amp;amp;exh_id=44&amp;amp;setlang=en"&gt;Mugshots exhibition in Yours Gallery&lt;/a&gt; in Warsaw based on &lt;a href="http://www.amazon.com/City-Shadows-Sydney-Photographs-1912-1948/dp/1876991208/ref=pd_bbs_sr_2?ie=UTF8&amp;amp;s=books&amp;amp;qid=1215106900&amp;amp;sr=8-2"&gt;work of Peter Doyle&lt;/a&gt;. I saw it with Kathleene, she took some pictures. Great! I have material to fill my new app, what else could I possibly dream of?! (yeah&amp;#8230; macbook, but it&amp;#8217;s obvious ;-).&lt;/p&gt;
&lt;p&gt;Now you can admire my hard work: &lt;a href="http://mugshots.drogomir.com/js/no-javascript/mugshots/"&gt;mugshots.drogomir.com/js/no-javascript/mugshots/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But wait&amp;#8230; It&amp;#8217;s not so cool&amp;#8230; where are all those shiny javascript effects? Don&amp;#8217;t worry. I will show you how to spice this dish.&lt;/p&gt;
&lt;p&gt;We will need:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.malsup.com/jquery/form/"&gt;jQuery form plugin&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://brandonaaron.net/docs/livequery/"&gt;jQuery livequery plugin&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://github.com/drogus/jquery-upload-progress/"&gt;jQuery upload progress&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://malsup.com/jquery/block/" title="used by multifile"&gt;jquery blockUI&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.fyneworks.com/jquery/multiple-file-upload/"&gt;jquery mutli file&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://github.com/drogus/lightbox-fu/tree/master"&gt;jquery lightBoxFu&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://leandrovieira.com/projects/jquery/lightbox/"&gt;lightbox&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;#8217;ve pushed application to github, so you can see entire code. Clone it or &lt;a href="http://cdsc"&gt;grab the tarball&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There is one thing that is not straight forward. @main_js variable in app/views/layouts/main.rhtml:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;
&amp;lt;%= javascript_include_tag @main_js %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&amp;#8217;s there for changing javascript file loaded. When url is app.com/js/some_javascript_file/mugshots, @main_js should be &amp;#8220;some_javascript_file.js&amp;#8221;. I&amp;#8217;ve done this to have possibility to show you app with different javascript files without changing the code. See routes and mugshots_controller.rb to find out how it was done (or run &amp;#8220;rake routes&amp;#8221; in app dir to see routes).&lt;/p&gt;
&lt;p&gt;Lets begin.&lt;/p&gt;
&lt;p&gt;What to do first? It&amp;#8217;s all about uploading files, so I would add upload progress bar to form in &lt;a href="http://mugshots.drogomir.com/mugshots/new"&gt;mugshots.drogomir.com/mugshots/new&lt;/a&gt;. To implement it you will need some kind of server module:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://drogomir.com/blog/2008/6/18/upload-progress-bar-with-mod_passenger-and-apache"&gt;Apache upload progress module&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://wiki.codemongers.com/NginxHttpUploadProgressModule"&gt;Nginx upload progress module&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://upload.lighttpd.net/"&gt;Lighttpd upload progress&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You have to install and enable one of the above modules to make progress bar work.&lt;/p&gt;
&lt;p&gt;Then add some javascript to applications.js. This example is using &amp;#8220;LightBoxFu&amp;#8221;: &amp;#8211; little script that I wrote to show progress bar as an overlay. It&amp;#8217;s based on &lt;a href="http://riddle.pl/"&gt;Riddle&amp;#8217;s work&lt;/a&gt; &amp;#8211; all positioning is in &lt;span class="caps"&gt;CSS&lt;/span&gt; (except expressions for IE) so it&amp;#8217;s really light and fast. Ideal for such a task. If you don&amp;#8217;t like lightBoxFu you can use any other form of displaying message (you can use some other lightbox with displaying code function or even blockUI plugin).&lt;/p&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
// handy trick, if we can't use $ beaceuse jQuery.noConflict
// was used, jQuery is passed as argument in document ready
// so we can name it $
jQuery(function($) {
  // add upload progress to our form
  $('form.progress').uploadProgress({
    start:function(){
      // after starting upload open lightBoxFu with our bar as html
      $.lightBoxFu.open({
        html: '&amp;lt;div id="uploading"&amp;gt;&amp;lt;span id="received"&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span id="size"&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;div id="progress" class="bar"&amp;gt;&amp;lt;div id="progressbar"&amp;gt;&amp;amp;nbsp;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;span id="percent"&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;',
        width: "250px",
        closeOnClick: false
      });
      jQuery('#received').html("Upload starting.");
      jQuery('#percent').html("0%");
    },
    uploading: function(upload) {
      // update upload info on each /progress response
      jQuery('#received').html("Uploading: "+parseInt(upload.received/1024)+"/");
      jQuery('#size').html(parseInt(upload.size/1024)+" kB");
      jQuery('#percent').html(upload.percents+"%");
    },
    interval: 2000,
    /* if we are using images it's good to preload them, safari has problems with
       downloading anything after hitting submit button. these are images for lightBoxFu
       and progress bar */
    preloadImages: ["/images/overlay.png", "/images/ajax-loader.gif"]
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And some styling for progress bar:&lt;br /&gt;
&lt;pre&gt;&lt;code class="css"&gt;
  #progress {
  margin: 8px;
  width: 220px;
  height: 19px;
}&lt;/p&gt;
&lt;p&gt;#progressbar {&lt;br /&gt;
  background: url(&amp;#8216;/images/ajax-loader.gif&amp;#8217;) no-repeat;&lt;br /&gt;
  width: 0px;&lt;br /&gt;
  height: 19px;&lt;br /&gt;
}&lt;br /&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s it, just add &amp;#8220;progress&amp;#8221; class to your form and progress bar is working:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;
&amp;lt;% form_for(:mugshot, :url =&amp;gt; mugshots_path, 
                      :html =&amp;gt; { :multipart =&amp;gt; true, :class =&amp;gt; "progress" }) do |f| -%&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Uploading files looks much better right now, check it here: &lt;a href="http://mugshots/drogomir.com/js/progress/mugshots/new"&gt;http://mugshots.drogomir.com/js/progress/mugshots/new&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So what now? I find the &amp;#8220;add photo, click New mugshot, add photo&amp;#8221; scenerio annoying. We could add more than one file on each submit. For that we will use jquery.MultiFile.js. This one is a bit tricky, cause we will need to tweak code handling uploads also.&lt;/p&gt;
&lt;p&gt;Javascript enabling mutlifile:&lt;/p&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
jQuery(function($) {
  $('.multi-file').each(function() {
    // change name of element before applying MultiFile
    // so array of files can be send to server with mugshot[uploaded_data][]
    $(this).attr('name', $(this).attr('name') + '[]');
  }).MultiFile();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We must also add &amp;#8220;multi-file&amp;#8221; class to file field:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;
&amp;lt;%= f.file_field :uploaded_data, :class =&amp;gt; 'multi-file' %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;From javascript point of view that&amp;#8217;s all. Let&amp;#8217;s see how uploaded photos are handled by rails app:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;
@mugshot = Mugshot.new(params[:mugshot])
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So mugshot.uploaded_data is filled with data from &lt;code&gt;params[:mugshot][:uploaded_data]&lt;/code&gt;. Good for one file. But with array of files we should create Mugshot for each file. I would add a method in model:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;
  def self.handle_upload(mugshot_params)
    # array for not saved mugshots
    mugshots = []
    if mugshot_params[:uploaded_data].kind_of?(Array)
      mugshot_params[:uploaded_data].each do |p| 
        unless p.blank?
          mugshot = Mugshot.new(:uploaded_data =&amp;gt; p)
          mugshots &amp;lt;&amp;lt; mugshot unless mugshot.save
        end
      end
    else
      mugshot = Mugshot.new(mugshot_params)
      mugshots &amp;lt;&amp;lt; mugshot unless mugshot.save
    end
    mugshots
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and slightly change controller code:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;
  def create
    @mugshots = Mugshot.handle_upload(params[:mugshot])
      
    # if @mugshots is empty there are no errors
    if @mugshots.blank?
      flash[:notice] = 'Mugshot was successfully created.'
      redirect_to mugshots_url
    else
      render :action =&amp;gt; :new
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only one problem left. Validation.&lt;/p&gt;
&lt;p&gt;Easiest way is to change error_messages_for:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;
&amp;lt;%= error_messages_for :object =&amp;gt; @mugshots %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It works. But suppose you are uploading 3 files and 2 of them are too big. You will end with:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Size is not included in the list&lt;/li&gt;
	&lt;li&gt;Size is not included in the list&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Which one was added? Some lottery here&amp;#8230;&lt;/p&gt;
&lt;p&gt;I would tweak attachment_fu error messages a bit. By default it uses validates_as_attachment method which simply adds:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;
  validates_presence_of :size, :content_type, :filename
  validate  :attachment_attributes_valid?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead validates_as_attachment we can isert our new code:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;
  validates_presence_of :size, :content_type, :filename, :message =&amp;gt; Proc.new { |mugshot| "can't be blank (#{mugshot.filename})" }
  validate  :attachment_attributes_valid?
  
  def attachment_attributes_valid?
    [:size, :content_type].each do |attr_name|
      enum = attachment_options[attr_name]
      errors.add attr_name, "#{ActiveRecord::Errors.default_error_messages[:inclusion]} (#{self.filename})" unless enum.nil? || enum.include?(send(attr_name))
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now it&amp;#8217;s a lot more readable:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Size is not included in the list (filename.jpg)&lt;/li&gt;
	&lt;li&gt;Size is not included in the list (filename1.jpg)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Submit form looks better now, but viewing files is still ugly. Maybe we could add some lightbox? No problem:&lt;/p&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
$('#mugshots li a').lightBox(); 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I used &lt;a href="http://leandrovieira.com/projects/jquery/lightbox/"&gt;that lightbox&lt;/a&gt; cause I had it configured for my previous rails apps, but pick your favourite one, as there are gazilions of them.&lt;/p&gt;
&lt;p&gt;This is first step of tweaking our app. Javascript is in step1.js file: &lt;a href="http://mugshots.drogomir.com/js/step1/mugshots/new"&gt;mugshots.drogomir.com/js/step1/mugshots/new&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What now? User can upload many files at one submit and see progress bar. What else do we need? Ajax! My preciousssss&amp;#8230;&lt;/p&gt;
&lt;p&gt;As all children know, XMLHttpRequest can&amp;#8217;t upload files. What a shame&amp;#8230; our new tweaked mugshots app is all about uploading files. Although you can&amp;#8217;t do it with &lt;span class="caps"&gt;XHR&lt;/span&gt;, there is a way to imitate it. It is obtained by creating an iframe and uploading files to it.&lt;/p&gt;
&lt;p&gt;Luckily Mike Malsup has done hard work for us writing &lt;a href="http://www.malsup.com/jquery/form/"&gt;jQuery form plugin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First, we need our form. I would place it instead &amp;#8220;New mugshot&amp;#8221; link. Link has id=&amp;#8220;new_mugshot_link&amp;#8221;, so this piece of code will replace it with form:&lt;/p&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
  /* create upload form with multifile instead of new mugshot link */
  var form = $('&amp;lt;form method="post" enctype="multipart/form-data" class="progress ajax" action="/mugshots"&amp;gt;');
  var label = $('&amp;lt;p&amp;gt;&amp;lt;label for="mugshot_uploaded_data"&amp;gt;Upload mugshot: &amp;lt;/label&amp;gt;&amp;lt;/p&amp;gt;');
  var input = $('&amp;lt;input type="file" class="multi-file" id="mugshot_uploaded_data" size="30" name="mugshot[uploaded_data]"/&amp;gt;');
  label.append(input).appendTo(form);
  form.append('&amp;lt;p&amp;gt;&amp;lt;input type="submit" value="Create" name="commit"/&amp;gt;&amp;lt;/p&amp;gt;');
  if (typeof(AUTH_TOKEN) != "undefined") form.append('&amp;lt;input type="hidden" value="'+AUTH_TOKEN+'" name="authenticity_token"/&amp;gt;'); 
  $('#new_mugshot_link').replaceWith(form);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our form has to be send to an iframe, so we have to apply ajaxForm to it. After replacing link with form we can&amp;#8217;t figure out when form is actually appended to &lt;span class="caps"&gt;DOM&lt;/span&gt;. To be sure that form is there, we can use livequery. It will fire callback function when &amp;#8216;form.ajax&amp;#8217; will be available:&lt;/p&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
  $('form.ajax').livequery(function() {
    $(this).ajaxForm({iframe: true, success: function (responseText, statusText, form) {
      var url = $(form).attr('action');
      /* get new files */
      $.ajax({
          url: url,
          dataType: "script",
          beforeSend: function(xhr) {xhr.setRequestHeader("Accept", "text/javascript");},
	  /* we need to update lightbox array to include new files */
          complete: function() { $('#mugshots li a').lightBox(); }
      });
    }});
  });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When new form tag with class &amp;#8220;ajax&amp;#8221; will be available callback function will be run. iframe option tells form plugin to add hidden iframe (it will handle file upload).&lt;/p&gt;
&lt;p&gt;The above code has ajax call to &amp;#8220;/mugshots&amp;#8221; url which will run index.js.erb (&lt;span class="caps"&gt;RJS&lt;/span&gt;), so we will need one:&lt;/p&gt;
&lt;p&gt;app/views/mugshots/index.js.erb&lt;br /&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;
  jQuery('#mugshots').html(&amp;lt;%= js render(:partial =&amp;gt; 'mugshot', :collection =&amp;gt; @mugshots) %&amp;gt;);
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;to handle it we need to use respond_to:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;
  respond_to do |format|
    format.html
    # layout =&amp;gt; false is here beaceuse without it rails are looking
    # for layouts/index.js.erb
    format.js { render :layout =&amp;gt; false }
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Normally I try not to use &lt;span class="caps"&gt;RJS&lt;/span&gt; to keep all my javascript (and ajax) logic in javascript files, but in case of images it isn&amp;#8217;t so esay. I will write about it and about javascript templating systems in one of the next posts.&lt;/p&gt;
&lt;p&gt;Take a look at: &lt;a href="http://mugshots.drogomir.com/js/step2/mugshots"&gt;mugshots.drogomir.com/js/step2/mugshots&lt;/a&gt; Doesn&amp;#8217;t it look nice?&lt;/p&gt;
&lt;p&gt;There is only one problem :) No ajax validation. After submitting files, javascript can&amp;#8217;t get any info about errors or uploaded files beaceuse it is treated like normal html request and response is loaded in an iframe. How to fix it? I&amp;#8217;ll write about it in the next post. :)&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Upload progress script with safari support</title>
   <link href="http://piotrsarnacki.com/2008/06/30/upload-progress-script-with-safari-support" />
   <published>2008-06-30T00:00:00-07:00</published>
   <updated>2008-06-30T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2008/06/30/upload-progress-script-with-safari-support</id>
   <content type="html">&lt;p&gt;Quick links:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Source on github for jquery version: &lt;a href="http://github.com/drogus/jquery-upload-progress"&gt;http://github.com/drogus/jquery-upload-progress&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;Source on github for prototype version: &lt;a href="http://github.com/drogus/prototype-upload-progress"&gt;http://github.com/drogus/prototype-upload-progress&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;jQuery demo: &lt;a href="http://drogomir.com/files/blog/jquery-upload-progress/example"&gt;http://drogomir.com/files/blog/jquery-upload-progress/example&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;Prototype demo: &lt;a href="http://drogomir.com/files/blog/prototype-upload-progress/example"&gt;http://drogomir.com/files/blog/prototype-upload-progress/example&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://drogomir.com/blog/2008/6/18/upload-progress-bar-with-mod_passenger-and-apache"&gt;Installing apache upload progress&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Recently I&amp;#8217;ve wrote about &lt;a href="http://drogomir.com/blog/2008/6/18/upload-progress-bar-with-mod_passenger-and-apache"&gt;apache upload progress module&lt;/a&gt;. I work mainly on linux and I haven&amp;#8217;t check my scripts on safari. It was working even on IE, what possibly could be harder to obtain? ;-) Some people reported that demo is not working on safari and Michele &lt;a href="http://drogomir.com/blog/2008/6/18/upload-progress-bar-with-mod_passenger-and-apache#comment-13"&gt;resolved the problem&lt;/a&gt; (thanks Michele :).&lt;/p&gt;
&lt;p&gt;Solved! The only thing to do was to open WinXP on &lt;a href="http://www.virtualbox.org/"&gt;VirtualBox&lt;/a&gt; and check it on Safari 3. Michele&amp;#8217;s solution worked well, but to make it work there must be html page with given structure and javascript. I like &amp;#8220;one file&amp;#8221; easy to use scripts without any issues with static files :) So what? Create an iframe dynamically, load scripts dynamically, one file, the only thing that user will have to set is path to scripts.&lt;/p&gt;
&lt;p&gt;With Safari? No, not really. I&amp;#8217;ve wrote it in a few minuttes and checked in firefox. It worked, great, now safari. Nope&amp;#8230;.&lt;/p&gt;
&lt;p&gt;Although Safari have great &lt;span class="caps"&gt;CSS&lt;/span&gt; support, it is really terrible with Javascript. &lt;span class="caps"&gt;WYSIWYG&lt;/span&gt;, javascript history, ajax issues, now the upload progress and iframes. In edge case libraries I often see hacks for IE and safari mainly.&lt;/p&gt;
&lt;p&gt;Luckily Apple released Windows version of safari and I can check my scripts and pages on safari. Good&amp;#8230; developer tools don&amp;#8217;t work, though&amp;#8230; I had to do some alert-like IE style debugging ;-) After many hours of trying new more and more insane ways to create iframe and load scripts into it, code with document.write() worked!&lt;/p&gt;
&lt;p&gt;&lt;a href="http://github.com/drogus/jquery-upload-progress/commit/cc8ebce8acbb54febb3d24c7a77118c1501a922f"&gt;Check the commit on github&lt;/a&gt;. Lines 18-22 especially. And 28-line issue with safari not waiting to load previous script.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s really sad that after working on firefox with firebug or opera with developer tools I have to fight with Safari which is supposed to be modern browser. I know that on Mac developing scripts for safari is easier, thanks to working debug tools, but hey! I work on Linux. Why do I have to run Safari on Wine or VirtualBox, lacking debug tools? It&amp;#8217;s a pain. I feel like I&amp;#8217;m working on explorer&amp;#8230;&lt;/p&gt;
&lt;p&gt;&lt;del&gt;I will add prototype version&lt;/del&gt; and possibly some usage page shortly (for know look at the &lt;a href="http://drogomir.com/files/blog/jquery-upload-progress/example"&gt;demo code&lt;/a&gt;).&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Upload progress bar with mod_passenger and apache</title>
   <link href="http://piotrsarnacki.com/2008/06/18/upload-progress-bar-with-mod_passenger-and-apache" />
   <published>2008-06-18T00:00:00-07:00</published>
   <updated>2008-06-18T00:00:00-07:00</updated>
   <id>http://piotrsarnacki.com/2008/06/18/upload-progress-bar-with-mod_passenger-and-apache</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;UPDATE&lt;/span&gt;&lt;/strong&gt;: I found 2 bugs in upload progress module. If you have already installed. update to at least 0.1 version: &lt;a href="http://github.com/drogus/apache-upload-progress-module/commits/0.1"&gt;http://github.com/drogus/apache-upload-progress-module/commits/0.1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve installed &lt;a href="http://www.modrails.com/"&gt;mod passenger&lt;/a&gt; on my server recently. It&amp;#8217;s really great software. Now I don&amp;#8217;t have to worry about monitoring, nginx proxy, load balancing, big file uploads&amp;#8230; and it&amp;#8217;s fast! With &lt;a href="http://www.rubyenterpriseedition.com/"&gt;Ruby Enterprise Edition&lt;/a&gt; it&amp;#8217;s even faster.&lt;/p&gt;
&lt;p&gt;Personally I don&amp;#8217;t care about people saying that phusion wants to &lt;a href="http://judofyr.net/posts/ruby-fishy-edition.html"&gt;promote themselves on &lt;span class="caps"&gt;REE&lt;/span&gt;&lt;/a&gt; as long as it gives faster ruby with lower memory use (but yes, I know, &lt;span class="caps"&gt;REE&lt;/span&gt; is not best choice for a name :).&lt;/p&gt;
&lt;p&gt;After installing I&amp;#8217;ve realised that my shiny upload progress bar (thanks to &lt;a href="http://wiki.codemongers.com/NginxHttpUploadProgressModule"&gt;Upload Progress Module for nginx&lt;/a&gt;) was not working. Oh gods! What a tragedy!&lt;/p&gt;
&lt;p&gt;But fear not. I&amp;#8217;ve written &lt;a href="http://github.com/drogus/apache-upload-progress-module/tree/master"&gt;apache upload progress module&lt;/a&gt; to have my lovely progress bar back again. As a lazy developer I&amp;#8217;ve implemented reports in the same way as in nginx upload progress, so my applications are working without changing any signle line of code. If you were using nginx upload progress just drop the module, change your config and you&amp;#8217;re good to go :)&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m testing it in one of my production servers, but be carefull &amp;#8211; it&amp;#8217;s not well tested in other enviroments (i have gentoo with apache 2.2.8). Any feedback will be helpfull. Give me a note in comments if you encounter any problems.&lt;/p&gt;
&lt;p&gt;So you want to be cool and have your own sexy progress bar in your app? Keep reading ;)&lt;/p&gt;
&lt;p&gt;To install module you must download it using git:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;git clone git://github.com/drogus/apache-upload-progress-module.git
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;or get the package: &lt;a href="http://github.com/drogus/apache-upload-progress-module/tarball/master"&gt;http://github.com/drogus/apache-upload-progress-module/tarball/master&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To compile/install/activate you have to use apxs2:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="apache"&gt;&lt;span class="nb"&gt;apxs2&lt;/span&gt; -c -i -a mod_upload_progress.c
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul&gt;
	&lt;li&gt;-c is for compiling&lt;/li&gt;
	&lt;li&gt;-i is for installing (copy mod_upload_progress.so to apache library dir)&lt;/li&gt;
	&lt;li&gt;-a is for activating (add LoadModule option into your apache conf file)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to install and activate run this command as a root. Otherwise you can just compile and add LoadModule to apache conf:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="apache"&gt;&lt;span class="nb"&gt;LoadModule&lt;/span&gt; upload_progress_module path/to/apache-upload-progress-module/.libs/mod_upload_progress.so
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Currently there is only one global option:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="apache"&gt;&lt;span class="nb"&gt;UploadProgressSharedMemorySize&lt;/span&gt; &lt;span class="m"&gt;1024000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This sets shared memory size to  1M. By default it&amp;#8217;s 100kB.&lt;/p&gt;
&lt;p&gt;To add tracking and reporting upload for a virtual host in apache you will need to add:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="apache"&gt;&lt;span class="nt"&gt;&amp;lt;Location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;# enable tracking uploads in /&lt;/span&gt;
    &lt;span class="nb"&gt;TrackUploads&lt;/span&gt; &lt;span class="k"&gt;On&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Location&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;Location&lt;/span&gt; &lt;span class="s"&gt;/progress&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;# enable upload progress reports in /progress&lt;/span&gt;
    &lt;span class="nb"&gt;ReportUploads&lt;/span&gt; &lt;span class="k"&gt;On&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Location&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now all uploads will be tracked and reports are under /progress&lt;/p&gt;
&lt;p&gt;Format of the report is &lt;span class="caps"&gt;JSON&lt;/span&gt;. From nginx wiki:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The returned document is a &lt;span class="caps"&gt;JSON&lt;/span&gt; text with the possible 4 results:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;the upload request hasn&amp;#8217;t been registered yet or is unknown:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;new Object({ &amp;#8216;state&amp;#8217; : &amp;#8216;starting&amp;#8217; })&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;the upload request has ended:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;new Object({ &amp;#8216;state&amp;#8217; : &amp;#8216;done&amp;#8217; })&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;the upload request generated an &lt;span class="caps"&gt;HTTP&lt;/span&gt; error:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;new Object({ &amp;#8216;state&amp;#8217; : &amp;#8216;error&amp;#8217;, &amp;#8216;status&amp;#8217; : &lt;error code&gt; })&lt;/p&gt;
&lt;p&gt;One error code that is interesting to track for clients is &lt;span class="caps"&gt;HTTP&lt;/span&gt; error 413 (Request entity too large)&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;the upload request is in progress:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;new Object({ &amp;#8216;state&amp;#8217; : &amp;#8216;uploading&amp;#8217;, &amp;#8216;received&amp;#8217; : &lt;size_received&gt;, &amp;#8216;size&amp;#8217; : &lt;total_size&gt;})&lt;/p&gt;
&lt;p&gt;The &lt;span class="caps"&gt;HTTP&lt;/span&gt; request to this location must have either an X-Progress-ID parameter or X-Progress-ID &lt;span class="caps"&gt;HTTP&lt;/span&gt; header containing the unique identifier as specified in your upload/&lt;span class="caps"&gt;POST&lt;/span&gt; request to the relevant tracked zone. If you are using the X-Progress-ID as a query-string parameter, ensure it is the &lt;span class="caps"&gt;LAST&lt;/span&gt; argument in the &lt;span class="caps"&gt;URL&lt;/span&gt;.&lt;/blockquote&gt;&lt;/p&gt;
&lt;p&gt;Now the last thing to do is to implement progress bar. I don&amp;#8217;t like repeating others and &lt;a href="http://blog.new-bamboo.co.uk/2007/11/23/upload-progress-with-nginx"&gt;there is great tutorial on setting up upload progress bar with nginx and merb&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;UPDATE&lt;/span&gt;&lt;/strong&gt;: I released jquery upload progress library with Safari 3 support. More info &lt;a href="http://drogomir.com/blog/2008/6/30/upload-progress-script-with-safari-support"&gt;here&lt;/a&gt;.&lt;br /&gt;
&lt;strong&gt;UPDATE2&lt;/strong&gt;: I&amp;#8217;ve upgraded prototype version to work in Safari.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s for merb and nginx but if you drop the scripts in your rails app and with apache-upload-progress-module it will work. :) Basically if you have your own code handling uploads (for example using attachment_fu) you can just add javascript and css &amp;#8211; it&amp;#8217;s unobtrusive.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re using prototype I&amp;#8217;ve rewritten script and made &lt;a href="http://drogomir.com/files/blog/prototype-upload-progress/example"&gt;a demo&lt;/a&gt;. &lt;a href="http://drogomir.com/files/blog/prototype-upload-progress/"&gt;You can also grab files&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I hope you enjoy this article. Progress bar is in my opinion one of the most useful technics &amp;#8211; there is nothing more annoying than large file uploading without any info on state of an upload.&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>

