<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0" xml:base="http://www.mediumexposure.com">
<channel>
 <title>hakunin's blog</title>
 <link>http://www.mediumexposure.com/techblog</link>
 <description />
 <language>en</language>
<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/mx_techblog" type="application/rss+xml" /><item>
 <title>Dumb tester_elf</title>
 <link>http://www.mediumexposure.com/2009/06/21/dumb-testerelf</link>
 <description>&lt;p&gt;This tester_elf script is very dumb, which is uncharacteristic of elves in general. Why did I name it tester_elf? Well, it&amp;#8217;s a &lt;em&gt;Santa&amp;#8217;s little helper&lt;/em&gt; type of elf as opposed to an individual of Tolkien&amp;#8217;s highly cultural race. I do crave some nice D&amp;#38;D &lt;span class="caps"&gt;RPG&lt;/span&gt; experience but there aren&amp;#8217;t any good new games in that direction. Everything gets dumbed down nowadays.&lt;/p&gt;
&lt;p&gt;Anyhow, &lt;a href="http://bit.ly/c4JpZ"&gt;here&amp;#8217;s the gist of it&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It just runs a test file every time you save it. Stupid but useful when autotest is too bulky. Instructions are in the gist.&lt;/p&gt;
</description>
 <comments>http://www.mediumexposure.com/2009/06/21/dumb-testerelf#comments</comments>
 <category domain="http://www.mediumexposure.com/tag/rails">rails</category>
 <category domain="http://www.mediumexposure.com/tag/ruby">ruby</category>
 <category domain="http://www.mediumexposure.com/taxonomy/term/3">solutions</category>
 <pubDate>Sun, 21 Jun 2009 20:23:40 +0000</pubDate>
 <dc:creator>hakunin</dc:creator>
 <guid isPermaLink="false">56 at http://www.mediumexposure.com</guid>
</item>
<item>
 <title>yui_compressor_fu</title>
 <link>http://www.mediumexposure.com/2009/05/29/yuicompressorfu</link>
 <description>&lt;p&gt;Please welcome a new thingie I just released &amp;#8211; &lt;a href="http://github.com/maxim/yui_compressor_fu"&gt;yui_compressor_fu&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Point&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://github.com/maxim/yui_compressor_fu"&gt;yui_compressor_fu&lt;/a&gt; delicately minifies all your assets (linked with &lt;span class="geshifilter"&gt;&lt;code class="geshifilter-text"&gt;javascript_include_tag&lt;/code&gt;&lt;/span&gt;, &lt;span class="geshifilter"&gt;&lt;code class="geshifilter-text"&gt;stylesheet_link_tag&lt;/code&gt;&lt;/span&gt; helpers) with &lt;a href="http://developer.yahoo.com/yui/compressor/"&gt;&lt;span class="caps"&gt;YUI&lt;/span&gt; Compressor&lt;/a&gt;. It abides Rails caching rules. All you have to do is install the plugin, and minifying will automatically kick-in when rails performs its normal caching routines.&lt;/p&gt;
&lt;h3&gt;Tale&lt;/h3&gt;
&lt;p&gt;Today I was having my usual cup of Earl Grey with lemon while trying to find some JS/CSS minifying solution. Nowadays Rails provides built-in concatenation and caching, but not compression. I wanted to find something that transparently adds some minifying to the mix. Well, not so fast.&lt;/p&gt;
&lt;p&gt;After flipping through quite a few choices, I wasn&amp;#8217;t impressed at all. Available plugins either require too much configuration (why do I have to list all my assets again, asset_packager?) or they use inferior compression algorithms (Packer/MinJS). According to my research (don&amp;#8217;t quote me) &amp;#8211; &lt;a href="http://developer.yahoo.com/yui/compressor/"&gt;&lt;span class="caps"&gt;YUI&lt;/span&gt; Compressor&lt;/a&gt; kicks some major ass. A few minutes of fruitless digging later I decided to make this plugin instead.&lt;/p&gt;
&lt;p&gt;Feel free to try it out and &lt;a href="http://github.com/maxim/yui_compressor_fu/issues"&gt;let me know of any issues&lt;/a&gt; you encounter.&lt;/p&gt;
</description>
 <comments>http://www.mediumexposure.com/2009/05/29/yuicompressorfu#comments</comments>
 <category domain="http://www.mediumexposure.com/tag/rails">rails</category>
 <category domain="http://www.mediumexposure.com/tag/ruby">ruby</category>
 <category domain="http://www.mediumexposure.com/taxonomy/term/3">solutions</category>
 <pubDate>Sat, 30 May 2009 06:08:30 +0000</pubDate>
 <dc:creator>hakunin</dc:creator>
 <guid isPermaLink="false">55 at http://www.mediumexposure.com</guid>
</item>
<item>
 <title>Better in-memory associations with :inverse_of</title>
 <link>http://www.mediumexposure.com/2009/05/04/better-memory-associations-inverseof</link>
 <description>&lt;p&gt;Today &lt;a href="http://rubyonrails.com"&gt;Rails&lt;/a&gt; gained a very interesting &lt;a href="http://github.com/rails/rails/commit/ccea98389abbf150b886c9f964b1def47f00f237"&gt;commit&lt;/a&gt; from &lt;a href="http://github.com/h-lame"&gt;h-lame&lt;/a&gt;. It&amp;#8217;s the first major step towards making associations aware of their parent model while still in-memory.  A few days ago I started &lt;a href="http://groups.google.com/group/rubyonrails-core/browse_thread/thread/eaba411bb69c58d8?fwc=1"&gt;this discussion&lt;/a&gt; which revealed to me that it had been a long time coming, just no one bothered.&lt;/p&gt;
