<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/stylesheets/rss.css" type="text/css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Jaros&#322;aw Zabie&#322;&#322;o - BLOG: Tag orm</title>
    <link>http://blog.zabiello.com/articles/tag/orm</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>moje notatki, linki, komentarze</description>
    <item>
      <title>Sequel vs Active Record. Cz&#281;&#347;&#263; 1 - s&#322;abo&#347;ci AR.</title>
      <description>&lt;p&gt;Gdy framework Ruby on Rails (dalej: RoR lub Rails) rozpoczyna&#322; swoj&#261; b&#322;yskotliw&#261; karier&#281;, zachwyca&#322; wszystkich prostot&#261; i elegancj&#261; konstrukcji. Jednym z fundamentalnych modu&#322;&#243;w Rails&#243;w jest Active Record, biblioteka realizuj&#261;ca symulacj&#281; wirtualnej bazy obiektowej, pozwalaj&#261;c&#261; na prac&#281; z relacyjnymi bazami danych za pomoc&#261; obiekt&#243;w j&#281;zyka Ruby. Active Record (dalej AR) nie unikn&#261;&#322; jednak kilku potkni&#281;&#263; projektowych.&lt;/p&gt;


	&lt;h3&gt;Brak parametryzowanych zapyta&#324; &lt;span class="caps"&gt;SQL&lt;/span&gt;&lt;/h3&gt;


	&lt;p&gt;Kompletnie nie rozumiem po co Active Record m&#281;czy si&#281; z budowaniem ca&#322;ej kwerendy &lt;span class="caps"&gt;SQL&lt;/span&gt;. Po co te&#380; jego tworcy trac&#261; czas na walk&#281; z analiz&#261; i odpowiednim escape&amp;#8217;owaniem znak&#243;w w danych przekazywanych do zapytania. Przecie&#380; &lt;a href="http://tmtm.org/en/mysql/ruby/"&gt;sterownik MySQL do Rubiego&lt;/a&gt; bez problemu to wszystko zrobi na nich! Ma&#322;o tego, u&#380;ywanie parametryzowanych zapyta&#324; &lt;span class="caps"&gt;SQL&lt;/span&gt; jest szybsze ni&#380; samodzielne budowanie ca&#322;ego stringa i wykonywanie na nim kwerendy.&lt;/p&gt;


	&lt;h3&gt;Brak iterator&#243;w&lt;/h3&gt;


	&lt;p&gt;Inn&#261; tajemnic&#261; developer&#243;w AR jest brak korzystania z iterator&#243;w. Zamiast tego AR, z uporem godnym lepszej sprawy, przepycha przez pami&#281;&#263; ca&#322;&#261; list&#281; obiekt&#243;w Rubiego. Im pobieramy wi&#281;cej rekord&#243;w z bazy tym wi&#281;cej obiekt&#243;w musimy przes&#322;a&#263; do pami&#281;ci (bo ka&#380;demu rekordowi odpowiada jeden egzemplarz klasy Active Record). AR wrzuca wi&#281;c &lt;strong&gt;wszystkie&lt;/strong&gt; obiekty &lt;span class="caps"&gt;ORM&lt;/span&gt; do pami&#281;ci. Troch&#281; to bez sensu, bo bazy danych s&#261; optymalizowane na pobieranie rekord&#243;w ma&#322;ymi porcjami. Grupowane pobieranie/wk&#322;adanie rekord&#243;w potrafi by&#263; nawet o dwa rz&#281;dy wielko&#347;ci szybsze. Gdyby &lt;span class="caps"&gt;DHH&lt;/span&gt; i koledzy popatrzyli dok&#322;adniej na standard &lt;a href="http://www.python.org/dev/peps/pep-0249/"&gt;&lt;span class="caps"&gt;PEP249&lt;/span&gt;&lt;/a&gt; Pythona a nie na chaotyczne praktyki rodem z &lt;span class="caps"&gt;PHP&lt;/span&gt;, to by takiego b&#322;&#281;du nie pope&#322;nili.&lt;/p&gt;


	&lt;h3&gt;Domy&#347;lne &lt;span class="caps"&gt;SELECT&lt;/span&gt; * i b&#322;&#261;d w :include&lt;/h3&gt;


	&lt;p&gt;Gdy AR wykonuje zapytanie komend&#281; Model.find(:all) to domy&#347;lnie wykonuje &amp;#8220;SELECT *...&amp;#8221; Nie by&#322;oby z tym problemu gdyby nie to, &#380;e MySQL nie potrafi indeksowa&#263; p&#243;&#322; typu &lt;span class="caps"&gt;TEXT&lt;/span&gt;/BLOB. Pobieranie &amp;#8220;hurtem&amp;#8221; wszystkich kolumn z kt&#243;rych jedna jest tego typu, dramatycznie spowalnia baz&#281; danych.&lt;/p&gt;


	&lt;p&gt;No dobrze, kto&#347; powie, &#380;e mo&#380;na zawsze u&#380;y&#263; opcji :select i wybra&#263; sobie pola jakie chcemy. Niestety to si&#281; sypie, gdy chcemy u&#380;y&#263; opcji :include aby uzyska&#263; &lt;span class="caps"&gt;LEFT OUTER JOIN&lt;/span&gt;. U&#380;ycie opcji :include anuluje bowiem efekt dzia&#322;ania opcji :select. (Aby to obej&#347;&#263; trzeba zast&#261;pi&#263; opcj&#281; :include opcj&#261; :joins i wklepa&#263; r&#281;cznie ca&#322;y fragment &lt;span class="caps"&gt;SQL&lt;/span&gt;. Opcja :joins nie koliduje z :select).&lt;/p&gt;


	&lt;h3&gt;Niesp&#243;jno&#347;&#263; sk&#322;adni :include i :joins.&lt;/h3&gt;


	&lt;p&gt;To mo&#380;e ma&#322;a sprawa, ale razi. Dlaczego :include pozwala na stosowanie &#322;adnego zapisy :include =&amp;gt; [:tabela1, :tabela2] a :joins przyjmuje tylko warto&#347;&#263; typu String, tj. czysty &lt;span class="caps"&gt;SQL&lt;/span&gt;?&lt;/p&gt;


	&lt;h3&gt;Aktualizacja jednej kolumny to aktualizacja wszystkich kolumn rekordu.&lt;/h3&gt;


	&lt;p&gt;To ju&#380; jest bardziej denerwuj&#261;ce. Chcemy zmieni&#263; fragment rekordu a AR serwuje nam update na wszystkich kolumnach. Nie ma jak to obej&#347;&#263; bez uciekania si&#281; do odpalenia czystego &lt;span class="caps"&gt;SQL&lt;/span&gt;&amp;#8217;a. Ca&#322;e szcz&#281;&#347;cie &#380;e AR na to pozwala.&lt;/p&gt;


	&lt;h3&gt;Brak obs&#322;ugi z&#322;o&#380;onych kluczy obcych&lt;/h3&gt;


	&lt;p&gt;AR domy&#347;lnie w og&#243;le nie obs&#322;uguje z&#322;o&#380;onych kluczy obcych (Sympatycy pythonowego Django nie maj&#261; tu te&#380; powodu do dumy, bo ich &lt;span class="caps"&gt;ORM&lt;/span&gt; r&#243;wnie&#380; tego nie potrafi). Sytuacj&#281; troch&#281; ratuje gem/plugin composite_primary_keys ale jak to z pluginami w Rails bywa, im ich mniej tym lepiej. No chyba, &#380;e kto&#347; sam prosi si&#281; o k&#322;opoty. Z jako&#347;ci&#261; plugin&#243;w jest r&#243;&#380;nie i trzeba ostro&#380;nie je dobiera&#263;.&lt;/p&gt;


	&lt;h3&gt;Brak krokowego budowania warunk&#243;w&lt;/h3&gt;


	&lt;p&gt;To jest jedna z bardziej denerwuj&#261;cych rzeczy. AR gromadzi warunki w warto&#347;ci do klucza/opcji :conditions. Mo&#380;e on przyjmowa&#263; warto&#347;&#263; typu String, Array lub Hash ale tylko jedn&#261; naraz i tylko raz. Je&#347;li chcemy zbudowa&#263; jaki&#347; bardziej z&#322;o&#380;ony warunek, to musimy skleja&#263; sobie r&#281;cznie kwerend&#281; i uwa&#380;a&#263; aby sobie przypadkiem nie strzeli&#263; jakim&#347; przypadkowym sql-injection w stop&#281;.&lt;/p&gt;


	&lt;p&gt;Nie wiem czy jest nadzieja na szybk&#261; popraw&#281; jako&#347;ci implementacji Active Record, bo mam wra&#380;enie &#380;e developerzy i kontrybutorzy skupieni wok&#243;&#322; tego frameworka wol&#261; goni&#263; za nowymi funkcjami zamiast zatrzyma&#263; si&#281; i dopracowa&#263; to, co ju&#380; stworzono. Na szcz&#281;&#347;cie nie trzeba czeka&#263;. Active Record doczeka&#322; si&#281; godnych nast&#281;pc&#243;w. Najbardziej znacz&#261;ce s&#261; dwa: &lt;a href="http://datamapper.org/"&gt;DataMapper&lt;/a&gt; i &lt;a href="http://code.google.com/p/ruby-sequel/"&gt;Sequel&lt;/a&gt;. Szczeg&#243;lnie godny uwagi jest Sequel. Ale o tym w nast&#281;pnej cz&#281;&#347;ci.&lt;/p&gt;


	&lt;p&gt;Zobacz &lt;a href="http://blog.zabiello.com/articles/2007/12/21/integracja-rails-z-sequelem"&gt;Sequel vs Active Record. Cz&#281;&#347;&#263; 2 &amp;#8211; integracja Rails z Sequelem&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Thu, 13 Dec 2007 00:37:00 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:9ab994be-9bca-4c3d-ae20-6fa8fee44696</guid>
      <author>Jaros&#322;aw Zabie&#322;&#322;o</author>
      <link>http://blog.zabiello.com/articles/2007/12/13/slabosci-active-record</link>
      <category>rails</category>
      <category>ruby</category>
      <category>orm</category>
      <category>activerecord</category>
    </item>
    <item>
      <title>Ma&#322;y przegl&#261;d ORM'&#243;w dla Rubiego</title>
      <description>&lt;p&gt;Wcze&#347;niej pisa&#322;em o &lt;a href="http://blog.zabiello.com/articles/2007/03/22/rails-templates"&gt;dost&#281;pnych bibliotekach&lt;/a&gt; implementuj&#261;cych warstw&#281; Widoku z modelu &lt;a href="http://blog.zabiello.com/articles/2007/01/03/templates-and-mvc"&gt;&lt;span class="caps"&gt;MVC&lt;/span&gt;&lt;/a&gt; dla Ruby on Rails. Teraz kr&#243;tko o &lt;a href="http://pl.wikipedia.org/wiki/ORM"&gt;&lt;span class="caps"&gt;ORM&lt;/span&gt;&amp;#8217;ach&lt;/a&gt;. Jest ich ju&#380; kilka.&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://ar.rubyonrails.com/"&gt;&lt;strong&gt;Active Record&lt;/strong&gt;&lt;/a&gt; &amp;#8211; podstawowy &lt;span class="caps"&gt;ORM&lt;/span&gt; u&#380;ywany we frameworku &lt;a href="http://rubyonrails.org"&gt;Ruby on Rails&lt;/a&gt;. Dosy&#263; prosty, ma &#322;adne walidacje, wywo&#322;ania zwrotne (callback), obs&#322;uguje relacje mi&#281;dzy tabelami. Wad&#261; jest stosunkowo s&#322;aba implementacja. Kod jest wci&#261;&#380; ma&#322;o zoptymalizowany wydajno&#347;ciowo i pod k&#261;tem zu&#380;ycia pami&#281;ci. Brakuje wci&#261;&#380; parametryzowanych wywo&#322;a&#324; &lt;span class="caps"&gt;SQL&lt;/span&gt;&amp;#8217;a, iterator&#243;w zamiast zwracania listy obiekt&#243;w, pr&#243;ba aktualizacji jednej kolumny w tabeli powoduje aktualizacj&#281; wszystkich p&#243;l rekordu. U&#380;ycie &lt;span class="caps"&gt;LEFT JOIN&lt;/span&gt;&amp;#8217;&#243;w zapomoc&#261; :include powoduje &#380;e nie dzia&#322;a :select (zwracane s&#261; wszystkie pola). Moim zdaniem, jest tu sporo do poprawienia.&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://code.google.com/p/ruby-sequel/"&gt;&lt;strong&gt;Sequel&lt;/strong&gt;&lt;/a&gt; &amp;#8211; lekki &lt;span class="caps"&gt;ORM&lt;/span&gt; przypominaj&#261;cy sk&#322;adnikowo ten, stosowany w Django. W efekcie jest troch&#281; bardziej obiektowy ni&#380; Active Record. Mniej brutalnych wstawek czystego &lt;span class="caps"&gt;SQL&lt;/span&gt;&amp;#8217;a jak to jest w Active Record. Zostawiam sobie go na p&#243;&#380;niej do zbadania. Mo&#380;liwy konkurent dla Data Mappera (o kt&#243;rym ni&#380;ej)&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://www.nitroproject.org/"&gt;&lt;strong&gt;OG&lt;/strong&gt;&lt;/a&gt; &amp;#8211; &lt;span class="caps"&gt;ORM&lt;/span&gt; u&#380;ywany w alternatywnym do Rails&#243;w frameworku Rubiego &amp;#8211; Nitro. Nie s&#322;ysza&#322;em aby kto&#347; u&#380;ywa&#322; go poza tym frameworkiem. Podobno s&#261; zwolennicy Nitro. Ale na razie nie spotka&#322;em si&#281; z przekonuj&#261;cymi argumentami o wy&#380;szo&#347;ci Nitro nad Rails.&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://ibatis.apache.org/docs/ruby/"&gt;&lt;strong&gt;RBatis&lt;/strong&gt;&lt;/a&gt; &amp;#8211; port javowego iBatis napisany w Ruby. Teoretycznie wepnie si&#281; w dowoln&#261; struktur&#281;, bo jest oparty na czystym &lt;span class="caps"&gt;SQL&lt;/span&gt;. Tzn. pozwala na odpowiednie zamapowanie tabel i ich p&#243;l na obiekty Rubiego. Co ciekawe, pozwala omin&#261;&#263; niewygodne nazwy p&#243;l tak, aby z poziomu obiektu Rubiego u&#380;ywa&#263; innej nazwy kolumny ni&#380; wyst&#281;puje w tabeli. Trudno mi co&#347; powiedzie&#263; o dojrza&#322;o&#347;ci tego projektu, ale chyba rozwija si&#281; dosy&#263; powoli. Troch&#281; mnie odepchn&#281;&#322;a informacja o tym, &#380;e wci&#261;&#380; nie ma zaimplementowanych join&#243;w.&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://datamapper.org/why.html"&gt;&lt;strong&gt;Data Mapper&lt;/strong&gt;&lt;/a&gt; &amp;#8211; na razie chyba najbardziej obiecuj&#261;cy &lt;span class="caps"&gt;ORM&lt;/span&gt; ktory mo&#380;na wpi&#261;&#263; w Rails zamiast/obok Active Record. Jest du&#380;o szybszy, bo lepiej zoptymalizowano budowanie zapyta&#324;. Posiada &#322;adniejsz&#261;, bardziej obiektow&#261; sk&#322;adni&#281; ni&#380; Active Record. Zreszt&#261; sami zobaczcie:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="constant"&gt;Zoo&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;all&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Dallas&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;

