<?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>Drogomir: Otwarte klasy w Rubim i ich praktyczne wykorzystanie</title>
    <link>http://blog.drogomir.com/articles/2008/01/08/otwarte-klasy-w-rubim-i-ich-praktyczne-wykorzystanie</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description></description>
    <item>
      <title>Otwarte klasy w Rubim i ich praktyczne wykorzystanie</title>
      <description>&lt;p&gt;Ruby posiada bardzo fajn&#261; w&#322;a&#347;ciwo&#347;&#263; nazwan&#261; otwartymi klasami. Znaczy to tyle, &#380;e je&#380;eli nikt nie zamrozi&#322; danej klasy/metody, mo&#380;na w dowolnym miejscu w kodzie nadpisa&#263; j&#261; klas&#281;, metod&#261;, doda&#263; nowe metody. Mo&#380;na tak nadpisa&#263; nawet klasy wbudowane w j&#281;zyk!&lt;/p&gt;


Na przyk&#322;ad taki kod (och jak ja lubi&#281; ten przyk&#322;ad):
&lt;code&gt;&lt;pre&gt;
class Fixnum
  alias old_plus +

  def +(other)
    (self.old_plus other) % 7
  end
end

4+4 #=&amp;gt; 1
&lt;/pre&gt;&lt;/code&gt;

	&lt;p&gt;4+4=1 ? nie tego si&#281; spodziewali&#347;my. A w&#322;a&#347;ciwie nie tego si&#281; spodziewali ludzie, kt&#243;rzy nie rozumiej&#261; jeszcze powy&#380;szego przyk&#322;adu (celowo doda&#322;em s&#322;owo jeszcze, b&#281;d&#281; na tyle cywilizowanym i mi&#322;ym cz&#322;owiekiem, &#380;e spr&#243;buj&#281; wyt&#322;umaczy&#263; o co chodzi).&lt;/p&gt;


	&lt;p&gt;Liczby ca&#322;kowite s&#261; klasy Fixnum. Powy&#380;szy kod modyfikuje t&#261; klas&#281;. Najpierw tworzony jest alias dla metody +, &#380;eby mo&#380;na by&#322;o jej p&#243;&#378;niej u&#380;ywa&#263;, a nast&#281;pnie owa metoda zostaje nadpisana w taki spos&#243;b, &#380;eby zwraca&#322;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&#322;u natchn&#261;&#322; mnie Daniel Owsia&#324;ski pisz&#261;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&#347;cie du&#380;o racji i moja paranoja, o kt&#243;rej pisa&#322;em u niego w komentarzach jest objawem przewra&#380;liwienia mojej m&#243;zgoczaszki w pewnych kwestiach. S&#261; jednak wypadki, w kt&#243;rych naprawd&#281; warto zachowa&#263; zgodno&#347;&#263; z nowymi wersjami. Pomaga tutaj powy&#380;sza w&#322;a&#347;ciwo&#347;&#263; j&#281;zyka Ruby. Pisa&#322; o tym kiedy&#347; autor bloga &lt;a href="http://errtheblog.com/"&gt;Err the Blog&lt;/a&gt; w kontek&#347;cie &lt;a href="http://errtheblog.com/posts/67-evil-twin-plugin"&gt;rozszerzania mo&#380;liwo&#347;ci plugin&#243;w&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;Cz&#281;sto w rozmowach o Ruby on Rails na r&#243;&#380;nych listach dyskusyjnych mo&#380;na us&#322;ysze&#263;, &#380;e gdy chcemy co&#347; zmieni&#263; w danej metodzie, najlepiej przekopiowa&#263; kod metody, nadpisa&#263; j&#261;, zmieni&#263; to co trzeba i voilla. Ale nie t&#281;dy droga panie i panowie :)&lt;/p&gt;


Wygl&#261;da&#322;oby to mniej wi&#281;cej tak. Chcemy na przyk&#322;ad nadpisa&#263; metod&#281; 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;code&gt;&lt;pre&gt;
#w modelu:
def self.find(*args)
  #jaki&#347; zmodyfikowany kod finda
end
&lt;/pre&gt;&lt;/code&gt;

	&lt;p&gt;Jakie minusy ma takie podej&#347;cie? Gdy mamy zainstalowan&#261; du&#380;&#261; liczb&#281; plugin&#243;w nigdy nie wiadomo czy kt&#243;ry&#347; z nich nie nadpisuje ju&#380; metody find i wtedy nadpisuj&#261;c j&#261; stracimy funkcjonalno&#347;&#263; dodan&#261; przez plugin. Smuteczek. Poza tym gdy zmieni si&#281; kod metody find w samym frameworku r&#243;wnie&#380; u nas b&#281;dziemy musieli go zmieni&#263;.&lt;/p&gt;


	&lt;p&gt;Jak to zrobi&#263; Ruby Way&amp;#8482;? Przypu&#347;&#263;my, &#380;e chcemy si&#281; popastwi&#263; nad wspomnian&#261; metod&#261; find:&lt;/p&gt;


&lt;code&gt;&lt;pre&gt;
#w modelu:

# metoda find jest metod&#261; klasy
class &amp;lt;&amp;lt; self
  alias :old_find :find

  def find(*args)
    args[1] ||= {}
    args[1][:conditions] ||= {} 
    args[1][:conditions] = [args[1][:conditions]] if args[1][:conditions].is_a?(String)
    case args[1][:conditions]
      when Hash: 
        args[1][:conditions].merge!(:deleted =&amp;gt; false)
      when Array:
        if args[1][:conditions][0].strip.blank?
          args[1][:conditions][0] = "deleted = ?" 
        else
          args[1][:conditions][0] = ["(#{args[1][:conditions][0]})", "deleted = ?"].join(' AND ')
        end
        args[1][:conditions] &amp;lt;&amp;lt; false       
    end
    old_find(*args)
  end
end
&lt;/pre&gt;&lt;/code&gt;

	&lt;p&gt;Powy&#380;szy kod dodaje do conditions warunek &amp;#8220;deleted = false&amp;#8221;, po czym wywo&#322;uje metod&#281; find z tak zmodyfikowanymi argumentami. Czasami trzeba jednak namiesza&#263; co&#347; w kodzie metody. Mo&#380;na wtedy doda&#263; dodatkowy argument. Nast&#281;pnie dajemy ifa &amp;#8211; je&#380;eli argument zwraca true wykonujemy zmodyfikowany kod, a je&#380;eli nie, wykonujemy kod oryginalnej metody.&lt;/p&gt;


	&lt;p&gt;Dzi&#281;ki takiemu podej&#347;ciu mo&#380;emy w miar&#281; &#322;atwo upgradowa&#263; pluginy i Railsy bez wi&#281;kszego stresu :)&lt;/p&gt;</description>
      <pubDate>Tue, 08 Jan 2008 11:06:00 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:241df52d-1bd2-452d-9cba-e478a82000ae</guid>
      <author>Piotr Sarnacki</author>
      <link>http://blog.drogomir.com/articles/2008/01/08/otwarte-klasy-w-rubim-i-ich-praktyczne-wykorzystanie</link>
      <category>Ruby on Rails</category>
      <category>rubyonrails</category>
      <category>ruby</category>
      <category>otwarte</category>
      <category>klasy</category>
      <category>hacking</category>
      <category>tips</category>
      <trackback:ping>http://blog.drogomir.com/articles/trackback/37</trackback:ping>
    </item>
  </channel>
</rss>