&lt;p&gt;Well, finally someone did.  I would like to thank &lt;a href="http://github.com/h-lame"&gt;h-lame&lt;/a&gt; for making &lt;a href="http://github.com/rails/rails/commit/ccea98389abbf150b886c9f964b1def47f00f237"&gt;this commit&lt;/a&gt; happen. Here&amp;#8217;s the excerpt from the commit message.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can now add an :inverse_of option to has_one, has_many and belongs_to associations. This is best described with an example:&lt;br /&gt;
    &lt;div class="geshifilter"&gt;&lt;pre class="rails geshifilter-rails" style="font-family:monospace;"&gt;      &lt;span style="color:#9966CC; font-weight:bold;"&gt;class&lt;/span&gt; Man &lt;span style="color:#006600; font-weight:bold;"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#6666ff; font-weight:bold;"&gt;ActiveRecord::Base&lt;/span&gt;
        &lt;span style="color:#5A0A0A; font-weight:bold;"&gt;has_one&lt;/span&gt; &lt;span style="color:#ff3333; font-weight:bold;"&gt;:face&lt;/span&gt;, &lt;span style="color:#ff3333; font-weight:bold;"&gt;:inverse_of&lt;/span&gt; &lt;span style="color:#006600; font-weight:bold;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#ff3333; font-weight:bold;"&gt;:man&lt;/span&gt;
      &lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;
&amp;nbsp;
      &lt;span style="color:#9966CC; font-weight:bold;"&gt;class&lt;/span&gt; Face &lt;span style="color:#006600; font-weight:bold;"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#6666ff; font-weight:bold;"&gt;ActiveRecord::Base&lt;/span&gt;
        &lt;span style="color:#5A0A0A; font-weight:bold;"&gt;belongs_to&lt;/span&gt; &lt;span style="color:#ff3333; font-weight:bold;"&gt;:man&lt;/span&gt;, &lt;span style="color:#ff3333; font-weight:bold;"&gt;:inverse_of&lt;/span&gt; &lt;span style="color:#006600; font-weight:bold;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#ff3333; font-weight:bold;"&gt;:face&lt;/span&gt;
      &lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;
&amp;nbsp;
      m = Man.&lt;span style="color:#5A0A0A; font-weight:bold;"&gt;first&lt;/span&gt;
      f = m.&lt;span style="color:#9900CC;"&gt;face&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
    Without :inverse_of m and f.man would be different instances of the same object (f.man being pulled from the database again).&lt;/p&gt;
&lt;/blockquote&gt;
</description>
 <comments>http://www.mediumexposure.com/2009/05/04/better-memory-associations-inverseof#comments</comments>
 <category domain="http://www.mediumexposure.com/tag/edge">edge</category>
 <category domain="http://www.mediumexposure.com/tag/rails">rails</category>
 <category domain="http://www.mediumexposure.com/tag/ruby">ruby</category>
 <pubDate>Tue, 05 May 2009 04:21:32 +0000</pubDate>
 <dc:creator>hakunin</dc:creator>
 <guid isPermaLink="false">54 at http://www.mediumexposure.com</guid>
</item>
<item>
 <title>Installing Sphinx with Postgres on Leopard</title>
 <link>http://www.mediumexposure.com/2009/05/01/installing-sphinx-postgres-leopard</link>
 <description>&lt;p&gt;I didn&amp;#8217;t find a quick short guide to installing &lt;a href="http://www.sphinxsearch.com"&gt;sphinx&lt;/a&gt; on Leopard with &lt;a href="http://www.postgresql.org"&gt;PostgreSQL&lt;/a&gt;, hence here goes (this assumes you already installed &lt;a href="http://www.postgresql.org"&gt;PostgreSQL&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Compile dependencies&lt;/h3&gt;
