<?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: Tag monkeypatching</title>
    <link>http://blog.zabiello.com/articles/tag/monkeypatching</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>moje notatki, linki, komentarze</description>
    <item>
      <title>Ulepszony String i sprawa parametr&#243;w</title>
      <description>&lt;p&gt;Ruby posiada bardzo bogate mo&#380;liwo&#347;ci przetwarzania tekstu. Zasugerowany sk&#322;adni&#261; Active Record i wcze&#347;niejszym do&#347;wiadczeniem z Pythonem troch&#281; si&#281; zdziwi&#322;em, &#380;e Matz nie zaimplementowa&#322; przekazywania hasza do operatora &lt;code&gt;%&lt;/code&gt;.&lt;/p&gt;


	&lt;h3&gt;Parametry w Active Record.&lt;/h3&gt;


	&lt;p&gt;Co prawda &lt;a href="http://ar.rubyonrails.org/"&gt;Active Record&lt;/a&gt; nie mia&#322; mo&#380;e nigdy ambicji kompletnego pozbycia si&#281; j&#281;zyka &lt;span class="caps"&gt;SQL&lt;/span&gt;, ale na tle takiego &lt;a href="http://sqlalchemy.org"&gt;SQLAlchemy&lt;/a&gt; wypada znacznie mniej obiektowo&lt;sup&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt;. Wida&#263; to po sposobie budowania warunk&#243;w. Active Record umo&#380;liwia przekazywanie parametr&#243;w do warunk&#243;w na trzy podstawowe&lt;sup&gt;&lt;a href="#fn2"&gt;2&lt;/a&gt;&lt;/sup&gt; sposoby.&lt;/p&gt;


	&lt;h4&gt;&amp;#8220;Chamskie&amp;#8221; budowanie stringu &lt;span class="caps"&gt;SQL&lt;/span&gt;&lt;/h4&gt;


	&lt;p&gt;Ta metoda jest najbardziej popularna u wszelkiej ma&#347;ci lamer&#243;w i os&#243;b pocz&#261;tkuj&#261;cych. Jest bardzo powszechna w&#347;r&#243;d pehapowc&#243;w, kt&#243;rzy niedawno zacz&#281;li bawi&#263; si&#281; Railsami. Przyk&#322;adowy kod tego typu wygl&#261;da&#322;by mniej wi&#281;cej tak:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;passwd&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;'],&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;passwd&lt;/span&gt;&lt;span class="punct"&gt;']&lt;/span&gt;
&lt;span class="constant"&gt;Model&lt;/span&gt;&lt;span class="punct"&gt;.&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;user='&lt;span class="expr"&gt;#{user}&lt;/span&gt;' AND login='&lt;span class="expr"&gt;#{passwd}&lt;/span&gt;'&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Taka &amp;#8220;radosna tfurczo&#347;&#263;&amp;#8221; jest z ca&#322;&#261; pewno&#347;ci&#261; zaproszeniem do klasycznego ataku &lt;a href="http://pl.wikipedia.org/wiki/SQL_Injection"&gt;&lt;span class="caps"&gt;SQL&lt;/span&gt; Injection&lt;/a&gt;. Rails posiada co prawda funkcj&#281; &lt;code&gt;sanitize_sql()&lt;/code&gt; kt&#243;ra pacyfikuje niebezpieczne znaki w kwerendzie &lt;span class="caps"&gt;SQL&lt;/span&gt;, ale mo&#380;na o niej zapomnie&#263;, a kod staje si&#281; po prostu brzydki. Zatem tego typu praktyce m&#243;wimy stanowcze: fuj!&lt;/p&gt;


	&lt;p&gt;Prawie wszystkie relacyjne bazy pozwalaj&#261; na budowanie parametryzowanego &lt;span class="caps"&gt;SQL&lt;/span&gt;&amp;#8217;a. Na tym bazuj&#261; dwie kolejne metody.&lt;/p&gt;


	&lt;h4&gt;Przekazywanie listy.&lt;/h4&gt;


	&lt;p&gt;Warunek &lt;code&gt;:conditions&lt;/code&gt; mo&#380;e przyjmowa&#263; zar&#243;wno String jak i Array. W tym drugim wypadku dostajemy za darmo kod uodporniony na atak &lt;span class="caps"&gt;SQL&lt;/span&gt; Injection. Nie trzeba pami&#281;ta&#263; o tym czy dane zawieraj&#261; jakie&#347; niebezpieczne znaki. Nie trzeba te&#380; zastanawia&#263; si&#281; czy przekazywny parametr jest liczb&#261;, czy stringiem (wymagaj&#261;cym apostrof&#243;w w kodzie &lt;span class="caps"&gt;SQL&lt;/span&gt;). Kod jest wi&#281;c i prostszy i bezpieczniejszy.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;passwd&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;'],&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;passwd&lt;/span&gt;&lt;span class="punct"&gt;']&lt;/span&gt;
