<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.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: Ulepszanie funkcjonalno&#347;ci Active Record</title>
    <link>http://blog.zabiello.com/articles/2007/06/24/extend-active-record</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>moje notatki, linki, komentarze</description>
    <item>
      <title>Ulepszanie funkcjonalno&#347;ci Active Record</title>
      <description>&lt;p&gt;&lt;a href="http://ar.rubyonrails.org/"&gt;Active Record&lt;/a&gt; to standardowy &lt;a href="http://en.wikipedia.org/wiki/Object-relational_mapping"&gt;&lt;span class="caps"&gt;ORM&lt;/span&gt;&lt;/a&gt; u&#380;ywany przez framework &lt;a href="http://rubyonrails.org"&gt;Rails&lt;/a&gt;. Zasadniczo jest dobrze zaprojektowany, prosty i wygodny. Nie trzeba &#380;mudnie definiowa&#263; ka&#380;dego pola w tabeli dla modelu, oraz mo&#380;na bez problemu podpina&#263; aplikacj&#281; do kilku r&#243;&#380;nych baz (czego np. nie ma &lt;a href="http://djangoproject.com"&gt;Django&lt;/a&gt;).&lt;/p&gt;


	&lt;h3&gt;Gdzie s&#261; te pola?&lt;/h3&gt;


	&lt;p&gt;Kwestia tego, czy w modelu definiowa&#263; pola czy nie, jest w zasadzie kontrowersyjna. Cho&#263; wad&#261; jest dodatkowy nak&#322;ad pracy zwi&#261;zany ze &#380;mudnym definiowaniem ka&#380;dego pola, to zalet&#261; jest to, &#380;e wystarczy spojrze&#263; na kod modelu, aby od razu wiedzie&#263; z jakimi polami mamy do czynienia. &lt;span class="caps"&gt;W AR&lt;/span&gt; ca&#322;a podstawowa definicja modelu sprowadza si&#281; (pomijam walidacje i relacje) do trywialnego zapisu:&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;Person&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;Taki zapis zak&#322;ada, &#380;e istnieje tabela o nazwie people i posiada klucz g&#322;&#243;wny o nazwie id. Jest to zgodne z konwencjami jakie przyj&#281;to w Railsach i oczywi&#347;cie mo&#380;na to zmieni&#263; je&#347;li jest taka potrzeba.&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;Person&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="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;table_name&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="symbol"&gt;:users&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;primary_key&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="symbol"&gt;:user_id&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Jednak przy du&#380;ej ilo&#347;ci modeli nie zawsze b&#281;dziemy pami&#281;ta&#263; z jakimi polami w tabeli mamy do czynienia. Z pomoc&#261; przychodzi plugin &lt;a href="http://pragdave.pragprog.com/pragdave/2006/02/annotate_models.html"&gt;Annotate Models&lt;/a&gt;. Po zainstalowaniu za pomoc&#261; komendy (odpalanej w katalogu aplikacji Rails) mo&#380;emy troch&#281; poprawi&#263; definicj&#281; modelu.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;script&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;plugin&lt;/span&gt; &lt;span class="ident"&gt;install&lt;/span&gt; &lt;span class="ident"&gt;http&lt;/span&gt;&lt;span class="punct"&gt;:/&lt;/span&gt;&lt;span class="regex"&gt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;svn&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;pragprog&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;com&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="constant"&gt;Public&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;plugins&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;annotate_models&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Po odpaleniu komendy&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;rake&lt;/span&gt; &lt;span class="ident"&gt;annotate_models&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Ruby &amp;#8220;automagicznie&amp;#8221; przejrzy zawarto&#347;&#263; tabeli odpowiadaj&#261;cej ka&#380;demu modelowi &lt;span class="caps"&gt;ORM&lt;/span&gt; i doda odpowiednie komentarze do pocz&#261;tku pliku. Np.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="comment"&gt;# == Schema Information&lt;/span&gt;
&lt;span class="comment"&gt;#&lt;/span&gt;
&lt;span class="comment"&gt;# Table name: news&lt;/span&gt;
&lt;span class="comment"&gt;#&lt;/span&gt;
&lt;span class="comment"&gt;#  id         :integer(11)   not null, primary key&lt;/span&gt;
&lt;span class="comment"&gt;#  header     :text          &lt;/span&gt;
&lt;span class="comment"&gt;#  message    :text          &lt;/span&gt;
&lt;span class="comment"&gt;#  updated_at :timestamp     not null&lt;/span&gt;
&lt;span class="comment"&gt;#&lt;/span&gt;