&lt;p&gt;These two libs are required by &lt;a href="http://www.sphinxsearch.com"&gt;sphinx&lt;/a&gt; according to &lt;a href="http://www.viget.com/extend/installing-sphinx-on-os-x-leopard/"&gt;this blog post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;div class="geshifilter"&gt;&lt;pre class="bash geshifilter-bash" style="font-family:monospace;"&gt;  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;mkdir&lt;/span&gt; src
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #7a0874; font-weight: bold;"&gt;cd&lt;/span&gt; src
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; curl &lt;span style="color: #660033;"&gt;-O&lt;/span&gt; http:&lt;span style="color: #000000; font-weight: bold;"&gt;//&lt;/span&gt;ftp.gnu.org&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;gnu&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;libiconv&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;libiconv-1.13.tar.gz
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;tar&lt;/span&gt; xzf libiconv-1.13.tar.gz
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #7a0874; font-weight: bold;"&gt;cd&lt;/span&gt; libiconv-&lt;span style="color: #000000;"&gt;1.13&lt;/span&gt;
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; .&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;configure &lt;span style="color: #660033;"&gt;--prefix&lt;/span&gt;=&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;usr&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;&lt;span style="color: #7a0874; font-weight: bold;"&gt;local&lt;/span&gt;
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;make&lt;/span&gt;
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;sudo&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;make&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;install&lt;/span&gt;
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #7a0874; font-weight: bold;"&gt;cd&lt;/span&gt; ..
&amp;nbsp;
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; curl &lt;span style="color: #660033;"&gt;-O&lt;/span&gt; http:&lt;span style="color: #000000; font-weight: bold;"&gt;//&lt;/span&gt;internap.dl.sourceforge.net&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;sourceforge&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;expat&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;expat-2.0.1.tar.gz
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;tar&lt;/span&gt; xzf expat-2.0.1.tar.gz
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #7a0874; font-weight: bold;"&gt;cd&lt;/span&gt; expat-2.0.1
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; .&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;configure &lt;span style="color: #660033;"&gt;--prefix&lt;/span&gt;=&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;usr&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;&lt;span style="color: #7a0874; font-weight: bold;"&gt;local&lt;/span&gt;
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;make&lt;/span&gt;
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;sudo&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;make&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;install&lt;/span&gt;
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #7a0874; font-weight: bold;"&gt;cd&lt;/span&gt; ..&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;h3&gt;Compile Sphinx&lt;/h3&gt;
&lt;p&gt;At this point latest stable release was 0.9.8.1.&lt;/p&gt;
&lt;p&gt;&lt;div class="geshifilter"&gt;&lt;pre class="bash geshifilter-bash" style="font-family:monospace;"&gt;  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; curl &lt;span style="color: #660033;"&gt;-O&lt;/span&gt; http:&lt;span style="color: #000000; font-weight: bold;"&gt;//&lt;/span&gt;www.sphinxsearch.com&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;downloads&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;sphinx-0.9.8.1.tar.gz
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;tar&lt;/span&gt; xzf sphinx-0.9.8.1.tar.gz
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #7a0874; font-weight: bold;"&gt;cd&lt;/span&gt; sphinx-0.9.8.1
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #7a0874; font-weight: bold;"&gt;export&lt;/span&gt; &lt;span style="color: #007800;"&gt;LDFLAGS&lt;/span&gt;=&lt;span style="color: #ff0000;"&gt;&amp;quot;-L/usr/lib&amp;quot;&lt;/span&gt; 
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; .&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;configure &lt;span style="color: #660033;"&gt;--prefix&lt;/span&gt;=&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;usr&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;&lt;span style="color: #7a0874; font-weight: bold;"&gt;local&lt;/span&gt; &lt;span style="color: #660033;"&gt;--with-pgsql&lt;/span&gt; &lt;span style="color: #660033;"&gt;--without-mysql&lt;/span&gt;
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;make&lt;/span&gt;
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;sudo&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;make&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;install&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;You&amp;#8217;re all set.&lt;/p&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.viget.com/extend/installing-sphinx-on-os-x-leopard/"&gt;http://www.viget.com/extend/installing-sphinx-on-os-x-leopard/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://autonomousmachine.com/2009/1/24/building-sphinx-0-9-9rc1-with-postgresql-8-3-support-on-mac-os-10-5"&gt;http://autonomousmachine.com/2009/1/24/building-sphinx-0-9-9rc1-with-postgresql-8-3-support-on-mac-os-10-5&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
 <comments>http://www.mediumexposure.com/2009/05/01/installing-sphinx-postgres-leopard#comments</comments>
 <category domain="http://www.mediumexposure.com/tag/osx">osx</category>
 <category domain="http://www.mediumexposure.com/taxonomy/term/3">solutions</category>
 <category domain="http://www.mediumexposure.com/tag/sphinx">sphinx</category>
 <pubDate>Fri, 01 May 2009 07:12:48 +0000</pubDate>
 <dc:creator>hakunin</dc:creator>
 <guid isPermaLink="false">53 at http://www.mediumexposure.com</guid>
</item>
<item>
 <title>Shmacros</title>
 <link>http://www.mediumexposure.com/techblog/shmacros</link>
 <description>&lt;p&gt;I use &lt;a href="http://thoughtbot.com/projects/shoulda"&gt;shoulda&lt;/a&gt; constantly as it is my favorite testing framework. I figured the custom macros I write may prove useful to others, thus I released them as a plugin called &lt;a href="http://github.com/maxim/shmacros" title="Shoulda Macros"&gt;shmacros&lt;/a&gt;. As of now it includes the following methods.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://github.com/maxim/shmacros/blob/9515713ae3f9dca4359458cdf54bd6feac785bfb/shoulda_macros/shmacros.rb#L8"&gt;should_accept_nested_attributes_for&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/maxim/shmacros/blob/9515713ae3f9dca4359458cdf54bd6feac785bfb/shoulda_macros/shmacros.rb#L28"&gt;should_act_as_taggable_on&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/maxim/shmacros/blob/9515713ae3f9dca4359458cdf54bd6feac785bfb/shoulda_macros/shmacros.rb#L45"&gt;should_be&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/maxim/shmacros/blob/9515713ae3f9dca4359458cdf54bd6feac785bfb/shoulda_macros/shmacros.rb#L64"&gt;should_callback&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/maxim/shmacros/blob/9515713ae3f9dca4359458cdf54bd6feac785bfb/shoulda_macros/shmacros.rb#L87"&gt;should_delegate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/maxim/shmacros/blob/9515713ae3f9dca4359458cdf54bd6feac785bfb/shoulda_macros/shmacros.rb#L121"&gt;&lt;del&gt;should_have_attached_file&lt;/del&gt;&lt;/a&gt; (&lt;a href="http://github.com/maxim/shmacros/commit/c09d3ac9a7a1594a7f121b65cf70bf58e1b7b479"&gt;removed&lt;/a&gt; in new revision)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/maxim/shmacros/blob/9515713ae3f9dca4359458cdf54bd6feac785bfb/shoulda_macros/shmacros.rb#L142"&gt;should_validate_associated&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&amp;#8217;s best to have &lt;a href="http://mocha.rubyforge.org"&gt;Mocha&lt;/a&gt; installed as some of the macros rely on its presence.&lt;/p&gt;