&lt;span class="comment"&gt;# odpowiednik dla&lt;/span&gt;

&lt;span class="constant"&gt;Zoo&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:conditions&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;name = ?&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Dallas&lt;/span&gt;&lt;span class="punct"&gt;']).&lt;/span&gt; 

&lt;span class="comment"&gt;# 'gt' znaczy wi&#281;kszy-ni&#380;. Istnieje r&#243;wnie&#380; 'lt' (mniejszy-ni&#380;)&lt;/span&gt;
&lt;span class="ident"&gt;Person&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;all&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:age&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;gt&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;30&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

&lt;span class="comment"&gt;# 'gte' znaczy wi&#281;kszy-lub-r&#243;wny-ni&#380;. Jest te&#380; 'lte'.&lt;/span&gt;
&lt;span class="constant"&gt;Person&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;all&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:age&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;gte&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;30&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

&lt;span class="constant"&gt;Person&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;all&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:name&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;not&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;bob&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;

&lt;span class="comment"&gt;# Je&#347;li warto&#347;ci&#261; w parze jest Arar, to generowany jest sql'owy warunek IN()&lt;/span&gt;
&lt;span class="constant"&gt;Person&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;all&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:name&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;like&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;S%&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:id&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="number"&gt;2&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="number"&gt;3&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="number"&gt;4&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="number"&gt;5&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;