&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;News&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;W efekcie mamy i prostot&#281; i potrzebn&#261; nam informacj&#281; w jednym miejscu.&lt;/p&gt;


	&lt;h3&gt;Nietypowe tabele &amp;#8211; z&#322;o&#380;one klucze g&#322;&#243;wne&lt;/h3&gt;


	&lt;p&gt;Nie jest tajemnic&#261;, &#380;e AR nie posiada wsparcia dla z&#322;o&#380;onych kluczy g&#322;&#243;wnych. Czy to znaczy, &#380;e Rails nie nadaje si&#281; do pracy z tabelami kt&#243;re takie klucze posiadaj&#261;? Nie. To, &#380;e AR nie posiada takiej mo&#380;liwo&#347;ci standardowo nic nie znaczy. Wystarczy doinstalowa&#263; &lt;a href="http://compositekeys.rubyforge.org/"&gt;Composite Primary Keys&lt;/a&gt; kt&#243;ry rozszerza standardowe mo&#380;liwo&#347;ci AR. Z do&#347;wiadczenia mog&#281; potwierdzi&#263;, &#380;e dzia&#322;a ca&#322;kiem dobrze. Jedyny problem jaki napotka&#322;em dotyczy&#322; tworzenia dynamicznego rusztowania (scaffolding) w modelu posiadaj&#261;cym z&#322;o&#380;one klucze g&#322;&#243;wne.&lt;/p&gt;


	&lt;h3&gt;ez_where &amp;#8211; bardziej obiektowe warunki wyszukiwania&lt;/h3&gt;


	&lt;p&gt;AR jest krytykowany za zbyt du&#380;&#261; ilo&#347;&#263; kodu &lt;span class="caps"&gt;SQL&lt;/span&gt; jaki trzeba u&#380;ywa&#263; w warunkach. Np.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;find_users&lt;/span&gt;
  &lt;span class="ident"&gt;find&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;[&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;name NOT LIKE ? AND rights IN(?)&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;kowalski&lt;/span&gt;&lt;span class="punct"&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;4&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;span class="number"&gt;8&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;Sytuacj&#281; poprawia plugin &lt;a href="http://agilewebdevelopment.com/plugins/ez_where"&gt;ez_where&lt;/a&gt;. Aby jednak stosowanie jego by&#322;o bardziej eleganckie, warto modyfikowa&#263; (otworzy&#263;) klas&#281; ActiveRecord:Base. Po dodaniu nast&#281;puj&#261;cego kodu do config/environment.rb:&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;ActiveRecord::Base&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.orm_conditions&lt;/span&gt;&lt;span class="punct"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="ident"&gt;block&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="constant"&gt;Caboose&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;EZ&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Condition&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="ident"&gt;block&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;to_sql&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;mo&#380;na w modelu stosowa&#263; wygodniejszy zapis warunk&#243;w wyszukiwania.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;find_users&lt;/span&gt;
  &lt;span class="ident"&gt;find&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="ident"&gt;orm_conditions&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
         &lt;span class="ident"&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;kowalski&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
         &lt;span class="ident"&gt;rights&lt;/span&gt; &lt;span class="punct"&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;4&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;span class="number"&gt;8&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;ez_where pozwala oczywi&#347;cie na bardziej skomplikowane konstrukcje. (Poza tym pluginem istnieje podobny, konkurencyjny &amp;#8211; &lt;a href="http://agilewebdevelopment.com/plugins/condition_builder"&gt;Condition Builder&lt;/a&gt;, kt&#243;rego mo&#380;na zintegrowa&#263; z AR w podobny spos&#243;b).&lt;/p&gt;


	&lt;h4&gt;Appendix 2007-07-04&lt;/h4&gt;


	&lt;p&gt;ez_where Ma ju&#380; zdefiniowan&#261; metod&#281; &lt;code&gt;c&lt;/code&gt;, wi&#281;c powy&#380;szy przyk&#322;ad mo&#380;na zapisa&#263;:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;find_users&lt;/span&gt;
  &lt;span class="ident"&gt;find&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="ident"&gt;c&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
         &lt;span class="ident"&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;kowalski&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
         &lt;span class="ident"&gt;rights&lt;/span&gt; &lt;span class="punct"&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;4&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;span class="number"&gt;8&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
       &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_sql&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;To nie jest wszystko o co mo&#380;na rozszerzy&#263; AR. Istnieje ju&#380; ca&#322;kiem &lt;a href="http://agilewebdevelopment.com/plugins/category/2"&gt;imponuj&#261;ca liczba plugin&#243;w&lt;/a&gt; kt&#243;re s&#261; w stanie uzupe&#322;ni&#263; AR o funkcjonalno&#347;ci jakich standardowo nie posiada. Jest to zgodne z polityk&#261; jak&#261; tw&#243;rcy Rails maj&#261; w tej dziedzinie. Rdze&#324; Rails&#243;w powinien by&#263; prosty i funkcjonalny do wi&#281;kszo&#347;ci zastosowa&#324;. A dla os&#243;b potrzebuj&#261;cych dodatkowych mo&#380;liwo&#347;ci, przewidziano &lt;a href="http://agilewebdevelopment.com/plugins"&gt;mechanizm plugin&#243;w&lt;/a&gt;.&lt;/p&gt;</description>
      <pubDate>Sun, 24 Jun 2007 14:19:00 +0200</pubDate>
      <guid isPermaLink="false">urn:uuid:a22b4618-277e-4930-b55e-de7e78b58a34</guid>
      <author>Jaros&#322;aw Zabie&#322;&#322;o</author>
      <link>http://blog.zabiello.com/articles/2007/06/24/extend-active-record</link>
      <category>rails</category>
      <category>activerecord</category>
    </item>
    <item>
      <title>"Ulepszanie funkcjonalno&#347;ci Active Record" by Jaros&#322;aw Zabie&#322;&#322;o</title>
      <description>&lt;p&gt;Ten Condition Builder to chyba te&#380; badziew. Jak tam zbudowa&#263; warunek &amp;#8220;field IS NOT NULL&amp;#8221;?&lt;/p&gt;</description>
      <pubDate>Tue, 03 Jul 2007 16:51:05 +0200</pubDate>
      <guid isPermaLink="false">urn:uuid:bda57a50-2c82-4940-8aa7-bc613dab5d82</guid>
      <link>http://blog.zabiello.com/articles/2007/06/24/extend-active-record#comment-854</link>
    </item>
    <item>
      <title>"Ulepszanie funkcjonalno&#347;ci Active Record" by pak</title>
      <description>&lt;p&gt;Condition Builder jest jak dlamnie idealny, bardzo prosty i w miare czytelny, nie korzysta z instance_eval wiec mam dostep do wszystkich zmiennych. Niestety wlasnie natknolem sie w nim na powazny blad.&lt;/p&gt;


	&lt;p&gt;Ten kod
&lt;a href="http://pastebin.4programmers.net/2714" rel="nofollow"&gt;http://pastebin.4programmers.net/2714&lt;/a&gt;
Powinine miec operatory logiczne OR, natomista wygenerowany sql zawiera tylko same AND&lt;/p&gt;</description>
      <pubDate>Thu, 28 Jun 2007 12:00:07 +0200</pubDate>
      <guid isPermaLink="false">urn:uuid:9c6a9794-f9e0-4116-9839-538a36ee44df</guid>
      <link>http://blog.zabiello.com/articles/2007/06/24/extend-active-record#comment-843</link>
    </item>
    <item>
      <title>"Ulepszanie funkcjonalno&#347;ci Active Record" by dr_bonzo</title>
      <description>&lt;p&gt;&amp;#8216;annotate_models&amp;#8217; musze sprawdzic, bo do tej pory otwieralem plik migracji w malym okienku (no i za to lubie eclipse, mozna pootwierac wiele Edytorow jednoczesnie, a w Netbinsie slabo mi ro wychodzi, o ile sie tak da :)); a czy te komentarze sa updatowane po wykonaniu rake db:migrate?
hmmmm chociaz to jest proste do zaimplementowania :D dwa taski. dobranoc&lt;/p&gt;</description>
      <pubDate>Thu, 28 Jun 2007 01:48:20 +0200</pubDate>
      <guid isPermaLink="false">urn:uuid:b9c7eeee-a705-4469-af78-218e162b44fb</guid>
      <link>http://blog.zabiello.com/articles/2007/06/24/extend-active-record#comment-841</link>
    </item>
    <item>
      <title>"Ulepszanie funkcjonalno&#347;ci Active Record" by lopex</title>
      <description>&lt;p&gt;Nie zapominajmy te&#380; o &lt;a href="http://magicmodels.rubyforge.org/" rel="nofollow"&gt;http://magicmodels.rubyforge.org/&lt;/a&gt; drnick&amp;#8217;a &lt;a href="http://www.drnicwilliams.com/" rel="nofollow"&gt;http://www.drnicwilliams.com/&lt;/a&gt;
pozwalaj&#261;ce np: na u&#380;ycie z&#322;o&#380;onych kluczy czy autokonfiguracj&#281; relacji&lt;/p&gt;</description>
      <pubDate>Mon, 25 Jun 2007 10:53:39 +0200</pubDate>
      <guid isPermaLink="false">urn:uuid:e80a8297-de1e-4523-a7ee-01ea74652f37</guid>
      <link>http://blog.zabiello.com/articles/2007/06/24/extend-active-record#comment-839</link>
    </item>
  </channel>
</rss>