&lt;p&gt;&lt;div class="geshifilter"&gt;&lt;pre class="bash geshifilter-bash" style="font-family:monospace;"&gt;  ~&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;dev&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;myapp&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; &lt;span style="color: #c20cb9; font-weight: bold;"&gt;sudo&lt;/span&gt; gem &lt;span style="color: #c20cb9; font-weight: bold;"&gt;install&lt;/span&gt; mocha
  ~&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;dev&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;myapp&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; script&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;plugin &lt;span style="color: #c20cb9; font-weight: bold;"&gt;install&lt;/span&gt; git:&lt;span style="color: #000000; font-weight: bold;"&gt;//&lt;/span&gt;github.com&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;maxim&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;shmacros.git&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
</description>
 <comments>http://www.mediumexposure.com/techblog/shmacros#comments</comments>
 <category domain="http://www.mediumexposure.com/tag/ruby">ruby</category>
 <category domain="http://www.mediumexposure.com/tag/shoulda">shoulda</category>
 <category domain="http://www.mediumexposure.com/taxonomy/term/3">solutions</category>
 <pubDate>Wed, 29 Apr 2009 08:28:04 +0000</pubDate>
 <dc:creator>hakunin</dc:creator>
 <guid isPermaLink="false">51 at http://www.mediumexposure.com</guid>
</item>
<item>
 <title>Give your :reject_if some sugar</title>
 <link>http://www.mediumexposure.com/techblog/give-your-rejectif-some-sugar</link>
 <description>&lt;p&gt;Tired of writing stuff like this?&lt;br /&gt;
&lt;div class="geshifilter"&gt;&lt;pre class="rails geshifilter-rails" style="font-family:monospace;"&gt;accepts_nested_attributes_for &lt;span style="color:#ff3333; font-weight:bold;"&gt;:photo&lt;/span&gt;,
                              &lt;span style="color:#ff3333; font-weight:bold;"&gt;:allow_destroy&lt;/span&gt; &lt;span style="color:#006600; font-weight:bold;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#0000FF; font-weight:bold;"&gt;true&lt;/span&gt;, 
                              &lt;span style="color:#ff3333; font-weight:bold;"&gt;:reject_if&lt;/span&gt; &lt;span style="color:#006600; font-weight:bold;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#CC0066; font-weight:bold;"&gt;proc&lt;/span&gt; &lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#123;&lt;/span&gt; &lt;span style="color:#006600; font-weight:bold;"&gt;|&lt;/span&gt;attrs&lt;span style="color:#006600; font-weight:bold;"&gt;|&lt;/span&gt; reject = &lt;span style="color:#006600; font-weight:bold;"&gt;%&lt;/span&gt;w&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#40;&lt;/span&gt;title picture&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#41;&lt;/span&gt;.&lt;span style="color:#9900CC;"&gt;all&lt;/span&gt;?&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#123;&lt;/span&gt;&lt;span style="color:#006600; font-weight:bold;"&gt;|&lt;/span&gt;a&lt;span style="color:#006600; font-weight:bold;"&gt;|&lt;/span&gt; attrs&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#91;&lt;/span&gt;a&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#93;&lt;/span&gt;.&lt;span style="color:#9900CC;"&gt;blank&lt;/span&gt;?&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#125;&lt;/span&gt; &lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;Throw this snippet into init.rb into a new plugin folder (I called it reject_if_missing).&lt;br /&gt;
&lt;div class="geshifilter"&gt;&lt;pre class="rails geshifilter-rails" style="font-family:monospace;"&gt;&lt;span style="color:#9966CC; font-weight:bold;"&gt;module&lt;/span&gt; RejectIfMissing
  &lt;span style="color:#9966CC; font-weight:bold;"&gt;def&lt;/span&gt; missing_attrs?&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color:#006600; font-weight:bold;"&gt;*&lt;/span&gt;required_attrs&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#41;&lt;/span&gt;
    &lt;span style="color:#CC00FF; font-weight:bold;"&gt;Proc&lt;/span&gt;.&lt;span style="color:#5A0A0A; font-weight:bold;"&gt;new&lt;/span&gt; &lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#123;&lt;/span&gt; &lt;span style="color:#006600; font-weight:bold;"&gt;|&lt;/span&gt;attrs&lt;span style="color:#006600; font-weight:bold;"&gt;|&lt;/span&gt; required_attrs.&lt;span style="color:#9900CC;"&gt;all&lt;/span&gt;?&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#123;&lt;/span&gt;&lt;span style="color:#006600; font-weight:bold;"&gt;|&lt;/span&gt;a&lt;span style="color:#006600; font-weight:bold;"&gt;|&lt;/span&gt; attrs&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#91;&lt;/span&gt;a&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#93;&lt;/span&gt;.&lt;span style="color:#9900CC;"&gt;blank&lt;/span&gt;?&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#125;&lt;/span&gt; &lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#125;&lt;/span&gt;
  &lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;
&lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;
&amp;nbsp;
&lt;span style="color:#6666ff; font-weight:bold;"&gt;ActiveRecord::Base&lt;/span&gt;.&lt;span style="color:#9900CC;"&gt;send&lt;/span&gt;&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color:#ff3333; font-weight:bold;"&gt;:extend&lt;/span&gt;, RejectIfMissing&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#41;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;Now you can do this instead.&lt;br /&gt;
&lt;div class="geshifilter"&gt;&lt;pre class="rails geshifilter-rails" style="font-family:monospace;"&gt;accepts_nested_attributes_for &lt;span style="color:#ff3333; font-weight:bold;"&gt;:photo&lt;/span&gt;,
                              &lt;span style="color:#ff3333; font-weight:bold;"&gt;:allow_destroy&lt;/span&gt;  &lt;span style="color:#006600; font-weight:bold;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#0000FF; font-weight:bold;"&gt;true&lt;/span&gt;, 
                              &lt;span style="color:#ff3333; font-weight:bold;"&gt;:reject_if&lt;/span&gt;      &lt;span style="color:#006600; font-weight:bold;"&gt;=&amp;gt;&lt;/span&gt; missing_attrs?&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color:#996600;"&gt;'title'&lt;/span&gt;, &lt;span style="color:#996600;"&gt;'picture'&lt;/span&gt;&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#41;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
</description>
 <comments>http://www.mediumexposure.com/techblog/give-your-rejectif-some-sugar#comments</comments>
 <category domain="http://www.mediumexposure.com/tag/rails">rails</category>
 <category domain="http://www.mediumexposure.com/tag/ruby">ruby</category>
 <category domain="http://www.mediumexposure.com/taxonomy/term/3">solutions</category>
 <pubDate>Thu, 09 Apr 2009 05:15:50 +0000</pubDate>
 <dc:creator>hakunin</dc:creator>
 <guid isPermaLink="false">50 at http://www.mediumexposure.com</guid>
</item>
<item>
 <title>Teach models to stalk each other with Stalker</title>
 <link>http://www.mediumexposure.com/techblog/teach-models-stalk-each-other-stalker</link>
 <description>&lt;p&gt;I often come by a case where models need to talk to each other. ModelA sometimes needs to ask ModelB to do something after_save. Usually you'd just use a callback.&lt;/p&gt;
&lt;p&gt;&lt;div class="geshifilter"&gt;&lt;pre class="rails geshifilter-rails" style="font-family:monospace;"&gt;&lt;span style="color:#9966CC; font-weight:bold;"&gt;class&lt;/span&gt; ModelA &lt;span style="color:#006600; font-weight:bold;"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#6666ff; font-weight:bold;"&gt;ActiveRecord::Base&lt;/span&gt;
  &lt;span style="color:#5A0A0A; font-weight:bold;"&gt;after_save&lt;/span&gt; &lt;span style="color:#ff3333; font-weight:bold;"&gt;:update_model_b&lt;/span&gt;
&amp;nbsp;
  &lt;span style="color:#9966CC; font-weight:bold;"&gt;def&lt;/span&gt; update_model_b
    ModelB.&lt;span style="color:#5A0A0A; font-weight:bold;"&gt;update&lt;/span&gt;!
  &lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;
&lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;This is obvious coupling. ModelA needs to know what to call in ModelB. This isn't ModelA's concern. Ideally, ModelA should simply notify ModelB that it saved.&lt;/p&gt;
&lt;p&gt;&lt;div class="geshifilter"&gt;&lt;pre class="rails geshifilter-rails" style="font-family:monospace;"&gt;&lt;span style="color:#9966CC; font-weight:bold;"&gt;class&lt;/span&gt; ModelA &lt;span style="color:#006600; font-weight:bold;"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#6666ff; font-weight:bold;"&gt;ActiveRecord::Base&lt;/span&gt;
  &lt;span style="color:#5A0A0A; font-weight:bold;"&gt;after_save&lt;/span&gt; &lt;span style="color:#ff3333; font-weight:bold;"&gt;:notify_model_b&lt;/span&gt;
&amp;nbsp;
  &lt;span style="color:#9966CC; font-weight:bold;"&gt;def&lt;/span&gt; notify_model_b
    ModelB.&lt;span style="color:#9900CC;"&gt;model_a_saved&lt;/span&gt;!&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color:#0000FF; font-weight:bold;"&gt;self&lt;/span&gt;&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#41;&lt;/span&gt;
  &lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;
&lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;
&amp;nbsp;
&lt;span style="color:#9966CC; font-weight:bold;"&gt;class&lt;/span&gt; ModelB &lt;span style="color:#006600; font-weight:bold;"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#6666ff; font-weight:bold;"&gt;ActiveRecord::Base&lt;/span&gt;
  &lt;span style="color:#9966CC; font-weight:bold;"&gt;class&lt;/span&gt; &lt;span style="color:#006600; font-weight:bold;"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#0000FF; font-weight:bold;"&gt;self&lt;/span&gt;
    &lt;span style="color:#9966CC; font-weight:bold;"&gt;def&lt;/span&gt; model_a_saved!&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#40;&lt;/span&gt;modela&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#41;&lt;/span&gt;
      &lt;span style="color:#0000FF; font-weight:bold;"&gt;self&lt;/span&gt;.&lt;span style="color:#9900CC;"&gt;do_something_about&lt;/span&gt;&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#40;&lt;/span&gt;modela&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#41;&lt;/span&gt;
    &lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;
  &lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;
&lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;This is a better way to do it since now ModelB is responsible for performing whatever it requires. Except, this is too verbose. Imagine you had to notify ModelC as well, it'd get ridiculous.&lt;/p&gt;
&lt;p&gt;&lt;div class="geshifilter"&gt;&lt;pre class="rails geshifilter-rails" style="font-family:monospace;"&gt;&lt;span style="color:#9966CC; font-weight:bold;"&gt;class&lt;/span&gt; ModelA &lt;span style="color:#006600; font-weight:bold;"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#6666ff; font-weight:bold;"&gt;ActiveRecord::Base&lt;/span&gt;
  &lt;span style="color:#5A0A0A; font-weight:bold;"&gt;after_save&lt;/span&gt; &lt;span style="color:#ff3333; font-weight:bold;"&gt;:notify_model_b&lt;/span&gt;
  &lt;span style="color:#5A0A0A; font-weight:bold;"&gt;after_save&lt;/span&gt; &lt;span style="color:#ff3333; font-weight:bold;"&gt;:notify_model_c&lt;/span&gt;
&amp;nbsp;
  &lt;span style="color:#9966CC; font-weight:bold;"&gt;def&lt;/span&gt; notify_model_b
    ModelB.&lt;span style="color:#9900CC;"&gt;model_a_saved&lt;/span&gt;!&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color:#0000FF; font-weight:bold;"&gt;self&lt;/span&gt;&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#41;&lt;/span&gt;
  &lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;
&amp;nbsp;
  &lt;span style="color:#9966CC; font-weight:bold;"&gt;def&lt;/span&gt; notify_model_c
    ModelC.&lt;span style="color:#9900CC;"&gt;model_a_saved&lt;/span&gt;!&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color:#0000FF; font-weight:bold;"&gt;self&lt;/span&gt;&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#41;&lt;/span&gt;
  &lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;
&lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;This is too much work for each such notification. Instead, you may notice that this can be solved by observer pattern. Rails already provides Observers, but they are completely separate classes, and don't save us any work in this case, since they can't be part of an existing model. This is where &lt;a href="http://github.com/maxim/stalker"&gt;Stalker&lt;/a&gt; comes in. Stalker allows you to have one model simply observe the other.&lt;/p&gt;
&lt;p&gt;&lt;div class="geshifilter"&gt;&lt;pre class="rails geshifilter-rails" style="font-family:monospace;"&gt;&lt;span style="color:#9966CC; font-weight:bold;"&gt;class&lt;/span&gt; ModelA &lt;span style="color:#006600; font-weight:bold;"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#6666ff; font-weight:bold;"&gt;ActiveRecord::Base&lt;/span&gt;
  &lt;span style="color:#008000; font-style:italic;"&gt;# nothing to do here&lt;/span&gt;
&lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;
&amp;nbsp;
&lt;span style="color:#9966CC; font-weight:bold;"&gt;class&lt;/span&gt; ModelB &lt;span style="color:#006600; font-weight:bold;"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#6666ff; font-weight:bold;"&gt;ActiveRecord::Base&lt;/span&gt;
  stalk ModelA
&amp;nbsp;
  &lt;span style="color:#9966CC; font-weight:bold;"&gt;class&lt;/span&gt; &lt;span style="color:#006600; font-weight:bold;"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#0000FF; font-weight:bold;"&gt;self&lt;/span&gt;
    &lt;span style="color:#9966CC; font-weight:bold;"&gt;def&lt;/span&gt; after_model_a_save&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#40;&lt;/span&gt;modela&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#41;&lt;/span&gt;
      &lt;span style="color:#0000FF; font-weight:bold;"&gt;self&lt;/span&gt;.&lt;span style="color:#9900CC;"&gt;do_something_about&lt;/span&gt;&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#40;&lt;/span&gt;modela&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#41;&lt;/span&gt;
    &lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;
  &lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;
&lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;The above solves separation of concerns issue.  ModelB is now fully responsible for watching ModelA, and can easily react to events by using callbacks such as after_[model_name]_save or before_[model_name]_create. In the parameter it will always automatically get the object which triggers the callback.&lt;/p&gt;
&lt;p&gt;Just like with Rails observers, you have to ensure that the stalker models load before the stalked ones. You can use the same active_record config for that.&lt;/p&gt;
&lt;p&gt;In your environment.rb have this line uncommented and make sure it includes the name of the stalker.&lt;br /&gt;
&lt;div class="geshifilter"&gt;&lt;pre class="rails geshifilter-rails" style="font-family:monospace;"&gt;config.&lt;span style="color:#9900CC;"&gt;active_record&lt;/span&gt;.&lt;span style="color:#9900CC;"&gt;observers&lt;/span&gt; = &lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#91;&lt;/span&gt;&lt;span style="color:#ff3333; font-weight:bold;"&gt;:model_b&lt;/span&gt;&lt;span style="color:#006600; font-weight:bold;"&gt;&amp;#93;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;h3&gt;Under the hood.&lt;/h3&gt;
&lt;p&gt;Stalker is a micro-plugin with only 35 lines of code. It takes away most of the boilerplate work from setting up model-model observers. The stalked model receives a "notify" method for each possible callback, and this method calls the after_[my_name]_save on the stalker model, just like in the above example. The difference is that you don't have to maintain ModelA at all, stalker will do it automatically. In addition, since you get the same code as in the above example, you'll also be able to interrupt the callback chain by returning false from the stalker model. &lt;/p&gt;
&lt;h3&gt;Install&lt;/h3&gt;
&lt;p&gt;&lt;div class="geshifilter"&gt;&lt;pre class="bash geshifilter-bash" style="font-family:monospace;"&gt;&lt;span style="color: #000000; font-weight: bold;"&gt;%&lt;/span&gt; script&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;plugin &lt;span style="color: #c20cb9; font-weight: bold;"&gt;install&lt;/span&gt; git:&lt;span style="color: #000000; font-weight: bold;"&gt;//&lt;/span&gt;github.com&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;maxim&lt;span style="color: #000000; font-weight: bold;"&gt;/&lt;/span&gt;stalker.git&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
</description>
 <comments>http://www.mediumexposure.com/techblog/teach-models-stalk-each-other-stalker#comments</comments>
 <category domain="http://www.mediumexposure.com/tag/rails">rails</category>
 <category domain="http://www.mediumexposure.com/tag/ruby">ruby</category>
 <category domain="http://www.mediumexposure.com/taxonomy/term/3">solutions</category>
 <pubDate>Fri, 03 Apr 2009 14:33:17 +0000</pubDate>
 <dc:creator>hakunin</dc:creator>
 <guid isPermaLink="false">49 at http://www.mediumexposure.com</guid>
</item>
<item>
 <title>Integer_fu: store and manipulate many boolean values in a single integer column, with convenience</title>
 <link>http://www.mediumexposure.com/techblog/integerfu-store-and-manipulate-many-boolean-values-single-integer-column-convenience</link>
 <description>&lt;p&gt;Link: &lt;a href="http://github.com/maxim/integer_fu"&gt;http://github.com/maxim/integer_fu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I just finished the first version of a convenient plugin I've been writing for my own project. Integer_fu lets you map an integer attribute in your model to many possible boolean values, and then access them easily using hash-like syntax and some magical methods. Check it out down at &lt;a href="http://github.com/maxim/integer_fu"&gt;Github&lt;/a&gt;. I tried to explain usage thoroughly in the readme (featuring &lt;a href="http://fluffyhorse.net"&gt;Fluffy Horse&lt;/a&gt;).&lt;/p&gt;
</description>
 <comments>http://www.mediumexposure.com/techblog/integerfu-store-and-manipulate-many-boolean-values-single-integer-column-convenience#comments</comments>
 <category domain="http://www.mediumexposure.com/tag/rails">rails</category>
 <category domain="http://www.mediumexposure.com/tag/ruby">ruby</category>
 <category domain="http://www.mediumexposure.com/taxonomy/term/3">solutions</category>
 <pubDate>Tue, 24 Mar 2009 05:05:55 +0000</pubDate>
 <dc:creator>hakunin</dc:creator>
 <guid isPermaLink="false">48 at http://www.mediumexposure.com</guid>
</item>
<item>
 <title>W3C macros is now a Rails plugin on GitHub</title>
 <link>http://www.mediumexposure.com/techblog/w3c-macros-now-rails-plugin-github</link>
 <description>&lt;p&gt;Feel free to grab it at &lt;a href="http://github.com/maxim/should_pass_w3c_validation"&gt;github.com/maxim/should_pass_w3c_validation&lt;/a&gt;.&lt;/p&gt;
</description>
 <comments>http://www.mediumexposure.com/techblog/w3c-macros-now-rails-plugin-github#comments</comments>
 <category domain="http://www.mediumexposure.com/tag/rails">rails</category>
 <category domain="http://www.mediumexposure.com/tag/ruby">ruby</category>
 <category domain="http://www.mediumexposure.com/taxonomy/term/3">solutions</category>
 <pubDate>Mon, 16 Mar 2009 20:06:22 +0000</pubDate>
 <dc:creator>hakunin</dc:creator>
 <guid isPermaLink="false">47 at http://www.mediumexposure.com</guid>
</item>
<item>
 <title>W3C validation in functional tests with shoulda macros</title>
 <link>http://www.mediumexposure.com/techblog/w3c-validation-functional-tests-shoulda-macros</link>
 <description>&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Added code explanation.&lt;/p&gt;