&lt;span class="comment"&gt;# To samo co Zoo.first(...)&lt;/span&gt;
&lt;span class="constant"&gt;Zoo&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Ft. Worth&lt;/span&gt;&lt;span class="punct"&gt;']&lt;/span&gt;

&lt;span class="comment"&gt;# To samo co Zoo.find(11)&lt;/span&gt;
&lt;span class="constant"&gt;Zoo&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;11&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;

&lt;span class="comment"&gt;# Przyk&#322;ad zwrotu NOT IN ().&lt;/span&gt;
&lt;span class="constant"&gt;Person&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;all&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:name&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;not&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;bob&lt;/span&gt;&lt;span class="punct"&gt;','&lt;/span&gt;&lt;span class="string"&gt;rick&lt;/span&gt;&lt;span class="punct"&gt;','&lt;/span&gt;&lt;span class="string"&gt;steve&lt;/span&gt;&lt;span class="punct"&gt;'])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Poza tym Data Mapper jest wielow&#261;tkowy i domy&#347;lnie u&#380;ywa &lt;span class="caps"&gt;LEFT JOIN&lt;/span&gt;&amp;#8217;&#243;w w wypadku u&#380;ycia relacji. Przeciwnie do Active Record, kt&#243;ry nie jest odporny na w&#261;tki i wymaga jawnego u&#380;ycia parametru :include aby w&#322;&#261;czy&#322; joiny (przy okazji likwiduj&#261;c dzia&#322;anie parametru :select).&lt;/p&gt;


	&lt;p&gt;(UPDATED 2007-12-16) Pod JRuby mo&#380;na odpala&#263; &lt;strong&gt;&lt;code&gt;Active Record JDBC&lt;/code&gt;&lt;/strong&gt; (zobacz list&#281; gem&#243;w: gem search -r ActiveRecord) oraz &lt;a href="http://code.google.com/p/activehibernate/"&gt;&lt;strong&gt;Active Hibernate&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;</description>
      <pubDate>Thu, 08 Nov 2007 00:16:00 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:06153712-0aba-44fc-bb16-55d2a8c99078</guid>
      <author>Jaros&#322;aw Zabie&#322;&#322;o</author>
      <link>http://blog.zabiello.com/articles/2007/11/08/rails-orms</link>
      <category>orm</category>
      <category>rails</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Elastyczno&#347;&#263; Rails&#243;w</title>
      <description>&lt;p&gt;Wi&#281;kszo&#347;&#263; os&#243;b, kt&#243;re zetkn&#281;&#322;y si&#281; z frameworkiem Ruby on Rails zauwa&#380;y&#322;a, &#380;e domy&#347;lne ustawienia s&#261; tam tak dobrane, aby maksymalnie upro&#347;ci&#263; i zminimalizowa&#263; ilo&#347;&#263; r&#281;cznej pracy przy ustawieniach. Je&#347;li wi&#281;c kto&#347; trzyma si&#281; flozofii i konwencji nazw (np. dla tabel i ich p&#243;l) b&#281;dzie mia&#322; znacznie mniej dodatkowej roboty z konfiguracj&#261; frameworka do pracy. Typowa definicja modelu  wygl&#261;da&#263; mo&#380;e nawet tak prosto:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;User&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Powy&#380;sza definicja zak&#322;ada, &#380;e tabela w bazie nosi nazw&#281; &amp;#8220;users&amp;#8221;, a jej primary key nazywa si&#281; &amp;#8220;id&amp;#8221;.  W przeciwie&#324;stwie do innych &lt;span class="caps"&gt;ORM&lt;/span&gt;&amp;#8217;&#243;w, railsowy ActiveRecord nie wymaga &#380;adnego wcze&#347;niejszego definiowania p&#243;l. Ruby bez problemu sam sobie wyciagnie wszystkie nazwy p&#243;l w spos&#243;b &lt;strong&gt;dynamiczny&lt;/strong&gt; wtedy, kiedy b&#281;dzie mu to potrzebne.&lt;/p&gt;


	&lt;h2&gt;Nietypowe struktury&lt;/h2&gt;


	&lt;p&gt;Niestety, nie zawsze jeste&#347;my w takiej komfortowej sytuacji, &#380;e mamy wp&#322;yw na nazewnictwo tabel i ich p&#243;l. Jednak to nie jest wi&#281;kszym problemem, bo Rails sobie z tym radzi r&#243;wnie prosto.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;User&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
  &lt;span class="ident"&gt;set_table_name&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;uzytkownicy&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
  &lt;span class="ident"&gt;set_primary_key&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;IdUzytkownika&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Jak wida&#263;, du&#380;o pracy nam nie przyby&#322;o. W tym wypadku musieli&#347;my tylko jawnie poda&#263; jak si&#281; nazywa nasza tabela i jak si&#281; nazywa jej klucz g&#322;&#243;wny.&lt;/p&gt;


	&lt;h2&gt;Korzystanie z wielu baz&lt;/h2&gt;


	&lt;p&gt;W momencie tworzenia projektu aplikacji RoR generowany jest automatycznie plik konfiguracyjny zawieraj&#261;cy po&#322;&#261;czenie do bazy danych. Przyk&#322;adowy plik config/database.yml mog&#322;by wygl&#261;da&#263; np. tak:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;default&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="punct"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ident"&gt;defaults&lt;/span&gt;
  &lt;span class="ident"&gt;adapter&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="ident"&gt;mysql&lt;/span&gt;
  &lt;span class="ident"&gt;host&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="ident"&gt;localhost&lt;/span&gt;
  &lt;span class="ident"&gt;encoding&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="ident"&gt;utf8&lt;/span&gt;
  &lt;span class="ident"&gt;username&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="ident"&gt;jakis_login&lt;/span&gt;
  &lt;span class="ident"&gt;password&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="ident"&gt;jakies_haslo&lt;/span&gt;

