Nick Sieger: RubyConf Day 1: Morning Sessions tag:blog.nicksieger.com,2005:Typo Typo 2007-11-02T15:37:59+00:00 Nick Sieger urn:uuid:ea073446-81d9-4a20-be61-db4c2bf2c771 2007-11-02T15:35:00+00:00 2007-11-02T15:37:59+00:00 RubyConf Day 1: Morning Sessions <h2>Marcel Molina: What Makes Code Beautiful?</h2> <p>What is beauty? Marcel explores this topic, starting with posing the question to the audience. &#8220;My wife!&#8221; Marcel: Why is she beautiful? &#8220;Longer answer than you want!&#8221;</p> <p>Marcel comes from a literature/linguistic background, and is interested in how meaning is conveyed, but even beyond the basic words themselves, but the context and expressivity as well.</p> <p><em>Note: Marcel has <a href="http://talklikeaduck.denhaven2.com/articles/2007/08/20/aspects-of-beauty-proportion-integrity-clarity-and-monkey-patching">given this talk before</a>.</em></p> <h3>History of beauty</h3> <p>Pythagoras: was out in the street, heard the blacksmith&#8217;s clanging hammer, and was drawn to the noise. He recognized, through closer inspection, that the different sounds that came from the different hammers had relationships, and eventually saw similar relationships in other parts of nature, architecture, and so on.</p> <p>Thomas Aquinas: Three things that define beauty: 1. Proportion. The economy of size and ratio of parts. The smallest thing that works. 2. Integrity. Well-suited for the purpose. 3. Clarity. Clear and simple.</p> <p>Each of the qualities are necessary, but none are sufficent. For example proportion (economy) will often clash with clarity. This is especially true in code.</p> <h3>Applied to software</h3> <p>Case study: coercion. Converting XML strings into rich Ruby equivalents. Marcel&#8217;s initial solution was a <code>CoercibleString &lt; String</code>, which used a generator to iteratively try to coerce XML attributes to a number of types, and return the results. ~20 lines of code to convert to 4 types. His second version was a simple class method on String with a case statement.</p> <p>Kent Beck, in his book <em>Smalltalk: Best Practice Patterns</em>, writes a book about writing good software, but in Marcel&#8217;s opinion, arrives at a definition of beauty by describing aspects of code that reflect proportion, integrity, and clarity.</p> <p>Niels Bohr: &#8220;An expert is a person who has made all the mistakes that can be made in a very narrow field.&#8221; Marcel calls his CoercibleString a mistake, but one that helped him learn more about coding.</p> <p>Luckily for us, Ruby is optimized for beauty.</p> <h2>Jim Weirich: Advanced Ruby Class Design</h2> <p>Emphasizing &#8220;Ruby&#8221; more so than &#8220;Advanced&#8221;, through three examples that illustrate techniques not commonly found in statically-typed OO languages (Java/C++/Eiffel).</p> <h3><code>Rake::FileList</code></h3> <div class="typocode"><pre><code class="typocode_ruby "><span class="constant">FileList</span><span class="punct">['</span><span class="string">lib/**/*.rb</span><span class="punct">']</span></code></pre></div> <p>FileList sports globbing, a specialized <code>to_s</code>, and lazy evaluation. First version: <code>class FileList &lt; Array; end</code>. Good idea, right? Well, with lazy evaluation, resolution of filenames happens only when the list is accessed, not created, so a lot of methods need to be overloaded:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">def </span><span class="method">[]</span><span class="punct">(</span><span class="ident">index</span><span class="punct">)</span> <span class="ident">resolve</span> <span class="keyword">unless</span> <span class="attribute">@resolved</span> <span class="keyword">super</span> <span class="keyword">end</span></code></pre></div> <p>The problem becomes that FileList too closely mimics Array, and cannot distinguish itself in the case that matters. So it was changed to delegate to array rather than inherit.</p> <p>Moral: when you want to mimic built-in classes, it might be better to implement <code>#to_ary</code> or <code>#to_str</code> rather than inherit.</p> <h3>Builder::XmlMarkup</h3> <p>What&#8217;s the problem here?</p> <div class="typocode"><pre><code class="typocode_ruby "> <span class="ident">b</span> <span class="punct">=</span> <span class="constant">Builder</span><span class="punct">::</span><span class="constant">XmlMarkup</span><span class="punct">.</span><span class="ident">new</span> <span class="ident">b</span><span class="punct">.</span><span class="ident">student</span> <span class="keyword">do</span> <span class="ident">b</span><span class="punct">.</span><span class="ident">name</span> <span class="punct">&quot;</span><span class="string">Jim</span><span class="punct">&quot;</span> <span class="ident">b</span><span class="punct">.</span><span class="ident">phone_number</span> <span class="punct">&quot;</span><span class="string">555-1234</span><span class="punct">&quot;</span> <span class="ident">b</span><span class="punct">.</span><span class="keyword">class </span><span class="class">&quot;Intro</span> <span class="ident">to</span> <span class="constant">Ruby</span><span class="punct">&quot;</span><span class="string"> end end</span></code></pre></div> <p><code>class</code> is already a method on Object. This begat <a href="http://builder.rubyforge.org/classes/Builder/BlankSlate.html">BlankSlate</a>, which removes unnecessary methods from <code>Object</code>. Several techniques were applied to eventually arrive at the latest version:</p> <ul> <li>Use <code>undef_method</code> to hide methods that we don&#8217;t want. Except, leave methods beginning with double-underscore alone (<code>__id__</code> and <code>__send__</code>).</li> <li>Catch new methods added via a <code>method_added</code> hook on Kernel, and an <code>append_features</code> hook on Object, to deal with methods defined and modules included after BlankSlate was created</li> </ul> <h3>TableNode</h3> <p>Problem: magic conversion of Rails conditions to SQL. An example: <code>User.find(:all).select{|u| u.name == "jim"}</code>. We don&#8217;t really want to load the entire database to do this, but we don&#8217;t like writing SQL either.</p> <p>Solution: Record the actions in the select block by yielding a special TableNode object that captures the method calls and translates to SQL on the fly. Now we can write <code>User.select {|u| u.name == "Jim"}</code> and have it still execute SQL</p> <ul> <li>Capture methods called and wrap in a MethodNode to convert to SQL column references</li> <li>Capture operators and wrap in a BinaryOpNode to handle <code>==</code>, <code>&lt;</code>, etc.</li> </ul> <p>Clever! Will this work? Here are some issues:</p> <ul> <li>Small issue &#8211; ordering: <code>User.select {|u| "Jim" == u.name}</code> will not work without messing with <code>String#==</code>.</li> <li>Bigger issues: <code>&amp;&amp;</code> and <code>||</code> are not override-able in Ruby. What&#8217;s worse, <code>!</code> has pre-defined semantics (in the parser) and cannot be captured.</li> </ul> <h3>Lessons learned</h3> <ul> <li>Don&#8217;t be afraid to think beyond prior experiences to come up with new ways of solving problems in code.</li> </ul>