&lt;p&gt;I found that the best place to put W3C validation tests would be among your controller tests. In your functionals you already walk across all your pages and deal with authentication, so why not put some more load on that donkey?&lt;/p&gt;
&lt;p&gt;First thing - install w3c_validators gem.&lt;br /&gt;
&lt;span class="geshifilter"&gt;&lt;code class="geshifilter-text"&gt;sudo gem install w3c_validators&lt;/code&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now, throw the following gist into&lt;br /&gt;
&lt;span class="geshifilter"&gt;&lt;code class="geshifilter-text"&gt;test/shoulda_macros/should_pass_w3c_validation_macros.rb&lt;/code&gt;&lt;/span&gt;&lt;/p&gt;
&lt;script src="http://gist.github.com/76394.js"&gt;&lt;/script&gt;&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;Now you have a nice w3c validator macros which can be used simply, as part of your controller testing with shoulda.&lt;/p&gt;
&lt;p&gt;&lt;div class="geshifilter"&gt;&lt;pre class="rails geshifilter-rails" style="font-family:monospace;"&gt;context &lt;span style="color:#996600;"&gt;&amp;quot;on /index&amp;quot;&lt;/span&gt; &lt;span style="color:#9966CC; font-weight:bold;"&gt;do&lt;/span&gt;
  setup &lt;span style="color:#9966CC; font-weight:bold;"&gt;do&lt;/span&gt;
    get &lt;span style="color:#ff3333; font-weight:bold;"&gt;:index&lt;/span&gt;
  &lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;
&amp;nbsp;
  should_respond_with &lt;span style="color:#ff3333; font-weight:bold;"&gt;:success&lt;/span&gt;
  should_assign_to    &lt;span style="color:#ff3333; font-weight:bold;"&gt;:assets&lt;/span&gt;
  should_render_with_layout
  should_pass_w3c_validation &lt;span style="color:#008000; font-style:italic;"&gt;# &amp;lt;= Hehe, there you are!&lt;/span&gt;
&lt;span style="color:#9966CC; font-weight:bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;h3&gt;What's happening in the code?&lt;/h3&gt;
&lt;p&gt;The main part of our macros code is validator itself - we instantiate it by calling &lt;span class="geshifilter"&gt;&lt;code class="geshifilter-text"&gt;MarkupValidator.new&lt;/code&gt;&lt;/span&gt;. It does all the validation work, but it takes a filename on input. Since we're actually in controller tests - we usually pull pages using methods like &lt;span class="geshifilter"&gt;&lt;code class="geshifilter-text"&gt;get &amp;quot;/path/to/page&amp;quot;&lt;/code&gt;&lt;/span&gt;. I need to somehow get the full html of that page to give it to the validator, and I need it in a file, since validator only accepts filenames. One way is to create a &lt;span class="geshifilter"&gt;&lt;code class="geshifilter-text"&gt;Tempfile&lt;/code&gt;&lt;/span&gt;, and throw the &lt;span class="geshifilter"&gt;&lt;code class="geshifilter-text"&gt;@response.body&lt;/code&gt;&lt;/span&gt; in there. @response object exists in all controller tests, and contains response data which was returned upon calling &lt;span class="geshifilter"&gt;&lt;code class="geshifilter-text"&gt;get &amp;quot;/path/to/page&amp;quot;&lt;/code&gt;&lt;/span&gt;. This is exactly what we need. &lt;span class="geshifilter"&gt;&lt;code class="geshifilter-text"&gt;@response.body&lt;/code&gt;&lt;/span&gt; holds all of html/js/css which server sends to your browser when you access a certain page in your controller test. All we need to do now is dump the response output into a tempfile so we can feed it to validator.&lt;/p&gt;
&lt;p&gt;Looking into the code you can see that I write @response.body to tempfile and pass the path to that tempfile into the validator. What happens next? We get a nicely formatted array of warnings and errors, courtesy of the validator itself.  It sends our file to W3C public service and returns with a bunch of errors/warnings. These messages are a perfect candidate for being displayed on test failure (the so-called 'fail_text') - since the mere fact that we got any of those messages means we failed. Thus by checking the sizes of both the warnings and the errors array I can tell if my test passed or not - they both have to be zero.  If the test fails I display all of them. So what is that substitution that's happening? Why do I have to go over each warning/error and remove some piece of text? That's because message contains path to the file - and in our case the path is always to some arbitrary tempfile. Nobody needs that - it only pollutes the test output. So the latter part of the code removes those paths from messages. That's all.&lt;/p&gt;
&lt;h3&gt;The crappy part&lt;/h3&gt;
&lt;p&gt;Obviously, your tests will get very slow, as now they rely on some public service provider. You should definitely implement a switch for turning this type of tests on/off in different circumstances (like when you're on a Boeing 737 making your way from Antigua to Newark).&lt;/p&gt;
</description>
 <comments>http://www.mediumexposure.com/techblog/w3c-validation-functional-tests-shoulda-macros#comments</comments>
 <category domain="http://www.mediumexposure.com/tag/rails">rails</category>
 <category domain="http://www.mediumexposure.com/tag/ruby">ruby</category>
 <category domain="http://www.mediumexposure.com/taxonomy/term/3">solutions</category>
 <pubDate>Mon, 09 Mar 2009 18:03:28 +0000</pubDate>
 <dc:creator>hakunin</dc:creator>
 <guid isPermaLink="false">46 at http://www.mediumexposure.com</guid>
</item>
</channel>
</rss>