&lt;span class="ident"&gt;development&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;
  &lt;span class="ident"&gt;database&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="ident"&gt;bookstore_dev&lt;/span&gt;
  &lt;span class="punct"&gt;&amp;lt;&amp;lt;:&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;defaults&lt;/span&gt;

&lt;span class="ident"&gt;test&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;
  &lt;span class="ident"&gt;database&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="ident"&gt;bookstore_test&lt;/span&gt;
  &lt;span class="ident"&gt;port&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="number"&gt;3307&lt;/span&gt;
  &lt;span class="punct"&gt;&amp;lt;&amp;lt;:&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;defaults&lt;/span&gt;

&lt;span class="ident"&gt;production&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;
  &lt;span class="ident"&gt;database&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="ident"&gt;bookstore&lt;/span&gt;
  &lt;span class="ident"&gt;socket&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;var&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;run&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;mysqld&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;mysqld&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;sock&lt;/span&gt;
  &lt;span class="punct"&gt;&amp;lt;&amp;lt;:&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;defaults&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Co robi powy&#380;szy kod? Definiuje 3 bazy: produkcyjn&#261;, robocz&#261; i testow&#261;. (Ta ostatnia jest u&#380;ywana tylko do test&#243;w jednostkowych wi&#281;c nie powinno si&#281; tam wstawia&#263; &#380;adnych istotnych danych) W w/w konfiguracji zdecydowali&#347;my, &#380;e wszystkie bazy b&#281;d&#261; korzysta&#263; z MySQL &amp;gt;=4.1 z ustawionym wew. kodowaniem &lt;span class="caps"&gt;UTF&lt;/span&gt;-8 (zalecane). Baza produkcyjna dzia&#322;a&#263; b&#281;dzie na Linuksie, wi&#281;c zamiast portu &lt;span class="caps"&gt;TCP&lt;/span&gt;, podano uniksowy socket. Baza testowa dzia&#322;a na porcie 3307, a robocza na domy&#347;lnym (3306). G&#322;&#243;wna sekcja &amp;#8220;defaults&amp;#8221; jest wsp&#243;lna dla wszystkich trzech baz zgodnie z zasad&#261; &lt;span class="caps"&gt;DRY&lt;/span&gt; (Don&amp;#8217;t Repeat Yourself).&lt;/p&gt;


	&lt;p&gt;Za&#322;&#243;&#380;my jednak, &#380;e chcemy korzysta&#263; z dodatkowej bazy i chcemy aby z niej korzysta&#322;y niekt&#243;re modele. &#379;aden problem. Do pliku dopisujemy tylko dodatkow&#261; definicj&#281; po&#322;&#261;czenia do innej bazy:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;other&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;
  &lt;span class="ident"&gt;adapter&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="ident"&gt;mysql&lt;/span&gt;
  &lt;span class="ident"&gt;host&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="number"&gt;192.168&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="number"&gt;0.123&lt;/span&gt;
  &lt;span class="ident"&gt;database&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="ident"&gt;customers&lt;/span&gt;
  &lt;span class="punct"&gt;&amp;lt;&amp;lt;:&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;defaults&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Jak wida&#263;, mo&#380;emy t&#261; metod&#261; tworzy&#263; aplikacj&#281; korzystaj&#261;c&#261; z rozproszonych baz. (W tym wypadku baza customers le&#380;y na zupe&#322;nie innym serwerze, ale korzysta z tego samego usera i has&#322;a.)&lt;/p&gt;


	&lt;p&gt;No dobrze, ale jak modele maj&#261; rozpozna&#263; z jakiego po&#322;&#261;czenia korzystaj&#261;? To te&#380; prosta sprawa. Domy&#347;lnym po&#322;&#261;czeniem jest to, co zdefiniowano w sekcji domy&#347;lnej. Jedynie modele, kt&#243;re korzystaj&#261; z innego po&#322;&#261;czenia, musz&#261; to mie&#263; jawnie zadeklarowane w definicji, np.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Client&lt;/span&gt;
  &lt;span class="ident"&gt;establish_connection&lt;/span&gt; &lt;span class="symbol"&gt;:other&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Model Client oraz  wszystkie modele utworzone z niego na drodze dziedziczenia, b&#281;d&#261; korzysta&#263; z innego po&#322;&#261;czenia ni&#380; domy&#347;lne.&lt;/p&gt;


	&lt;p&gt;&lt;span class="caps"&gt;BTW&lt;/span&gt;, to jedna z bardziej brakuj&#261;cych mi cech we frameworku &lt;a href="http://djangoproject.com"&gt;Django&lt;/a&gt; (&lt;a href="http://pylonshq.com"&gt;Pylons&lt;/a&gt; nie ma z tym &#380;adnego problemu). Mam nadziej&#281;, &#380;e wkr&#243;tce to poprawi&#261; i w &lt;a href="http://djangoproject.com"&gt;Django&lt;/a&gt; b&#281;dzie mo&#380;na bez problemu korzysta&#263; z wielu baz.&lt;/p&gt;


	&lt;h2&gt;Wspolne modele dla wielu aplikacji Rails&lt;/h2&gt;


	&lt;p&gt;W tym scenariuszu mamy za zadanie stworzy&#263; szereg aplikacji webowych korzystaj&#261;cych z oddzielnych baz o &lt;em&gt;tej samej&lt;/em&gt; strukturze tabel. Chcemy unikn&#261;&#263; powielania kodu niezb&#281;dnego dla modeli &lt;span class="caps"&gt;ORM&lt;/span&gt;. Nie mamy te&#380; czasu (ani ochoty) aby siedzie&#263; i stworzy&#263; definicje do wszystkich tabel wraz z wyposa&#380;eniem ich w nasze w&#322;asne, dodatkowe metody uwzgl&#281;dniaj&#261;ce ka&#380;d&#261;, mo&#380;liw&#261; potrzeb&#281; w przysz&#322;o&#347;ci.  Zamiast tego, wolimy tworzy&#263; i  rozbudowywa&#263; obiektowy opis naszej bazy dla wszystkich aplikacji w miar&#281; ich rozwoju i wzrostu z&#322;o&#380;ono&#347;ci.  Chcemy oby nasz model biznesowy, opisywany przez ActiveRecord, r&#243;s&#322; stopniowo wraz ze wzrostem naszych aplikacji RoR.&lt;/p&gt;


	&lt;p&gt;Do takiego zadania, najlepiej aby wszystkie aplikacje RoR korzysta&#322;y ze wsp&#243;lnego repozytorium modeli. W wypadku system&#243;w &lt;span class="caps"&gt;POSIX&lt;/span&gt; (Linux, czy MacOS-X) najpro&#347;ciej to rozwi&#261;za&#263; za pomoc&#261; link&#243;w symbolicznych. Jednak&#380;e, je&#347;li chcemy aby nasz kod by&#322; bardziej przeno&#347;ny i dzia&#322;a&#322; tak&#380;e pod Windowsami musimy inaczej podej&#347;&#263; do tego problemu: wyniesiemy definicje naszych modeli do zewn&#281;trznych modu&#322;&#243;w Rubiego aby domieszkowa&#322;y one klasy poszczeg&#243;lnych modeli Rails.&lt;/p&gt;


	&lt;p&gt;Wpierw musimy dla ka&#380;dej aplikacji RoR wskaza&#263; gdzie le&#380;y repozytorium z wpo&#322;dzielonymi  modu&#322;ami. Najlepiej wstawi&#263; do pliku conf/environment.rb zmienn&#261; globaln&#261; Rubiego o nazwie, np. $SHARED.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="global"&gt;$SHARED&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;File&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;dirname&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;__FILE__&lt;/span&gt;&lt;span class="punct"&gt;)+'&lt;/span&gt;&lt;span class="string"&gt;/../../shared/&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Konstrukcja File.dirname&amp;#8230; jest zalecana, gdy&#380; daje pewny i jednoznaczny dost&#281;p do pliku bez wzgl&#281;dy na to gdzie przekopiujemy ca&#322;y nasz projekt. (Identycznie nale&#380;y robi&#263; pod &lt;span class="caps"&gt;PHP&lt;/span&gt; aby unikn&#261;&#263; problem&#243;w z ustawieniami zmiennej include_dirs. Wi&#281;kszo&#347;&#263; pehapowego lamerstwa u&#380;ywa zapisu require(&amp;#8220;plik.php&amp;#8221;) i potem si&#281; dziwi &#380;e co&#347; im nie dzia&#322;a)&lt;/p&gt;


	&lt;p&gt;Za&#322;&#243;&#380;my &#380;e wcze&#347;niej mieli&#347;my model  zdefiniowany nast&#281;puj&#261;co:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Book&lt;/span&gt;
  &lt;span class="ident"&gt;set_table_name&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;ksiazki&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
  &lt;span class="ident"&gt;belongs_to&lt;/span&gt; &lt;span class="symbol"&gt;:author&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.commented&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;user_id&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:conditions=&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;['&lt;/span&gt;&lt;span class="string"&gt;user_id=?&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt;&lt;span class="ident"&gt;user_id&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Mamy tu wi&#281;c nie tylko nazw&#281; tabeli niezgodn&#261; z konwencj&#261; Rails&#243;w, ale tak&#380;e dodatkow&#261; metod&#281; klasow&#261;. Wpierw zr&#243;bmy z tego modu&#322;:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;BookModule&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.extended&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;c&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;c&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;set_table_name&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;ksiazki&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
    &lt;span class="ident"&gt;c&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;belongs_to&lt;/span&gt; &lt;span class="symbol"&gt;:author&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;commented&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;user_id&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:conditions=&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;['&lt;/span&gt;&lt;span class="string"&gt;user_id=?&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt;&lt;span class="ident"&gt;user_id&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Kod korzystaj&#261;cy z tego modu&#322;u przyjmie posta&#263;:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="constant"&gt;RAILS_ENV&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;development&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;load&lt;/span&gt; &lt;span class="global"&gt;$SHARED&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;models/book.rb&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="keyword"&gt;else&lt;/span&gt;
  &lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="global"&gt;$SHARED&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;models/book.rb&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Book&lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
  &lt;span class="ident"&gt;include&lt;/span&gt; &lt;span class="constant"&gt;BookModule&lt;/span&gt;
  &lt;span class="ident"&gt;extend&lt;/span&gt; &lt;span class="constant"&gt;BookModule&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;G&#243;rny fragment jest potrzebny je&#347;li chcemy aby Webrick/Mongrel w trybie development si&#281; prze&#322;adowywa&#322; podczas zmiany kodu w Ruby. &amp;#8220;include&amp;#8221; w&#322;&#261;cza metody instancji.  Za&#347; &amp;#8220;extend&amp;#8221; w&#322;&#261;cza wszystkie emetody modu&#322;u _BookModule _do klasy jako jej metody &lt;strong&gt;klasowe&lt;/strong&gt;. Zwr&#243;&#263; uwag&#281;, &#380;e w module nie definiowali&#347;my ich jako z prefiksem self.  Co prawda, efektem ubocznym tego przyk&#322;adu jest to, &#380;e metoda commented() b&#281;dzie mo&#380;liwa do uruchomienia jako metoda zar&#243;wno instancji jak i klasy, ale to raczej nie ma &#380;adnego znaczenia.&lt;/p&gt;</description>
      <pubDate>Wed, 04 Oct 2006 17:01:00 +0200</pubDate>
      <guid isPermaLink="false">urn:uuid:c5f8cc00-6a5c-414b-9df7-beeb67e3259a</guid>
      <author>Jaros&#322;aw Zabie&#322;&#322;o</author>
      <link>http://blog.zabiello.com/articles/2006/10/04/flexiblerails</link>
      <category>Ruby on Rails</category>
      <category>rails</category>
      <category>activerecord</category>
      <category>orm</category>
    </item>
  </channel>
</rss>