&lt;span class="constant"&gt;Model&lt;/span&gt;&lt;span class="punct"&gt;.&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;user=? AND login=?&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="ident"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;passwd&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h4&gt;Przekazywanie hasza.&lt;/h4&gt;


	&lt;p&gt;Przekazywanie listy ma pewn&#261; wad&#281;: trzeba pami&#281;ta&#263; o kolejno&#347;ci parametr&#243;w. Tak&#380;e zawiera niepotrzebn&#261; redundancj&#281; w wypadku kiedy jaki&#347; parametr musi si&#281; powtarza&#263;. Dlatego Active Record umo&#380;liwia korzystanie ze sk&#322;adni inspirowanej baz&#261; Oracle.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;passwd&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;'],&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;passwd&lt;/span&gt;&lt;span class="punct"&gt;']&lt;/span&gt;
&lt;span class="constant"&gt;Model&lt;/span&gt;&lt;span class="punct"&gt;.&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;user=:user AND login=:login&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;&lt;span class="symbol"&gt;:user&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:passwd&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;passwd&lt;/span&gt;&lt;span class="punct"&gt;}]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;lub jeszcze kr&#243;cej:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="constant"&gt;Model&lt;/span&gt;&lt;span class="punct"&gt;.&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;user=:user AND login=:login&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h3&gt;Parametry do stringa.&lt;/h3&gt;


	&lt;p&gt;Po tym wst&#281;pie, pora na (niemi&#322;&#261;) niespodziank&#281; jak&#261; spotka&#322;em w implementacji Rubiego. Generalnie Ruby posiada bardzo bogate mo&#380;liwo&#347;ci przetwarzania tekstu. Podobnie jak Python, posiada te&#380; skr&#243;con&#261; form&#281; funkcji &lt;code&gt;sprintf&lt;/code&gt; realizowan&#261; za pomoc&#261;  operatora &lt;code&gt;%&lt;/code&gt;.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;%s can use %s.&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;%&lt;/span&gt; &lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;Ruby&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;list of parameters&lt;/span&gt;&lt;span class="punct"&gt;']&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;Ruby can use list of parameters.&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Python jednak&#380;e pozwala tak&#380;e na u&#380;ywanie s&#322;ownik&#243;w.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;params&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{'&lt;/span&gt;&lt;span class="string"&gt;lang&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="symbol"&gt;:'&lt;/span&gt;&lt;span class="symbol"&gt;Python&lt;/span&gt;&lt;span class="symbol"&gt;'&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;what&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="symbol"&gt;:'&lt;/span&gt;&lt;span class="symbol"&gt;a dictionary&lt;/span&gt;&lt;span class="symbol"&gt;'&lt;/span&gt;&lt;span class="punct"&gt;}&lt;/span&gt;
&lt;span class="ident"&gt;print&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;%(lang)s can also use %(what)s.&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;%&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;
&lt;span class="constant"&gt;Python&lt;/span&gt; &lt;span class="ident"&gt;can&lt;/span&gt; &lt;span class="ident"&gt;also&lt;/span&gt; &lt;span class="ident"&gt;use&lt;/span&gt; &lt;span class="ident"&gt;a&lt;/span&gt; &lt;span class="ident"&gt;dictionary&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Troch&#281; mnie zdziwi&#322;o, &#380;e Ruby nie posiada odpowiednika tej funkcjonalno&#347;ci&amp;#8230;. Na szcz&#281;&#347;cie, dzi&#281;ki otwartym klasom, &lt;a href="http://en.wikipedia.org/wiki/Monkey_patch"&gt;monkey patching&lt;/a&gt; w przypadku Rubiego nabiera zupe&#322;nie innego wymiaru. Nic nie stoi na przeszkodzie aby ulepszy&#263; standardowy obiekt String.&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;String&lt;/span&gt;  
  &lt;span class="ident"&gt;alias_method&lt;/span&gt; &lt;span class="symbol"&gt;:old_percent&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;:%&lt;/span&gt;
  &lt;span class="comment"&gt;# Template strings. &lt;/span&gt;
  &lt;span class="comment"&gt;# Inspired by Python %(key)s and $key syntax.&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;%&lt;/span&gt; &lt;span class="ident"&gt;var&lt;/span&gt;
    &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;var&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;==&lt;/span&gt; &lt;span class="ident"&gt;Hash&lt;/span&gt;
      &lt;span class="ident"&gt;var&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each_pair&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;key&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
        &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;key&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;^[a-z]+$&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;gsub!&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;$(&lt;span class="expr"&gt;#{key}&lt;/span&gt;)&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&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;gsub!&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;$&lt;span class="expr"&gt;#{key}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;
        &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;    
    &lt;span class="ident"&gt;old_percent&lt;/span&gt; &lt;span class="ident"&gt;var&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="ident"&gt;hash&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;&lt;span class="symbol"&gt;:lang&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;Ruby&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:what&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;a hash&lt;/span&gt;&lt;span class="punct"&gt;'}&lt;/span&gt;
&lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Now $lang can also use $what :)&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;%&lt;/span&gt; &lt;span class="ident"&gt;hash&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;Now Ruby can also use a hash :)&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;I jak tu nie powiedzie&#263;, &#380;e Ruby jest pi&#281;kny? :)&lt;/p&gt;


&lt;hr size="1" width="30%" align="left" /&gt;

	&lt;p id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; Active Record jest te&#380; mniej zaawansowany ni&#380; np. SQLAlchemy. Funkcjonalno&#347;&#263; Active Record jest mo&#380;e i &#347;wietna, ale jego implementacja w wielu punktach jest z pewno&#347;ci&#261; niedojrza&#322;a. Np. nie mam poj&#281;cia dlaczego Active Record zwraca list&#281; obiekt&#243;w zamiast generator, to pozwoli&#322;oby na wi&#281;ksze oszcz&#281;dno&#347;ci pami&#281;ci.&lt;/p&gt;


	&lt;p id="fn2"&gt;&lt;sup&gt;2&lt;/sup&gt; Bardziej obiektowe budowanie warunk&#243;w zapewniaj&#261; pluginy &lt;a href="http://agilewebdevelopment.com/plugins/ez_where"&gt;ez_where&lt;/a&gt; i &lt;a href="http://agilewebdevelopment.com/plugins/condition_builder"&gt;Condition Builder&lt;/a&gt;.&lt;/p&gt;</description>
      <pubDate>Fri, 29 Jun 2007 02:42:00 +0200</pubDate>
      <guid isPermaLink="false">urn:uuid:fbba7197-20e9-4c68-ba9b-f30633f57374</guid>
      <author>Jaros&#322;aw Zabie&#322;&#322;o</author>
      <link>http://blog.zabiello.com/articles/2007/06/29/improved-string</link>
      <category>ruby</category>
      <category>rails</category>
      <category>activerecord</category>
      <category>monkeypatching</category>
    </item>
  </channel>
</rss>
